@rizom/brain 0.2.0-alpha.6 → 0.2.0-alpha.61

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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/brain.js +4763 -26510
  3. package/dist/deploy.d.ts +17 -26
  4. package/dist/deploy.js +25 -25
  5. package/dist/deploy.js.map +4 -4
  6. package/dist/entities.d.ts +561 -0
  7. package/dist/entities.js +172 -0
  8. package/dist/entities.js.map +242 -0
  9. package/dist/index.d.ts +65 -0
  10. package/dist/index.js +5 -0
  11. package/dist/index.js.map +11 -0
  12. package/dist/interfaces.d.ts +923 -0
  13. package/dist/interfaces.js +172 -0
  14. package/dist/interfaces.js.map +209 -0
  15. package/dist/plugins.d.ts +1195 -0
  16. package/dist/plugins.js +178 -0
  17. package/dist/plugins.js.map +294 -0
  18. package/dist/seed-content/relay/README.md +28 -371
  19. package/dist/seed-content/relay/anchor-profile/anchor-profile.md +8 -11
  20. package/dist/seed-content/relay/brain-character/brain-character.md +5 -4
  21. package/dist/seed-content/relay/site-content/about/about.md +20 -0
  22. package/dist/seed-content/relay/site-content/home/diagram.md +184 -0
  23. package/dist/seed-content/relay/site-info/site-info.md +11 -1
  24. package/dist/services.d.ts +601 -0
  25. package/dist/services.js +172 -0
  26. package/dist/services.js.map +242 -0
  27. package/dist/site.d.ts +56 -157
  28. package/dist/site.js +323 -463
  29. package/dist/site.js.map +131 -249
  30. package/dist/templates.d.ts +302 -0
  31. package/dist/templates.js +172 -0
  32. package/dist/templates.js.map +206 -0
  33. package/dist/themes.d.ts +5 -44
  34. package/dist/themes.js +91 -8
  35. package/dist/themes.js.map +2 -2
  36. package/package.json +35 -4
  37. package/templates/deploy/scripts/provision-server.ts +109 -0
  38. package/templates/deploy/scripts/update-dns.ts +65 -0
  39. package/templates/deploy/scripts/write-ssh-key.ts +17 -0
  40. package/tsconfig.instance.json +31 -0
package/dist/themes.d.ts CHANGED
@@ -1,48 +1,9 @@
1
1
  /**
2
- * ⚠️ TEMPORARY HAND-WRITTEN PUBLIC API SURFACE ⚠️
2
+ * Compose a complete theme by prepending shared base utilities.
3
3
  *
4
- * Public API contract for `@rizom/brain/themes`. Hand-maintained
5
- * as a stopgap because the auto-bundlers choke on the size and
6
- * edge cases of the `@brains/*` type graph. See
7
- * `docs/plans/external-plugin-api.md` for the replacement plan.
8
- *
9
- * **Sync rules:**
10
- * - When `composeTheme`'s signature changes in `@brains/theme-base`,
11
- * update this file in the same commit.
12
- * - The runtime side (`../entries/themes.ts`) re-exports the real
13
- * implementation from `@brains/theme-base`. The .js bundle
14
- * produced by `scripts/build.ts` is what consumers execute.
15
- * This .d.ts file is what their tsc sees.
4
+ * Base utilities live in @layer theme-base; theme-specific styles
5
+ * should use @layer theme-override to guarantee correct cascade order.
16
6
  */
7
+ declare function composeTheme(themeCSS: string): string;
17
8
 
18
- /**
19
- * Prepend the shared base theme utilities to a raw theme CSS string
20
- * and return the combined result.
21
- *
22
- * The base utilities layer contains:
23
- *
24
- * - Palette tokens (`--palette-*`) for neutral colors, status colors,
25
- * and universal UI signals
26
- * - `@theme inline` declarations that expose the semantic color
27
- * tokens (`--color-brand`, `--color-bg`, `--color-text`, etc.) to
28
- * Tailwind v4's JIT so utilities like `bg-brand`, `text-brand`,
29
- * `border-brand`, `focus-visible:ring-brand` auto-generate
30
- * - Layer ordering (`@layer theme-base, theme-override`) so theme
31
- * overrides cascade correctly regardless of concat order
32
- * - Universal gradient, status, selection, and warning utilities
33
- * - Prose color slots for `@tailwindcss/typography`
34
- *
35
- * The framework resolver uses this helper when it loads a raw theme
36
- * package or inline theme CSS. Advanced consumers can use the same
37
- * helper when they need a fully composed CSS string outside the
38
- * resolver.
39
- *
40
- * @example
41
- * ```ts
42
- * import { composeTheme } from "@rizom/brain/themes";
43
- * import themeCSS from "./theme.css" with { type: "text" };
44
- *
45
- * const fullThemeCSS = composeTheme(themeCSS);
46
- * ```
47
- */
48
- export function composeTheme(themeCSS: string): string;
9
+ export { composeTheme };
package/dist/themes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var t=`/* ============================================
2
+ var e=`/* ============================================
3
3
  THEME BASE - Shared utilities for all themes
4
4
  ============================================
5
5
  This file contains CSS that is IDENTICAL across all themes.
@@ -45,11 +45,17 @@ var t=`/* ============================================
45
45
  /* Selection colors */
46
46
  --color-selection: var(--color-selection);
47
47
 
48
+ /* Rule colors \u2014 subtle hairlines for dividers, entry borders, footer top */
49
+ --color-rule: var(--color-rule);
50
+ --color-rule-strong: var(--color-rule-strong);
51
+
48
52
  /* Monospace font \u2014 used by ui-library widgets, code blocks, etc.
49
53
  Defined by every theme (or left empty for ranger). Brand-specific
50
54
  fonts (\`font-display\`, \`font-body\`, \`font-label\`, \`font-nav\`)
51
55
  and the typography scale live in the brand theme that uses them
52
56
  so non-brand themes don't ship broken utilities. */
57
+ --font-sans: var(--font-sans);
58
+ --font-heading: var(--font-heading);
53
59
  --font-mono: var(--font-mono);
54
60
 
55
61
  /* Typography scale \u2014 each entry bundles font-size + line-height +
@@ -150,6 +156,7 @@ var t=`/* ============================================
150
156
  .text-theme-muted { color: var(--color-text-muted); }
151
157
  .text-theme-light { color: var(--color-text-light); }
152
158
  .text-theme-inverse { color: var(--color-text-inverse); }
159
+ .text-theme-on-dark { color: var(--color-text-on-dark, var(--color-text-inverse)); }
153
160
  .text-heading { color: var(--color-heading); }
154
161
  .text-selection { color: var(--color-selection); }
155
162
  .border-theme { border-color: var(--color-border); }
@@ -186,6 +193,82 @@ var t=`/* ============================================
186
193
  background-color: var(--color-selection-bg);
187
194
  color: var(--color-selection-text);
188
195
  }
196
+
197
+ /* Sticky-footer hygiene for site layouts that use flex-grow mains. */
198
+ body {
199
+ min-height: 100dvh;
200
+ display: flex;
201
+ flex-direction: column;
202
+ }
203
+
204
+ /* Reusable editorial decoration hooks. Themes provide colors through
205
+ semantic tokens; these classes only define layering and gradients. */
206
+ .hero-decor,
207
+ .cta-decor {
208
+ position: relative;
209
+ overflow: hidden;
210
+ isolation: isolate;
211
+ }
212
+
213
+ .hero-decor > *,
214
+ .cta-decor > * {
215
+ position: relative;
216
+ z-index: 1;
217
+ }
218
+
219
+ .hero-decor::before,
220
+ .hero-decor::after,
221
+ .cta-decor::before,
222
+ .cta-decor::after {
223
+ content: "";
224
+ position: absolute;
225
+ pointer-events: none;
226
+ z-index: 0;
227
+ }
228
+
229
+ .hero-decor::before {
230
+ inset: -30% -20% auto;
231
+ height: 120%;
232
+ background:
233
+ radial-gradient(circle at 50% 20%, rgb(from var(--color-accent) r g b / 0.5), transparent 28%),
234
+ radial-gradient(circle at 68% 38%, rgb(from var(--color-brand-dark) r g b / 0.35), transparent 36%);
235
+ filter: blur(8px);
236
+ }
237
+
238
+ .hero-decor::after,
239
+ .cta-decor::before {
240
+ inset: 0;
241
+ opacity: 0.12;
242
+ mix-blend-mode: soft-light;
243
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.65'/%3E%3C/svg%3E");
244
+ }
245
+
246
+ .cta-decor::after {
247
+ width: min(48rem, 70vw);
248
+ aspect-ratio: 1;
249
+ right: -18rem;
250
+ bottom: -28rem;
251
+ border-radius: 9999px;
252
+ background: radial-gradient(circle, rgb(from var(--color-brand) r g b / 0.42), transparent 66%);
253
+ filter: blur(6px);
254
+ }
255
+
256
+ .pulse-sparkle {
257
+ animation: hero-pulse 2.8s ease-in-out infinite;
258
+ }
259
+
260
+ @keyframes hero-pulse {
261
+ 0%, 100% {
262
+ opacity: 0.55;
263
+ transform: scale(1);
264
+ box-shadow: 0 0 0 0 rgb(from var(--color-accent) r g b / 0.28);
265
+ }
266
+ 50% {
267
+ opacity: 1;
268
+ transform: scale(1.18);
269
+ box-shadow: 0 0 0 10px rgb(from var(--color-accent) r g b / 0);
270
+ }
271
+ }
189
272
  }
