ghostykit 0.0.0-stage → 0.2.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.
@@ -0,0 +1,32 @@
1
+ import { type PilotTransport } from "./types";
2
+ export * from "./types";
3
+ export { createPostStreamTransport, readSse, type PostStreamTransportOptions, type SseMessage } from "./postStream";
4
+ /** Los eventos con nombre del contrato. Un evento SSE sin `addEventListener` no se despacha nunca. */
5
+ export declare const SSE_EVENTS: readonly ["started", "status", "caps", "models", "chunk", "thought", "tool", "artifact", "usage", "title", "done", "error", "warning", "reemplazo", "permission", "permission-resolved", "closed", "page-plan", "section-building", "section-update", "section", "page-ready", "page-error", "page-planning", "page-drop", "review", "page-version", "page-edit", "snapshot", "idle", "interrupted", "capacity"];
6
+ /**
7
+ * Tope de silencio del cliente, la red de seguridad: pasado esto SIN NINGÚN evento —ni el `status`
8
+ * que el servidor manda cada 25 s con turno vivo— la conexión está muerta aunque el navegador no lo
9
+ * note. No se da el turno por perdido: se reconecta y el servidor dice la verdad. ≥ el silencio
10
+ * máximo del servidor (10 min) para no adelantarse nunca a él si no manda latido.
11
+ */
12
+ export declare const SILENCE_MAX_MS: number;
13
+ /** Con latido anunciado, basta con perder unos cuantos para saber que la conexión murió. */
14
+ export declare const MISSED_BEATS = 4;
15
+ /** Piso del tope con latido: un latido muy corto no debe reconectar a lo loco. */
16
+ export declare const MIN_SILENCE_MS = 90000;
17
+ export interface SseTransportOptions {
18
+ /** Para mandar cabeceras propias (auth) en los POST. */
19
+ fetch?: typeof fetch;
20
+ /** Cookies en un stream de otro origen (widget embebido). */
21
+ withCredentials?: boolean;
22
+ /** Para pruebas o entornos sin `EventSource` global. */
23
+ EventSource?: typeof EventSource;
24
+ /** Eventos extra a escuchar (además de `SSE_EVENTS`). */
25
+ events?: string[];
26
+ /** Cada cuánto se revisa el silencio. */
27
+ checkEveryMs?: number;
28
+ /** Reloj inyectable (pruebas). */
29
+ now?: () => number;
30
+ }
31
+ /** El transporte SSE de Ghosty: `GET ${base}/events` (stream) y POST a `messages|cancel|model|permission`. */
32
+ export declare function createSseTransport(base: string, opts?: SseTransportOptions): PilotTransport;
@@ -0,0 +1,20 @@
1
+ import {
2
+ MIN_SILENCE_MS,
3
+ MISSED_BEATS,
4
+ PilotHttpError,
5
+ SILENCE_MAX_MS,
6
+ SSE_EVENTS,
7
+ createPostStreamTransport,
8
+ createSseTransport,
9
+ readSse
10
+ } from "../chunks/chunk-KZOMMG2D.js";
11
+ export {
12
+ MIN_SILENCE_MS,
13
+ MISSED_BEATS,
14
+ PilotHttpError,
15
+ SILENCE_MAX_MS,
16
+ SSE_EVENTS,
17
+ createPostStreamTransport,
18
+ createSseTransport,
19
+ readSse
20
+ };
@@ -0,0 +1,44 @@
1
+ import type { SendInput, Turn } from "../core/types";
2
+ import { type PilotTransport } from "./types";
3
+ /** Un evento SSE ya separado: nombre + data crudo. */
4
+ export interface SseMessage {
5
+ event: string;
6
+ data: string;
7
+ }
8
+ /**
9
+ * Lee un stream SSE (`text/event-stream`) de un `fetch`. Llama `onMessage` por cada evento con
10
+ * nombre y `onBytes` con CUALQUIER cosa que llegue (los comentarios `: ka` cuentan como vida).
11
+ */
12
+ export declare function readSse(body: ReadableStream<Uint8Array>, onMessage: (m: SseMessage) => void, onBytes?: () => void): Promise<void>;
13
+ export interface PostStreamTransportOptions {
14
+ fetch?: typeof fetch;
15
+ /** Default `same-origin` (la cookie de sesión viaja; EasyBits rechaza cualquier Bearer). */
16
+ credentials?: RequestCredentials;
17
+ /** Cuerpo del POST. Default: el contrato de EasyBits. */
18
+ body?: (input: SendInput & {
19
+ turnId: string;
20
+ }) => unknown;
21
+ /** URL del re-enganche. Default `${url}?attach=1`; `null` = sin re-enganche. */
22
+ attachUrl?: string | null;
23
+ /** URL del historial (JSON). Default `url`; `null` = sin historial. */
24
+ historyUrl?: string | null;
25
+ /** Cómo leer el historial. Default: `{messages:[{role:"user"|"agent",text}]}`. */
26
+ parseHistory?: (json: unknown) => Turn[];
27
+ /** URL para cancelar (POST). Sin ella, «detener» sólo deja de escuchar: el turno sigue en el servidor. */
28
+ cancelUrl?: string;
29
+ /** Silencio (sin un solo byte, ni `: ka`) tras el que la conexión se da por muerta. Default 60 s. */
30
+ silenceMs?: number;
31
+ /** Reintentos de un 503 (deploy) o de un re-enganche. Default 6. */
32
+ retries?: number;
33
+ newTurnId?: () => string;
34
+ /** Reloj y espera inyectables (pruebas). */
35
+ sleep?: (ms: number) => Promise<void>;
36
+ }
37
+ /**
38
+ * El transporte de «POST que devuelve el stream». Cada turno es su propio stream; si se corta sin
39
+ * evento final (red, deploy), se re-engancha con el GET de attach y el servidor dice la verdad
40
+ * (`snapshot` y sigue, o el evento final, o `idle`).
41
+ */
42
+ export declare function createPostStreamTransport(target: string | (PostStreamTransportOptions & {
43
+ url: string;
44
+ }), options?: PostStreamTransportOptions): PilotTransport;
@@ -0,0 +1,37 @@
1
+ import type { SendInput, SendResult, Turn } from "../core/types";
2
+ /** Un evento con nombre y su `data` ya parseado (`null` si no traía o no era JSON). */
3
+ export type PilotListener = (name: string, data: unknown) => void;
4
+ export interface SubscribeOptions {
5
+ /** ¿Hay un turno en vuelo? Con turno vivo, un silencio largo = conexión muerta → se reabre. */
6
+ isBusy?: () => boolean;
7
+ }
8
+ export interface PilotTransport {
9
+ /** Abre el stream de eventos. Devuelve con qué cerrarlo. */
10
+ subscribe(listener: PilotListener, opts?: SubscribeOptions): () => void;
11
+ /** Encarga un turno (o lo mete al turno en vuelo). Rechaza con `PilotHttpError` si el servidor dice que no. */
12
+ send(input: SendInput): Promise<SendResult>;
13
+ /** Detiene el turno en curso. */
14
+ cancel(): Promise<void>;
15
+ /** Cambia el modelo de la sesión (el stream confirma con `models`). */
16
+ setModel?(value: string): Promise<void>;
17
+ /** Contesta un pedido de permiso. `resolved: false` = ya no estaba abierto (se agotó el plazo). */
18
+ respondPermission?(id: string, optionId: string): Promise<{
19
+ resolved: boolean;
20
+ }>;
21
+ /** El hilo guardado, para pintarlo al montar (si la app no pasa `initialTurns`). */
22
+ history?(): Promise<Turn[]>;
23
+ /**
24
+ * ¿Se puede mandar un mensaje con un turno en vuelo (steer)? Default `true` (Ghosty). Con
25
+ * `false` (el servidor contesta 409) el composer no deja mandar hasta que el turno cierre.
26
+ */
27
+ steer?: boolean;
28
+ }
29
+ /** El servidor rechazó el encargo (sin saldo, sin cuenta, agente de otro). */
30
+ export declare class PilotHttpError extends Error {
31
+ status: number;
32
+ /** El servidor pide cuenta (`X-Needs-Login`). */
33
+ needsLogin: boolean;
34
+ /** El `error` del cuerpo, si el servidor lo dio (`turn_busy`, `draining`…). */
35
+ code?: string;
36
+ constructor(message: string, status: number, needsLogin?: boolean);
37
+ }
package/package.json CHANGED
@@ -1,6 +1,93 @@
1
1
  {
2
2
  "name": "ghostykit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Piloto UI \u2014 the in-app copilot chat from Ghosty Studio: streaming, agent steps, attachments, voice, reconnection and human approval cards. Pure event reducer + SSE transport + React components.",
5
+ "type": "module",
6
+ "main": "./dist/core/index.js",
7
+ "types": "./dist/core/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/react/index.d.ts",
11
+ "import": "./dist/react/index.js"
12
+ },
13
+ "./core": {
14
+ "types": "./dist/core/index.d.ts",
15
+ "import": "./dist/core/index.js"
16
+ },
17
+ "./transport": {
18
+ "types": "./dist/transport/index.d.ts",
19
+ "import": "./dist/transport/index.js"
20
+ },
21
+ "./react": {
22
+ "types": "./dist/react/index.d.ts",
23
+ "import": "./dist/react/index.js"
24
+ },
25
+ "./pointer": {
26
+ "types": "./dist/pointer/index.d.ts",
27
+ "import": "./dist/pointer/index.js"
28
+ },
29
+ "./styles.css": "./dist/styles.css",
30
+ "./package.json": "./package.json",
31
+ "./fonts.css": "./dist/fonts.css"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "sideEffects": [
39
+ "*.css"
40
+ ],
41
+ "scripts": {
42
+ "build": "rm -rf dist && node scripts/build.mjs && tsc -p tsconfig.build.json && node scripts/build-css.mjs",
43
+ "typecheck": "tsc -p tsconfig.json --noEmit",
44
+ "test": "vitest run",
45
+ "prepack": "cp ../../LICENSE . && npm run build"
46
+ },
47
+ "peerDependencies": {
48
+ "@ghostystudio/pointer": ">=0.4.0",
49
+ "motion": ">=11",
50
+ "react": ">=18",
51
+ "react-dom": ">=18",
52
+ "streamdown": ">=2.5.0 <3"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "@ghostystudio/pointer": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "devDependencies": {
60
+ "@ghostystudio/pointer": "^0.4.0",
61
+ "@tailwindcss/cli": "^4.1.0",
62
+ "@types/react": "^19.0.0",
63
+ "@types/react-dom": "^19.0.0",
64
+ "esbuild": "^0.25.0",
65
+ "lucide-react": "^1.44.0",
66
+ "motion": "^12.38.0",
67
+ "postcss": "^8.5.0",
68
+ "react": "^19.2.0",
69
+ "react-dom": "^19.2.0",
70
+ "streamdown": "^2.7.0",
71
+ "tailwindcss": "^4.1.0",
72
+ "typescript": "^5.9.0",
73
+ "vitest": "^3.2.0"
74
+ },
75
+ "keywords": [
76
+ "ai",
77
+ "agent",
78
+ "copilot",
79
+ "chat",
80
+ "react",
81
+ "sse",
82
+ "acp",
83
+ "human-in-the-loop",
84
+ "ghosty"
85
+ ],
86
+ "license": "MIT",
87
+ "repository": {
88
+ "type": "git",
89
+ "url": "git+https://github.com/blissito/ghostykit.git",
90
+ "directory": "packages/ghostykit"
91
+ },
92
+ "homepage": "https://github.com/blissito/ghostykit#readme"
93
+ }