relmio 0.2.14 → 0.3.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 CHANGED
@@ -7,6 +7,41 @@ checks the registry separately after publication.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## [0.3.0] - 2026-08-05
11
+
12
+ ### Added
13
+
14
+ - Add a copy-ready n8n HTTP Request recipe to the local wizard, including the
15
+ private Chat Completions URL, Generic Credential Type → Bearer Auth fields,
16
+ the harmless `local-only` bearer placeholder, JSON headers, the structured
17
+ response-format body, and a full recipe copy action.
18
+ - Add the same structured `gpt-5.6-sol` example and importable cURL recipe to
19
+ the GitHub README, npm README, and n8n configuration guide.
20
+ - Add a repository-local changelog skill that standardizes Relmio's patch,
21
+ pre-1.0 feature, and stable major release numbering and metadata checks.
22
+
23
+ ### Changed
24
+
25
+ - Treat `0.3.0` as Relmio's major feature release within the pre-1.0 series;
26
+ it consolidates the key improvements shipped from v0.2.10 through v0.2.14:
27
+ resilient Windows OAuth/bootstrap flows, native Command Prompt installation,
28
+ compact accessible wizard recipes, verified Homebrew/package-manager
29
+ preparation, and release-time package checks.
30
+ - Keep the HTTP Request body aligned with n8n's `messages` format by targeting
31
+ `/v1/chat/completions`; the separate OpenAI Chat Model guidance continues to
32
+ support the Responses API where that node exposes the switch.
33
+
34
+ ## [0.2.15] - 2026-08-05
35
+
36
+ ### Fixed
37
+
38
+ - Exit cleanly when WinGet or another non-interactive validator probes the
39
+ portable command without arguments, while keeping the browser wizard for
40
+ interactive Command Prompt and PowerShell sessions.
41
+ - Add a redirected-stdio portable smoke test so future WinGet candidates cannot
42
+ regress into a never-ending default launch.
43
+ - Exclude local npm cache directories from portable release archives.
44
+
10
45
  ## [0.2.14] - 2026-08-04
11
46
 
12
47
  ### Added
package/README.md CHANGED
@@ -72,7 +72,7 @@ installing the private n8n sidecar.
72
72
  </figure>
73
73
 
74
74
  Use the site's [Install wizard](https://relmio.vercel.app/install) page for a
75
- clickable macOS/Linux, PowerShell, Command Prompt, and NPX command switcher
75
+ clickable macOS/Linux, Homebrew, PowerShell, Command Prompt, and NPX command switcher
76
76
  tailored to the current self-hosted n8n and Hostinger VPS setup path.
77
77
 
78
78
  ### Browser interface and theme modes
@@ -96,6 +96,12 @@ the VPS.
96
96
  curl -fsSL https://relmio.vercel.app/install.sh | sh
97
97
  ```
98
98
 
99
+ ### Homebrew (macOS or Linux)
100
+
101
+ ```bash
102
+ brew tap Demonbane18/relmio && brew install relmio
103
+ ```
104
+
99
105
  ### Windows PowerShell
100
106
 
101
107
  ```powershell
@@ -120,9 +126,9 @@ the temporary runtime when the wizard closes. The Command Prompt path runs as
120
126
  the current user and does not request administrator access or change Windows
121
127
  security policy.
122
128
 
123
- Homebrew tap publication and WinGet catalog approval are pending. Their install
124
- commands will be added only after each public package passes a real
125
- package-manager installation test. Use a direct installer above today.
129
+ Homebrew is available from the public `Demonbane18/relmio` tap. The WinGet
130
+ command stays hidden until Microsoft accepts its catalog pull request and the
131
+ catalog updates. Until then, use Homebrew or a direct installer above.
126
132
 
127
133
  Users who already have Node.js 22 or newer can run the npm package directly:
128
134
 
@@ -213,6 +219,43 @@ Add Custom Header: Off
213
219
  The `local-only` value is a placeholder required by n8n. It is not an OpenAI
214
220
  Platform API key.
215
221
 