190
273
 
191
274
  /* ============================================
@@ -268,21 +351,21 @@ var t=`/* ============================================
268
351
  ============================================ */
269
352
  @layer theme-base {
270
353
  [data-theme="dark"] .sun-icon {
271
- display: block;
354
+ display: none;
272
355
  }
273
356
  [data-theme="dark"] .moon-icon {
274
- display: none;
357
+ display: block;
275
358
  }
276
359
  [data-theme="light"] .sun-icon {
277
- display: none;
360
+ display: block;
278
361
  }
279
362
  [data-theme="light"] .moon-icon {
280
- display: block;
363
+ display: none;
281
364
  }
282
365
  }
283
- `;function o(e){return t+`
366
+ `;function o(t){return e+`
284
367
 
285
- `+e}export{o as composeTheme};
368
+ `+t}export{o as composeTheme};
286
369
 
287
- //# debugId=AA4E52472A90D8F564756E2164756E21
370
+ //# debugId=11AD1BC3215EB2DC64756E2164756E21
288
371
  //# sourceMappingURL=themes.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "// Types for CSS imports are defined in types.d.ts\n/// <reference types=\"./types.d.ts\" />\n\nimport themeBaseCSS from \"./theme-base.css\" with { type: \"text\" };\n\n/**\n * Compose a complete theme by prepending shared base utilities.\n *\n * Base utilities live in @layer theme-base; theme-specific styles\n * should use @layer theme-override to guarantee correct cascade order.\n */\nexport function composeTheme(themeCSS: string): string {\n return themeBaseCSS + \"\\n\\n\" + themeCSS;\n}\n\nexport default themeBaseCSS;\nexport { themeBaseCSS };\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWO,SAAS,CAAY,CAAC,EAA0B,CACrD,OAAO,EAAe;AAAA;AAAA,EAAS",
8
- "debugId": "AA4E52472A90D8F564756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWO,SAAS,CAAY,CAAC,EAA0B,CACrD,OAAO,EAAe;AAAA;AAAA,EAAS",
8
+ "debugId": "11AD1BC3215EB2DC64756E2164756E21",
9
9
  "names": []
10
10
  }
package/package.json CHANGED
@@ -1,13 +1,37 @@
1
1
  {
2
2
  "name": "@rizom/brain",
3
- "version": "0.2.0-alpha.6",
3
+ "version": "0.2.0-alpha.61",
4
4
  "description": "Brain runtime + CLI — scaffold, run, and manage AI brain instances",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "brain": "./dist/brain.js"
8
8
  },
9
9
  "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
10
14
  "./cli": "./dist/brain.js",
