relmio 0.2.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/CHANGELOG.md +190 -0
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/SPEC.md +140 -0
- package/docs/architecture.md +127 -0
- package/docs/brand.md +43 -0
- package/docs/images/brand/relmio-concept-source.png +0 -0
- package/docs/images/brand/relmio-mark.svg +16 -0
- package/docs/images/setup/01-local-sign-in-ready.png +0 -0
- package/docs/images/setup/02-vps-identity-confirmed.png +0 -0
- package/docs/images/setup/03-n8n-detected.png +0 -0
- package/docs/images/setup/04-install-plan.png +0 -0
- package/docs/images/setup/05-bridge-ready.png +0 -0
- package/docs/maintenance.md +157 -0
- package/docs/manual-install.md +282 -0
- package/docs/n8n-configuration.md +199 -0
- package/docs/npm-publish.md +285 -0
- package/docs/roadmap.md +109 -0
- package/docs/security.md +105 -0
- package/docs/troubleshooting.md +193 -0
- package/docs/video-outline.md +152 -0
- package/package.json +45 -0
- package/scripts/build-npm-package.js +112 -0
- package/scripts/check-release-metadata.js +100 -0
- package/scripts/check-syntax.js +59 -0
- package/scripts/preview.js +69 -0
- package/src/cli.js +57 -0
- package/src/domain/safety.js +59 -0
- package/src/domain/templates.js +65 -0
- package/src/domain/validation.js +76 -0
- package/src/infrastructure/ssh.js +268 -0
- package/src/services/discovery.js +96 -0
- package/src/services/installer.js +239 -0
- package/src/services/oauth.js +351 -0
- package/src/ui/app.js +526 -0
- package/src/ui/index.html +440 -0
- package/src/ui/styles.css +645 -0
- package/src/ui/time.js +15 -0
- package/src/web/server.js +489 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta
|
|
7
|
+
name="description"
|
|
8
|
+
content="Relmio connects compatible AI clients to a private ChatGPT/Codex OAuth sidecar, starting with self-hosted n8n."
|
|
9
|
+
/>
|
|
10
|
+
<title>Relmio — n8n Setup</title>
|
|
11
|
+
<link
|
|
12
|
+
rel="icon"
|
|
13
|
+
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23137c74' d='M7 8h19l31 24H41L26 20v10L7 17Z'/%3E%3Cpath fill='%2312211f' d='M7 47l19-13v10l15-12h16L26 56H7Z'/%3E%3C/svg%3E"
|
|
14
|
+
/>
|
|
15
|
+
<link rel="stylesheet" href="/styles.css" />
|
|
16
|
+
<script src="/app.js" type="module"></script>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<a class="skip-link" href="#main-content">Skip to setup</a>
|
|
20
|
+
|
|
21
|
+
<header class="site-header">
|
|
22
|
+
<div class="brand">
|
|
23
|
+
<svg
|
|
24
|
+
class="brand-mark"
|
|
25
|
+
viewBox="0 0 64 64"
|
|
26
|
+
aria-hidden="true"
|
|
27
|
+
focusable="false"
|
|
28
|
+
>
|
|
29
|
+
<path fill="#137c74" d="M7 8h19l31 24H41L26 20v10L7 17Z" />
|
|
30
|
+
<path fill="#12211f" d="M7 47l19-13v10l15-12h16L26 56H7Z" />
|
|
31
|
+
</svg>
|
|
32
|
+
<span>Relmio</span>
|
|
33
|
+
</div>
|
|
34
|
+
<span class="local-badge">Runs only on this computer</span>
|
|
35
|
+
</header>
|
|
36
|
+
|
|
37
|
+
<main id="main-content" class="shell">
|
|
38
|
+
<section class="intro" aria-labelledby="page-title">
|
|
39
|
+
<p class="eyebrow">Guided VPS setup</p>
|
|
40
|
+
<h1 id="page-title">Connect n8n without changing n8n</h1>
|
|
41
|
+
<p class="lede">
|
|
42
|
+
This wizard installs one separate Docker sidecar. Your existing n8n
|
|
43
|
+
image, Compose file, container, and workflows stay untouched.
|
|
44
|
+
</p>
|
|
45
|
+
</section>
|
|
46
|
+
|
|
47
|
+
<aside class="safety-note" aria-label="Safety guarantee">
|
|
48
|
+
<strong>Sidecar-only guarantee</strong>
|
|
49
|
+
<span>
|
|
50
|
+
No n8n restart. No n8n rebuild. No public port. You approve the exact
|
|
51
|
+
plan before anything is written.
|
|
52
|
+
</span>
|
|
53
|
+
</aside>
|
|
54
|
+
|
|
55
|
+
<aside class="safety-note backup-note" aria-label="Backup reminder">
|
|
56
|
+
<strong>Back up first</strong>
|
|
57
|
+
<span>
|
|
58
|
+
Export your n8n workflows before connecting. This wizard uses
|
|
59
|
+
sidecar-only commands, but it still writes to your VPS.
|
|
60
|
+
</span>
|
|
61
|
+
</aside>
|
|
62
|
+
|
|
63
|
+
<nav class="steps" aria-label="Setup progress">
|
|
64
|
+
<ol>
|
|
65
|
+
<li data-step-marker="1" aria-current="step">
|
|
66
|
+
<span>1</span><em>Sign in</em>
|
|
67
|
+
</li>
|
|
68
|
+
<li data-step-marker="2"><span>2</span><em>VPS</em></li>
|
|
69
|
+
<li data-step-marker="3"><span>3</span><em>n8n</em></li>
|
|
70
|
+
<li data-step-marker="4"><span>4</span><em>Review</em></li>
|
|
71
|
+
<li data-step-marker="5"><span>5</span><em>Done</em></li>
|
|
72
|
+
</ol>
|
|
73
|
+
</nav>
|
|
74
|
+
|
|
75
|
+
<div id="global-message" class="message" role="status" aria-live="polite">
|
|
76
|
+
Checking your local sign-in…
|
|
77
|
+
</div>
|
|
78
|
+
<div id="global-error" class="error" role="alert" tabindex="-1" hidden></div>
|
|
79
|
+
|
|
80
|
+
<section class="panel" data-step="1" aria-labelledby="signin-title">
|
|
81
|
+
<div class="panel-heading">
|
|
82
|
+
<span class="step-kicker">Step 1 of 5</span>
|
|
83
|
+
<h2 id="signin-title" tabindex="-1">Use your local ChatGPT sign-in</h2>
|
|
84
|
+
<p>
|
|
85
|
+
The OAuth credential stays on this computer until you approve its
|
|
86
|
+
encrypted SSH upload to your VPS.
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="status-row">
|
|
91
|
+
<span id="auth-indicator" class="status-dot" aria-hidden="true"></span>
|
|
92
|
+
<div>
|
|
93
|
+
<strong id="auth-title">Checking…</strong>
|
|
94
|
+
<p id="auth-detail">Looking for your local Codex credential.</p>
|
|
95
|
+
<p id="auth-updated" class="credential-time" hidden>
|
|
96
|
+
Credential updated
|
|
97
|
+
<time id="auth-updated-time"></time>
|
|
98
|
+
(local time).
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="actions">
|
|
104
|
+
<button id="login-button" class="button secondary" type="button">
|
|
105
|
+
Sign in with ChatGPT
|
|
106
|
+
</button>
|
|
107
|
+
<a
|
|
108
|
+
id="login-link"
|
|
109
|
+
class="button secondary"
|
|
110
|
+
target="_blank"
|
|
111
|
+
rel="noopener noreferrer"
|
|
112
|
+
hidden
|
|
113
|
+
>
|
|
114
|
+
Open fresh ChatGPT sign-in
|
|
115
|
+
</a>
|
|
116
|
+
<button id="signin-next" class="button primary" type="button" disabled>
|
|
117
|
+
Continue to VPS
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
|
|
122
|
+
<section class="panel" data-step="2" aria-labelledby="vps-title" hidden>
|
|
123
|
+
<div class="panel-heading">
|
|
124
|
+
<span class="step-kicker">Step 2 of 5</span>
|
|
125
|
+
<h2 id="vps-title" tabindex="-1">Connect to your VPS</h2>
|
|
126
|
+
<p>
|
|
127
|
+
Copy the address exactly from Hostinger. Your password is used only
|
|
128
|
+
for this live SSH connection and is never saved.
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<form id="vps-form">
|
|
133
|
+
<div class="field-grid">
|
|
134
|
+
<label class="field">
|
|
135
|
+
<span>VPS address</span>
|
|
136
|
+
<input
|
|
137
|
+
id="host"
|
|
138
|
+
name="host"
|
|
139
|
+
inputmode="url"
|
|
140
|
+
autocomplete="off"
|
|
141
|
+
placeholder="your-vps-ip-or-hostname"
|
|
142
|
+
required
|
|
143
|
+
/>
|
|
144
|
+
<small>Use the full IP address—do not add <code>ssh root@</code>.</small>
|
|
145
|
+
</label>
|
|
146
|
+
|
|
147
|
+
<label class="field compact">
|
|
148
|
+
<span>SSH port</span>
|
|
149
|
+
<input
|
|
150
|
+
id="port"
|
|
151
|
+
name="port"
|
|
152
|
+
type="number"
|
|
153
|
+
min="1"
|
|
154
|
+
max="65535"
|
|
155
|
+
value="22"
|
|
156
|
+
required
|
|
157
|
+
/>
|
|
158
|
+
</label>
|
|
159
|
+
|
|
160
|
+
<label class="field compact">
|
|
161
|
+
<span>Username</span>
|
|
162
|
+
<input
|
|
163
|
+
id="username"
|
|
164
|
+
name="username"
|
|
165
|
+
autocomplete="username"
|
|
166
|
+
value="root"
|
|
167
|
+
required
|
|
168
|
+
/>
|
|
169
|
+
<small>
|
|
170
|
+
The current guided setup expects the VPS root account.
|
|
171
|
+
</small>
|
|
172
|
+
</label>
|
|
173
|
+
|
|
174
|
+
<label class="field">
|
|
175
|
+
<span>VPS password</span>
|
|
176
|
+
<input
|
|
177
|
+
id="password"
|
|
178
|
+
name="password"
|
|
179
|
+
type="password"
|
|
180
|
+
autocomplete="current-password"
|
|
181
|
+
disabled
|
|
182
|
+
required
|
|
183
|
+
/>
|
|
184
|
+
<small>
|
|
185
|
+
This unlocks after you confirm the server identity, then clears
|
|
186
|
+
immediately after connecting.
|
|
187
|
+
</small>
|
|
188
|
+
</label>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<button id="fingerprint-button" class="button secondary" type="button">
|
|
192
|
+
Check server identity
|
|
193
|
+
</button>
|
|
194
|
+
|
|
195
|
+
<div id="fingerprint-box" class="fingerprint" hidden>
|
|
196
|
+
<span>SSH fingerprint</span>
|
|
197
|
+
<code id="fingerprint-value"></code>
|
|
198
|
+
<label class="check-row">
|
|
199
|
+
<input id="fingerprint-confirm" type="checkbox" />
|
|
200
|
+
<span>
|
|
201
|
+
This is the VPS I intended to connect to. I checked the address
|
|
202
|
+
carefully.
|
|
203
|
+
</span>
|
|
204
|
+
</label>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div class="actions">
|
|
208
|
+
<button class="button ghost back-button" data-back="1" type="button">
|
|
209
|
+
Back
|
|
210
|
+
</button>
|
|
211
|
+
<button id="connect-button" class="button primary" type="submit" disabled>
|
|
212
|
+
Connect and inspect Docker
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
</form>
|
|
216
|
+
</section>
|
|
217
|
+
|
|
218
|
+
<section class="panel" data-step="3" aria-labelledby="n8n-title" hidden>
|
|
219
|
+
<div class="panel-heading">
|
|
220
|
+
<span class="step-kicker">Step 3 of 5</span>
|
|
221
|
+
<h2 id="n8n-title" tabindex="-1">Choose the detected n8n</h2>
|
|
222
|
+
<p>
|
|
223
|
+
Discovery uses read-only Docker commands. It does not execute
|
|
224
|
+
anything inside n8n.
|
|
225
|
+
</p>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<div class="version-strip" aria-label="Detected Docker versions">
|
|
229
|
+
<span>Docker <strong id="docker-version">—</strong></span>
|
|
230
|
+
<span>Compose <strong id="compose-version">—</strong></span>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<div class="field-grid single">
|
|
234
|
+
<label class="field">
|
|
235
|
+
<span>Running n8n container</span>
|
|
236
|
+
<select id="container-select"></select>
|
|
237
|
+
</label>
|
|
238
|
+
|
|
239
|
+
<label class="field">
|
|
240
|
+
<span>Shared Docker network</span>
|
|
241
|
+
<select id="network-select"></select>
|
|
242
|
+
<small>The sidecar joins this network; n8n itself is not changed.</small>
|
|
243
|
+
</label>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<div class="actions">
|
|
247
|
+
<button class="button ghost back-button" data-back="2" type="button">
|
|
248
|
+
Back
|
|
249
|
+
</button>
|
|
250
|
+
<button id="review-button" class="button primary" type="button">
|
|
251
|
+
Review the exact plan
|
|
252
|
+
</button>
|
|
253
|
+
</div>
|
|
254
|
+
</section>
|
|
255
|
+
|
|
256
|
+
<section class="panel" data-step="4" aria-labelledby="review-title" hidden>
|
|
257
|
+
<div class="panel-heading">
|
|
258
|
+
<span class="step-kicker">Step 4 of 5</span>
|
|
259
|
+
<h2 id="review-title" tabindex="-1">Review before installing</h2>
|
|
260
|
+
<p>No existing n8n files or containers will be changed.</p>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div class="review-grid">
|
|
264
|
+
<section aria-labelledby="will-title">
|
|
265
|
+
<h3 id="will-title">The wizard will</h3>
|
|
266
|
+
<ul class="check-list">
|
|
267
|
+
<li>Create or update <code>/docker/n8n-openai-oauth</code></li>
|
|
268
|
+
<li>Build and start only the <code>openai-oauth</code> sidecar</li>
|
|
269
|
+
<li>Attach the sidecar to <strong id="review-network">—</strong></li>
|
|
270
|
+
<li>
|
|
271
|
+
Use the private hostname
|
|
272
|
+
<code id="review-endpoint">n8n-openai-oauth</code>
|
|
273
|
+
</li>
|
|
274
|
+
<li>Upload the OAuth file with owner-only permissions</li>
|
|
275
|
+
</ul>
|
|
276
|
+
</section>
|
|
277
|
+
<section aria-labelledby="wont-title">
|
|
278
|
+
<h3 id="wont-title">The wizard will not</h3>
|
|
279
|
+
<ul class="no-list">
|
|
280
|
+
<li>Edit or rebuild the n8n image</li>
|
|
281
|
+
<li>Stop, restart, or recreate n8n</li>
|
|
282
|
+
<li>Publish port <code>10531</code></li>
|
|
283
|
+
<li>Create a Traefik route</li>
|
|
284
|
+
</ul>
|
|
285
|
+
</section>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<label class="check-row approval">
|
|
289
|
+
<input id="install-confirm" type="checkbox" />
|
|
290
|
+
<span>
|
|
291
|
+
I approve this sidecar-only installation and understand
|
|
292
|
+
<code>openai-oauth</code> is an unofficial project.
|
|
293
|
+
</span>
|
|
294
|
+
</label>
|
|
295
|
+
|
|
296
|
+
<div class="actions">
|
|
297
|
+
<button class="button ghost back-button" data-back="3" type="button">
|
|
298
|
+
Back
|
|
299
|
+
</button>
|
|
300
|
+
<button id="install-button" class="button primary" type="button" disabled>
|
|
301
|
+
Install the sidecar
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
</section>
|
|
305
|
+
|
|
306
|
+
<section class="panel success-panel" data-step="5" aria-labelledby="done-title" hidden>
|
|
307
|
+
<div class="success-mark" aria-hidden="true">✓</div>
|
|
308
|
+
<div class="panel-heading">
|
|
309
|
+
<span class="step-kicker">Step 5 of 5</span>
|
|
310
|
+
<h2 id="done-title" tabindex="-1">The private bridge is ready</h2>
|
|
311
|
+
<p>
|
|
312
|
+
Copy the OpenAI credential values first, then use one of the node
|
|
313
|
+
recipes below.
|
|
314
|
+
</p>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<h3>1. OpenAI credential</h3>
|
|
318
|
+
<dl class="result-list">
|
|
319
|
+
<div>
|
|
320
|
+
<dt>Base URL</dt>
|
|
321
|
+
<dd class="result-value">
|
|
322
|
+
<code id="result-url">http://n8n-openai-oauth:10531/v1</code>
|
|
323
|
+
<button
|
|
324
|
+
class="button secondary copy-value"
|
|
325
|
+
type="button"
|
|
326
|
+
data-copy-target="result-url"
|
|
327
|
+
data-copy-label="Base URL"
|
|
328
|
+
aria-label="Copy Base URL"
|
|
329
|
+
>
|
|
330
|
+
Copy
|
|
331
|
+
</button>
|
|
332
|
+
</dd>
|
|
333
|
+
</div>
|
|
334
|
+
<div>
|
|
335
|
+
<dt>API key</dt>
|
|
336
|
+
<dd class="result-value">
|
|
337
|
+
<code id="result-key">local-only</code>
|
|
338
|
+
<button
|
|
339
|
+
class="button secondary copy-value"
|
|
340
|
+
type="button"
|
|
341
|
+
data-copy-target="result-key"
|
|
342
|
+
data-copy-label="API key"
|
|
343
|
+
aria-label="Copy API key"
|
|
344
|
+
>
|
|
345
|
+
Copy
|
|
346
|
+
</button>
|
|
347
|
+
</dd>
|
|
348
|
+
</div>
|
|
349
|
+
<div>
|
|
350
|
+
<dt>Organization ID</dt>
|
|
351
|
+
<dd>Leave empty</dd>
|
|
352
|
+
</div>
|
|
353
|
+
<div>
|
|
354
|
+
<dt>Custom header</dt>
|
|
355
|
+
<dd><strong>Off</strong></dd>
|
|
356
|
+
</div>
|
|
357
|
+
</dl>
|
|
358
|
+
|
|
359
|
+
<button id="copy-settings" class="button secondary" type="button">
|
|
360
|
+
Copy credential settings
|
|
361
|
+
</button>
|
|
362
|
+
|
|
363
|
+
<div class="model-box">
|
|
364
|
+
<h3>2. OpenAI Chat Model — AI Agent or Basic LLM Chain</h3>
|
|
365
|
+
<dl class="result-list">
|
|
366
|
+
<div>
|
|
367
|
+
<dt>Model ID</dt>
|
|
368
|
+
<dd class="result-value">
|
|
369
|
+
<code id="result-model">—</code>
|
|
370
|
+
<button
|
|
371
|
+
class="button secondary copy-value"
|
|
372
|
+
type="button"
|
|
373
|
+
data-copy-target="result-model"
|
|
374
|
+
data-copy-label="model ID"
|
|
375
|
+
aria-label="Copy model ID"
|
|
376
|
+
>
|
|
377
|
+
Copy
|
|
378
|
+
</button>
|
|
379
|
+
</dd>
|
|
380
|
+
</div>
|
|
381
|
+
<div>
|
|
382
|
+
<dt>Responses API</dt>
|
|
383
|
+
<dd><strong>On</strong> for Chat Model node version 1.3</dd>
|
|
384
|
+
</div>
|
|
385
|
+
</dl>
|
|
386
|
+
<p>All models detected: <span id="result-models">—</span></p>
|
|
387
|
+
<p>
|
|
388
|
+
If the Responses API switch is absent, keep the node's default
|
|
389
|
+
Chat Completions behavior; the bridge supports that route too.
|
|
390
|
+
</p>
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<div class="model-box">
|
|
394
|
+
<h3>3. HTTP Request node</h3>
|
|
395
|
+
<dl class="result-list">
|
|
396
|
+
<div>
|
|
397
|
+
<dt>Method</dt>
|
|
398
|
+
<dd><code>POST</code></dd>
|
|
399
|
+
</div>
|
|
400
|
+
<div>
|
|
401
|
+
<dt>URL</dt>
|
|
402
|
+
<dd class="result-value">
|
|
403
|
+
<code id="result-http-url">
|
|
404
|
+
http://n8n-openai-oauth:10531/v1/responses
|
|
405
|
+
</code>
|
|
406
|
+
<button
|
|
407
|
+
class="button secondary copy-value"
|
|
408
|
+
type="button"
|
|
409
|
+
data-copy-target="result-http-url"
|
|
410
|
+
data-copy-label="HTTP endpoint"
|
|
411
|
+
aria-label="Copy HTTP endpoint"
|
|
412
|
+
>
|
|
413
|
+
Copy
|
|
414
|
+
</button>
|
|
415
|
+
</dd>
|
|
416
|
+
</div>
|
|
417
|
+
<div>
|
|
418
|
+
<dt>Authentication</dt>
|
|
419
|
+
<dd>None</dd>
|
|
420
|
+
</div>
|
|
421
|
+
</dl>
|
|
422
|
+
</div>
|
|
423
|
+
|
|
424
|
+
<p class="final-note">
|
|
425
|
+
The README includes complete AI Agent, Basic LLM Chain, JSON, and
|
|
426
|
+
importable cURL examples. Start with no built-in tools. You can now
|
|
427
|
+
return to Terminal and press Control+C to close this local wizard;
|
|
428
|
+
the VPS sidecar will keep running.
|
|
429
|
+
</p>
|
|
430
|
+
</section>
|
|
431
|
+
</main>
|
|
432
|
+
|
|
433
|
+
<footer>
|
|
434
|
+
<p>
|
|
435
|
+
Personal and experimental use only. Keep your OAuth credential private
|
|
436
|
+
and follow OpenAI’s terms and usage policies.
|
|
437
|
+
</p>
|
|
438
|
+
</footer>
|
|
439
|
+
</body>
|
|
440
|
+
</html>
|