orionfold-relay 0.15.4 → 0.15.5

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/cli.js CHANGED
@@ -25235,8 +25235,8 @@ import { execFileSync as execFileSync3 } from "child_process";
25235
25235
  import yaml12 from "js-yaml";
25236
25236
  import semver from "semver";
25237
25237
  function relayCoreVersion() {
25238
- if (semver.valid("0.15.4")) {
25239
- return "0.15.4";
25238
+ if (semver.valid("0.15.5")) {
25239
+ return "0.15.5";
25240
25240
  }
25241
25241
  try {
25242
25242
  const root = getAppRoot(import.meta.dirname, 3);
@@ -26049,7 +26049,13 @@ async function main() {
26049
26049
  RELAY_DATA_DIR: DATA_DIR,
26050
26050
  RELAY_LAUNCH_CWD: launchCwd2,
26051
26051
  PORT: String(actualPort),
26052
- ...opts.safeMode ? { RELAY_SAFE_MODE: "true" } : {}
26052
+ ...opts.safeMode ? { RELAY_SAFE_MODE: "true" } : {},
26053
+ // In dev mode, Next blocks cross-origin /_next/* dev-asset requests from
26054
+ // the LAN client's IP, breaking the app over the network (issue #13).
26055
+ // When the operator has opted into non-loopback binding, tell next.config
26056
+ // to allow any dev origin. Mirrors the same trust decision as the warning
26057
+ // above; harmless for the prebuilt `next start` path (no dev-origin gate).
26058
+ ...isNonLoopbackHost(bindHost) ? { RELAY_ALLOW_LAN_ORIGINS: "true" } : {}
26053
26059
  }
26054
26060
  });
26055
26061
  if (opts.open !== false) {
package/next.config.mjs CHANGED
@@ -1,8 +1,28 @@
1
+ // When the operator opts into LAN binding (`--hostname` to a non-loopback host,
2
+ // see bin/cli.ts), the CLI sets RELAY_ALLOW_LAN_ORIGINS=true. In dev mode Next
3
+ // otherwise blocks cross-origin requests to /_next/* dev assets from the LAN
4
+ // client's IP — which silently breaks the whole app over the network (issue
5
+ // #13). The client IP is unknowable at config-load time (the bind host is
6
+ // 0.0.0.0 = "all interfaces"), and Next's matcher explicitly rejects a bare
7
+ // "*"/"**" catch-all, so we allow every RFC1918 private-network range instead.
8
+ // This matches the "trusted network" assumption the --hostname warning already
9
+ // states, while still blocking public origins. (Prod `next start` has no such
10
+ // gate; this only affects the dev-mode npx path.)
11
+ const RFC1918_DEV_ORIGINS = [
12
+ "10.*.*.*",
13
+ "192.168.*.*",
14
+ // 172.16.0.0/12 — Next's matcher globs per-octet, so enumerate 16–31.
15
+ ...Array.from({ length: 16 }, (_, i) => `172.${16 + i}.*.*`),
16
+ ];
17
+ const allowLanDevOrigins = process.env.RELAY_ALLOW_LAN_ORIGINS === "true";
18
+
1
19
  /** @type {import('next').NextConfig} */
2
20
  const nextConfig = {
3
21
  serverExternalPackages: ["better-sqlite3", "pdf-parse", "pdfjs-dist"],
4
22
  devIndicators: false,
5
- allowedDevOrigins: ["127.0.0.1"],
23
+ allowedDevOrigins: allowLanDevOrigins
24
+ ? ["127.0.0.1", ...RFC1918_DEV_ORIGINS]
25
+ : ["127.0.0.1"],
6
26
  // The in-app kindle reader was removed; the book lives at ainative.business.
7
27
  // Redirect legacy /book links (and any chapter-anchored deep links) there.
8
28
  async redirects() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orionfold-relay",
3
- "version": "0.15.4",
3
+ "version": "0.15.5",
4
4
  "description": "Orionfold Relay — a local-first, multi-agent orchestration runtime and builder scaffold for AI-native work.",
5
5
  "keywords": [
6
6
  "ai",
@@ -68,7 +68,13 @@ const CRITICAL_THEME_CSS = `
68
68
  --surface-2: oklch(0.16 0.02 250);
69
69
  --border: oklch(0.26 0.015 250);
70
70
  }
71
- html { background: var(--background); font-size: 14px; }
71
+ /* Root rem base. Fixed 14px left the whole rem-based design tiny on high-res
72
+ 4K displays (issue #4) — text stayed 14px regardless of viewport, forcing
73
+ browser zoom. This clamp holds a flat 14px through 1920px-wide viewports
74
+ (unchanged for existing laptop/desktop users) and only ramps up on QHD/4K:
75
+ ~16px at 2560px, capped at 18px from 3200px+. Type and spacing grow
76
+ proportionally (the design is rem-based) with no component changes. */
77
+ html { background: var(--background); font-size: clamp(14px, 0.35vw + 7.3px, 18px); }
72
78
  `.replace(/\s+/g, " ").trim();
73
79
 
74
80
  export default async function RootLayout({
@@ -187,7 +187,7 @@ export function InstanceSection() {
187
187
  Running on the main dev repo. Instance upgrade features are disabled.
188
188
  Set{" "}
189
189
  <code className="font-mono text-[11px] px-1 py-0.5 rounded bg-muted">
190
- AINATIVE_INSTANCE_MODE=true
190
+ RELAY_INSTANCE_MODE=true
191
191
  </code>{" "}
192
192
  to test.
193
193
  </p>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import {
5
+ AlertTriangle,
5
6
  ChevronDown,
6
7
  ChevronRight,
7
8
  Network,
@@ -286,6 +287,7 @@ function ProviderRow({
286
287
  export function ProvidersAndRuntimesSection() {
287
288
  const [data, setData] = useState<ProvidersPayload | null>(null);
288
289
  const [loading, setLoading] = useState(true);
290
+ const [error, setError] = useState<string | null>(null);
289
291
  const [anthropicOpen, setAnthropicOpen] = useState(false);
290
292
  const [openAIOpen, setOpenAIOpen] = useState(false);
291
293
  const [openAILoginState, setOpenAILoginState] = useState<OpenAILoginState | null>(null);
@@ -298,8 +300,20 @@ export function ProvidersAndRuntimesSection() {
298
300
  if (res.ok) {
299
301
  const json = (await res.json()) as ProvidersPayload;
300
302
  setData(json);
303
+ setError(null);
301
304
  return json;
302
305
  }
306
+ // A non-OK response left the section spinning forever (issue #9): the
307
+ // render guard is `loading || !data`, so `data` staying null on error
308
+ // meant a permanent "Loading…" card with no visible failure. Surface it.
309
+ setError(`Failed to load provider configuration (HTTP ${res.status}).`);
310
+ return null;
311
+ } catch (err) {
312
+ setError(
313
+ err instanceof Error
314
+ ? `Failed to load provider configuration: ${err.message}`
315
+ : "Failed to load provider configuration.",
316
+ );
303
317
  return null;
304
318
  } finally {
305
319
  setLoading(false);
@@ -630,7 +644,7 @@ export function ProvidersAndRuntimesSection() {
630
644
 
631
645
  // ── Render ───────────────────────────────────────────────────────
632
646
 
633
- if (loading || !data) {
647
+ if (loading) {
634
648
  return (
635
649
  <Card className="surface-card">
636
650
  <CardHeader>
@@ -644,6 +658,38 @@ export function ProvidersAndRuntimesSection() {
644
658
  );
645
659
  }
646
660
 
661
+ // Fetch finished but produced no data (non-OK response or thrown error).
662
+ // Show an actionable error with a retry instead of an endless spinner —
663
+ // the previous `loading || !data` guard silently hung here (issue #9).
664
+ if (!data) {
665
+ return (
666
+ <Card className="surface-card">
667
+ <CardHeader>
668
+ <CardTitle className="flex items-center gap-2">
669
+ <AlertTriangle className="h-5 w-5 text-destructive" />
670
+ Providers &amp; Runtimes
671
+ </CardTitle>
672
+ <CardDescription>
673
+ {error ?? "Failed to load provider configuration."}
674
+ </CardDescription>
675
+ </CardHeader>
676
+ <CardContent>
677
+ <button
678
+ type="button"
679
+ onClick={() => {
680
+ setLoading(true);
681
+ setError(null);
682
+ void fetchData();
683
+ }}
684
+ className="rounded-md border border-input bg-background px-3 py-1.5 text-sm font-medium hover:bg-accent hover:text-accent-foreground"
685
+ >
686
+ Retry
687
+ </button>
688
+ </CardContent>
689
+ </Card>
690
+ );
691
+ }
692
+
647
693
  const { providers, routingPreference, configuredProviderCount } = data;
648
694
  const openAIProvider: ProviderState = {
649
695
  ...providers.openai,