@promte/app-library 0.1.2 → 0.1.3

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/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @promte/app-library
2
+
3
+ Docs for using the app DB API in embedded apps.
4
+
5
+ ## What "DB" means here
6
+
7
+ In this library, DB access is exposed as:
8
+
9
+ ```ts
10
+ const client = usePromteClient();
11
+ const db = client.data(); // default scope: "private"
12
+ ```
13
+
14
+ ## Scopes
15
+
16
+ - `private` (default): data is scoped per user.
17
+ - `public`: shared for the whole workspace.
18
+ - Reads are allowed for users with workspace access.
19
+ - Writes (`set`, `update`, `remove`, `push`) require workspace admin/owner.
20
+
21
+ ```ts
22
+ const privateDb = client.data(); // same as client.data("private")
23
+ const publicDb = client.data("public");
24
+ ```
25
+
26
+ ## Paths and refs
27
+
28
+ Use slash-separated paths:
29
+
30
+ ```ts
31
+ const settingsRef = client.data("public").ref("translator/settings");
32
+ ```
33
+
34
+ Path normalization trims spaces and removes extra slashes, so `" /a//b/ "` becomes `"a/b"`.
35
+
36
+ ## Core operations
37
+
38
+ ```ts
39
+ // Read
40
+ const settings = await client.data("public").ref("translator/settings").get();
41
+
42
+ // Set/replace value at path
43
+ await client.data("public").ref("translator/settings").set({
44
+ temperature: 0.2,
45
+ });
46
+
47
+ // Update multiple child paths relative to current ref
48
+ await client.data("public").ref("translator").update({
49
+ "settings/temperature": 0.3,
50
+ "features/context": true,
51
+ });
52
+
53
+ // Remove path
54
+ await client.data("public").ref("translator/features").remove();
55
+
56
+ // Push into array at path (creates array if missing/not array)
57
+ await client.data().ref("sessions/session-1/events").push({
58
+ type: "message",
59
+ at: Date.now(),
60
+ });
61
+ ```
62
+
63
+ ## Models/capabilities
64
+
65
+ You can fetch configured models/capabilities from backend:
66
+
67
+ ```ts
68
+ const client = usePromteClient();
69
+ const allModels = await client.models();
70
+ const llmsOnly = await client.models({ type: "llm", configuredOnly: true });
71
+ ```
72
+
73
+ Or use the built-in hook:
74
+
75
+ ```ts
76
+ const { models, loading, error, reload } = usePromteModels({
77
+ type: "llm",
78
+ configuredOnly: true,
79
+ });
80
+ ```
81
+
82
+ Each model includes `id`, `label`, `type` (`llm` | `embedding` | `image` | `stt` | `tts`) and `configured`.
83
+
84
+ ## Return behavior
85
+
86
+ - `get()` returns the value at the current path, or `null` if missing.
87
+ - Mutations (`set`, `update`, `remove`, `push`) return the updated scope object.
88
+
89
+ ## Common patterns
90
+
91
+ ```ts
92
+ // Save one record by id
93
+ await client.data().ref(`sessions/${sessionId}`).set(session);
94
+
95
+ // List records under a collection-like object
96
+ const sessionsObj = await client.data().ref("sessions").get();
97
+ const sessions = sessionsObj ? Object.values(sessionsObj) : [];
98
+ ```
99
+
100
+ ## Errors to expect
101
+
102
+ - `Promte settings not received yet` / `Promte token not received yet`:
103
+ provider handshake not ready.
104
+ - `Admin only`:
105
+ trying to write to `public` scope without admin rights.
106
+ - `update requires an object`:
107
+ `ref.update(...)` got a non-object input.
108
+ - `push requires a path`:
109
+ attempted `client.data().push(...)` without a ref path.
package/dist/index.d.ts CHANGED
@@ -89,6 +89,7 @@ export declare type PromteAppProviderProps = {
89
89
  export declare type PromteClient = {
90
90
  isReady(): boolean;
91
91
  getBackendUrl(): string | null;
92
+ models(req?: PromteModelsRequest): Promise<PromteModelInfo[]>;
92
93
  chatCompletions(req: ChatCompletionsRequest): Promise<ChatCompletionsResponse>;
93
94
  chatText(req: ChatCompletionsRequest): Promise<string>;
94
95
  audio: {
@@ -112,6 +113,21 @@ declare type PromteCtxValue = {
112
113
 
113
114
  export declare type PromteHandshakeState = "booting" | "waiting_for_settings" | "waiting_for_token" | "ready";
114
115
 
116
+ export declare type PromteModelInfo = {
117
+ id: string;
118
+ label: string;
119
+ type: PromteModelType;
120
+ configured: boolean;
121
+ source?: string;
122
+ };
123
+
124
+ export declare type PromteModelsRequest = {
125
+ type?: PromteModelType | PromteModelType[];
126
+ configuredOnly?: boolean;
127
+ };
128
+
129
+ export declare type PromteModelType = "llm" | "embedding" | "image" | "stt" | "tts";
130
+
115
131
  export declare type PromteSettings = {
116
132
  workspaceId: string;
117
133
  version: number;
@@ -135,6 +151,17 @@ export declare function usePromteClient(): PromteClient;
135
151
 
136
152
  export declare function usePromteDarkMode(): boolean;
137
153
 
154
+ export declare function usePromteModels(options?: UsePromteModelsOptions): {
155
+ models: PromteModelInfo[];
156
+ loading: boolean;
157
+ error: string;
158
+ reload: () => Promise<PromteModelInfo[]>;
159
+ };
160
+
161
+ export declare type UsePromteModelsOptions = PromteModelsRequest & {
162
+ auto?: boolean;
163
+ };
164
+
138
165
  export declare function usePromteSettings(): PromteSettings;
139
166
 
140
167
  export declare function usePromteToken(): string;
package/dist/index.mjs CHANGED
@@ -1,298 +1,344 @@
1
- import { jsx as M } from "react/jsx-runtime";
2
- import { createContext as A, useState as E, useMemo as j, useCallback as D, useEffect as R, useContext as L } from "react";
3
- const U = A(null);
1
+ import { jsx as B } from "react/jsx-runtime";
2
+ import { createContext as L, useState as j, useMemo as P, useCallback as M, useEffect as S, useContext as D } from "react";
3
+ const U = L(null);
4
4
  function z({
5
- children: y,
6
- expectedParentOrigin: h,
7
- initTargetOrigin: v,
8
- retryInit: f = { enabled: !0, intervalMs: 500, maxAttempts: 20 }
5
+ children: f,
6
+ expectedParentOrigin: p,
7
+ initTargetOrigin: E,
8
+ retryInit: d = { enabled: !0, intervalMs: 500, maxAttempts: 20 }
9
9
  }) {
10
- const [u, P] = E(null), [p, x] = E(null), [k, e] = E(null), [r, n] = E(null), [t, o] = E(null), i = j(() => t ? "booting" : u ? p ? "ready" : "waiting_for_token" : "waiting_for_settings", [u, p, t]), l = i === "ready", w = j(() => v || h || "*", [v, h]), d = D(() => {
11
- var g;
10
+ const [m, x] = j(null), [h, g] = j(null), [k, b] = j(null), [t, r] = j(null), [n, e] = j(null), o = P(() => n ? "booting" : m ? h ? "ready" : "waiting_for_token" : "waiting_for_settings", [m, h, n]), u = o === "ready", s = P(() => E || p || "*", [E, p]), i = M(() => {
11
+ var v;
12
12
  try {
13
- const m = { type: "init" };
14
- (g = window.parent) == null || g.postMessage(m, w);
15
- } catch (m) {
16
- o((m == null ? void 0 : m.message) || "Failed to post init");
13
+ const w = { type: "init" };
14
+ (v = window.parent) == null || v.postMessage(w, s);
15
+ } catch (w) {
16
+ e((w == null ? void 0 : w.message) || "Failed to post init");
17
17
  }
18
- }, [w]);
19
- R(() => {
20
- d();
21
- }, [d]), R(() => {
22
- if (!(f != null && f.enabled) || u) return;
23
- const g = f.intervalMs ?? 500, m = f.maxAttempts ?? 20;
24
- let a = 0;
25
- const c = window.setInterval(() => {
26
- if (u) {
27
- window.clearInterval(c);
18
+ }, [s]);
19
+ S(() => {
20
+ i();
21
+ }, [i]), S(() => {
22
+ if (!(d != null && d.enabled) || m) return;
23
+ const v = d.intervalMs ?? 500, w = d.maxAttempts ?? 20;
24
+ let c = 0;
25
+ const l = window.setInterval(() => {
26
+ if (m) {
27
+ window.clearInterval(l);
28
28
  return;
29
29
  }
30
- if (a += 1, a > m) {
31
- window.clearInterval(c);
30
+ if (c += 1, c > w) {
31
+ window.clearInterval(l);
32
32
  return;
33
33
  }
34
- d();
35
- }, g);
36
- return () => window.clearInterval(c);
37
- }, [f, u, d]), R(() => {
38
- const g = (m) => {
39
- if (h && m.origin !== h) return;
40
- const a = m.data;
41
- if (!(!a || typeof a != "object")) {
42
- if (a.type === "set-settings") {
43
- const c = a.value;
44
- if (!(c != null && c.backendUrl) || c.parentOrigin && h && c.parentOrigin !== h)
34
+ i();
35
+ }, v);
36
+ return () => window.clearInterval(l);
37
+ }, [d, m, i]), S(() => {
38
+ const v = (w) => {
39
+ if (p && w.origin !== p) return;
40
+ const c = w.data;
41
+ if (!(!c || typeof c != "object")) {
42
+ if (c.type === "set-settings") {
43
+ const l = c.value;
44
+ if (!(l != null && l.backendUrl) || l.parentOrigin && p && l.parentOrigin !== p)
45
45
  return;
46
- P(c), c != null && c.user && n(c.user), o(null);
46
+ x(l), l != null && l.user && r(l.user), e(null);
47
47
  return;
48
48
  }
49
- if (a.type === "set-token" && typeof a.value == "string") {
50
- x(a.value), o(null);
49
+ if (c.type === "set-token" && typeof c.value == "string") {
50
+ g(c.value), e(null);
51
51
  return;
52
52
  }
53
- if (a.type === "set-dark-mode" && typeof a.value == "boolean") {
54
- e(a.value);
53
+ if (c.type === "set-dark-mode" && typeof c.value == "boolean") {
54
+ b(c.value);
55
55
  return;
56
56
  }
57
- if (a.type === "set-user" && typeof a.value == "object") {
58
- n(a.value);
57
+ if (c.type === "set-user" && typeof c.value == "object") {
58
+ r(c.value);
59
59
  return;
60
60
  }
61
61
  }
62
62
  };
63
- return window.addEventListener("message", g), () => window.removeEventListener("message", g);
64
- }, [h]);
65
- const s = j(() => F(
66
- () => u,
67
- () => p
68
- ), [u, p]), T = j(
63
+ return window.addEventListener("message", v), () => window.removeEventListener("message", v);
64
+ }, [p]);
65
+ const y = P(() => O(
66
+ () => m,
67
+ () => h
68
+ ), [m, h]), a = P(
69
69
  () => ({
70
- settings: u,
71
- token: p,
70
+ settings: m,
71
+ token: h,
72
72
  isDarkMode: k,
73
- user: r,
74
- ready: l,
75
- state: i,
76
- error: t,
77
- client: s
73
+ user: t,
74
+ ready: u,
75
+ state: o,
76
+ error: n,
77
+ client: y
78
78
  }),
79
- [u, p, k, r, l, i, t, s]
79
+ [m, h, k, t, u, o, n, y]
80
80
  );
81
- return /* @__PURE__ */ M(U.Provider, { value: T, children: y });
81
+ return /* @__PURE__ */ B(U.Provider, { value: a, children: f });
82
82
  }
83
- function b() {
84
- const y = L(U);
85
- if (!y)
83
+ function $() {
84
+ const f = D(U);
85
+ if (!f)
86
86
  throw new Error("usePromte must be used inside <PromteAppProvider>");
87
- return y;
88
- }
89
- function G() {
90
- return b().settings;
87
+ return f;
91
88
  }
92
89
  function J() {
93
- return b().token;
90
+ return $().settings;
94
91
  }
95
92
  function N() {
96
- return b().isDarkMode;
93
+ return $().token;
97
94
  }
98
- function O() {
99
- return b().user;
95
+ function V() {
96
+ return $().isDarkMode;
100
97
  }
101
98
  function I() {
102
- return b().client;
99
+ return $().user;
100
+ }
101
+ function F() {
102
+ return $().client;
103
103
  }
104
- function F(y, h) {
105
- const v = (e) => typeof e == "string" && e.startsWith("gpt-5");
106
- async function f(e, r) {
107
- const n = y(), t = h();
104
+ function K(f = {}) {
105
+ const { ready: p } = $(), E = F(), [d, m] = j([]), [x, h] = j(!1), [g, k] = j(null), b = P(() => (Array.isArray(f.type) ? f.type : f.type ? [f.type] : []).filter(Boolean).map((o) => String(o)).join(","), [f.type]), t = f.configuredOnly === !0, r = f.auto !== !1, n = M(async () => {
106
+ if (!p) return [];
107
+ h(!0), k(null);
108
+ try {
109
+ const e = b ? b.split(",").filter(Boolean) : void 0, o = await E.models({ type: e, configuredOnly: t });
110
+ return m(o), o;
111
+ } catch (e) {
112
+ const o = e instanceof Error ? e.message : String(e);
113
+ throw k(o), e;
114
+ } finally {
115
+ h(!1);
116
+ }
117
+ }, [E, t, p, b]);
118
+ return S(() => {
119
+ !r || !p || n().catch(() => {
120
+ });
121
+ }, [r, p, n]), { models: d, loading: x, error: g, reload: n };
122
+ }
123
+ function O(f, p) {
124
+ const E = (t) => typeof t == "string" && t.startsWith("gpt-5");
125
+ async function d(t, r) {
126
+ const n = f(), e = p();
108
127
  if (!n) throw new Error("Promte settings not received yet");
109
- if (!t) throw new Error("Promte token not received yet");
110
- const o = new URL(n.backendUrl + "/" + e).toString();
111
- return await fetch(o, {
128
+ if (!e) throw new Error("Promte token not received yet");
129
+ const o = n.backendUrl.replace(/\/+$/, ""), u = String(t || "").replace(/^\/+/, ""), s = `${o}/${u}`;
130
+ return await fetch(s, {
112
131
  ...r,
113
132
  headers: {
114
133
  ...r.headers || {},
115
- Authorization: `Bearer ${t}`
134
+ Authorization: `Bearer ${e}`
116
135
  }
117
136
  });
118
137
  }
119
- async function u(e, r) {
120
- const n = await f(`/app-api/data/${e}`, {
138
+ async function m(t, r) {
139
+ const n = await d(`/app-api/data/${t}`, {
121
140
  method: "POST",
122
141
  headers: { "Content-Type": "application/json" },
123
142
  body: JSON.stringify(r)
124
- }), t = await n.json().catch(() => ({}));
143
+ }), e = await n.json().catch(() => ({}));
125
144
  if (!n.ok)
126
145
  throw new Error(
127
- (t == null ? void 0 : t.error) || `Request failed (${n.status})`
146
+ (e == null ? void 0 : e.error) || `Request failed (${n.status})`
128
147
  );
129
- return t == null ? void 0 : t.value;
148
+ return e == null ? void 0 : e.value;
130
149
  }
131
- async function P(e) {
150
+ async function x(t) {
132
151
  const r = new URLSearchParams();
133
- e.scope && r.set("scope", e.scope), e.path && r.set("path", e.path);
134
- const n = await f(`/app-api/data?${r.toString()}`, {
152
+ t.scope && r.set("scope", t.scope), t.path && r.set("path", t.path);
153
+ const n = await d(`/app-api/data?${r.toString()}`, {
135
154
  method: "GET"
136
- }), t = await n.json().catch(() => ({}));
155
+ }), e = await n.json().catch(() => ({}));
137
156
  if (!n.ok)
138
- throw new Error((t == null ? void 0 : t.error) || `Request failed (${n.status})`);
139
- return t == null ? void 0 : t.value;
157
+ throw new Error((e == null ? void 0 : e.error) || `Request failed (${n.status})`);
158
+ return e == null ? void 0 : e.value;
159
+ }
160
+ async function h(t) {
161
+ const r = new URLSearchParams(), n = Array.isArray(t == null ? void 0 : t.type) ? t == null ? void 0 : t.type : t != null && t.type ? [t.type] : [];
162
+ n.length > 0 && r.set("type", n.join(",")), t != null && t.configuredOnly && r.set("configuredOnly", "true");
163
+ const e = await d(
164
+ `/app-api/models${r.toString() ? `?${r.toString()}` : ""}`,
165
+ { method: "GET" }
166
+ ), o = await e.json().catch(() => ({}));
167
+ if (!e.ok)
168
+ throw new Error((o == null ? void 0 : o.error) || `Request failed (${e.status})`);
169
+ return (Array.isArray(o == null ? void 0 : o.models) ? o.models : []).map((s) => {
170
+ if (!s || typeof s != "object") return null;
171
+ const i = typeof s.id == "string" ? s.id.trim() : "", y = typeof s.label == "string" ? s.label.trim() : i, a = s.type, v = s.configured === !0, w = typeof s.source == "string" && s.source.trim() ? s.source.trim() : void 0;
172
+ return !i || a !== "llm" && a !== "embedding" && a !== "image" && a !== "stt" && a !== "tts" ? null : {
173
+ id: i,
174
+ label: y || i,
175
+ type: a,
176
+ configured: v,
177
+ source: w
178
+ };
179
+ }).filter(
180
+ (s) => !!s
181
+ );
140
182
  }
141
- const p = (e) => e ? e.split("/").map((r) => r.trim()).filter(Boolean).join("/") : "", x = (e, r) => {
142
- const n = p(e), t = p(r);
143
- return n ? t ? `${n}/${t}` : n : t;
144
- }, k = (e, r) => {
145
- const n = { scope: e };
183
+ const g = (t) => t ? t.split("/").map((r) => r.trim()).filter(Boolean).join("/") : "", k = (t, r) => {
184
+ const n = g(t), e = g(r);
185
+ return n ? e ? `${n}/${e}` : n : e;
186
+ }, b = (t, r) => {
187
+ const n = { scope: t };
146
188
  return {
147
189
  async get() {
148
- const t = p(r);
149
- return P({ ...n, path: t || void 0 });
190
+ const e = g(r);
191
+ return x({ ...n, path: e || void 0 });
150
192
  },
151
- async set(t) {
152
- const o = p(r);
153
- return u("set", {
193
+ async set(e) {
194
+ const o = g(r);
195
+ return m("set", {
154
196
  ...n,
155
197
  path: o || void 0,
156
- value: t
198
+ value: e
157
199
  });
158
200
  },
159
- async update(t) {
160
- if (!t || typeof t != "object")
201
+ async update(e) {
202
+ if (!e || typeof e != "object")
161
203
  throw new Error("update requires an object");
162
204
  const o = {};
163
- for (const [i, l] of Object.entries(t)) {
164
- const w = x(r, i);
165
- if (!w)
205
+ for (const [u, s] of Object.entries(e)) {
206
+ const i = k(r, u);
207
+ if (!i)
166
208
  throw new Error("update paths must be non-empty");
167
- o[w] = l;
209
+ o[i] = s;
168
210
  }
169
- return u("update", { ...n, updates: o });
211
+ return m("update", { ...n, updates: o });
170
212
  },
171
213
  async remove() {
172
- const t = p(r);
173
- return u("remove", { ...n, path: t || void 0 });
214
+ const e = g(r);
215
+ return m("remove", { ...n, path: e || void 0 });
174
216
  },
175
- async push(t) {
176
- const o = p(r);
217
+ async push(e) {
218
+ const o = g(r);
177
219
  if (!o) throw new Error("push requires a path");
178
- return u("array-push", { ...n, path: o, value: t });
220
+ return m("array-push", { ...n, path: o, value: e });
179
221
  },
180
- ref(t) {
181
- const o = x(r, t);
222
+ ref(e) {
223
+ const o = k(r, e);
182
224
  if (!o) throw new Error("ref requires a path");
183
- return k(e, o);
225
+ return b(t, o);
184
226
  }
185
227
  };
186
228
  };
187
229
  return {
188
230
  isReady() {
189
- return !!y() && !!h();
231
+ return !!f() && !!p();
190
232
  },
191
233
  getBackendUrl() {
192
- var e;
193
- return ((e = y()) == null ? void 0 : e.backendUrl) ?? null;
234
+ var t;
235
+ return ((t = f()) == null ? void 0 : t.backendUrl) ?? null;
236
+ },
237
+ async models(t) {
238
+ return h(t);
194
239
  },
195
- async chatCompletions(e) {
196
- var s;
240
+ async chatCompletions(t) {
241
+ var a;
197
242
  const {
198
243
  reasoningEffort: r,
199
244
  reasoning_effort: n,
200
- ...t
201
- } = e, o = r ?? n, i = {
202
- ...t,
245
+ ...e
246
+ } = t, o = r ?? n, u = {
247
+ ...e,
203
248
  stream: !1
204
249
  };
205
- o && v(e.model) && (i.reasoning_effort = o);
206
- const l = await f("/openai/chat/completions", {
250
+ o && E(t.model) && (u.reasoning_effort = o);
251
+ const s = await d("/openai/chat/completions", {
207
252
  method: "POST",
208
253
  headers: { "Content-Type": "application/json" },
209
- body: JSON.stringify(i)
210
- }), d = (l.headers.get("content-type") || "").includes("application/json") ? await l.json() : await l.text();
211
- if (!l.ok)
254
+ body: JSON.stringify(u)
255
+ }), y = (s.headers.get("content-type") || "").includes("application/json") ? await s.json() : await s.text();
256
+ if (!s.ok)
212
257
  throw new Error(
213
- typeof d == "string" ? d : ((s = d == null ? void 0 : d.error) == null ? void 0 : s.message) || `Request failed (${l.status})`
258
+ typeof y == "string" ? y : ((a = y == null ? void 0 : y.error) == null ? void 0 : a.message) || `Request failed (${s.status})`
214
259
  );
215
- return d;
260
+ return y;
216
261
  },
217
- async chatText(e) {
218
- var n, t, o;
219
- const r = await this.chatCompletions(e);
220
- return ((o = (t = (n = r == null ? void 0 : r.choices) == null ? void 0 : n[0]) == null ? void 0 : t.message) == null ? void 0 : o.content) ?? "";
262
+ async chatText(t) {
263
+ var n, e, o;
264
+ const r = await this.chatCompletions(t);
265
+ return ((o = (e = (n = r == null ? void 0 : r.choices) == null ? void 0 : n[0]) == null ? void 0 : e.message) == null ? void 0 : o.content) ?? "";
221
266
  },
222
267
  audio: {
223
- async tts(e) {
268
+ async tts(t) {
224
269
  var o;
225
- const r = (o = e == null ? void 0 : e.text) == null ? void 0 : o.trim();
270
+ const r = (o = t == null ? void 0 : t.text) == null ? void 0 : o.trim();
226
271
  if (!r) throw new Error("audio.tts requires non-empty text");
227
- const n = await f("/app-api/audio/tts", {
272
+ const n = await d("/app-api/audio/tts", {
228
273
  method: "POST",
229
274
  headers: { "Content-Type": "application/json" },
230
275
  body: JSON.stringify({
231
276
  text: r,
232
- voice: e.voice,
233
- lang: e.lang
277
+ voice: t.voice,
278
+ lang: t.lang
234
279
  })
235
280
  });
236
281
  if (!n.ok) {
237
- const i = await n.text();
238
- throw new Error(i || `Request failed (${n.status})`);
282
+ const u = await n.text();
283
+ throw new Error(u || `Request failed (${n.status})`);
239
284
  }
240
285
  return {
241
286
  audio: await n.arrayBuffer(),
242
287
  contentType: n.headers.get("content-type")
243
288
  };
244
289
  },
245
- async stt(e) {
246
- var g, m, a;
247
- if (!(e != null && e.file)) throw new Error("audio.stt requires a file");
248
- const r = (c) => {
249
- if (!c) return;
250
- const $ = c.trim();
251
- if (!$) return;
252
- const C = $.toLowerCase(), S = C.split("-")[0];
253
- return S.length === 2 ? S : C;
254
- }, n = typeof File < "u" && e.file instanceof File, t = n && e.file.name.split(".").pop() || "webm", o = e.ext || t, i = new FormData();
255
- n ? i.set("file", e.file) : i.set("file", e.file, `audio.${o}`);
256
- const l = r(e.language);
257
- l && i.set("language", l), e.prompt && i.set("prompt", e.prompt);
258
- const w = await f(
290
+ async stt(t) {
291
+ var w, c, l;
292
+ if (!(t != null && t.file)) throw new Error("audio.stt requires a file");
293
+ const r = (R) => {
294
+ if (!R) return;
295
+ const T = R.trim();
296
+ if (!T) return;
297
+ const A = T.toLowerCase(), C = A.split("-")[0];
298
+ return C.length === 2 ? C : A;
299
+ }, n = typeof File < "u" && t.file instanceof File, e = n && t.file.name.split(".").pop() || "webm", o = t.ext || e, u = new FormData();
300
+ n ? u.set("file", t.file) : u.set("file", t.file, `audio.${o}`);
301
+ const s = r(t.language);
302
+ s && u.set("language", s), t.prompt && u.set("prompt", t.prompt);
303
+ const i = await d(
259
304
  `/app-api/audio/stt?ext=${encodeURIComponent(o)}`,
260
305
  {
261
306
  method: "POST",
262
- body: i
307
+ body: u
263
308
  }
264
- ), s = (w.headers.get("content-type") || "").includes("application/json") ? await w.json() : await w.text();
265
- if (!w.ok)
309
+ ), a = (i.headers.get("content-type") || "").includes("application/json") ? await i.json() : await i.text();
310
+ if (!i.ok)
266
311
  throw new Error(
267
- typeof s == "string" ? s : ((g = s == null ? void 0 : s.error) == null ? void 0 : g.message) || `Request failed (${w.status})`
312
+ typeof a == "string" ? a : ((w = a == null ? void 0 : a.error) == null ? void 0 : w.message) || `Request failed (${i.status})`
268
313
  );
269
- return { text: typeof s == "string" ? s : (s == null ? void 0 : s.text) || (s == null ? void 0 : s.original) || ((a = (m = s == null ? void 0 : s.results) == null ? void 0 : m[0]) == null ? void 0 : a.text) || "", raw: s };
314
+ return { text: typeof a == "string" ? a : (a == null ? void 0 : a.text) || (a == null ? void 0 : a.original) || ((l = (c = a == null ? void 0 : a.results) == null ? void 0 : c[0]) == null ? void 0 : l.text) || "", raw: a };
270
315
  },
271
- async voices(e) {
316
+ async voices(t) {
272
317
  const r = new URLSearchParams();
273
- e != null && e.locale && r.set("locale", e.locale);
274
- const n = await f(
318
+ t != null && t.locale && r.set("locale", t.locale);
319
+ const n = await d(
275
320
  `/app-api/audio/tts/voices${r.toString() ? `?${r}` : ""}`,
276
321
  { method: "GET" }
277
- ), t = await n.json().catch(() => []);
322
+ ), e = await n.json().catch(() => []);
278
323
  if (!n.ok)
279
324
  throw new Error(
280
- (t == null ? void 0 : t.error) || `Request failed (${n.status})`
325
+ (e == null ? void 0 : e.error) || `Request failed (${n.status})`
281
326
  );
282
- return Array.isArray(t) ? t : (t == null ? void 0 : t.voices) || [];
327
+ return Array.isArray(e) ? e : (e == null ? void 0 : e.voices) || [];
283
328
  }
284
329
  },
285
- data(e = "private") {
286
- return k(e, "");
330
+ data(t = "private") {
331
+ return b(t, "");
287
332
  }
288
333
  };
289
334
  }
290
335
  export {
291
336
  z as PromteAppProvider,
292
- b as usePromte,
293
- I as usePromteClient,
294
- N as usePromteDarkMode,
295
- G as usePromteSettings,
296
- J as usePromteToken,
297
- O as usePromteUser
337
+ $ as usePromte,
338
+ F as usePromteClient,
339
+ V as usePromteDarkMode,
340
+ K as usePromteModels,
341
+ J as usePromteSettings,
342
+ N as usePromteToken,
343
+ I as usePromteUser
298
344
  };
@@ -1 +1 @@
1
- (function(f,P){typeof exports=="object"&&typeof module<"u"?P(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],P):(f=typeof globalThis<"u"?globalThis:f||self,P(f.PromteAppLibrary={},f.jsxRuntime,f.react))})(this,function(f,P,c){"use strict";const x=c.createContext(null);function U({children:k,expectedParentOrigin:y,initTargetOrigin:j,retryInit:l={enabled:!0,intervalMs:500,maxAttempts:20}}){const[p,T]=c.useState(null),[m,S]=c.useState(null),[b,e]=c.useState(null),[o,n]=c.useState(null),[t,r]=c.useState(null),a=c.useMemo(()=>t?"booting":p?m?"ready":"waiting_for_token":"waiting_for_settings",[p,m,t]),d=a==="ready",h=c.useMemo(()=>j||y||"*",[j,y]),g=c.useCallback(()=>{var v;try{const w={type:"init"};(v=window.parent)==null||v.postMessage(w,h)}catch(w){r((w==null?void 0:w.message)||"Failed to post init")}},[h]);c.useEffect(()=>{g()},[g]),c.useEffect(()=>{if(!(l!=null&&l.enabled)||p)return;const v=l.intervalMs??500,w=l.maxAttempts??20;let i=0;const u=window.setInterval(()=>{if(p){window.clearInterval(u);return}if(i+=1,i>w){window.clearInterval(u);return}g()},v);return()=>window.clearInterval(u)},[l,p,g]),c.useEffect(()=>{const v=w=>{if(y&&w.origin!==y)return;const i=w.data;if(!(!i||typeof i!="object")){if(i.type==="set-settings"){const u=i.value;if(!(u!=null&&u.backendUrl)||u.parentOrigin&&y&&u.parentOrigin!==y)return;T(u),u!=null&&u.user&&n(u.user),r(null);return}if(i.type==="set-token"&&typeof i.value=="string"){S(i.value),r(null);return}if(i.type==="set-dark-mode"&&typeof i.value=="boolean"){e(i.value);return}if(i.type==="set-user"&&typeof i.value=="object"){n(i.value);return}}};return window.addEventListener("message",v),()=>window.removeEventListener("message",v)},[y]);const s=c.useMemo(()=>B(()=>p,()=>m),[p,m]),R=c.useMemo(()=>({settings:p,token:m,isDarkMode:b,user:o,ready:d,state:a,error:t,client:s}),[p,m,b,o,d,a,t,s]);return P.jsx(x.Provider,{value:R,children:k})}function E(){const k=c.useContext(x);if(!k)throw new Error("usePromte must be used inside <PromteAppProvider>");return k}function A(){return E().settings}function D(){return E().token}function L(){return E().isDarkMode}function F(){return E().user}function _(){return E().client}function B(k,y){const j=e=>typeof e=="string"&&e.startsWith("gpt-5");async function l(e,o){const n=k(),t=y();if(!n)throw new Error("Promte settings not received yet");if(!t)throw new Error("Promte token not received yet");const r=new URL(n.backendUrl+"/"+e).toString();return await fetch(r,{...o,headers:{...o.headers||{},Authorization:`Bearer ${t}`}})}async function p(e,o){const n=await l(`/app-api/data/${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}),t=await n.json().catch(()=>({}));if(!n.ok)throw new Error((t==null?void 0:t.error)||`Request failed (${n.status})`);return t==null?void 0:t.value}async function T(e){const o=new URLSearchParams;e.scope&&o.set("scope",e.scope),e.path&&o.set("path",e.path);const n=await l(`/app-api/data?${o.toString()}`,{method:"GET"}),t=await n.json().catch(()=>({}));if(!n.ok)throw new Error((t==null?void 0:t.error)||`Request failed (${n.status})`);return t==null?void 0:t.value}const m=e=>e?e.split("/").map(o=>o.trim()).filter(Boolean).join("/"):"",S=(e,o)=>{const n=m(e),t=m(o);return n?t?`${n}/${t}`:n:t},b=(e,o)=>{const n={scope:e};return{async get(){const t=m(o);return T({...n,path:t||void 0})},async set(t){const r=m(o);return p("set",{...n,path:r||void 0,value:t})},async update(t){if(!t||typeof t!="object")throw new Error("update requires an object");const r={};for(const[a,d]of Object.entries(t)){const h=S(o,a);if(!h)throw new Error("update paths must be non-empty");r[h]=d}return p("update",{...n,updates:r})},async remove(){const t=m(o);return p("remove",{...n,path:t||void 0})},async push(t){const r=m(o);if(!r)throw new Error("push requires a path");return p("array-push",{...n,path:r,value:t})},ref(t){const r=S(o,t);if(!r)throw new Error("ref requires a path");return b(e,r)}}};return{isReady(){return!!k()&&!!y()},getBackendUrl(){var e;return((e=k())==null?void 0:e.backendUrl)??null},async chatCompletions(e){var s;const{reasoningEffort:o,reasoning_effort:n,...t}=e,r=o??n,a={...t,stream:!1};r&&j(e.model)&&(a.reasoning_effort=r);const d=await l("/openai/chat/completions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)}),g=(d.headers.get("content-type")||"").includes("application/json")?await d.json():await d.text();if(!d.ok)throw new Error(typeof g=="string"?g:((s=g==null?void 0:g.error)==null?void 0:s.message)||`Request failed (${d.status})`);return g},async chatText(e){var n,t,r;const o=await this.chatCompletions(e);return((r=(t=(n=o==null?void 0:o.choices)==null?void 0:n[0])==null?void 0:t.message)==null?void 0:r.content)??""},audio:{async tts(e){var r;const o=(r=e==null?void 0:e.text)==null?void 0:r.trim();if(!o)throw new Error("audio.tts requires non-empty text");const n=await l("/app-api/audio/tts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:o,voice:e.voice,lang:e.lang})});if(!n.ok){const a=await n.text();throw new Error(a||`Request failed (${n.status})`)}return{audio:await n.arrayBuffer(),contentType:n.headers.get("content-type")}},async stt(e){var v,w,i;if(!(e!=null&&e.file))throw new Error("audio.stt requires a file");const o=u=>{if(!u)return;const C=u.trim();if(!C)return;const M=C.toLowerCase(),$=M.split("-")[0];return $.length===2?$:M},n=typeof File<"u"&&e.file instanceof File,t=n&&e.file.name.split(".").pop()||"webm",r=e.ext||t,a=new FormData;n?a.set("file",e.file):a.set("file",e.file,`audio.${r}`);const d=o(e.language);d&&a.set("language",d),e.prompt&&a.set("prompt",e.prompt);const h=await l(`/app-api/audio/stt?ext=${encodeURIComponent(r)}`,{method:"POST",body:a}),s=(h.headers.get("content-type")||"").includes("application/json")?await h.json():await h.text();if(!h.ok)throw new Error(typeof s=="string"?s:((v=s==null?void 0:s.error)==null?void 0:v.message)||`Request failed (${h.status})`);return{text:typeof s=="string"?s:(s==null?void 0:s.text)||(s==null?void 0:s.original)||((i=(w=s==null?void 0:s.results)==null?void 0:w[0])==null?void 0:i.text)||"",raw:s}},async voices(e){const o=new URLSearchParams;e!=null&&e.locale&&o.set("locale",e.locale);const n=await l(`/app-api/audio/tts/voices${o.toString()?`?${o}`:""}`,{method:"GET"}),t=await n.json().catch(()=>[]);if(!n.ok)throw new Error((t==null?void 0:t.error)||`Request failed (${n.status})`);return Array.isArray(t)?t:(t==null?void 0:t.voices)||[]}},data(e="private"){return b(e,"")}}}f.PromteAppProvider=U,f.usePromte=E,f.usePromteClient=_,f.usePromteDarkMode=L,f.usePromteSettings=A,f.usePromteToken=D,f.usePromteUser=F,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
1
+ (function(y,M){typeof exports=="object"&&typeof module<"u"?M(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],M):(y=typeof globalThis<"u"?globalThis:y||self,M(y.PromteAppLibrary={},y.jsxRuntime,y.react))})(this,function(y,M,i){"use strict";const T=i.createContext(null);function L({children:p,expectedParentOrigin:d,initTargetOrigin:P,retryInit:m={enabled:!0,intervalMs:500,maxAttempts:20}}){const[w,$]=i.useState(null),[v,E]=i.useState(null),[b,S]=i.useState(null),[t,s]=i.useState(null),[n,e]=i.useState(null),o=i.useMemo(()=>n?"booting":w?v?"ready":"waiting_for_token":"waiting_for_settings",[w,v,n]),f=o==="ready",r=i.useMemo(()=>P||d||"*",[P,d]),u=i.useCallback(()=>{var k;try{const h={type:"init"};(k=window.parent)==null||k.postMessage(h,r)}catch(h){e((h==null?void 0:h.message)||"Failed to post init")}},[r]);i.useEffect(()=>{u()},[u]),i.useEffect(()=>{if(!(m!=null&&m.enabled)||w)return;const k=m.intervalMs??500,h=m.maxAttempts??20;let c=0;const l=window.setInterval(()=>{if(w){window.clearInterval(l);return}if(c+=1,c>h){window.clearInterval(l);return}u()},k);return()=>window.clearInterval(l)},[m,w,u]),i.useEffect(()=>{const k=h=>{if(d&&h.origin!==d)return;const c=h.data;if(!(!c||typeof c!="object")){if(c.type==="set-settings"){const l=c.value;if(!(l!=null&&l.backendUrl)||l.parentOrigin&&d&&l.parentOrigin!==d)return;$(l),l!=null&&l.user&&s(l.user),e(null);return}if(c.type==="set-token"&&typeof c.value=="string"){E(c.value),e(null);return}if(c.type==="set-dark-mode"&&typeof c.value=="boolean"){S(c.value);return}if(c.type==="set-user"&&typeof c.value=="object"){s(c.value);return}}};return window.addEventListener("message",k),()=>window.removeEventListener("message",k)},[d]);const g=i.useMemo(()=>G(()=>w,()=>v),[w,v]),a=i.useMemo(()=>({settings:w,token:v,isDarkMode:b,user:t,ready:f,state:o,error:n,client:g}),[w,v,b,t,f,o,n,g]);return M.jsx(T.Provider,{value:a,children:p})}function j(){const p=i.useContext(T);if(!p)throw new Error("usePromte must be used inside <PromteAppProvider>");return p}function B(){return j().settings}function D(){return j().token}function O(){return j().isDarkMode}function F(){return j().user}function R(){return j().client}function _(p={}){const{ready:d}=j(),P=R(),[m,w]=i.useState([]),[$,v]=i.useState(!1),[E,b]=i.useState(null),S=i.useMemo(()=>(Array.isArray(p.type)?p.type:p.type?[p.type]:[]).filter(Boolean).map(o=>String(o)).join(","),[p.type]),t=p.configuredOnly===!0,s=p.auto!==!1,n=i.useCallback(async()=>{if(!d)return[];v(!0),b(null);try{const e=S?S.split(",").filter(Boolean):void 0,o=await P.models({type:e,configuredOnly:t});return w(o),o}catch(e){const o=e instanceof Error?e.message:String(e);throw b(o),e}finally{v(!1)}},[P,t,d,S]);return i.useEffect(()=>{!s||!d||n().catch(()=>{})},[s,d,n]),{models:m,loading:$,error:E,reload:n}}function G(p,d){const P=t=>typeof t=="string"&&t.startsWith("gpt-5");async function m(t,s){const n=p(),e=d();if(!n)throw new Error("Promte settings not received yet");if(!e)throw new Error("Promte token not received yet");const o=n.backendUrl.replace(/\/+$/,""),f=String(t||"").replace(/^\/+/,""),r=`${o}/${f}`;return await fetch(r,{...s,headers:{...s.headers||{},Authorization:`Bearer ${e}`}})}async function w(t,s){const n=await m(`/app-api/data/${t}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),e=await n.json().catch(()=>({}));if(!n.ok)throw new Error((e==null?void 0:e.error)||`Request failed (${n.status})`);return e==null?void 0:e.value}async function $(t){const s=new URLSearchParams;t.scope&&s.set("scope",t.scope),t.path&&s.set("path",t.path);const n=await m(`/app-api/data?${s.toString()}`,{method:"GET"}),e=await n.json().catch(()=>({}));if(!n.ok)throw new Error((e==null?void 0:e.error)||`Request failed (${n.status})`);return e==null?void 0:e.value}async function v(t){const s=new URLSearchParams,n=Array.isArray(t==null?void 0:t.type)?t==null?void 0:t.type:t!=null&&t.type?[t.type]:[];n.length>0&&s.set("type",n.join(",")),t!=null&&t.configuredOnly&&s.set("configuredOnly","true");const e=await m(`/app-api/models${s.toString()?`?${s.toString()}`:""}`,{method:"GET"}),o=await e.json().catch(()=>({}));if(!e.ok)throw new Error((o==null?void 0:o.error)||`Request failed (${e.status})`);return(Array.isArray(o==null?void 0:o.models)?o.models:[]).map(r=>{if(!r||typeof r!="object")return null;const u=typeof r.id=="string"?r.id.trim():"",g=typeof r.label=="string"?r.label.trim():u,a=r.type,k=r.configured===!0,h=typeof r.source=="string"&&r.source.trim()?r.source.trim():void 0;return!u||a!=="llm"&&a!=="embedding"&&a!=="image"&&a!=="stt"&&a!=="tts"?null:{id:u,label:g||u,type:a,configured:k,source:h}}).filter(r=>!!r)}const E=t=>t?t.split("/").map(s=>s.trim()).filter(Boolean).join("/"):"",b=(t,s)=>{const n=E(t),e=E(s);return n?e?`${n}/${e}`:n:e},S=(t,s)=>{const n={scope:t};return{async get(){const e=E(s);return $({...n,path:e||void 0})},async set(e){const o=E(s);return w("set",{...n,path:o||void 0,value:e})},async update(e){if(!e||typeof e!="object")throw new Error("update requires an object");const o={};for(const[f,r]of Object.entries(e)){const u=b(s,f);if(!u)throw new Error("update paths must be non-empty");o[u]=r}return w("update",{...n,updates:o})},async remove(){const e=E(s);return w("remove",{...n,path:e||void 0})},async push(e){const o=E(s);if(!o)throw new Error("push requires a path");return w("array-push",{...n,path:o,value:e})},ref(e){const o=b(s,e);if(!o)throw new Error("ref requires a path");return S(t,o)}}};return{isReady(){return!!p()&&!!d()},getBackendUrl(){var t;return((t=p())==null?void 0:t.backendUrl)??null},async models(t){return v(t)},async chatCompletions(t){var a;const{reasoningEffort:s,reasoning_effort:n,...e}=t,o=s??n,f={...e,stream:!1};o&&P(t.model)&&(f.reasoning_effort=o);const r=await m("/openai/chat/completions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(f)}),g=(r.headers.get("content-type")||"").includes("application/json")?await r.json():await r.text();if(!r.ok)throw new Error(typeof g=="string"?g:((a=g==null?void 0:g.error)==null?void 0:a.message)||`Request failed (${r.status})`);return g},async chatText(t){var n,e,o;const s=await this.chatCompletions(t);return((o=(e=(n=s==null?void 0:s.choices)==null?void 0:n[0])==null?void 0:e.message)==null?void 0:o.content)??""},audio:{async tts(t){var o;const s=(o=t==null?void 0:t.text)==null?void 0:o.trim();if(!s)throw new Error("audio.tts requires non-empty text");const n=await m("/app-api/audio/tts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:s,voice:t.voice,lang:t.lang})});if(!n.ok){const f=await n.text();throw new Error(f||`Request failed (${n.status})`)}return{audio:await n.arrayBuffer(),contentType:n.headers.get("content-type")}},async stt(t){var h,c,l;if(!(t!=null&&t.file))throw new Error("audio.stt requires a file");const s=x=>{if(!x)return;const A=x.trim();if(!A)return;const C=A.toLowerCase(),U=C.split("-")[0];return U.length===2?U:C},n=typeof File<"u"&&t.file instanceof File,e=n&&t.file.name.split(".").pop()||"webm",o=t.ext||e,f=new FormData;n?f.set("file",t.file):f.set("file",t.file,`audio.${o}`);const r=s(t.language);r&&f.set("language",r),t.prompt&&f.set("prompt",t.prompt);const u=await m(`/app-api/audio/stt?ext=${encodeURIComponent(o)}`,{method:"POST",body:f}),a=(u.headers.get("content-type")||"").includes("application/json")?await u.json():await u.text();if(!u.ok)throw new Error(typeof a=="string"?a:((h=a==null?void 0:a.error)==null?void 0:h.message)||`Request failed (${u.status})`);return{text:typeof a=="string"?a:(a==null?void 0:a.text)||(a==null?void 0:a.original)||((l=(c=a==null?void 0:a.results)==null?void 0:c[0])==null?void 0:l.text)||"",raw:a}},async voices(t){const s=new URLSearchParams;t!=null&&t.locale&&s.set("locale",t.locale);const n=await m(`/app-api/audio/tts/voices${s.toString()?`?${s}`:""}`,{method:"GET"}),e=await n.json().catch(()=>[]);if(!n.ok)throw new Error((e==null?void 0:e.error)||`Request failed (${n.status})`);return Array.isArray(e)?e:(e==null?void 0:e.voices)||[]}},data(t="private"){return S(t,"")}}}y.PromteAppProvider=L,y.usePromte=j,y.usePromteClient=R,y.usePromteDarkMode=O,y.usePromteModels=_,y.usePromteSettings=B,y.usePromteToken=D,y.usePromteUser=F,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promte/app-library",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [