@unoverse-platform/studio 0.1.42 → 0.1.43

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unoverse-platform/studio",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
4
4
  "type": "module",
5
5
  "description": "Unoverse Studio: the local authoring app. Reads your own rx/ folder, no backend.",
6
6
  "scripts": {
@@ -26,7 +26,7 @@ import { ComponentStore } from "@unoverse/sdk";
26
26
  import { StreamedUnoverseTemplate, useUnoverseConnection, computeAppWidth, resolveActiveLayout, visible, type ResolvedTheme, type UseVoiceServiceConfig } from "@unoverse/sdk";
27
27
  import { NativeAppHost } from "./NativeAppHost";
28
28
  import { inArrangement, viewOfSlice } from "./states";
29
- import { client, authedFetch, hasAuth, UNOVERSE_BASE } from "studio-host";
29
+ import { client, authedFetch, hasAuth, UNOVERSE_BASE, liveUniverseUrl } from "studio-host";
30
30
 
31
31
  // Per-APP test conversation: each app chats in its own conversation id, so testing
32
32
  // one client's app never inherits another's agent history/memory (the "BPP answers
@@ -45,15 +45,23 @@ const chatIdFor = (app: string) => `demo-${app.toLowerCase()}-${SESSION_NONCE}`;
45
45
  // :4105 — since the workbench retired, uWS binds the public port DIRECTLY in dev, same
46
46
  // as prod (the retired workbench was :4205, the retired gateway :4100). Prod uses the
47
47
  // page origin.
48
- const CONN_DEFAULTS = { server: "localhost:4105", workflowId: "wf-r4jzo7", triggerNode: "inputtrigger1", token: "" };
49
- const CONN_KEY = "unoverse.workbench.conn";
48
+ // THE LIVE SERVER IS NOT CONFIGURED HERE. The universe Studio is signed into (the
49
+ // header connection, via studio-host's liveUniverseUrl) IS the live environment, so
50
+ // being signed into one universe while previewing against another is impossible by
51
+ // construction. Only with NO universe connected does the preview fall back to the
52
+ // local dev platform. The old hardcoded default (`localhost:4105` + a sample
53
+ // workflow id, persisted under "unoverse.workbench.conn") is retired: it kept green
54
+ // "Connected" sessions against stale localhost stacks while the header showed a
55
+ // production universe. Workflow + trigger come from the app's manifest binding; the
56
+ // only thing persisted now is a hand-pasted JWT for auth-off dev servers.
57
+ const LOCAL_DEV_SERVER = "localhost:4105";
58
+ const CONN_DEFAULTS = { token: "" };
59
+ const CONN_KEY = "unoverse.workbench.conn.v2";
50
60
 
51
61
  function loadConn(): typeof CONN_DEFAULTS {
52
62
  try {
53
63
  const saved = JSON.parse(localStorage.getItem(CONN_KEY) ?? "{}");
54
- // Migration: drop retired ports (gateway :4100, workbench :4205) so the new default applies.
55
- if (saved.server === "localhost:4100" || saved.server === "localhost:4205") delete saved.server;
56
- return { ...CONN_DEFAULTS, ...saved };
64
+ return { token: typeof saved.token === "string" ? saved.token : "" };
57
65
  } catch {
58
66
  return { ...CONN_DEFAULTS };
59
67
  }
@@ -238,23 +246,30 @@ export function TemplatesView({
238
246
  const authToken = auth?.user?.access_token;
239
247
  const token = authToken || connCfg.token;
240
248
 
249
+ // The live server: the signed-in universe, else the local dev platform (see the
250
+ // CONN_DEFAULTS note). A universe URL is already an https origin; the bare local
251
+ // fallback gets http.
252
+ const universeUrl = liveUniverseUrl();
253
+ const liveServer = universeUrl ?? LOCAL_DEV_SERVER;
254
+ const liveBase = liveServer.includes("://") ? liveServer : `http://${liveServer}`;
255
+
241
256
  const config = useMemo(
242
257
  () => ({
243
- apiUrl: `http://${connCfg.server}`,
258
+ apiUrl: liveBase,
244
259
  // Component stream rides the Unoverse `/stream` MCP endpoint (§5b). In the canvas
245
260
  // the path that reaches Unoverse is UNOVERSE_BASE (/unoverse-mcp proxy in dev, the
246
261
  // explicit gated URL in prod) — always absolute there. In local Studio UNOVERSE_BASE
247
262
  // is "" (a folder, not a universe; its origin serves no /stream), so the stream goes
248
263
  // to the CONNECTED universe — same base the apiUrl above uses. An empty string here
249
264
  // threw `Invalid URL` inside the SDK and killed the stream silently.
250
- streamUrl: UNOVERSE_BASE || `http://${connCfg.server}`,
265
+ streamUrl: UNOVERSE_BASE || liveBase,
251
266
  // A real JWT makes the workbench a proper authenticated channel: it rides the
252
267
  // MCP stream's `Authorization` header and the REST `Authorization` header, so the
253
268
  // workflow gets full user context (incl. the email claim → CRM join). See
254
269
  // docs/AUTH_TOKEN_FLOW.md. Blank → no token (server must run with DISABLE_AUTH=true).
255
270
  getAccessToken: token ? async () => token : undefined,
256
271
  }),
257
- [connCfg.server, token],
272
+ [liveBase, token],
258
273
  );
259
274
  // The APP decides which workflow runs: the selected template's manifest binding
260
275
  // (workflow + trigger) drives the connection. No manual entry — MCP decides.
@@ -395,7 +410,7 @@ export function TemplatesView({
395
410
  // START_CALL REST triggers the workflow on the gateway. The audio WS lane is
396
411
  // derived by the SDK from the client's server origin (see the renderer) — the
397
412
  // host passes NO wsUrl, so every host (incl. srcdoc iframes) is identical.
398
- apiUrl: `http://${connCfg.server}`,
413
+ apiUrl: liveBase,
399
414
  getAccessToken: token ? async () => token : undefined,
400
415
  session: {
401
416
  userId: session.userId,
@@ -409,7 +424,7 @@ export function TemplatesView({
409
424
  store,
410
425
  }
411
426
  : undefined,
412
- [isVoice, connCfg.server, token, session, store],
427
+ [isVoice, liveBase, token, session, store],
413
428
  );
414
429
 
415
430
  // "Load the app → it calls its workflow" — but ONLY for apps with autoTrigger (Mode B).
@@ -808,7 +823,7 @@ export function TemplatesView({
808
823
  come from the selected app's manifest binding (no manual entry). */}
809
824
  {(
810
825
  [
811
- { label: "Server", value: connCfg.server, note: "environment" },
826
+ { label: "Server", value: liveServer, note: universeUrl ? "signed-in universe" : "local dev (no universe connected)" },
812
827
  { label: "Workflow", value: binding?.workflow ?? "— no app binding", note: "from app" },
813
828
  { label: "Trigger node", value: binding?.trigger ?? "—", note: "from app" },
814
829
  ]
@@ -1017,7 +1032,7 @@ export function TemplatesView({
1017
1032
  /* NATIVE: the universe serves the app; Studio just hosts it. Same
1018
1033
  ui:// shell ChatGPT loads, same boot contract, its own connections. */
1019
1034
  <NativeAppHost
1020
- server={UNOVERSE_BASE || `http://${connCfg.server}`}
1035
+ server={UNOVERSE_BASE || liveBase}
1021
1036
  token={token}
1022
1037
  templateId={`${selectedDef?.org ? `${selectedDef.org}/` : ""}${selected ?? ""}`}
1023
1038
  userId={session.userId}
package/src/host.ts CHANGED
@@ -21,6 +21,24 @@ export const UNOVERSE_BASE = "";
21
21
  /** No login offline. Kept so the shared screens can ask without branching on host. */
22
22
  export const hasAuth = false;
23
23
 
24
+ /**
25
+ * The universe Studio is signed into (the header connection), or null when offline.
26
+ * THE LIVE PREVIEW'S SERVER. One source of truth: the same selection App.tsx keeps
27
+ * (`studio:universe` is its usePersistentState pointer into the saved universe list),
28
+ * so it is impossible to be signed into one universe and silently previewing against
29
+ * another. The shared screens fall back to the local dev platform only when no
30
+ * universe is connected.
31
+ */
32
+ export function liveUniverseUrl(): string | null {
33
+ try {
34
+ const raw = localStorage.getItem("studio:universe");
35
+ const url = raw === null ? null : (JSON.parse(raw) as string | null);
36
+ return url || null;
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
24
42
  /**
25
43
  * SOURCE ACCESS: this host serves the files behind a definition, so the screens may show
26
44
  * the source view. Local Studio reads the developer's own folder, so it can.