@lotics/app-sdk 0.55.0 → 0.55.1

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/src/rpc.d.ts CHANGED
@@ -159,6 +159,16 @@ export interface AgentRunContinuePayload {
159
159
  * `rpcAgentRun` streams the first.
160
160
  */
161
161
  export declare function rpcAgentRunContinue(payload: AgentRunContinuePayload, onText: (chunk: string) => void): AgentRunHandle;
162
+ /**
163
+ * The password-session header. It is deliberately NOT `Authorization`: this
164
+ * token authenticates nobody — it proves the visitor knows the app's shared
165
+ * link password — and the credential header already carries API keys and OAuth
166
+ * bearers. Sharing it meant the server's auth middleware rejected the request
167
+ * as an unknown bearer before the password gate ever ran, which took every
168
+ * password-gated public app offline. Wire constant, mirrored server-side by
169
+ * `APP_PUBLIC_SESSION_HEADER`; both sides are pinned by tests.
170
+ */
171
+ export declare const APP_PUBLIC_SESSION_HEADER = "x-lotics-app-session";
162
172
  /**
163
173
  * The error message for a non-ok response. A genuine JSON error (a 4xx carrying
164
174
  * a `message`) surfaces verbatim; a non-JSON body (a gateway HTML page), any
package/dist/src/rpc.js CHANGED
@@ -250,7 +250,7 @@ export function rpcAgentRunContinue(payload, onText) {
250
250
  const { app_id } = await boot();
251
251
  const headers = { "content-type": "application/json" };
252
252
  if (sessionToken)
253
- headers["authorization"] = `Bearer ${sessionToken}`;
253
+ headers[APP_PUBLIC_SESSION_HEADER] = sessionToken;
254
254
  const res = await fetch(`${API_BASE}/v1/apps/${app_id}/agent-runs/${encodeURIComponent(payload.run_id)}/continue`, {
255
255
  method: "POST",
256
256
  headers,
@@ -288,7 +288,7 @@ function agentRunStandalone(payload, onText, onRunId) {
288
288
  const { app_id } = await boot();
289
289
  const headers = { "content-type": "application/json" };
290
290
  if (sessionToken)
291
- headers["authorization"] = `Bearer ${sessionToken}`;
291
+ headers[APP_PUBLIC_SESSION_HEADER] = sessionToken;
292
292
  const res = await fetch(`${API_BASE}/v1/apps/${app_id}/agents/${encodeURIComponent(payload.alias)}/runs`, {
293
293
  method: "POST",
294
294
  headers,
@@ -336,6 +336,16 @@ const PASSWORD_REQUIRED_CODE = "PASSWORD_REQUIRED";
336
336
  let bootPromise = null;
337
337
  let appInfoPromise = null;
338
338
  let sessionToken = null;
339
+ /**
340
+ * The password-session header. It is deliberately NOT `Authorization`: this
341
+ * token authenticates nobody — it proves the visitor knows the app's shared
342
+ * link password — and the credential header already carries API keys and OAuth
343
+ * bearers. Sharing it meant the server's auth middleware rejected the request
344
+ * as an unknown bearer before the password gate ever ran, which took every
345
+ * password-gated public app offline. Wire constant, mirrored server-side by
346
+ * `APP_PUBLIC_SESSION_HEADER`; both sides are pinned by tests.
347
+ */
348
+ export const APP_PUBLIC_SESSION_HEADER = "x-lotics-app-session";
339
349
  function sessionStorageKey(appId) {
340
350
  return `lotics_app_session:${appId}`;
341
351
  }
@@ -478,7 +488,7 @@ async function apiCall(method, path, body, opts) {
478
488
  if (body)
479
489
  headers["content-type"] = "application/json";
480
490
  if (sessionToken && !opts?.skipAuth) {
481
- headers["authorization"] = `Bearer ${sessionToken}`;
491
+ headers[APP_PUBLIC_SESSION_HEADER] = sessionToken;
482
492
  }
483
493
  const controller = new AbortController();
484
494
  let didTimeout = false;
package/docs/runtime.md CHANGED
@@ -132,8 +132,12 @@ coalesce; a transient failure isn't cached, the next call retries). If the app
132
132
  is password-protected, the SDK renders its own full-screen password overlay
133
133
  (plain DOM, so it works before React data arrives), exchanges the password for a
134
134
  session token, and stores it in `localStorage` under
135
- `lotics_app_session:<app_id>` with its expiry. The token rides as a `Bearer`
136
- header on subsequent calls; a `401` with error code `PASSWORD_REQUIRED` (the
135
+ `lotics_app_session:<app_id>` with its expiry. The token rides on subsequent
136
+ calls in its own `X-Lotics-App-Session` header deliberately not
137
+ `Authorization`, which carries API keys and OAuth bearers: this token
138
+ authenticates nobody, it proves the visitor knows the shared password, and
139
+ sharing the credential header made the auth layer reject it before the password
140
+ gate could read it. A `401` with error code `PASSWORD_REQUIRED` (the
137
141
  owner rotated or cleared the password) drops the stored token, re-prompts, and
138
142
  retries the original call once. There is no cancel button — the visitor enters
139
143
  the password or leaves. When `localStorage` is unavailable (private browsing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/app-sdk",
3
- "version": "0.55.0",
3
+ "version": "0.55.1",
4
4
  "description": "Runtime SDK for Lotics custom-code apps — typed hooks, postMessage bridge, mount entry point",
5
5
  "type": "module",
6
6
  "exports": {