15
+ "./plugins": {
16
+ "types": "./dist/plugins.d.ts",
17
+ "import": "./dist/plugins.js"
18
+ },
19
+ "./entities": {
20
+ "types": "./dist/entities.d.ts",
21
+ "import": "./dist/entities.js"
22
+ },
23
+ "./services": {
24
+ "types": "./dist/services.d.ts",
25
+ "import": "./dist/services.js"
26
+ },
27
+ "./interfaces": {
28
+ "types": "./dist/interfaces.d.ts",
29
+ "import": "./dist/interfaces.js"
30
+ },
31
+ "./templates": {
32
+ "types": "./dist/templates.d.ts",
33
+ "import": "./dist/templates.js"
34
+ },
11
35
  "./site": {
12
36
  "types": "./dist/site.d.ts",
13
37
  "import": "./dist/site.js"
@@ -19,14 +43,18 @@
19
43
  "./deploy": {
20
44
  "types": "./dist/deploy.d.ts",
21
45
  "import": "./dist/deploy.js"
22
- }
46
+ },
47
+ "./tsconfig.instance.json": "./tsconfig.instance.json"
23
48
  },
24
49
  "files": [
25
- "dist"
50
+ "dist",
51
+ "templates",
52
+ "tsconfig.instance.json"
26
53
  ],
27
54
  "scripts": {
28
55
  "build": "bun scripts/build.ts",
29
56
  "prepublishOnly": "bun scripts/build.ts",
57
+ "dev:start": "bun dist/brain.js start",
30
58
  "typecheck": "tsc --noEmit",
31
59
  "test": "bun test",
32
60
  "lint": "eslint . --ext .ts"
@@ -42,6 +70,7 @@
42
70
  "tailwindcss": "^4.1.11"
43
71
  },
44
72
  "optionalDependencies": {
73
+ "@bitwarden/sdk-napi": "^1.0.0",
45
74
  "@libsql/client": "^0.15.7",
46
75
  "@tailwindcss/oxide": "^4.1.4",
47
76
  "better-sqlite3": "^11.8.1",
@@ -51,6 +80,7 @@
51
80
  },
52
81
  "devDependencies": {
53
82
  "@brains/app": "workspace:*",
83
+ "@brains/deploy-templates": "workspace:*",
54
84
  "@brains/eslint-config": "workspace:*",
55
85
  "@brains/mcp-service": "workspace:*",
56
86
  "@brains/plugins": "workspace:*",
@@ -61,12 +91,13 @@
61
91
  "@brains/site-default": "workspace:*",
62
92
  "@brains/site-personal": "workspace:*",
63
93
  "@brains/site-professional": "workspace:*",
64
- "@brains/site-rizom": "workspace:*",
65
94
  "@brains/theme-default": "workspace:*",
66
95
  "@brains/theme-rizom": "workspace:*",
67
96
  "@brains/typescript-config": "workspace:*",
68
97
  "@brains/utils": "workspace:*",
69
98
  "@types/bun": "latest",
99
+ "rollup": "^4.60.2",
100
+ "rollup-plugin-dts": "^6.4.1",
70
101
  "typescript": "^5.3.3"
71
102
  },
72
103
  "publishConfig": {
@@ -0,0 +1,109 @@
1
+ import {
2
+ readJsonResponse,
3
+ requireEnv,
4
+ writeGitHubOutput,
5
+ writeGitHubEnv,
6
+ } from "./helpers";
7
+
8
+ const token = requireEnv("HCLOUD_TOKEN");
9
+ const instanceName = requireEnv("INSTANCE_NAME");
10
+ const sshKeyName = requireEnv("HCLOUD_SSH_KEY_NAME");
11
+ const serverType = requireEnv("HCLOUD_SERVER_TYPE");
12
+ const location = requireEnv("HCLOUD_LOCATION");
13
+
14
+ const headers: Record<string, string> = {
15
+ Authorization: `Bearer ${token}`,
16
+ "Content-Type": "application/json",
17
+ };
18
+ const baseUrl = "https://api.hetzner.cloud/v1";
19
+ const labelSelector = `brain=${instanceName}`;
20
+ const MAX_POLLS = 30;
21
+ const POLL_INTERVAL_MS = 10_000;
22
+
23
+ interface HetznerServer {
24
+ id: number;
25
+ status: string;
26
+ public_net?: { ipv4?: { ip?: string } };
27
+ }
28
+
29
+ function sleep(ms: number): Promise<void> {
30
+ return new Promise((resolve) => setTimeout(resolve, ms));
31
+ }
32
+
33
+ async function listServers(): Promise<HetznerServer[]> {
34
+ const url = `${baseUrl}/servers?label_selector=${encodeURIComponent(labelSelector)}`;
35
+ const response = await fetch(url, { headers });
36
+ const payload = (await readJsonResponse(
37
+ response,
38
+ "Hetzner server lookup",
39
+ )) as {
40
+ servers?: HetznerServer[];
41
+ };
42
+ if (!response.ok || !payload.servers) {
43
+ throw new Error(`Hetzner server lookup failed: ${JSON.stringify(payload)}`);
44
+ }
45
+ return payload.servers;
46
+ }
47
+
48
+ async function createServer(): Promise<HetznerServer> {
49
+ const response = await fetch(`${baseUrl}/servers`, {
50
+ method: "POST",
51
+ headers,
52
+ body: JSON.stringify({
53
+ name: instanceName,
54
+ server_type: serverType,
55
+ image: "ubuntu-22.04",
56
+ location,
57
+ ssh_keys: [sshKeyName],
58
+ labels: { brain: instanceName },
59
+ }),
60
+ });
61
+ const payload = (await readJsonResponse(
62
+ response,
63
+ "Hetzner server create",
64
+ )) as {
65
+ server?: HetznerServer;
66
+ };
67
+ if (!response.ok || !payload.server) {
68
+ throw new Error(`Hetzner server create failed: ${JSON.stringify(payload)}`);
69
+ }
70
+ return payload.server;
71
+ }
72
+
73
+ async function getServer(id: number): Promise<HetznerServer> {
74
+ const response = await fetch(`${baseUrl}/servers/${id}`, { headers });
75
+ const payload = (await readJsonResponse(response, "Hetzner server poll")) as {
76
+ server?: HetznerServer;
77
+ };
78
+ if (!response.ok || !payload.server) {
79
+ throw new Error(`Hetzner server poll failed: ${JSON.stringify(payload)}`);
80
+ }
81
+ return payload.server;
82
+ }
83
+
84
+ let server: HetznerServer | undefined = (await listServers())[0];
85
+ server ??= await createServer();
86
+
87
+ let polls = 0;
88
+ while (server.status !== "running" || !server.public_net?.ipv4?.ip) {
89
+ if (++polls > MAX_POLLS) {
90
+ throw new Error(
91
+ `Server ${server.id} did not become ready after ${(MAX_POLLS * POLL_INTERVAL_MS) / 1000}s (status: ${server.status})`,
92
+ );
93
+ }
94
+ if (server.status === "error") {
95
+ throw new Error(`Server ${server.id} entered error state`);
96
+ }
97
+ console.log(
98
+ `Waiting for server ${server.id} (status: ${server.status}, poll ${polls}/${MAX_POLLS})...`,
99
+ );
100
+ await sleep(POLL_INTERVAL_MS);
101
+ server = await getServer(server.id);
102
+ }
103
+
104
+ const serverIp = server.public_net.ipv4.ip;
105
+ if (!serverIp) {
106
+ throw new Error(`Server ${server.id} running but has no IPv4 address`);
107
+ }
108
+ writeGitHubOutput("server_ip", serverIp);
109
+ writeGitHubEnv("SERVER_IP", serverIp);
@@ -0,0 +1,65 @@
1
+ import { readJsonResponse, requireEnv } from "./helpers";
2
+
3
+ const token = requireEnv("CF_API_TOKEN");
4
+ const zoneId = requireEnv("CF_ZONE_ID");
5
+ const domain = requireEnv("BRAIN_DOMAIN");
6
+ const serverIp = requireEnv("SERVER_IP");
7
+
8
+ const headers: Record<string, string> = {
9
+ Authorization: `Bearer ${token}`,
10
+ "Content-Type": "application/json",
11
+ };
12
+ const baseUrl = "https://api.cloudflare.com/client/v4";
13
+
14
+ interface CloudflareResult {
15
+ success: boolean;
16
+ result?: Array<{ id: string }>;
17
+ }
18
+
19
+ async function findRecordId(
20
+ name: string,
21
+ type: "A" | "CNAME",
22
+ ): Promise<string | undefined> {
23
+ const lookupUrl = `${baseUrl}/zones/${zoneId}/dns_records?type=${type}&name=${encodeURIComponent(name)}`;
24
+ const lookup = await fetch(lookupUrl, { headers });
25
+ const payload = (await readJsonResponse(
26
+ lookup,
27
+ "Cloudflare DNS lookup",
28
+ )) as CloudflareResult;
29
+ if (!lookup.ok || !payload.success) {
30
+ throw new Error(`Cloudflare DNS lookup failed: ${JSON.stringify(payload)}`);
31
+ }
32
+
33
+ return payload.result?.[0]?.id;
34
+ }
35
+
36
+ async function upsertRecord(name: string): Promise<void> {
37
+ // Prefer an existing A record. If the hostname currently has a CNAME,
38
+ // replace that CNAME in-place so deploys can claim legacy www aliases.
39
+ const existing =
40
+ (await findRecordId(name, "A")) ?? (await findRecordId(name, "CNAME"));
41
+ const url = existing
42
+ ? `${baseUrl}/zones/${zoneId}/dns_records/${existing}`
43
+ : `${baseUrl}/zones/${zoneId}/dns_records`;
44
+
45
+ const response = await fetch(url, {
46
+ method: existing ? "PUT" : "POST",
47
+ headers,
48
+ body: JSON.stringify({
49
+ type: "A",
50
+ name,
51
+ content: serverIp,
52
+ ttl: 1,
53
+ proxied: true,
54
+ }),
55
+ });
56
+ const result = (await readJsonResponse(
57
+ response,
58
+ "Cloudflare DNS upsert",
59
+ )) as CloudflareResult;
60
+ if (!response.ok || !result.success) {
61
+ throw new Error(`Cloudflare DNS upsert failed: ${JSON.stringify(result)}`);
62
+ }
63
+ }
64
+
65
+ await upsertRecord(domain);
@@ -0,0 +1,17 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { requireEnv } from "./helpers";
4
+
5
+ const privateKey = requireEnv("KAMAL_SSH_PRIVATE_KEY");
6
+
7
+ let normalized = privateKey.replace(/\r\n/g, "\n").replace(/\\n/g, "\n");
8
+ if (!normalized.endsWith("\n")) {
9
+ normalized += "\n";
10
+ }
11
+
12
+ const sshDir = join(process.env["HOME"] ?? "/root", ".ssh");
13
+ mkdirSync(sshDir, { recursive: true });
14
+ writeFileSync(join(sshDir, "id_ed25519"), normalized, {
15
+ encoding: "utf8",
16
+ mode: 0o600,
17
+ });
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Rizom Brain Instance",
4
+ "compilerOptions": {
5
+ "esModuleInterop": true,
6
+ "forceConsistentCasingInFileNames": true,
7
+ "isolatedModules": true,
8
+ "moduleResolution": "bundler",
9
+ "resolveJsonModule": true,
10
+ "noUnusedLocals": true,
11
+ "noUnusedParameters": true,
12
+ "noImplicitAny": true,
13
+ "exactOptionalPropertyTypes": true,
14
+ "noImplicitReturns": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUncheckedIndexedAccess": true,
17
+ "noPropertyAccessFromIndexSignature": true,
18
+ "allowUnreachableCode": false,
19
+ "allowUnusedLabels": false,
20
+ "noImplicitOverride": true,
21
+ "preserveWatchOutput": true,
22
+ "skipLibCheck": true,
23
+ "strict": true,
24
+ "target": "ES2022",
25
+ "module": "ESNext",
26
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
27
+ "jsx": "react-jsx",
28
+ "jsxImportSource": "preact",
29
+ "noEmit": true
30
+ }
31
+ }