222
+ For an n8n **HTTP Request** node, use **Generic Credential Type** → **Bearer
223
+ Auth**, name the credential `openai-oauth`, and enter `local-only` as the
224
+ bearer token. Enable **Send Headers** with `Content-Type: application/json`,
225
+ then enable **Send Body** → **JSON** → **Using JSON** and paste:
226
+
227
+ ```json
228
+ {
229
+ "model": "gpt-5.6-sol",
230
+ "messages": [
231
+ {
232
+ "role": "user",
233
+ "content": "What is a robot?"
234
+ }
235
+ ],
236
+ "response_format": {
237
+ "type": "json_schema",
238
+ "json_schema": {
239
+ "name": "answer",
240
+ "schema": {
241
+ "type": "object",
242
+ "properties": {
243
+ "content": { "type": "string" }
244
+ },
245
+ "required": ["content"],
246
+ "additionalProperties": false
247
+ },
248
+ "strict": true
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ Use `POST http://n8n-openai-oauth:10531/v1/chat/completions` as the URL. The
255
+ local wizard's **Copy HTTP request recipe** action supplies the same fields.
256
+ Replace the model only if the wizard reports a different ID. The full guide
257
+ has the importable cURL version.
258
+
216
259
  ## Important boundaries
217
260
 
218
261
  - Relmio does not create an OpenAI Platform API key.
@@ -97,8 +97,9 @@ the chain.
97
97
 
98
98
  ## 3. HTTP Request node
99
99
 
100
- The HTTP Request recipe calls the bridge directly and does not need the n8n
101
- OpenAI credential.
100
+ The HTTP Request recipe calls the bridge directly with n8n's generic Bearer
101
+ Auth credential. It uses the Chat Completions route because the body below uses
102
+ the `messages` format shown in the n8n node.
102
103
 
103
104
  ### Copy-paste fields
104
105
 
@@ -111,69 +112,120 @@ POST
111
112
  URL:
112
113
 
113
114
  ```text
114
- http://n8n-openai-oauth:10531/v1/responses
115
+ http://n8n-openai-oauth:10531/v1/chat/completions
115
116
  ```
116
117
 
117
118
  Authentication:
118
119
 
119
120
  ```text
120
- None
121
+ Generic Credential Type
121
122
  ```
122
123
 
123
- Header 1 name:
124
+ Generic Auth Type:
124
125
 
125
126
  ```text
126
- Authorization
127
+ Bearer Auth
127
128
  ```
128
129
 
129
- Header 1 value:
130
+ Credential name:
130
131
 
131
132
  ```text
132
- Bearer local-only
133
+ openai-oauth
133
134
  ```
134
135
 
135
- Header 2 name:
136
+ Bearer token:
137
+
138
+ ```text
139
+ local-only
140
+ ```
141
+
142
+ Enable **Send Headers** and add this header:
143
+
144
+ Header name:
136
145
 
137
146
  ```text
138
147
  Content-Type
139
148
  ```
140
149
 
141
- Header 2 value:
150
+ Header value:
142
151
 
143
152
  ```text
144
153
  application/json
145
154
  ```
146
155
 
147
- JSON body:
156
+ Enable **Send Body**, select **JSON** for **Body Content Type**, and choose
157
+ **Using JSON** for **Specify Body**. Paste this body:
148
158
 
149
159
  ```json
150
160
  {
151
- "model": "PASTE_ONE_MODEL_ID_FROM_THE_WIZARD",
152
- "input": "Reply with exactly: bridge works"
161
+ "model": "gpt-5.6-sol",
162
+ "messages": [
163
+ {
164
+ "role": "user",
165
+ "content": "What is a robot?"
166
+ }
167
+ ],
168
+ "response_format": {
169
+ "type": "json_schema",
170
+ "json_schema": {
171
+ "name": "answer",
172
+ "schema": {
173
+ "type": "object",
174
+ "properties": {
175
+ "content": { "type": "string" }
176
+ },
177
+ "required": ["content"],
178
+ "additionalProperties": false
179
+ },
180
+ "strict": true
181
+ }
182
+ }
153
183
  }
154
184
  ```
155
185
 
156
- Enable **Send Headers** and **Send Body**, select a JSON body, and paste the
157
- object above.
186
+ If the wizard reports a different model ID, replace only `gpt-5.6-sol` with
187
+ that detected ID. The `local-only` bearer value is a harmless n8n placeholder;
188
+ it is not an OpenAI Platform API key.
158
189
 
159
190
  ### Importable cURL version
160
191
 
161
- The n8n HTTP Request node can import a cURL command. Replace only the model
162
- placeholder:
192
+ The n8n HTTP Request node can import this cURL command. Replace only the model
193
+ if the wizard reports a different ID:
163
194
 
