@kazzle/app 0.1.929 → 0.1.930

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,11 @@
1
+ export { Kazzle } from './kazzle';
2
+ export { KazzleApiError, type KazzleOptions } from './http';
3
+ export { ExecStream, KazzleExecError, type ExecEvent, type ExecResult } from './sse';
4
+ export { ComputersApi } from './computers';
5
+ export { FsApi, type FsGrepOptions } from './computers.fs';
6
+ export { TerminalsApi } from './computers.terminals';
7
+ export { DesktopApi } from './computers.desktop';
8
+ export { Browser, BrowsersApi } from './browsers';
9
+ export { TabsApi } from './browsers.tabs';
10
+ export { ProfilesApi } from './browsers.profiles';
11
+ export type { ActionResult, BrowserCreateOptions, BrowserCreateResponse, BrowserRow, BrowserState, ComputerCreateResponse, ComputerRow, ExecOptions, FsDeleteResponse, FsGlobResponse, FsGrepMatch, FsGrepResponse, FsReadResponse, FsTransferResponse, FsWriteResponse, ListResponse, OkResponse, ProfileCreateOptions, ProfileCreateResponse, ProfileRow, TabRow, } from './sdk.types';
@@ -0,0 +1,12 @@
1
+ // kazzle — typed client for the Kazzle computers and browsers REST API.
2
+ // Quickstart in README.md; endpoint semantics in docs/platform/computers-api.mdx.
3
+ export { Kazzle } from './kazzle';
4
+ export { KazzleApiError } from './http';
5
+ export { ExecStream, KazzleExecError } from './sse';
6
+ export { ComputersApi } from './computers';
7
+ export { FsApi } from './computers.fs';
8
+ export { TerminalsApi } from './computers.terminals';
9
+ export { DesktopApi } from './computers.desktop';
10
+ export { Browser, BrowsersApi } from './browsers';
11
+ export { TabsApi } from './browsers.tabs';
12
+ export { ProfilesApi } from './browsers.profiles';
@@ -0,0 +1,10 @@
1
+ import { type KazzleOptions } from './http';
2
+ import { ComputersApi } from './computers';
3
+ import { BrowsersApi } from './browsers';
4
+ export declare class Kazzle {
5
+ readonly computers: ComputersApi;
6
+ readonly browsers: BrowsersApi;
7
+ /** Resolved API base URL (option, then KAZZLE_API_URL, then https://api.kazzle.app). */
8
+ readonly baseUrl: string;
9
+ constructor(options?: KazzleOptions);
10
+ }
@@ -0,0 +1,18 @@
1
+ // Kazzle SDK entry: `new Kazzle({ apiKey })` → kazzle.computers.* and
2
+ // kazzle.browsers.*. One KazzleHttp instance owns auth and error mapping for
3
+ // every namespace.
4
+ import { KazzleHttp } from './http';
5
+ import { ComputersApi } from './computers';
6
+ import { BrowsersApi } from './browsers';
7
+ export class Kazzle {
8
+ computers;
9
+ browsers;
10
+ /** Resolved API base URL (option, then KAZZLE_API_URL, then https://api.kazzle.app). */
11
+ baseUrl;
12
+ constructor(options = {}) {
13
+ const http = new KazzleHttp(options);
14
+ this.baseUrl = http.baseUrl;
15
+ this.computers = new ComputersApi(http);
16
+ this.browsers = new BrowsersApi(http);
17
+ }
18
+ }
@@ -0,0 +1,134 @@
1
+ /** Every list endpoint returns { items, total }. */
2
+ export interface ListResponse<T> {
3
+ items: T[];
4
+ total: number;
5
+ }
6
+ export interface OkResponse {
7
+ ok: boolean;
8
+ }
9
+ export interface ComputerCreateResponse {
10
+ id: string;
11
+ /** Connection state: "offline" right after create, "online" once up. */
12
+ state: string;
13
+ }
14
+ export interface ComputerRow {
15
+ id: string;
16
+ label: string | null;
17
+ type: string | null;
18
+ platform: string;
19
+ state: string;
20
+ created_at: number;
21
+ }
22
+ export interface ExecOptions {
23
+ command: string;
24
+ cwd?: string;
25
+ shell?: string;
26
+ timeoutMs?: number;
27
+ env?: Record<string, string>;
28
+ }
29
+ /**
30
+ * Tool-action result (terminals, desktop, tabs): `content` plus optional
31
+ * `row`/`items`/`total` and action-specific fields. All keys are snake_case
32
+ * on the wire, matching the documented API surface.
33
+ */
34
+ export interface ActionResult {
35
+ content?: string;
36
+ row?: Record<string, unknown>;
37
+ items?: unknown[];
38
+ total?: number;
39
+ [key: string]: unknown;
40
+ }
41
+ /** POST /computers/{id}/terminals — the new PTY session id is top-level. */
42
+ export interface TerminalCreateResponse extends ActionResult {
43
+ session_id: string;
44
+ }
45
+ export interface FsReadResponse {
46
+ path: string;
47
+ /** utf8 text, or base64 when `encoding` is "base64". */
48
+ content: string;
49
+ encoding?: 'base64';
50
+ size_bytes: number;
51
+ }
52
+ export interface FsWriteResponse {
53
+ ok: boolean;
54
+ path: string;
55
+ size_bytes: number;
56
+ }
57
+ export interface FsDeleteResponse {
58
+ ok: boolean;
59
+ path: string;
60
+ }
61
+ export interface FsTransferResponse {
62
+ ok: boolean;
63
+ from: string;
64
+ to: string;
65
+ }
66
+ export interface FsGrepMatch {
67
+ path: string;
68
+ line: number;
69
+ snippet: string;
70
+ }
71
+ export interface FsGrepResponse {
72
+ matches: FsGrepMatch[];
73
+ total: number;
74
+ }
75
+ export interface FsGlobResponse {
76
+ files: string[];
77
+ total: number;
78
+ }
79
+ export interface BrowserCreateOptions {
80
+ /** Host the browser on this computer (its own pages: app previews, localhost). Omit for a stealth cloud browser. */
81
+ computerId?: string;
82
+ /** Reuse a saved profile's logins and cookies. */
83
+ profileId?: string;
84
+ /** Open this page in the first tab. */
85
+ url?: string;
86
+ }
87
+ export interface BrowserCreateResponse {
88
+ id: string;
89
+ tab_id: string;
90
+ computer_id: string | null;
91
+ provider: string;
92
+ url: string | null;
93
+ live_view_url: string | null;
94
+ }
95
+ export interface BrowserRow {
96
+ id: string;
97
+ computer_id: string | null;
98
+ provider: string;
99
+ live_view_url: string | null;
100
+ }
101
+ export interface TabRow {
102
+ id: string;
103
+ url: string | null;
104
+ title: string | null;
105
+ active: boolean | null;
106
+ }
107
+ export interface BrowserState {
108
+ id: string;
109
+ computer_id: string | null;
110
+ provider: string;
111
+ profile_id: string | null;
112
+ live_view_url: string | null;
113
+ tabs: TabRow[];
114
+ }
115
+ /** POST /browsers/{id}/tabs — the new tab id is top-level. */
116
+ export interface TabOpenResponse extends ActionResult {
117
+ tab_id: string;
118
+ }
119
+ export interface ProfileCreateOptions {
120
+ /** The computer whose built-in browser profile to ensure. Omit for the space's stealth profile. */
121
+ computerId?: string;
122
+ name?: string;
123
+ }
124
+ export interface ProfileRow {
125
+ id: string;
126
+ name: string;
127
+ provider: string;
128
+ computer_id: string | null;
129
+ created_at: number;
130
+ }
131
+ export interface ProfileCreateResponse extends ProfileRow {
132
+ /** False when the durable profile already existed (create is an ensure). */
133
+ created: boolean;
134
+ }
@@ -0,0 +1,6 @@
1
+ // Wire types for the Kazzle computers and browsers REST API.
2
+ //
3
+ // Field names mirror the JSON the API returns (snake_case), so a response can
4
+ // be logged or forwarded without translation. Request options are camelCase
5
+ // and mapped to the wire shape inside the SDK.
6
+ export {};
@@ -0,0 +1,45 @@
1
+ export type ExecEvent = {
2
+ type: 'stdout';
3
+ text: string;
4
+ } | {
5
+ type: 'stderr';
6
+ text: string;
7
+ } | {
8
+ type: 'error';
9
+ error: string;
10
+ } | {
11
+ type: 'exit';
12
+ code: number;
13
+ };
14
+ export interface ExecResult {
15
+ /** Joined stdout (stderr is merged into stdout by the PTY). */
16
+ text: string;
17
+ exitCode: number;
18
+ }
19
+ /** The exec `error` frame: offline computer, spawn failure, or timeout. */
20
+ export declare class KazzleExecError extends Error {
21
+ readonly result: ExecResult;
22
+ constructor(message: string, result: ExecResult);
23
+ }
24
+ interface SseFrame {
25
+ event: string;
26
+ data: string;
27
+ }
28
+ /** Parse an SSE byte stream into frames. Handles frames split across chunks. */
29
+ export declare function parseSseStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<SseFrame>;
30
+ /**
31
+ * The result of `kazzle.computers.exec()`: an async iterable of ExecEvents,
32
+ * plus `.text()` which drains the stream and resolves the joined stdout and
33
+ * exit code. Iterate OR call `.text()` — the underlying stream reads once.
34
+ */
35
+ export declare class ExecStream implements AsyncIterable<ExecEvent> {
36
+ private readonly response;
37
+ constructor(response: Promise<Response>);
38
+ [Symbol.asyncIterator](): AsyncIterator<ExecEvent>;
39
+ /**
40
+ * Drain the stream: joined stdout plus the exit code. Throws KazzleExecError
41
+ * when the server reports an `error` frame (offline, spawn failure, timeout).
42
+ */
43
+ text(): Promise<ExecResult>;
44
+ }
45
+ export {};
@@ -0,0 +1,112 @@
1
+ // Server-sent events parsing for `POST /computers/{id}/exec`.
2
+ //
3
+ // The exec endpoint streams `stdout` frames ({ text }), optional `error`
4
+ // frames ({ error }), then exactly one `exit` frame ({ code }). ExecStream
5
+ // exposes the frames as an async iterator plus a `.text()` convenience.
6
+ /** The exec `error` frame: offline computer, spawn failure, or timeout. */
7
+ export class KazzleExecError extends Error {
8
+ result;
9
+ constructor(message, result) {
10
+ super(message);
11
+ this.name = 'KazzleExecError';
12
+ this.result = result;
13
+ }
14
+ }
15
+ /** Parse an SSE byte stream into frames. Handles frames split across chunks. */
16
+ export async function* parseSseStream(stream) {
17
+ const decoder = new TextDecoder();
18
+ const reader = stream.getReader();
19
+ let buffer = '';
20
+ try {
21
+ while (true) {
22
+ const { done, value } = await reader.read();
23
+ if (done)
24
+ break;
25
+ buffer += decoder.decode(value, { stream: true });
26
+ let boundary;
27
+ while ((boundary = buffer.indexOf('\n\n')) !== -1) {
28
+ const raw = buffer.slice(0, boundary);
29
+ buffer = buffer.slice(boundary + 2);
30
+ const frame = parseFrame(raw);
31
+ if (frame)
32
+ yield frame;
33
+ }
34
+ }
35
+ const tail = parseFrame(buffer);
36
+ if (tail)
37
+ yield tail;
38
+ }
39
+ finally {
40
+ reader.releaseLock();
41
+ }
42
+ }
43
+ function parseFrame(raw) {
44
+ let event = 'message';
45
+ const data = [];
46
+ for (const line of raw.split('\n')) {
47
+ if (line.startsWith('event:'))
48
+ event = line.slice('event:'.length).trim();
49
+ else if (line.startsWith('data:'))
50
+ data.push(line.slice('data:'.length).trimStart());
51
+ }
52
+ if (data.length === 0)
53
+ return null;
54
+ return { event, data: data.join('\n') };
55
+ }
56
+ function toExecEvent(frame) {
57
+ const payload = JSON.parse(frame.data);
58
+ switch (frame.event) {
59
+ case 'stdout':
60
+ return { type: 'stdout', text: String(payload.text ?? '') };
61
+ case 'stderr':
62
+ return { type: 'stderr', text: String(payload.text ?? '') };
63
+ case 'error':
64
+ return { type: 'error', error: String(payload.error ?? 'Command failed') };
65
+ case 'exit':
66
+ return { type: 'exit', code: typeof payload.code === 'number' ? payload.code : -1 };
67
+ default:
68
+ return null;
69
+ }
70
+ }
71
+ /**
72
+ * The result of `kazzle.computers.exec()`: an async iterable of ExecEvents,
73
+ * plus `.text()` which drains the stream and resolves the joined stdout and
74
+ * exit code. Iterate OR call `.text()` — the underlying stream reads once.
75
+ */
76
+ export class ExecStream {
77
+ response;
78
+ constructor(response) {
79
+ this.response = response;
80
+ }
81
+ async *[Symbol.asyncIterator]() {
82
+ const response = await this.response;
83
+ if (!response.body)
84
+ throw new Error('Exec response has no body stream');
85
+ for await (const frame of parseSseStream(response.body)) {
86
+ const event = toExecEvent(frame);
87
+ if (event)
88
+ yield event;
89
+ }
90
+ }
91
+ /**
92
+ * Drain the stream: joined stdout plus the exit code. Throws KazzleExecError
93
+ * when the server reports an `error` frame (offline, spawn failure, timeout).
94
+ */
95
+ async text() {
96
+ const chunks = [];
97
+ let exitCode = -1;
98
+ let error;
99
+ for await (const event of this) {
100
+ if (event.type === 'stdout' || event.type === 'stderr')
101
+ chunks.push(event.text);
102
+ else if (event.type === 'exit')
103
+ exitCode = event.code;
104
+ else if (event.type === 'error')
105
+ error = event.error;
106
+ }
107
+ const result = { text: chunks.join(''), exitCode };
108
+ if (error)
109
+ throw new KazzleExecError(error, result);
110
+ return result;
111
+ }
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kazzle/app",
3
- "version": "0.1.929",
3
+ "version": "0.1.930",
4
4
  "description": "Contracts, tool helpers, Vite helper, and templates for building Kazzle apps.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {