@rizom/brain 0.2.0-alpha.5 → 0.2.0-alpha.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brain.js +3834 -25926
- package/dist/deploy.d.ts +17 -26
- package/dist/deploy.js +25 -25
- package/dist/deploy.js.map +4 -4
- package/dist/entities.d.ts +521 -0
- package/dist/entities.js +172 -0
- package/dist/entities.js.map +242 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +11 -0
- package/dist/interfaces.d.ts +917 -0
- package/dist/interfaces.js +172 -0
- package/dist/interfaces.js.map +211 -0
- package/dist/plugins.d.ts +1120 -0
- package/dist/plugins.js +178 -0
- package/dist/plugins.js.map +294 -0
- package/dist/seed-content/relay/README.md +27 -371
- package/dist/services.d.ts +561 -0
- package/dist/services.js +172 -0
- package/dist/services.js.map +242 -0
- package/dist/site.d.ts +56 -157
- package/dist/site.js +316 -459
- package/dist/site.js.map +102 -223
- package/dist/templates.d.ts +302 -0
- package/dist/templates.js +172 -0
- package/dist/templates.js.map +206 -0
- package/dist/themes.d.ts +5 -44
- package/dist/themes.js +83 -4
- package/dist/themes.js.map +2 -2
- package/package.json +35 -4
- package/templates/deploy/scripts/provision-server.ts +109 -0
- package/templates/deploy/scripts/update-dns.ts +55 -0
- package/templates/deploy/scripts/write-ssh-key.ts +17 -0
- package/tsconfig.instance.json +31 -0
package/dist/themes.d.ts
CHANGED
|
@@ -1,48 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Compose a complete theme by prepending shared base utilities.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
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.
|
|
@@ -50,6 +50,8 @@ var t=`/* ============================================
|
|
|
50
50
|
fonts (\`font-display\`, \`font-body\`, \`font-label\`, \`font-nav\`)
|
|
51
51
|
and the typography scale live in the brand theme that uses them
|
|
52
52
|
so non-brand themes don't ship broken utilities. */
|
|
53
|
+
--font-sans: var(--font-sans);
|
|
54
|
+
--font-heading: var(--font-heading);
|
|
53
55
|
--font-mono: var(--font-mono);
|
|
54
56
|
|
|
55
57
|
/* Typography scale \u2014 each entry bundles font-size + line-height +
|
|
@@ -150,6 +152,7 @@ var t=`/* ============================================
|
|
|
150
152
|
.text-theme-muted { color: var(--color-text-muted); }
|
|
151
153
|
.text-theme-light { color: var(--color-text-light); }
|
|
152
154
|
.text-theme-inverse { color: var(--color-text-inverse); }
|
|
155
|
+
.text-theme-on-dark { color: var(--color-text-on-dark, var(--color-text-inverse)); }
|
|
153
156
|
.text-heading { color: var(--color-heading); }
|
|
154
157
|
.text-selection { color: var(--color-selection); }
|
|
155
158
|
.border-theme { border-color: var(--color-border); }
|
|
@@ -186,6 +189,82 @@ var t=`/* ============================================
|
|
|
186
189
|
background-color: var(--color-selection-bg);
|
|
187
190
|
color: var(--color-selection-text);
|
|
188
191
|
}
|
|
192
|
+
|
|
193
|
+
/* Sticky-footer hygiene for site layouts that use flex-grow mains. */
|
|
194
|
+
body {
|
|
195
|
+
min-height: 100dvh;
|
|
196
|
+
display: flex;
|
|
197
|
+
flex-direction: column;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Reusable editorial decoration hooks. Themes provide colors through
|
|
201
|
+
semantic tokens; these classes only define layering and gradients. */
|
|
202
|
+
.hero-decor,
|
|
203
|
+
.cta-decor {
|
|
204
|
+
position: relative;
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
isolation: isolate;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.hero-decor > *,
|
|
210
|
+
.cta-decor > * {
|
|
211
|
+
position: relative;
|
|
212
|
+
z-index: 1;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.hero-decor::before,
|
|
216
|
+
.hero-decor::after,
|
|
217
|
+
.cta-decor::before,
|
|
218
|
+
.cta-decor::after {
|
|
219
|
+
content: "";
|
|
220
|
+
position: absolute;
|
|
221
|
+
pointer-events: none;
|
|
222
|
+
z-index: 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.hero-decor::before {
|
|
226
|
+
inset: -30% -20% auto;
|
|
227
|
+
height: 120%;
|
|
228
|
+
background:
|
|
229
|
+
radial-gradient(circle at 50% 20%, rgb(from var(--color-accent) r g b / 0.5), transparent 28%),
|
|
230
|
+
radial-gradient(circle at 68% 38%, rgb(from var(--color-brand-dark) r g b / 0.35), transparent 36%);
|
|
231
|
+
filter: blur(8px);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.hero-decor::after,
|
|
235
|
+
.cta-decor::before {
|
|
236
|
+
inset: 0;
|
|
237
|
+
opacity: 0.12;
|
|
238
|
+
mix-blend-mode: soft-light;
|
|
239
|
+
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");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.cta-decor::after {
|
|
243
|
+
width: min(48rem, 70vw);
|
|
244
|
+
aspect-ratio: 1;
|
|
245
|
+
right: -18rem;
|
|
246
|
+
bottom: -28rem;
|
|
247
|
+
border-radius: 9999px;
|
|
248
|
+
background: radial-gradient(circle, rgb(from var(--color-brand) r g b / 0.42), transparent 66%);
|
|
249
|
+
filter: blur(6px);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.pulse-sparkle {
|
|
253
|
+
animation: hero-pulse 2.8s ease-in-out infinite;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@keyframes hero-pulse {
|
|
257
|
+
0%, 100% {
|
|
258
|
+
opacity: 0.55;
|
|
259
|
+
transform: scale(1);
|
|
260
|
+
box-shadow: 0 0 0 0 rgb(from var(--color-accent) r g b / 0.28);
|
|
261
|
+
}
|
|
262
|
+
50% {
|
|
263
|
+
opacity: 1;
|
|
264
|
+
transform: scale(1.18);
|
|
265
|
+
box-shadow: 0 0 0 10px rgb(from var(--color-accent) r g b / 0);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
189
268
|
}
|
|
190
269
|
|
|
191
270
|
/* ============================================
|
|
@@ -280,9 +359,9 @@ var t=`/* ============================================
|
|
|
280
359
|
display: block;
|
|
281
360
|
}
|
|
282
361
|
}
|
|
283
|
-
`;function o(
|
|
362
|
+
`;function o(t){return e+`
|
|
284
363
|
|
|
285
|
-
`+
|
|
364
|
+
`+t}export{o as composeTheme};
|
|
286
365
|
|
|
287
|
-
//# debugId=
|
|
366
|
+
//# debugId=D9CE28F8B5B44B4064756E2164756E21
|
|
288
367
|
//# sourceMappingURL=themes.js.map
|
package/dist/themes.js.map
CHANGED
|
@@ -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": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWO,SAAS,CAAY,CAAC,EAA0B,CACrD,OAAO,EAAe;AAAA;AAAA,EAAS",
|
|
8
|
+
"debugId": "D9CE28F8B5B44B4064756E2164756E21",
|
|
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.
|
|
3
|
+
"version": "0.2.0-alpha.50",
|
|
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,55 @@
|
|
|
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 upsertRecord(name: string): Promise<void> {
|
|
20
|
+
const lookupUrl = `${baseUrl}/zones/${zoneId}/dns_records?type=A&name=${encodeURIComponent(name)}`;
|
|
21
|
+
const lookup = await fetch(lookupUrl, { headers });
|
|
22
|
+
const payload = (await readJsonResponse(
|
|
23
|
+
lookup,
|
|
24
|
+
"Cloudflare DNS lookup",
|
|
25
|
+
)) as CloudflareResult;
|
|
26
|
+
if (!lookup.ok || !payload.success) {
|
|
27
|
+
throw new Error(`Cloudflare DNS lookup failed: ${JSON.stringify(payload)}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const existing = payload.result?.[0];
|
|
31
|
+
const url = existing
|
|
32
|
+
? `${baseUrl}/zones/${zoneId}/dns_records/${existing.id}`
|
|
33
|
+
: `${baseUrl}/zones/${zoneId}/dns_records`;
|
|
34
|
+
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
method: existing ? "PUT" : "POST",
|
|
37
|
+
headers,
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
type: "A",
|
|
40
|
+
name,
|
|
41
|
+
content: serverIp,
|
|
42
|
+
ttl: 1,
|
|
43
|
+
proxied: true,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
const result = (await readJsonResponse(
|
|
47
|
+
response,
|
|
48
|
+
"Cloudflare DNS upsert",
|
|
49
|
+
)) as CloudflareResult;
|
|
50
|
+
if (!response.ok || !result.success) {
|
|
51
|
+
throw new Error(`Cloudflare DNS upsert failed: ${JSON.stringify(result)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
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
|
+
}
|