create-terse 0.0.1

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/LICENSE.md ADDED
@@ -0,0 +1,83 @@
1
+ # License
2
+
3
+ Portions of this software are licensed as follows:
4
+
5
+ - Content of branches other than the main branch (i.e. "main") are not licensed.
6
+ - All third party components incorporated into the Terse Software are licensed under the original license
7
+ provided by the owner of the applicable component.
8
+ - Content outside of the above mentioned files or restrictions is available under the "Sustainable Use
9
+ License" as defined below.
10
+
11
+ ## Sustainable Use License
12
+
13
+ Version 1.0
14
+
15
+ ### Acceptance
16
+
17
+ By using the software, you agree to all of the terms and conditions below.
18
+
19
+ ### Copyright License
20
+
21
+ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license
22
+ to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject
23
+ to the limitations below.
24
+
25
+ ### Limitations
26
+
27
+ You may use or modify the software only for your own internal business purposes or for non-commercial or
28
+ personal use. You may distribute the software or provide it to others only if you do so free of charge for
29
+ non-commercial purposes. You may not alter, remove, or obscure any licensing, copyright, or other notices of
30
+ the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
31
+
32
+ ### Patents
33
+
34
+ The licensor grants you a license, under any patent claims the licensor can license, or becomes able to
35
+ license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case
36
+ subject to the limitations and conditions in this license. This license does not cover any patent claims that
37
+ you cause to be infringed by modifications or additions to the software. If you or your company make any
38
+ written claim that the software infringes or contributes to infringement of any patent, your patent license
39
+ for the software granted under these terms ends immediately. If your company makes such a claim, your patent
40
+ license ends immediately for work on behalf of your company.
41
+
42
+ ### Notices
43
+
44
+ You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these
45
+ terms. If you modify the software, you must include in any modified copies of the software a prominent notice
46
+ stating that you have modified the software.
47
+
48
+ ### No Other Rights
49
+
50
+ These terms do not imply any licenses other than those expressly granted in these terms.
51
+
52
+ ### Termination
53
+
54
+ If you use the software in violation of these terms, such use is not licensed, and your license will
55
+ automatically terminate. If the licensor provides you with a notice of your violation, and you cease all
56
+ violation of this license no later than 30 days after you receive that notice, your license will be reinstated
57
+ retroactively. However, if you violate these terms after such reinstatement, any additional violation of these
58
+ terms will cause your license to terminate automatically and permanently.
59
+
60
+ ### No Liability
61
+
62
+ As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will
63
+ not be liable to you for any damages arising out of these terms or the use or nature of the software, under
64
+ any kind of legal claim.
65
+
66
+ ### Definitions
67
+
68
+ The “licensor” is the entity offering these terms.
69
+
70
+ The “software” is the software the licensor makes available under these terms, including any portion of it.
71
+
72
+ “You” refers to the individual or entity agreeing to these terms.
73
+
74
+ “Your company” is any legal entity, sole proprietorship, or other kind of organization that you work for, plus
75
+ all organizations that have control over, are under the control of, or are under common control with that
76
+ organization. Control means ownership of substantially all the assets of an entity, or the power to direct its
77
+ management and policies by vote, contract, or otherwise. Control can be direct or indirect.
78
+
79
+ “Your license” is the license granted to you for the software under these terms.
80
+
81
+ “Use” means anything you do with the software requiring your license.
82
+
83
+ “Trademark” means trademarks, service marks, and similar rights.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # create-terse
2
+
3
+ Scaffold and launch a self-hosted [Terse](https://useterse.ai) instance with one command.
4
+
5
+ Terse is a code-first AI workflow platform. You write workflows in TypeScript, generate a typed SDK from your connected integrations, and deploy them. Full docs at [docs.useterse.ai](https://docs.useterse.ai).
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ npm create terse
11
+ ```
12
+
13
+ or
14
+
15
+ ```bash
16
+ npx create-terse
17
+ ```
18
+
19
+ This walks you through a short setup, writes a `docker-compose.yml` and `.env` into a target directory, pulls the Terse image, starts the services (Postgres, backend, frontend), and installs the `terse` CLI pointed at your new instance.
20
+
21
+ ## Options
22
+
23
+ ```bash
24
+ npx create-terse [options]
25
+ ```
26
+
27
+ | Option | Description |
28
+ |---|---|
29
+ | `-y`, `--non-interactive` | Skip prompts and use defaults (auto-enabled when stdin/stdout is not a TTY). |
30
+ | `--target <dir>` | Target directory (default: `./terse`). |
31
+ | `--frontend-url <url>` | Frontend URL as seen from your browser (default: `http://localhost:5173`). |
32
+ | `--backend-url <url>` | Backend URL as seen from your browser (default: `http://localhost:3001`). |
33
+ | `--no-open` | Don't open the frontend in your browser when setup completes. |
34
+ | `-h`, `--help` | Show help. |
35
+
36
+ ### Examples
37
+
38
+ ```bash
39
+ npm create terse
40
+ npx create-terse -y
41
+ npx create-terse -y --target ./my-terse --backend-url http://localhost:3001
42
+ ```
43
+
44
+ ## Requirements
45
+
46
+ - Node.js >= 18
47
+ - Docker with Compose v2 (`docker compose`)
48
+
49
+ ## After setup
50
+
51
+ The generated `README.md` in your target directory covers daily operations (logs, upgrades, backups, troubleshooting). See the [self-hosting guide](https://docs.useterse.ai/self-hosting) for the full walkthrough.
52
+
53
+ ## License
54
+
55
+ See [LICENSE.md](./LICENSE.md).
package/dist/index.js ADDED
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ import { cancel, intro, isCancel, log, outro, spinner, text } from "@clack/prompts";
3
+ import chalk from "chalk";
4
+ import { execSync, spawn } from "node:child_process";
5
+ import { exec } from "node:child_process";
6
+ import { randomBytes } from "node:crypto";
7
+ import fs from "node:fs/promises";
8
+ import net from "node:net";
9
+ import path from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { promisify } from "node:util";
12
+ const execAsync = promisify(exec);
13
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
14
+ const TEMPLATES_DIR = path.resolve(SCRIPT_DIR, "../templates");
15
+ const DEFAULTS = {
16
+ targetDir: "./terse",
17
+ frontendUrl: "http://localhost:5173",
18
+ backendUrl: "http://localhost:3001"
19
+ };
20
+ async function main() {
21
+ const args = parseArgs(process.argv.slice(2));
22
+ if (args.help) {
23
+ printHelp();
24
+ return;
25
+ }
26
+ const nonInteractive = args.nonInteractive || !process.stdin.isTTY || !process.stdout.isTTY;
27
+ intro(chalk.bold(chalk.magenta(" Terse self-host setup ")));
28
+ if (nonInteractive)
29
+ log.info("Running non-interactively. Pass --target / --frontend-url / --backend-url to override defaults.");
30
+ const targetDir = await promptOr(nonInteractive, args.targetDir, "Where should we set up Terse?", DEFAULTS.targetDir);
31
+ const resolved = path.resolve(targetDir);
32
+ await fs.mkdir(resolved, { recursive: true });
33
+ await fs.copyFile(path.join(TEMPLATES_DIR, "docker-compose.yml"), path.join(resolved, "docker-compose.yml"));
34
+ await fs.copyFile(path.join(TEMPLATES_DIR, "README.md"), path.join(resolved, "README.md"));
35
+ const frontendUrl = await promptOr(nonInteractive, args.frontendUrl, "Frontend URL (as seen from your browser)", DEFAULTS.frontendUrl);
36
+ const backendUrl = await promptOr(nonInteractive, args.backendUrl, "Backend URL (as seen from your browser)", DEFAULTS.backendUrl);
37
+ const postgresPort = await findFreePort(5432);
38
+ if (postgresPort !== 5432) {
39
+ log.warn(`Port 5432 is taken on this machine, exposing the Terse Postgres container on ${postgresPort} instead.`);
40
+ }
41
+ const frontendPort = hostPortFromUrl(frontendUrl);
42
+ const backendPort = hostPortFromUrl(backendUrl);
43
+ await writeEnv(resolved, { frontendUrl, backendUrl, frontendPort, backendPort, postgresPort });
44
+ log.success(`Wrote .env (edit ${path.join(targetDir, ".env")} to tweak ports, URLs, or secrets)`);
45
+ await runStepStreaming("Pulling Terse image (first run downloads ~500MB)", "docker compose pull", resolved);
46
+ await runStep("Starting Terse services", "docker compose up -d", resolved);
47
+ await waitForBackend(backendUrl);
48
+ await installTerseCli();
49
+ await pointTerseCliAtLocal({ frontendUrl, backendUrl });
50
+ printNextSteps({ frontendUrl });
51
+ outro(chalk.green(`Setup complete. Terse is running at ${chalk.bold(frontendUrl)}.`));
52
+ if (!nonInteractive && !args.noOpen) {
53
+ await openInBrowser(frontendUrl);
54
+ }
55
+ }
56
+ async function openInBrowser(url) {
57
+ const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
58
+ try {
59
+ await execAsync(`${opener} ${shellEscape(url)}`);
60
+ }
61
+ catch {
62
+ // browser open is a nice-to-have; if it fails the user already has the URL in the outro
63
+ }
64
+ }
65
+ function printNextSteps(args) {
66
+ log.message([
67
+ chalk.bold("Next steps:"),
68
+ ` 1. Open ${chalk.cyan(args.frontendUrl)} ${chalk.dim("to bootstrap your single admin user.")}`,
69
+ ` 2. ${chalk.cyan("terse init my-job")} ${chalk.dim("# scaffold your first job (CLI is installed and pointed at your local backend).")}`,
70
+ "",
71
+ chalk.dim("See `README.md` in this folder for daily ops (logs, upgrades, backups, troubleshooting)."),
72
+ chalk.dim("Run `terse target` any time to see which backend the CLI is pointing at.")
73
+ ].join("\n"));
74
+ }
75
+ async function installTerseCli() {
76
+ if (commandExists("terse")) {
77
+ log.info("terse CLI already installed, skipping global install");
78
+ return;
79
+ }
80
+ await runStep("Installing terse CLI globally (npm i -g terse-cli)", "npm install -g terse-cli", process.cwd());
81
+ }
82
+ async function pointTerseCliAtLocal(urls) {
83
+ if (!commandExists("terse")) {
84
+ log.warn("terse CLI not on PATH, skipping `terse target use`. Run it manually once installed.");
85
+ return;
86
+ }
87
+ const cmd = `terse target use --backend-url ${shellEscape(urls.backendUrl)} --frontend-url ${shellEscape(urls.frontendUrl)} --yes`;
88
+ await runStep(`Pointing terse CLI at ${urls.backendUrl}`, cmd, process.cwd());
89
+ }
90
+ function commandExists(name) {
91
+ try {
92
+ execSync(`command -v ${name}`, { stdio: "pipe" });
93
+ return true;
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
99
+ async function writeEnv(targetDir, config) {
100
+ const socketUrl = toWebSocketUrl(config.backendUrl);
101
+ const lines = [
102
+ "# Terse self-host config. Edit values then `docker compose up -d` to apply.",
103
+ "",
104
+ "# ── URLs (what your browser sees) ─────────────────────────────",
105
+ `FRONTEND_URL=${config.frontendUrl}`,
106
+ `BACKEND_URL=${config.backendUrl}`,
107
+ "",
108
+ "# ── Host ports (must match the ports in the URLs above) ───────",
109
+ `FRONTEND_PORT=${config.frontendPort}`,
110
+ `BACKEND_PORT=${config.backendPort}`,
111
+ "",
112
+ "# ── Postgres ──────────────────────────────────────────────────",
113
+ "POSTGRES_USER=postgres",
114
+ "POSTGRES_PASSWORD=postgres",
115
+ "POSTGRES_DB=terse",
116
+ `POSTGRES_PORT=${config.postgresPort}`,
117
+ "",
118
+ "# ── Secrets (rotate by replacing the values) ──────────────────",
119
+ `JWT_SECRET=${randomBytes(32).toString("base64url")}`,
120
+ `LOCAL_SECRETS_ENCRYPTION_KEY=${randomBytes(32).toString("base64url")}`,
121
+ "",
122
+ "# ── Frontend (must match BACKEND_URL above) ───────────────────",
123
+ `VITE_API_BASE_URL=${config.backendUrl}`,
124
+ `VITE_BACKEND_REDIRECT_URL=${config.backendUrl}`,
125
+ `VITE_SOCKET_URL=${socketUrl}`,
126
+ "",
127
+ "# ── Internal backend URL (how the backend container reaches itself) ─",
128
+ "# Job runs talk to the backend from inside the container, where it",
129
+ "# always listens on 3001. This is NOT the host port in BACKEND_URL.",
130
+ "# Leave as-is unless you change the backend's in-container listen port.",
131
+ "INTERNAL_BACKEND_URL=http://localhost:3001",
132
+ "",
133
+ "# ── Runtime ───────────────────────────────────────────────────",
134
+ "NODE_ENV=development",
135
+ "",
136
+ "# ── Image (uncomment to pin to a specific tag) ────────────────",
137
+ "# TERSE_IMAGE=us-central1-docker.pkg.dev/fluid-analogy-473415-c2/public/terse",
138
+ ""
139
+ ];
140
+ await fs.writeFile(path.join(targetDir, ".env"), lines.join("\n"));
141
+ }
142
+ function toWebSocketUrl(httpUrl) {
143
+ const url = new URL(httpUrl);
144
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
145
+ return url.toString().replace(/\/$/, "");
146
+ }
147
+ function hostPortFromUrl(httpUrl) {
148
+ const url = new URL(httpUrl);
149
+ if (url.port)
150
+ return Number(url.port);
151
+ return url.protocol === "https:" ? 443 : 80;
152
+ }
153
+ async function promptOr(nonInteractive, override, message, placeholder) {
154
+ if (override !== undefined) {
155
+ log.info(`${message} ${chalk.dim(`→ ${override}`)}`);
156
+ return override;
157
+ }
158
+ if (nonInteractive) {
159
+ log.info(`${message} ${chalk.dim(`→ ${placeholder} (default)`)}`);
160
+ return placeholder;
161
+ }
162
+ const result = await text({ message, placeholder, defaultValue: placeholder });
163
+ if (isCancel(result)) {
164
+ cancel("Cancelled.");
165
+ process.exit(0);
166
+ }
167
+ return result;
168
+ }
169
+ function parseArgs(argv) {
170
+ const out = { nonInteractive: false, noOpen: false, help: false };
171
+ for (let i = 0; i < argv.length; i++) {
172
+ const arg = argv[i];
173
+ const next = () => {
174
+ const v = argv[++i];
175
+ if (v === undefined)
176
+ throw new Error(`Missing value for ${arg}`);
177
+ return v;
178
+ };
179
+ const valueOf = (flag) => {
180
+ const eq = arg.indexOf("=");
181
+ return eq >= 0 ? arg.slice(eq + 1) : next();
182
+ };
183
+ if (arg === "-y" || arg === "--yes" || arg === "--non-interactive")
184
+ out.nonInteractive = true;
185
+ else if (arg === "-h" || arg === "--help")
186
+ out.help = true;
187
+ else if (arg === "--target" || arg.startsWith("--target="))
188
+ out.targetDir = valueOf(arg);
189
+ else if (arg === "--frontend-url" || arg.startsWith("--frontend-url="))
190
+ out.frontendUrl = valueOf(arg);
191
+ else if (arg === "--backend-url" || arg.startsWith("--backend-url="))
192
+ out.backendUrl = valueOf(arg);
193
+ else if (arg === "--no-open")
194
+ out.noOpen = true;
195
+ else
196
+ throw new Error(`Unknown argument: ${arg}. Run with --help for usage.`);
197
+ }
198
+ return out;
199
+ }
200
+ function printHelp() {
201
+ const lines = [
202
+ chalk.bold("create-terse — scaffold a self-hosted Terse instance"),
203
+ "",
204
+ chalk.bold("Usage:"),
205
+ " npx create-terse [options]",
206
+ "",
207
+ chalk.bold("Options:"),
208
+ " -y, --non-interactive Skip prompts and use defaults (also auto-enabled when stdin/stdout is not a TTY)",
209
+ ` --target <dir> Target directory (default: ${DEFAULTS.targetDir})`,
210
+ ` --frontend-url <url> Frontend URL (default: ${DEFAULTS.frontendUrl})`,
211
+ ` --backend-url <url> Backend URL (default: ${DEFAULTS.backendUrl})`,
212
+ " --no-open Skip opening the frontend in your browser when setup completes",
213
+ " -h, --help Show this help",
214
+ "",
215
+ chalk.bold("Examples:"),
216
+ " npx create-terse",
217
+ " npx create-terse -y",
218
+ " npx create-terse -y --target ./my-terse --backend-url http://localhost:3001"
219
+ ];
220
+ console.log(lines.join("\n"));
221
+ }
222
+ async function runStep(label, command, cwd) {
223
+ const s = spinner();
224
+ s.start(label);
225
+ try {
226
+ await execAsync(command, { cwd });
227
+ s.stop(`${label} ✓`);
228
+ }
229
+ catch (err) {
230
+ s.stop(`${label} ✗`);
231
+ const message = err instanceof Error ? err.message : String(err);
232
+ const stdout = err.stdout ?? "";
233
+ const stderr = err.stderr ?? "";
234
+ throw new Error([message, stdout, stderr].filter(Boolean).join("\n"));
235
+ }
236
+ }
237
+ async function runStepStreaming(label, command, cwd) {
238
+ log.info(label);
239
+ await new Promise((resolve, reject) => {
240
+ const child = spawn(command, { cwd, stdio: "inherit", shell: true });
241
+ child.on("exit", code => {
242
+ if (code === 0) {
243
+ log.success(`${label} ✓`);
244
+ resolve();
245
+ }
246
+ else {
247
+ reject(new Error(`${label} failed with exit code ${code}`));
248
+ }
249
+ });
250
+ child.on("error", reject);
251
+ });
252
+ }
253
+ function shellEscape(value) {
254
+ return `'${value.replace(/'/g, "'\\''")}'`;
255
+ }
256
+ async function findFreePort(preferred) {
257
+ const candidates = [preferred, 54322, 54323, 54324, 54325];
258
+ for (const port of candidates) {
259
+ if (await isPortFree(port))
260
+ return port;
261
+ }
262
+ throw new Error("Could not find a free TCP port for Postgres");
263
+ }
264
+ async function isPortFree(port) {
265
+ const v4Busy = await tryConnect("127.0.0.1", port);
266
+ const v6Busy = await tryConnect("::1", port);
267
+ return !v4Busy && !v6Busy;
268
+ }
269
+ function tryConnect(host, port) {
270
+ return new Promise(resolve => {
271
+ const socket = net.connect({ host, port });
272
+ const done = (ok) => {
273
+ socket.removeAllListeners();
274
+ socket.destroy();
275
+ resolve(ok);
276
+ };
277
+ socket.once("connect", () => done(true));
278
+ socket.once("error", () => done(false));
279
+ });
280
+ }
281
+ async function waitForBackend(backendUrl) {
282
+ const s = spinner();
283
+ s.start(`Waiting for backend at ${backendUrl}`);
284
+ const deadline = Date.now() + 120_000;
285
+ while (Date.now() < deadline) {
286
+ try {
287
+ const response = await fetch(backendUrl, { signal: AbortSignal.timeout(2000) });
288
+ if (response.status < 500) {
289
+ s.stop(`Backend is responding ✓`);
290
+ return;
291
+ }
292
+ }
293
+ catch {
294
+ // not ready yet
295
+ }
296
+ await new Promise(resolve => setTimeout(resolve, 1000));
297
+ }
298
+ s.stop(`Backend did not become ready in 120s ✗`);
299
+ throw new Error(`Backend at ${backendUrl} never started responding. Check logs with: docker compose logs backend`);
300
+ }
301
+ main().catch(err => {
302
+ log.error(err instanceof Error ? err.message : String(err));
303
+ process.exit(1);
304
+ });
305
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "create-terse",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Scaffold and launch a self-hosted Terse instance with one command.",
6
+ "keywords": [
7
+ "terse",
8
+ "create-terse",
9
+ "scaffold",
10
+ "self-hosted",
11
+ "ai",
12
+ "workflow",
13
+ "docker-compose"
14
+ ],
15
+ "license": "SEE LICENSE IN LICENSE.md",
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/TerseAI/Terse.git",
22
+ "directory": "packages/create-terse"
23
+ },
24
+ "homepage": "https://docs.useterse.ai",
25
+ "bugs": {
26
+ "url": "https://github.com/TerseAI/Terse/issues"
27
+ },
28
+ "bin": {
29
+ "create-terse": "dist/index.js"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "templates"
34
+ ],
35
+ "dependencies": {
36
+ "@clack/prompts": "^1.0.0",
37
+ "chalk": "^5.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "typescript": "^5.0.0"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "dev": "tsc --watch --preserveWatchOutput",
46
+ "install-global": "npm run build && npm link",
47
+ "format": "pnpm exec prettier --config ../../.prettierrc --write \"src/**/*.ts\"",
48
+ "format:check": "pnpm exec prettier --config ../../.prettierrc --check \"src/**/*.ts\""
49
+ }
50
+ }
@@ -0,0 +1,103 @@
1
+ # Terse (self-hosted)
2
+
3
+ You're running a self-hosted Terse instance. This directory holds everything needed to operate it:
4
+
5
+ ```
6
+ docker-compose.yml # service definitions (postgres, backend, frontend)
7
+ .env # all configuration (URLs, ports, secrets) — edit here
8
+ README.md # you are here
9
+ ```
10
+
11
+ ## Daily operations
12
+
13
+ | Goal | Command |
14
+ | ----------------------------- | ------------------------------------ |
15
+ | Open the UI | visit the `FRONTEND_URL` from `.env` |
16
+ | Tail logs (all services) | `docker compose logs -f` |
17
+ | Tail backend only | `docker compose logs -f backend` |
18
+ | Restart after editing `.env` | `docker compose up -d` |
19
+ | Stop everything | `docker compose down` |
20
+ | Stop and delete all data | `docker compose down -v` |
21
+ | Pull the latest image | `docker compose pull && docker compose up -d` |
22
+ | Open a shell in the backend | `docker compose exec backend sh` |
23
+ | Check service status | `docker compose ps` |
24
+
25
+ ## What's running
26
+
27
+ - **postgres** — Postgres 16, data persisted in the `terse_postgres` Docker volume.
28
+ - **backend** — Terse API, port from `BACKEND_URL` in `.env`. SQLite for local secrets lives in the `terse_sqlite` volume.
29
+ - **frontend** — Vite dev server, port from `FRONTEND_URL` in `.env`.
30
+
31
+ Both backend and frontend run from the same prebuilt image: `us-central1-docker.pkg.dev/fluid-analogy-473415-c2/public/terse:latest`. To pin to a specific tag, uncomment `TERSE_IMAGE=` at the bottom of `.env`.
32
+
33
+ ## Configuration
34
+
35
+ Everything user-tunable lives in `.env`:
36
+
37
+ - **URLs** — `FRONTEND_URL` / `BACKEND_URL` must match what your browser sees. If you put Terse behind a reverse proxy or a real domain, change both and restart.
38
+ - **Postgres** — credentials and host port. Change before first start; changing later requires `docker compose down -v` (destroys data).
39
+ - **Secrets** — `JWT_SECRET` and `LOCAL_SECRETS_ENCRYPTION_KEY` are generated for you. Rotating `LOCAL_SECRETS_ENCRYPTION_KEY` invalidates any stored integration credentials.
40
+
41
+ After any edit, run `docker compose up -d` to apply.
42
+
43
+ ## Connecting integrations
44
+
45
+ All integration config lives in this same `.env`. There's no second config file and no per-integration secrets store. After editing, `docker compose up -d` restarts the affected containers and the integration appears in the dashboard.
46
+
47
+ - **API-key integrations** — Datadog, Snowflake, HeyReach, LaunchDarkly, PostHog. No env vars needed; paste the API key in the dashboard at connect time.
48
+ - **OAuth integrations** — Gmail, GitHub, Slack, Linear, Notion, Attio. Gated by env vars. Every required `<PROVIDER>_CLIENT_ID` / `_CLIENT_SECRET` / `_REDIRECT_URI` (names vary slightly per provider) must be set, otherwise the integration is hidden.
49
+
50
+ Callback URLs (`_REDIRECT_URI`, `_OAUTH_CALLBACK_URL`, etc.) must be reachable from the user's browser. For a default local install that's `http://localhost:3001/<provider>/callback`. If the OAuth provider rejects `localhost`, expose the backend via a tunnel (ngrok, Cloudflare Tunnel) and point the callback URL at the public hostname.
51
+
52
+ The full env-var list per integration is in [docs.useterse.ai/self-hosting-control-plane](https://docs.useterse.ai/self-hosting-control-plane#integrations).
53
+
54
+ ## The `terse` CLI
55
+
56
+ The installer also globally installs `terse-cli` (`npm i -g terse-cli`) and points it at this instance. From any directory:
57
+
58
+ ```
59
+ terse target # show which backend the CLI talks to
60
+ terse init my-job # scaffold a new job
61
+ terse deploy # deploy a job to your instance
62
+ ```
63
+
64
+ CLI reference: [docs.useterse.ai/reference/cli](https://docs.useterse.ai/reference/cli).
65
+
66
+ ## Upgrading
67
+
68
+ ```
69
+ docker compose pull
70
+ docker compose up -d
71
+ ```
72
+
73
+ The `latest` tag is updated on every push to `main` in the [TerseAI/Terse](https://github.com/TerseAI/Terse) repo. Pin `TERSE_IMAGE` in `.env` if you want explicit control over upgrades.
74
+
75
+ ## Backups
76
+
77
+ The only stateful data lives in two Docker volumes:
78
+
79
+ ```
80
+ docker volume ls | grep terse
81
+ # terse_postgres ← Postgres data
82
+ # terse_sqlite ← local secrets DB
83
+ ```
84
+
85
+ Back them up with `docker run --rm -v terse_postgres:/data -v $PWD:/backup alpine tar czf /backup/postgres.tar.gz -C /data .` (and similarly for `terse_sqlite`).
86
+
87
+ ## Resources
88
+
89
+ - **Docs** — [docs.useterse.ai](https://docs.useterse.ai)
90
+ - **Quickstart** — [docs.useterse.ai/quickstart](https://docs.useterse.ai/quickstart)
91
+ - **Self-hosting guide** — [docs.useterse.ai/self-hosting](https://docs.useterse.ai/self-hosting)
92
+ - **SDK reference** — [docs.useterse.ai/reference/typescript-sdk](https://docs.useterse.ai/reference/typescript-sdk)
93
+ - **Website** — [useterse.ai](https://useterse.ai)
94
+ - **Source** — [github.com/TerseAI/Terse](https://github.com/TerseAI/Terse)
95
+
96
+ ## Getting help
97
+
98
+ - **Bugs / feature requests** — open an issue at [github.com/TerseAI/Terse/issues](https://github.com/TerseAI/Terse/issues). Include `docker compose logs` output and the relevant section of `.env` (with secrets redacted).
99
+ - **Questions** — check [docs.useterse.ai](https://docs.useterse.ai) first, then open a Discussion at [github.com/TerseAI/Terse/discussions](https://github.com/TerseAI/Terse/discussions).
100
+
101
+ ## Contributing
102
+
103
+ Terse code is available on [Github](https://github.com/TerseAI/Terse). The contribution guide lives in the repo at [CONTRIBUTING.md](https://github.com/TerseAI/Terse/blob/main/CONTRIBUTING.md). PRs welcome for bug fixes, integrations, and docs improvements.
@@ -0,0 +1,59 @@
1
+ services:
2
+ postgres:
3
+ image: postgres:16-alpine
4
+ container_name: terse-postgres
5
+ restart: unless-stopped
6
+ environment:
7
+ POSTGRES_USER: ${POSTGRES_USER:-postgres}
8
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
9
+ POSTGRES_DB: ${POSTGRES_DB:-terse}
10
+ ports:
11
+ - "${POSTGRES_PORT:-5432}:5432"
12
+ volumes:
13
+ - terse_postgres:/var/lib/postgresql/data
14
+ healthcheck:
15
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
16
+ interval: 5s
17
+ timeout: 5s
18
+ retries: 10
19
+
20
+ backend:
21
+ image: ${TERSE_IMAGE:-us-central1-docker.pkg.dev/fluid-analogy-473415-c2/public/terse:latest}
22
+ pull_policy: always
23
+ container_name: terse-backend
24
+ restart: unless-stopped
25
+ working_dir: /workspace/backend
26
+ depends_on:
27
+ postgres:
28
+ condition: service_healthy
29
+ env_file:
30
+ - ./.env
31
+ environment:
32
+ DATABASE_URL: postgres://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-terse}
33
+ LOCAL_DB_URL: file:/data/local.db
34
+ ports:
35
+ - "${BACKEND_PORT:-3001}:3001"
36
+ volumes:
37
+ - terse_sqlite:/data
38
+ command: >
39
+ sh -c "pnpm exec prisma migrate deploy &&
40
+ pnpm exec prisma migrate deploy --schema=./prisma/local/schema.prisma &&
41
+ pnpm run dev:server"
42
+
43
+ frontend:
44
+ image: ${TERSE_IMAGE:-us-central1-docker.pkg.dev/fluid-analogy-473415-c2/public/terse:latest}
45
+ pull_policy: always
46
+ container_name: terse-frontend
47
+ restart: unless-stopped
48
+ working_dir: /workspace/frontend
49
+ depends_on:
50
+ - backend
51
+ env_file:
52
+ - ./.env
53
+ ports:
54
+ - "${FRONTEND_PORT:-5173}:5173"
55
+ command: pnpm exec vite --host 0.0.0.0 --port 5173
56
+
57
+ volumes:
58
+ terse_postgres:
59
+ terse_sqlite: