@supcoflow/chat 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Supcoflow
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @supcoflow/chat
2
+
3
+ El chat de intake de Supcoflow, como componente de React. Se monta en el sitio de un cliente y
4
+ conversa con un agente que orienta sobre el producto y, cuando hay un bug de verdad, abre un
5
+ reporte en Supcoflow.
6
+
7
+ ## Requisitos
8
+
9
+ - **React 19** (`peerDependencies: react >=19`)
10
+ - Un proyecto de Supcoflow con **el chat activado** — que exige una skill aprobada y la lista de
11
+ Origins admitidos escrita. Si el chat está apagado, el widget **no se dibuja** (ver abajo).
12
+
13
+ ## Instalación
14
+
15
+ ```bash
16
+ npm i @supcoflow/chat
17
+ ```
18
+
19
+ Mientras el paquete no esté publicado, se instala del tarball que produce `npm pack -w @supcoflow/chat`:
20
+
21
+ ```bash
22
+ npm i /ruta/al/supcoflow-chat-0.1.0.tgz
23
+ ```
24
+
25
+ ## Uso
26
+
27
+ ```tsx
28
+ import { ChatDeIntake } from "@supcoflow/chat";
29
+
30
+ <ChatDeIntake
31
+ tenantId="tnt_xxx"
32
+ projectId="proj_xxx"
33
+ backendUrl="https://chat.supcoflow.dev"
34
+ contexto={{ url: location.href, versionApp: "1.4.2" }} // opcional
35
+ />
36
+ ```
37
+
38
+ Los tres identificadores **son públicos** — modelo DSN. La contención no es un secreto: es la
39
+ lista de Origins admitidos del proyecto más el tope de mensajes por día, los dos del lado del
40
+ motor. Poner el `tenantId` en el HTML de un sitio público es el uso previsto.
41
+
42
+ `contexto` es opcional y viaja al reporte: sirve para que el issue diga en qué URL y con qué
43
+ versión pasó.
44
+
45
+ ## Lo que hace cuando algo falla: nada visible
46
+
47
+ El widget **se oculta** —no muestra un error— si el chat está apagado, si el tope del día se
48
+ agotó, si el Origin del sitio no está admitido o si el backend no contesta. Es deliberado: quien
49
+ está del otro lado es un usuario final del producto del cliente, y no tiene nada que hacer con
50
+ "el proyecto tiene el tope agotado". Si esperabas ver el chat y no aparece, mirá la respuesta de
51
+ `GET {backendUrl}/api/chat/estado?tenant_id=…&project_id=…`, que dice la causa por status: 403
52
+ (chat apagado u Origin no admitido), 429 (tope agotado), 404 (proyecto desconocido), 502 (backend
53
+ sin poder hablar con el motor).
54
+
55
+ ## Lo que este paquete NO hace
56
+
57
+ - No persiste la conversación: el historial vive en el navegador y viaja entero en cada pedido.
58
+ - No le da seguimiento del reporte al que reportó. Es a propósito — el único lugar donde un
59
+ reporte se ve es la consola del operador.
60
+ - No trae estilos configurables todavía: el panel usa estilos en línea y hereda la tipografía del
61
+ sitio.
62
+
63
+ ## Nota para quien lo consuma sin bundler
64
+
65
+ El paquete emite ESM con imports relativos sin extensión (`./ChatDeIntake`), que cualquier bundler
66
+ —Next, Vite, webpack— resuelve. Node ESM en crudo, sin bundler, no: es un componente de cliente,
67
+ así que ese caso no existe en la práctica, pero queda dicho.
@@ -0,0 +1,11 @@
1
+ export interface ContextoDelReporte {
2
+ url?: string;
3
+ versionApp?: string;
4
+ }
5
+ export declare function ChatDeIntake(props: {
6
+ tenantId: string;
7
+ projectId: string;
8
+ backendUrl: string;
9
+ contexto?: ContextoDelReporte;
10
+ }): import("react").JSX.Element | null;
11
+ //# sourceMappingURL=ChatDeIntake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatDeIntake.d.ts","sourceRoot":"","sources":["../src/ChatDeIntake.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,sCAoEA"}
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useChat } from "@ai-sdk/react";
4
+ import { DefaultChatTransport } from "ai";
5
+ import { useEffect, useMemo, useState } from "react";
6
+ import { decidirVisibilidad, generarCanalId, mostrarIndicadorDeEscritura, normalizarBackendUrl, } from "./estado-del-widget";
7
+ export function ChatDeIntake(props) {
8
+ const [visibilidad, setVisibilidad] = useState("oculto");
9
+ const [entrada, setEntrada] = useState("");
10
+ // Un id de canal por montaje: identifica la conversación en `reportado_por_externo`
11
+ // sin identificar a la persona (el chat es anónimo, §9 del diseño).
12
+ const canalId = useMemo(() => generarCanalId(), []);
13
+ const backendUrl = useMemo(() => normalizarBackendUrl(props.backendUrl), [props.backendUrl]);
14
+ useEffect(() => {
15
+ let vivo = true;
16
+ fetch(`${backendUrl}/api/chat/estado?tenant_id=${encodeURIComponent(props.tenantId)}&project_id=${encodeURIComponent(props.projectId)}`)
17
+ .then((r) => r.json())
18
+ .then((cuerpo) => { if (vivo)
19
+ setVisibilidad(decidirVisibilidad({ respuesta: cuerpo, huboError: false })); })
20
+ .catch(() => { if (vivo)
21
+ setVisibilidad(decidirVisibilidad({ respuesta: null, huboError: true })); });
22
+ return () => { vivo = false; };
23
+ }, [backendUrl, props.tenantId, props.projectId]);
24
+ const { messages, sendMessage, status, error } = useChat({
25
+ transport: new DefaultChatTransport({
26
+ api: `${backendUrl}/api/chat`,
27
+ body: { tenant_id: props.tenantId, project_id: props.projectId, canal_id: canalId },
28
+ }),
29
+ });
30
+ // La misma política que el montaje: un error en medio de la conversación repliega el panel.
31
+ useEffect(() => { if (error !== undefined)
32
+ setVisibilidad("oculto"); }, [error]);
33
+ if (visibilidad === "oculto")
34
+ return null;
35
+ // Entre Enviar y el primer token del stream pasan 4-8 s en el peor caso (hallazgo de la
36
+ // pasada visual: el dueño lo leyó como "se rompió el botón" con la pantalla en silencio).
37
+ // El indicador cubre ese hueco sin prometer nada sobre CUÁNTO va a tardar.
38
+ const mostrarIndicador = mostrarIndicadorDeEscritura({ status, ultimoMensaje: messages[messages.length - 1] });
39
+ return (_jsxs("div", { style: { border: "1px solid #ccc", borderRadius: 8, maxWidth: 420, display: "flex", flexDirection: "column", fontFamily: "inherit" }, children: [_jsxs("div", { style: { padding: 12, overflowY: "auto", maxHeight: 420, display: "flex", flexDirection: "column", gap: 8 }, children: [messages.map((m) => (_jsx("div", { style: { alignSelf: m.role === "user" ? "flex-end" : "flex-start", whiteSpace: "pre-wrap" }, children: m.parts.map((p, i) => (p.type === "text" ? _jsx("span", { children: p.text }, i) : null)) }, m.id))), mostrarIndicador && (_jsx("div", { style: { alignSelf: "flex-start", whiteSpace: "pre-wrap" }, "aria-live": "polite", children: "\u2026" }))] }), _jsxs("form", { style: { display: "flex", gap: 8, padding: 12, borderTop: "1px solid #ccc" }, onSubmit: (e) => {
40
+ e.preventDefault();
41
+ if (entrada.trim().length === 0 || status !== "ready")
42
+ return;
43
+ sendMessage({ text: entrada });
44
+ setEntrada("");
45
+ }, children: [_jsx("input", { style: { flex: 1 }, value: entrada, onChange: (e) => setEntrada(e.target.value), placeholder: "Pregunt\u00E1 o cont\u00E1 qu\u00E9 pas\u00F3\u2026", "aria-label": "Mensaje" }), _jsx("button", { type: "submit", disabled: status !== "ready", children: "Enviar" })] })] }));
46
+ }
47
+ //# sourceMappingURL=ChatDeIntake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatDeIntake.js","sourceRoot":"","sources":["../src/ChatDeIntake.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export type Visibilidad = "visible" | "oculto";
2
+ /** `crypto.randomUUID` no existe en un contexto no-seguro (un sitio embebido por HTTP
3
+ * plano, no HTTPS) — sin este chequeo, el `useMemo` del widget lanza en el render y
4
+ * tira abajo el host entero, justo lo opuesto a la promesa de "se oculta" del §8. El
5
+ * fallback no necesita ser criptográfico: es un id de canal anónimo, no un secreto. */
6
+ export declare function generarCanalId(): string;
7
+ /** Una barra final en `backendUrl` ("https://x.com/", la forma más natural de
8
+ * escribirlo) deja `//` duplicado en las URLs armadas más abajo: el motor responde con
9
+ * un 308 sin headers de CORS y el navegador lo bloquea sin explicar que el problema es
10
+ * la barra. Mismo patrón que `motor.ts` usa para su propia base URL. */
11
+ export declare function normalizarBackendUrl(backendUrl: string): string;
12
+ /** §8 del diseño: sin backend, sin tope, o ante cualquier duda, el widget NO se dibuja.
13
+ * Ocultarse es lo honesto: no hay nada que decirle al usuario final sobre el estado interno
14
+ * del proyecto de otra empresa. */
15
+ export declare function decidirVisibilidad(h: {
16
+ respuesta: {
17
+ ok: boolean;
18
+ } | null;
19
+ huboError: boolean;
20
+ }): Visibilidad;
21
+ export type EstadoDelChat = "submitted" | "streaming" | "ready" | "error";
22
+ interface ParteMinima {
23
+ type: string;
24
+ text?: string;
25
+ }
26
+ interface MensajeMinimo {
27
+ role: string;
28
+ parts: ParteMinima[];
29
+ }
30
+ /** Entre Enviar y el primer token del stream pasan 4-8 s en el peor caso (medido en la pasada
31
+ * visual del demo) — el backend responde perfecto, pero un silencio así se lee como "se rompió
32
+ * el botón", no como "está pensando". Mostramos un indicador mientras el pedido está en vuelo
33
+ * ("submitted") y mientras ya empezó a transmitir pero todavía no llegó texto del asistente
34
+ * ("streaming" sin parts de texto no vacíos en el último mensaje). */
35
+ export declare function mostrarIndicadorDeEscritura(h: {
36
+ status: EstadoDelChat;
37
+ ultimoMensaje: MensajeMinimo | undefined;
38
+ }): boolean;
39
+ export {};
40
+ //# sourceMappingURL=estado-del-widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estado-del-widget.d.ts","sourceRoot":"","sources":["../src/estado-del-widget.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C;;;uFAGuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,CAKvC;AAED;;;wEAGwE;AACxE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;mCAEmC;AACnC,wBAAgB,kBAAkB,CAAC,CAAC,EAAE;IACpC,SAAS,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,WAAW,CAGd;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1E,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED;;;;sEAIsE;AACtE,wBAAgB,2BAA2B,CAAC,CAAC,EAAE;IAC7C,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;CAC1C,GAAG,OAAO,CAOV"}
@@ -0,0 +1,40 @@
1
+ /** `crypto.randomUUID` no existe en un contexto no-seguro (un sitio embebido por HTTP
2
+ * plano, no HTTPS) — sin este chequeo, el `useMemo` del widget lanza en el render y
3
+ * tira abajo el host entero, justo lo opuesto a la promesa de "se oculta" del §8. El
4
+ * fallback no necesita ser criptográfico: es un id de canal anónimo, no un secreto. */
5
+ export function generarCanalId() {
6
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
7
+ return crypto.randomUUID();
8
+ }
9
+ return `canal_${Date.now()}_${Math.random().toString(36).slice(2)}`;
10
+ }
11
+ /** Una barra final en `backendUrl` ("https://x.com/", la forma más natural de
12
+ * escribirlo) deja `//` duplicado en las URLs armadas más abajo: el motor responde con
13
+ * un 308 sin headers de CORS y el navegador lo bloquea sin explicar que el problema es
14
+ * la barra. Mismo patrón que `motor.ts` usa para su propia base URL. */
15
+ export function normalizarBackendUrl(backendUrl) {
16
+ return backendUrl.replace(/\/$/, "");
17
+ }
18
+ /** §8 del diseño: sin backend, sin tope, o ante cualquier duda, el widget NO se dibuja.
19
+ * Ocultarse es lo honesto: no hay nada que decirle al usuario final sobre el estado interno
20
+ * del proyecto de otra empresa. */
21
+ export function decidirVisibilidad(h) {
22
+ if (h.huboError || h.respuesta === null || !h.respuesta.ok)
23
+ return "oculto";
24
+ return "visible";
25
+ }
26
+ /** Entre Enviar y el primer token del stream pasan 4-8 s en el peor caso (medido en la pasada
27
+ * visual del demo) — el backend responde perfecto, pero un silencio así se lee como "se rompió
28
+ * el botón", no como "está pensando". Mostramos un indicador mientras el pedido está en vuelo
29
+ * ("submitted") y mientras ya empezó a transmitir pero todavía no llegó texto del asistente
30
+ * ("streaming" sin parts de texto no vacíos en el último mensaje). */
31
+ export function mostrarIndicadorDeEscritura(h) {
32
+ if (h.status === "submitted")
33
+ return true;
34
+ if (h.status !== "streaming")
35
+ return false;
36
+ const yaHayTexto = h.ultimoMensaje?.role === "assistant" &&
37
+ h.ultimoMensaje.parts.some((p) => p.type === "text" && (p.text?.length ?? 0) > 0);
38
+ return !yaHayTexto;
39
+ }
40
+ //# sourceMappingURL=estado-del-widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estado-del-widget.js","sourceRoot":"","sources":["../src/estado-del-widget.ts"],"names":[],"mappings":"AAEA;;;uFAGuF;AACvF,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAC7E,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACtE,CAAC;AAED;;;wEAGwE;AACxE,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;mCAEmC;AACnC,MAAM,UAAU,kBAAkB,CAAC,CAGlC;IACC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC5E,OAAO,SAAS,CAAC;AACnB,CAAC;AAcD;;;;sEAIsE;AACtE,MAAM,UAAU,2BAA2B,CAAC,CAG3C;IACC,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAC3C,MAAM,UAAU,GACd,CAAC,CAAC,aAAa,EAAE,IAAI,KAAK,WAAW;QACrC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpF,OAAO,CAAC,UAAU,CAAC;AACrB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ChatDeIntake, type ContextoDelReporte } from "./ChatDeIntake";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { ChatDeIntake } from "./ChatDeIntake";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA2B,MAAM,gBAAgB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@supcoflow/chat",
3
+ "version": "0.1.0",
4
+ "description": "El chat de intake de Supcoflow como componente de React: orienta sobre el producto y abre el reporte cuando el problema es un bug.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/supcoflow/sdk.git",
9
+ "directory": "packages/widget"
10
+ },
11
+ "homepage": "https://supcoflow.dev",
12
+ "keywords": [
13
+ "supcoflow",
14
+ "chat",
15
+ "intake",
16
+ "bug-report",
17
+ "react",
18
+ "widget"
19
+ ],
20
+ "type": "module",
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc -p tsconfig.build.json",
34
+ "prepare": "npm run build"
35
+ },
36
+ "peerDependencies": {
37
+ "react": ">=19"
38
+ },
39
+ "dependencies": {
40
+ "@ai-sdk/react": "^4.0.0",
41
+ "ai": "^7.0.67"
42
+ },
43
+ "devDependencies": {
44
+ "@types/react": "19.2.18",
45
+ "typescript": "^5.7.0",
46
+ "vitest": "^4.0.0"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }