sixty

agente de cliente

Supabase

El único agente que sabe qué *es* el endpoint en vez de cuánto costó — así que puede distinguir una consulta lenta de una política que rechazó una.

paquete
@sixty-sh/supabase en npm
funciona sobre
Junto al agente de navegador, en un proyecto cuyo backend es Supabase.
código
sixty-sh/sixty-supabase

Cómo instalarlo

For a frontend with no server of your own to deploy: runs entirely in the browser with a public, origin-pinned key. If the project has any server, use browser or node.

La instalación está escrita como un prompt para el agente de código que ya tienes abierto, no como una lista de tareas para ti. Es deliberado: nombra lo que tiene que ser cierto cuando la instalación esté terminada en vez de qué archivos editar, porque dónde va el código depende del framework y ponerlo en el sitio equivocado falla en silencio. Un agente puede leer tu repositorio y deducirlo; un párrafo en una página de documentación no.

El mismo texto es lo que devuelve install_sixty a través de el servidor MCP y lo que el colector sirve en /v1/setup?kind=lovable. Hay una sola copia.

la instalación de Supabase, completa
Add sixty.sh performance monitoring to this app. It measures Supabase query
shape, page speed, stuck loading states and browser errors, and reports
anonymously.

Before anything else: does this project run a server of its own? A Lovable
app often grows one — an API route, a serverless function, an Express or
FastAPI backend added later, a Supabase edge function you deploy. If it has
one, STOP and ask for the "browser" install instead, or the "node" or
"python" one if the server holds logic worth measuring. Those keep the key
server-side behind a proxy and can measure the server as well as the page.
This install is for the case where the frontend is genuinely the only code
you deploy, and it trades a world-readable key for that.

If that is this project, work out which shape it is, because it decides where
the code goes and there is no error if you get it wrong — it just silently
never runs:

  A. Client-only Vite SPA. There is a createRoot(...) call in a file like
     src/main.tsx and no server entry.
  B. Server-rendered framework app — React Router v7 framework mode, Remix,
     or similar. Signs: an entry.client.tsx / entry.server.tsx pair, a
     root.tsx, a hydrateRoot(...) call, or a routes.ts.

Then:

1. Add two dependencies: @sixty-sh/supabase and @sixty-sh/browser.

2. Call init() from "@sixty-sh/browser" exactly once, in the browser.

      init({ key: "sixty_pk_YOUR_KEY" })

   In shape A, module scope in the app entry is fine.
   In shape B it MUST go in the client entry (entry.client.tsx), after
   hydration. Do not put it in a shared module that the server also
   evaluates: there it runs during server rendering, finds no window,
   returns null, and reports nothing at all — with no error to tell you.

3. Find where the Supabase client is created. Search the repository for
   "createClient(" rather than guessing a path — it is often in
   src/integrations/supabase/client.ts but in this kind of project it may be
   anywhere. Then, at module scope in that same file:

      import { withSixty } from "@sixty-sh/supabase"
      withSixty(supabase, { key: "sixty_pk_YOUR_KEY", service: "my-app" })

   Use the client that already exists. withSixty patches the PostgREST
   prototype and returns the same object it was given, so it instruments every
   query in the app without replacing the export.

Constraints — correctness requirements, not style preferences:

- Do NOT create a second Supabase client. There must still be exactly one.
- Do NOT edit vite.config.ts and do NOT add a Vite plugin.
- Do NOT change any existing query, component, or hook. This is
  instrumentation only; no behaviour should change.
- The key above is PUBLIC by design: write-only, pinned to one service, and
  refused from any origin it does not list. Paste it literally. Do not move it
  into an environment variable or a secret.
- Do NOT add any analytics library, user id, session id, or cookie. This agent
  is deliberately anonymous and must stay that way.

When you are done, tell me which shape you found and which files you changed,
then publish the app. Findings need two published releases with traffic under
each before anything can be compared.

Necesita una clave pública — anclada a tus orígenes, porque una aplicación sin servidor propio no tiene dónde guardar un secreto.

Qué mide

señalunidadqué significa
errorserror ratea larger fraction of calls are throwing
new_erroroccurrencesan error that did not occur in the previous release
latencyms per callthis operation takes longer end to end than it used to
silent_empty—The query runs and succeeds, and returns no rows where it used to return plenty. Nothing reports an error, so the page just renders blank — this is what a broken permission rule looks like from the outside.
auth_failures—The server is turning these away on permission grounds rather than failing. People see an empty page or a save that quietly does nothing.

Fallos a los que puede poner nombre

No son señales. Son las clases que pone sobre un error, que es lo que marca la diferencia entre «una llamada falló» y «una política la rechazó».

rls_deniedA row-level security policy refused the statement (Postgres 42501). It reaches the browser as an empty list and your logs as nothing at all.
schema_missingA column, table, relationship or function the code expects is not in the database.
constraint_violatedA write was rejected by a database constraint.
jwt_expiredA session token was expired or invalid where one was required.
realtime_duplicate_subscriptionOne topic subscribed concurrently three times or more — an effect with no teardown, seen from the wire.
realtime_channel_errorA channel reported CHANNEL_ERROR or TIMED_OUT instead of subscribing.

Dónde se engancha

  • supabase-js — Llamadas a PostgREST, canales en tiempo real y auth, instrumentados donde se hacen.
  • Vite — Un plugin, para proyectos construidos con él.

Bases de datos

  • PostgREST — La petición describe la consulta, así que la tabla, los filtros y el código de fallo son legibles sin necesidad de una sentencia.

Qué hace solo este

  • Un nombre para el fallo — Cualquier otro agente puede decir que una llamada falló. Este puede decir que una política la rechazó, que falta una columna, o que el token había caducado — porque una petición de PostgREST describe la consulta en vez de limitarse a costar algo.
  • Tiempo real además de lecturas — Canales que dan error, que expiran, o que se suscriben tres veces porque un efecto no tiene limpieza.

Qué no puede hacer

  • Esto es un añadido al agente de navegador o al de Lovable, no un sustituto. Explica los fallos de Supabase; no mide la página.
  • Un proyecto sin servidor propio usa una clave pública anclada al origen. Si le ha salido una ruta de API o una función edge, coge el nivel de navegador o el de Node — la clave se queda en el servidor y además se mide el servidor.

Configuración

Todos los agentes leen las mismas cuatro variables, y DRIFT_* sigue respondiendo allí donde lo hace SIXTY_* — el producto se renombró y ese nombre no es nuestro para retirarlo de los despliegues de otra gente.

SIXTY_API_KEYSin ella el agente se queda inerte y lo dice. Nunca adivina, nunca reintenta contra un endpoint desconocido, y nunca lanza una excepción.
SIXTY_SERVICECómo llamar a este servicio. Por defecto, el nombre del proyecto donde sea legible.
SIXTY_RELEASELa que más importa. Se recoge automáticamente en Vercel, Render, Railway, Fly, Heroku y GitHub Actions; en cualquier otro sitio, ponla al SHA del commit. Sin ella cada medición cae en un único cubo sin nombre y no se puede hacer ninguna comparación.
SIXTY_ENDPOINTDónde reportar. Por defecto http://localhost:4319, que es correcto en un portátil y erróneo en cuanto la aplicación se sirve a alguien más.

El resto — intervalo de envío, tasa de muestreo, qué instrumentar — está en el propio README del paquete, que es donde puede seguir siendo cierto según cambia el agente.

El agente Supabase de sixty — qué mide y cómo se instala