164
195
  ```bash
165
196
  curl --request POST \
166
- --url http://n8n-openai-oauth:10531/v1/responses \
197
+ --url http://n8n-openai-oauth:10531/v1/chat/completions \
167
198
  --header 'Authorization: Bearer local-only' \
168
199
  --header 'Content-Type: application/json' \
169
200
  --data '{
170
- "model": "PASTE_ONE_MODEL_ID_FROM_THE_WIZARD",
171
- "input": "Reply with exactly: bridge works"
201
+ "model": "gpt-5.6-sol",
202
+ "messages": [
203
+ {
204
+ "role": "user",
205
+ "content": "What is a robot?"
206
+ }
207
+ ],
208
+ "response_format": {
209
+ "type": "json_schema",
210
+ "json_schema": {
211
+ "name": "answer",
212
+ "schema": {
213
+ "type": "object",
214
+ "properties": {
215
+ "content": { "type": "string" }
216
+ },
217
+ "required": ["content"],
218
+ "additionalProperties": false
219
+ },
220
+ "strict": true
221
+ }
222
+ }
172
223
  }'
173
224
  ```
174
225
 
175
- The placeholder header is included because it matches OpenAI-compatible
176
- client behavior. It is not a real API credential.
226
+ The cURL `Authorization` header is equivalent to the n8n Bearer Auth
227
+ credential. It is included so the command can be imported or run as a direct
228
+ connectivity check.
177
229
 
178
230
  ### Expression-driven HTTP body
179
231
 
@@ -182,8 +234,26 @@ After the fixed test succeeds, switch the entire JSON body field to
182
234
 
183
235
  ```javascript
184
236
  ={{ {
185
- model: "PASTE_ONE_MODEL_ID_FROM_THE_WIZARD",
186
- input: $json.prompt
237
+ model: "gpt-5.6-sol",
238
+ messages: [
239
+ {
240
+ role: "user",
241
+ content: $json.prompt
242
+ }
243
+ ],
244
+ response_format: {
245
+ type: "json_schema",
246
+ json_schema: {
247
+ name: "answer",
248
+ schema: {
249
+ type: "object",
250
+ properties: { content: { type: "string" } },
251
+ required: ["content"],
252
+ additionalProperties: false
253
+ },
254
+ strict: true
255
+ }
256
+ }
187
257
  } }}
188
258
  ```
189
259
 
@@ -40,6 +40,12 @@ macOS, Linux, WSL, or Git Bash:
40
40
  curl -fsSL https://relmio.vercel.app/install.sh | sh
41
41
  ```
42
42
 
43
+ Homebrew (macOS or Linux):
44
+
45
+ ```bash
46
+ brew tap Demonbane18/relmio && brew install relmio
47
+ ```
48
+
43
49
  Windows PowerShell:
44
50
 
45
51
  ```powershell
@@ -59,8 +65,9 @@ temporary runtime and verifies its SHA-256 checksum before execution. The
59
65
  Command Prompt route is PowerShell-free, non-admin, and does not change Windows
60
66
  security policy.
61
67
 
62
- Homebrew tap publication and WinGet catalog approval are pending. Until each
63
- public package passes its real install test, use one of the direct bootstrap
68
+ Homebrew is available from the public `Demonbane18/relmio` tap. The WinGet
69
+ command stays hidden until Microsoft accepts its catalog pull request and the
70
+ catalog updates. Until then, use Homebrew or one of the direct bootstrap
64
71
  commands on this page.
65
72
 
66
73
  If you choose the existing-Node fallback, confirm Node is version 22 or newer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relmio",
3
- "version": "0.2.14",
3
+ "version": "0.3.0",
4
4
  "description": "Turn a supported ChatGPT/Codex OAuth sign-in into a private OpenAI-compatible endpoint, starting with self-hosted n8n.",
5
5
  "keywords": [
6
6
  "relmio",
package/src/cli.js CHANGED
@@ -29,10 +29,18 @@ export function cliMode(argumentsList) {
29
29
  : "wizard";
30
30
  }
31
31
 
32
+ export function hasInteractiveTerminal({
33
+ input = process.stdin,
34
+ output = process.stdout,
35
+ } = {}) {
36
+ return Boolean(input?.isTTY && output?.isTTY);
37
+ }
38
+
32
39
  export async function runCli({
33
40
  argumentsList = process.argv.slice(2),
34
41
  log = console.log,
35
42
  readPackage = () => readFile(packageJsonUrl, "utf8"),
43
+ isInteractive = hasInteractiveTerminal,
36
44
  } = {}) {
37
45
  if (cliMode(argumentsList) === "version") {
38
46
  const packageJson = JSON.parse(await readPackage());
@@ -43,6 +51,13 @@ export async function runCli({
43
51
  return;
44
52
  }
45
53
 
54
+ if (!isInteractive()) {
55
+ log(
56
+ "Relmio needs an interactive terminal to open the local wizard. Run relmio from Command Prompt, PowerShell, or another terminal.",
57
+ );
58
+ return;
59
+ }
60
+
46
61
  const sessionToken = randomBytes(32).toString("base64url");
47
62
  const wizard = await startWizardServer({ sessionToken });
48
63
  const url = `${wizard.origin}/?session=${sessionToken}`;
package/src/ui/app.js CHANGED
@@ -123,8 +123,28 @@ async function copyText(value) {
123
123
  function renderHttpRequestBody(model) {
124
124
  element("result-http-body").textContent = JSON.stringify(
125
125
  {
126
- model,
127
- input: "Reply with exactly: bridge works",
126
+ model: model === "Not detected" ? "gpt-5.6-sol" : model,
127
+ messages: [
128
+ {
129
+ role: "user",
130
+ content: "What is a robot?",
131
+ },
132
+ ],
133
+ response_format: {
134
+ type: "json_schema",
135
+ json_schema: {
136
+ name: "answer",
137
+ schema: {
138
+ type: "object",
139
+ properties: {
140
+ content: { type: "string" },
141
+ },
142
+ required: ["content"],
143
+ additionalProperties: false,
144
+ },
145
+ strict: true,
146
+ },
147
+ },
128
148
  },
129
149
  null,
130
150
  2,
@@ -144,9 +164,16 @@ function copyHttpRecipe() {
144
164
  return [
145
165
  "Method: POST",
146
166
  `URL: ${element("result-http-url").textContent}`,
147
- "Authorization: Bearer local-only",
167
+ "Authentication: Generic Credential Type",
168
+ "Generic Auth Type: Bearer Auth",
169
+ "Credential: openai-oauth",
170
+ `Bearer token: ${element("result-key").textContent}`,
171
+ `Authorization: ${element("result-http-auth").textContent}`,
148
172
  "Content-Type: application/json",
149
- "Authentication: None",
173
+ "Send Headers: On",
174
+ "Send Body: On",
175
+ "Body Content Type: JSON",
176
+ "Specify Body: Using JSON",
150
177
  "JSON body:",
151
178
  element("result-http-body").textContent,
152
179
  ].join("\n");
@@ -598,7 +625,7 @@ element("install-button").addEventListener("click", async (event) => {
598
625
  element("result-model").textContent = firstModel;
599
626
  element("result-models").textContent = result.models.join(", ");
600
627
  element("result-http-url").textContent =
601
- `${result.baseUrl.replace(/\/$/u, "")}/responses`;
628
+ `${result.baseUrl.replace(/\/$/u, "")}/chat/completions`;
602
629
  renderHttpRequestBody(firstModel);
603
630
  showStep(5);
604
631
  setMessage(
package/src/ui/index.html CHANGED
@@ -488,7 +488,7 @@
488
488
  <div>
489
489
  <dt>URL</dt>
490
490
  <dd class="result-value">
491
- <code id="result-http-url">http://n8n-openai-oauth:10531/v1/responses</code>
491
+ <code id="result-http-url">http://n8n-openai-oauth:10531/v1/chat/completions</code>
492
492
  <button
493
493
  class="button secondary copy-value"
494
494
  type="button"
@@ -501,16 +501,43 @@
501
501
  </dd>
502
502
  </div>
503
503
  <div>
504
- <dt>Authorization</dt>
505
- <dd><code>Bearer local-only</code></dd>
504
+ <dt>Authentication</dt>
505
+ <dd>Generic Credential Type</dd>
506
+ </div>
507
+ <div>
508
+ <dt>Generic Auth Type</dt>
509
+ <dd>Bearer Auth</dd>
510
+ </div>
511
+ <div>
512
+ <dt>Credential</dt>
513
+ <dd><code>openai-oauth</code></dd>
514
+ </div>
515
+ <div>
516
+ <dt>Bearer token</dt>
517
+ <dd><code>local-only</code></dd>
518
+ </div>
519
+ <div>
520
+ <dt>Authorization header</dt>
521
+ <dd class="result-value">
522
+ <code id="result-http-auth">Bearer local-only</code>
523
+ <button
524
+ class="button secondary copy-value"
525
+ type="button"
526
+ data-copy-target="result-http-auth"
527
+ data-copy-label="Authorization header"
528
+ aria-label="Copy Authorization header"
529
+ >
530
+ Copy
531
+ </button>
532
+ </dd>
506
533
  </div>
507
534
  <div>
508
535
  <dt>Content-Type</dt>
509
536
  <dd><code>application/json</code></dd>
510
537
  </div>
511
538
  <div>
512
- <dt>Authentication</dt>
513
- <dd>None</dd>
539
+ <dt>Send body</dt>
540
+ <dd>On · JSON · Using JSON</dd>
514
541
  </div>
515
542
  <div class="sample-body-row">
516
543
  <dt>JSON body</dt>