@kazzle/app 0.1.929 → 0.1.931
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/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +7 -4
- package/dist/vendor/kazzle/browsers.d.ts +56 -0
- package/dist/vendor/kazzle/browsers.js +88 -0
- package/dist/vendor/kazzle/browsers.profiles.d.ts +16 -0
- package/dist/vendor/kazzle/browsers.profiles.js +26 -0
- package/dist/vendor/kazzle/browsers.tabs.d.ts +62 -0
- package/dist/vendor/kazzle/browsers.tabs.js +132 -0
- package/dist/vendor/kazzle/computers.d.ts +35 -0
- package/dist/vendor/kazzle/computers.desktop.d.ts +152 -0
- package/dist/vendor/kazzle/computers.desktop.js +144 -0
- package/dist/vendor/kazzle/computers.fs.d.ts +44 -0
- package/dist/vendor/kazzle/computers.fs.js +37 -0
- package/dist/vendor/kazzle/computers.js +55 -0
- package/dist/vendor/kazzle/computers.terminals.d.ts +36 -0
- package/dist/vendor/kazzle/computers.terminals.js +38 -0
- package/dist/vendor/kazzle/generated/operations.d.ts +8 -0
- package/dist/vendor/kazzle/generated/operations.js +137 -0
- package/dist/vendor/kazzle/http.d.ts +39 -0
- package/dist/vendor/kazzle/http.js +103 -0
- package/dist/vendor/kazzle/index.d.ts +11 -0
- package/dist/vendor/kazzle/index.js +12 -0
- package/dist/vendor/kazzle/kazzle.d.ts +10 -0
- package/dist/vendor/kazzle/kazzle.js +18 -0
- package/dist/vendor/kazzle/sdk.types.d.ts +134 -0
- package/dist/vendor/kazzle/sdk.types.js +6 -0
- package/dist/vendor/kazzle/sse.d.ts +45 -0
- package/dist/vendor/kazzle/sse.js +112 -0
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// kazzle.computers.desktop.* — screen capture, input, apps, and windows
|
|
2
|
+
// (/computers/{id}/desktop/*). Input actions target a live desktop session
|
|
3
|
+
// (virtual display) created with sessions.create().
|
|
4
|
+
import { apiPath } from './http';
|
|
5
|
+
class DesktopSessionsApi {
|
|
6
|
+
http;
|
|
7
|
+
constructor(http) {
|
|
8
|
+
this.http = http;
|
|
9
|
+
}
|
|
10
|
+
/** Create a live desktop session (virtual display) for input actions. */
|
|
11
|
+
create(computerId, options = {}) {
|
|
12
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/desktop/sessions`, options);
|
|
13
|
+
}
|
|
14
|
+
list(computerId) {
|
|
15
|
+
return this.http.json('GET', apiPath `/computers/${computerId}/desktop/sessions`);
|
|
16
|
+
}
|
|
17
|
+
close(computerId, sessionId) {
|
|
18
|
+
return this.http.json('DELETE', apiPath `/computers/${computerId}/desktop/sessions/${sessionId}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Mouse and keyboard input inside a live session. Every body carries the sessionId. */
|
|
22
|
+
class DesktopInputApi {
|
|
23
|
+
http;
|
|
24
|
+
constructor(http) {
|
|
25
|
+
this.http = http;
|
|
26
|
+
}
|
|
27
|
+
post(computerId, segment, body) {
|
|
28
|
+
return this.http.json('POST', `${apiPath `/computers/${computerId}/desktop/input/`}${segment}`, body);
|
|
29
|
+
}
|
|
30
|
+
click(computerId, body) {
|
|
31
|
+
return this.post(computerId, 'click', body);
|
|
32
|
+
}
|
|
33
|
+
doubleClick(computerId, body) {
|
|
34
|
+
return this.post(computerId, 'double_click', body);
|
|
35
|
+
}
|
|
36
|
+
mouseDown(computerId, body) {
|
|
37
|
+
return this.post(computerId, 'mouse_down', body);
|
|
38
|
+
}
|
|
39
|
+
mouseUp(computerId, body) {
|
|
40
|
+
return this.post(computerId, 'mouse_up', body);
|
|
41
|
+
}
|
|
42
|
+
move(computerId, body) {
|
|
43
|
+
return this.post(computerId, 'move', body);
|
|
44
|
+
}
|
|
45
|
+
drag(computerId, body) {
|
|
46
|
+
return this.post(computerId, 'drag', body);
|
|
47
|
+
}
|
|
48
|
+
scroll(computerId, body) {
|
|
49
|
+
return this.post(computerId, 'scroll', body);
|
|
50
|
+
}
|
|
51
|
+
type(computerId, body) {
|
|
52
|
+
return this.post(computerId, 'type', body);
|
|
53
|
+
}
|
|
54
|
+
/** Press a key combo, e.g. "ctrl+c". */
|
|
55
|
+
key(computerId, body) {
|
|
56
|
+
return this.post(computerId, 'key', body);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Structured control of applications (attach, inspect the UI tree, press elements). */
|
|
60
|
+
class DesktopAppsApi {
|
|
61
|
+
http;
|
|
62
|
+
constructor(http) {
|
|
63
|
+
this.http = http;
|
|
64
|
+
}
|
|
65
|
+
post(computerId, segment, body) {
|
|
66
|
+
return this.http.json('POST', `${apiPath `/computers/${computerId}/desktop/apps/`}${segment}`, body);
|
|
67
|
+
}
|
|
68
|
+
/** List the applications available on the computer with their bundle ids. */
|
|
69
|
+
list(computerId) {
|
|
70
|
+
return this.http.json('GET', apiPath `/computers/${computerId}/desktop/apps`);
|
|
71
|
+
}
|
|
72
|
+
/** Start (attach to) an application. The result row carries controlled_app_session_id. */
|
|
73
|
+
start(computerId, body) {
|
|
74
|
+
return this.post(computerId, 'start', body);
|
|
75
|
+
}
|
|
76
|
+
stop(computerId, body) {
|
|
77
|
+
return this.post(computerId, 'stop', body);
|
|
78
|
+
}
|
|
79
|
+
status(computerId, body) {
|
|
80
|
+
return this.post(computerId, 'status', body);
|
|
81
|
+
}
|
|
82
|
+
/** Accessibility-tree inspection of the controlled application. */
|
|
83
|
+
inspect(computerId, body) {
|
|
84
|
+
return this.post(computerId, 'inspect', body);
|
|
85
|
+
}
|
|
86
|
+
press(computerId, body) {
|
|
87
|
+
return this.post(computerId, 'press', body);
|
|
88
|
+
}
|
|
89
|
+
setValue(computerId, body) {
|
|
90
|
+
return this.post(computerId, 'set_value', body);
|
|
91
|
+
}
|
|
92
|
+
/** Screenshot the controlled application's window. Returns PNG bytes. */
|
|
93
|
+
capture(computerId, body) {
|
|
94
|
+
return this.http.binary(apiPath `/computers/${computerId}/desktop/apps/capture`, body);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
class DesktopWindowsApi {
|
|
98
|
+
http;
|
|
99
|
+
constructor(http) {
|
|
100
|
+
this.http = http;
|
|
101
|
+
}
|
|
102
|
+
/** The active window in a live session. */
|
|
103
|
+
active(computerId, body) {
|
|
104
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/desktop/windows/active`, body);
|
|
105
|
+
}
|
|
106
|
+
/** Raise a window of a controlled application. */
|
|
107
|
+
raise(computerId, body) {
|
|
108
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/desktop/windows/raise`, body);
|
|
109
|
+
}
|
|
110
|
+
/** Move/resize a window inside a live session. */
|
|
111
|
+
place(computerId, body) {
|
|
112
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/desktop/windows/place`, body);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export class DesktopApi {
|
|
116
|
+
http;
|
|
117
|
+
sessions;
|
|
118
|
+
input;
|
|
119
|
+
apps;
|
|
120
|
+
windows;
|
|
121
|
+
constructor(http) {
|
|
122
|
+
this.http = http;
|
|
123
|
+
this.sessions = new DesktopSessionsApi(http);
|
|
124
|
+
this.input = new DesktopInputApi(http);
|
|
125
|
+
this.apps = new DesktopAppsApi(http);
|
|
126
|
+
this.windows = new DesktopWindowsApi(http);
|
|
127
|
+
}
|
|
128
|
+
/** Screenshot a display. Omit displayId for the primary display. Returns PNG bytes. */
|
|
129
|
+
capture(computerId, options = {}) {
|
|
130
|
+
return this.http.binary(apiPath `/computers/${computerId}/desktop/capture`, options);
|
|
131
|
+
}
|
|
132
|
+
/** List the computer's displays with ids for capture(). */
|
|
133
|
+
displays(computerId) {
|
|
134
|
+
return this.http.json('GET', apiPath `/computers/${computerId}/desktop/displays`);
|
|
135
|
+
}
|
|
136
|
+
/** Check screen-recording / input permissions on the computer. */
|
|
137
|
+
permissions(computerId) {
|
|
138
|
+
return this.http.json('GET', apiPath `/computers/${computerId}/desktop/permissions`);
|
|
139
|
+
}
|
|
140
|
+
/** Screen geometry for a live session. */
|
|
141
|
+
screen(computerId, body) {
|
|
142
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/desktop/screen`, body);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type KazzleHttp } from './http';
|
|
2
|
+
import type { FsDeleteResponse, FsGlobResponse, FsGrepResponse, FsReadResponse, FsTransferResponse, FsWriteResponse } from './sdk.types';
|
|
3
|
+
export interface FsGrepOptions {
|
|
4
|
+
pattern: string;
|
|
5
|
+
path?: string;
|
|
6
|
+
include?: string;
|
|
7
|
+
caseSensitive?: boolean;
|
|
8
|
+
maxResults?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class FsApi {
|
|
11
|
+
private readonly http;
|
|
12
|
+
constructor(http: KazzleHttp);
|
|
13
|
+
/** Read a file. Text comes back as utf8; binary as base64 with `encoding: "base64"`. Reads are capped at 2 MB. */
|
|
14
|
+
read(computerId: string, options: {
|
|
15
|
+
path: string;
|
|
16
|
+
}): Promise<FsReadResponse>;
|
|
17
|
+
/** Write a file, creating parent directories as needed. Pass `encoding: "base64"` for binary payloads. */
|
|
18
|
+
write(computerId: string, options: {
|
|
19
|
+
path: string;
|
|
20
|
+
content: string;
|
|
21
|
+
encoding?: 'utf-8' | 'base64';
|
|
22
|
+
}): Promise<FsWriteResponse>;
|
|
23
|
+
/** Delete a file or directory. */
|
|
24
|
+
delete(computerId: string, options: {
|
|
25
|
+
path: string;
|
|
26
|
+
}): Promise<FsDeleteResponse>;
|
|
27
|
+
/** Move (rename) a file or directory. */
|
|
28
|
+
move(computerId: string, options: {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
}): Promise<FsTransferResponse>;
|
|
32
|
+
/** Copy a file or directory. */
|
|
33
|
+
copy(computerId: string, options: {
|
|
34
|
+
from: string;
|
|
35
|
+
to: string;
|
|
36
|
+
}): Promise<FsTransferResponse>;
|
|
37
|
+
/** Search file contents (ripgrep semantics). */
|
|
38
|
+
grep(computerId: string, options: FsGrepOptions): Promise<FsGrepResponse>;
|
|
39
|
+
/** Find files by glob pattern, e.g. "**\/*.ts". */
|
|
40
|
+
glob(computerId: string, options: {
|
|
41
|
+
pattern: string;
|
|
42
|
+
maxResults?: number;
|
|
43
|
+
}): Promise<FsGlobResponse>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// kazzle.computers.fs.* — computer filesystem operations (/computers/{id}/fs/*).
|
|
2
|
+
// Paths are relative to the computer's space root.
|
|
3
|
+
import { apiPath } from './http';
|
|
4
|
+
export class FsApi {
|
|
5
|
+
http;
|
|
6
|
+
constructor(http) {
|
|
7
|
+
this.http = http;
|
|
8
|
+
}
|
|
9
|
+
/** Read a file. Text comes back as utf8; binary as base64 with `encoding: "base64"`. Reads are capped at 2 MB. */
|
|
10
|
+
read(computerId, options) {
|
|
11
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/read`, options);
|
|
12
|
+
}
|
|
13
|
+
/** Write a file, creating parent directories as needed. Pass `encoding: "base64"` for binary payloads. */
|
|
14
|
+
write(computerId, options) {
|
|
15
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/write`, options);
|
|
16
|
+
}
|
|
17
|
+
/** Delete a file or directory. */
|
|
18
|
+
delete(computerId, options) {
|
|
19
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/delete`, options);
|
|
20
|
+
}
|
|
21
|
+
/** Move (rename) a file or directory. */
|
|
22
|
+
move(computerId, options) {
|
|
23
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/move`, options);
|
|
24
|
+
}
|
|
25
|
+
/** Copy a file or directory. */
|
|
26
|
+
copy(computerId, options) {
|
|
27
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/copy`, options);
|
|
28
|
+
}
|
|
29
|
+
/** Search file contents (ripgrep semantics). */
|
|
30
|
+
grep(computerId, options) {
|
|
31
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/grep`, options);
|
|
32
|
+
}
|
|
33
|
+
/** Find files by glob pattern, e.g. "**\/*.ts". */
|
|
34
|
+
glob(computerId, options) {
|
|
35
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/fs/glob`, options);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// kazzle.computers.* — cloud computer lifecycle plus the per-computer
|
|
2
|
+
// sub-surfaces: fs, exec (SSE), terminals, desktop.
|
|
3
|
+
import { apiPath } from './http';
|
|
4
|
+
import { ExecStream } from './sse';
|
|
5
|
+
import { FsApi } from './computers.fs';
|
|
6
|
+
import { TerminalsApi } from './computers.terminals';
|
|
7
|
+
import { DesktopApi } from './computers.desktop';
|
|
8
|
+
export class ComputersApi {
|
|
9
|
+
http;
|
|
10
|
+
fs;
|
|
11
|
+
terminals;
|
|
12
|
+
desktop;
|
|
13
|
+
constructor(http) {
|
|
14
|
+
this.http = http;
|
|
15
|
+
this.fs = new FsApi(http);
|
|
16
|
+
this.terminals = new TerminalsApi(http);
|
|
17
|
+
this.desktop = new DesktopApi(http);
|
|
18
|
+
}
|
|
19
|
+
/** Create a cloud computer. It boots with a persistent disk; state is "offline" until it comes up. */
|
|
20
|
+
create() {
|
|
21
|
+
return this.http.json('POST', '/computers', {});
|
|
22
|
+
}
|
|
23
|
+
/** List the computers in the API key's space. */
|
|
24
|
+
list() {
|
|
25
|
+
return this.http.json('GET', '/computers');
|
|
26
|
+
}
|
|
27
|
+
get(computerId) {
|
|
28
|
+
return this.http.json('GET', apiPath `/computers/${computerId}`);
|
|
29
|
+
}
|
|
30
|
+
/** Resume a suspended computer from its snapshot. Wakes take seconds; the disk is preserved. */
|
|
31
|
+
wake(computerId) {
|
|
32
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/wake`, {});
|
|
33
|
+
}
|
|
34
|
+
/** Suspend the computer. The disk is kept; wake it later to resume. */
|
|
35
|
+
stop(computerId) {
|
|
36
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/stop`, {});
|
|
37
|
+
}
|
|
38
|
+
/** Re-provision a computer whose sandbox is missing or wedged. The persistent volume is reattached. */
|
|
39
|
+
repair(computerId) {
|
|
40
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/repair`, {});
|
|
41
|
+
}
|
|
42
|
+
/** Destroy the computer and delete its disk. Permanent; use stop() to keep the files. */
|
|
43
|
+
destroy(computerId) {
|
|
44
|
+
return this.http.json('DELETE', apiPath `/computers/${computerId}`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run one command and stream the result. Returns an ExecStream: iterate the
|
|
48
|
+
* SSE events (`stdout`/`error`/`exit`), or call `.text()` for the joined
|
|
49
|
+
* stdout and exit code. stderr is merged into stdout (PTY). For long-running
|
|
50
|
+
* processes use terminals instead.
|
|
51
|
+
*/
|
|
52
|
+
exec(computerId, options) {
|
|
53
|
+
return new ExecStream(this.http.request('POST', apiPath `/computers/${computerId}/exec`, options));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type KazzleHttp } from './http';
|
|
2
|
+
import type { ActionResult, TerminalCreateResponse } from './sdk.types';
|
|
3
|
+
export declare class TerminalsApi {
|
|
4
|
+
private readonly http;
|
|
5
|
+
constructor(http: KazzleHttp);
|
|
6
|
+
/** Open a persistent PTY session. The response's session_id is the id for the other calls. */
|
|
7
|
+
create(computerId: string, options?: {
|
|
8
|
+
shell?: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
}): Promise<TerminalCreateResponse>;
|
|
11
|
+
/** List terminal sessions on this computer. */
|
|
12
|
+
list(computerId: string, options?: {
|
|
13
|
+
offset?: number;
|
|
14
|
+
limit?: number;
|
|
15
|
+
}): Promise<ActionResult>;
|
|
16
|
+
/** Send input to the session's stdin. Include \n to submit. */
|
|
17
|
+
write(computerId: string, sessionId: string, options: {
|
|
18
|
+
input: string;
|
|
19
|
+
}): Promise<ActionResult>;
|
|
20
|
+
/** Read buffered output (live or recently exited). Lines are 1-based; the response reports total_lines. */
|
|
21
|
+
read(computerId: string, sessionId: string, options?: {
|
|
22
|
+
startLine?: number;
|
|
23
|
+
endLine?: number;
|
|
24
|
+
}): Promise<ActionResult>;
|
|
25
|
+
/** Send a control signal: "interrupt", "eof", or "suspend". */
|
|
26
|
+
ctrl(computerId: string, sessionId: string, options: {
|
|
27
|
+
signal: 'interrupt' | 'eof' | 'suspend';
|
|
28
|
+
}): Promise<ActionResult>;
|
|
29
|
+
/** Block until a regex matches the session output or the timeout elapses. */
|
|
30
|
+
wait(computerId: string, sessionId: string, options: {
|
|
31
|
+
pattern: string;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}): Promise<ActionResult>;
|
|
34
|
+
/** Kill the session's process and delete the session. */
|
|
35
|
+
kill(computerId: string, sessionId: string): Promise<ActionResult>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// kazzle.computers.terminals.* — persistent PTY sessions (/computers/{id}/terminals).
|
|
2
|
+
// Use these for long-running processes (dev servers, watchers); use exec() for
|
|
3
|
+
// one-shot commands.
|
|
4
|
+
import { apiPath } from './http';
|
|
5
|
+
export class TerminalsApi {
|
|
6
|
+
http;
|
|
7
|
+
constructor(http) {
|
|
8
|
+
this.http = http;
|
|
9
|
+
}
|
|
10
|
+
/** Open a persistent PTY session. The response's session_id is the id for the other calls. */
|
|
11
|
+
create(computerId, options = {}) {
|
|
12
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/terminals`, options);
|
|
13
|
+
}
|
|
14
|
+
/** List terminal sessions on this computer. */
|
|
15
|
+
list(computerId, options = {}) {
|
|
16
|
+
return this.http.json('GET', apiPath `/computers/${computerId}/terminals`, undefined, options);
|
|
17
|
+
}
|
|
18
|
+
/** Send input to the session's stdin. Include \n to submit. */
|
|
19
|
+
write(computerId, sessionId, options) {
|
|
20
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/terminals/${sessionId}/write`, options);
|
|
21
|
+
}
|
|
22
|
+
/** Read buffered output (live or recently exited). Lines are 1-based; the response reports total_lines. */
|
|
23
|
+
read(computerId, sessionId, options = {}) {
|
|
24
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/terminals/${sessionId}/read`, options);
|
|
25
|
+
}
|
|
26
|
+
/** Send a control signal: "interrupt", "eof", or "suspend". */
|
|
27
|
+
ctrl(computerId, sessionId, options) {
|
|
28
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/terminals/${sessionId}/ctrl`, options);
|
|
29
|
+
}
|
|
30
|
+
/** Block until a regex matches the session output or the timeout elapses. */
|
|
31
|
+
wait(computerId, sessionId, options) {
|
|
32
|
+
return this.http.json('POST', apiPath `/computers/${computerId}/terminals/${sessionId}/wait`, options);
|
|
33
|
+
}
|
|
34
|
+
/** Kill the session's process and delete the session. */
|
|
35
|
+
kill(computerId, sessionId) {
|
|
36
|
+
return this.http.json('DELETE', apiPath `/computers/${computerId}/terminals/${sessionId}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ApiOperation {
|
|
2
|
+
method: string;
|
|
3
|
+
path: string;
|
|
4
|
+
}
|
|
5
|
+
/** Every customer API operation the SDK must cover. */
|
|
6
|
+
export declare const API_OPERATIONS: readonly ApiOperation[];
|
|
7
|
+
/** URL segments under /browsers/{browserId}/tabs/{tabId}/. */
|
|
8
|
+
export declare const TAB_ACTION_SEGMENTS: readonly string[];
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// GENERATED FILE — do not edit.
|
|
2
|
+
// Source of truth: shared/api/openapi.json (Computers API + Browsers API tags).
|
|
3
|
+
// Regenerate with `bun run generate` in packages/kazzle-sdk after the server
|
|
4
|
+
// API surface changes; src/operations.sync.test.ts fails on drift.
|
|
5
|
+
/** Every customer API operation the SDK must cover. */
|
|
6
|
+
export const API_OPERATIONS = [
|
|
7
|
+
{ method: 'GET', path: '/browsers' },
|
|
8
|
+
{ method: 'POST', path: '/browsers' },
|
|
9
|
+
{ method: 'DELETE', path: '/browsers/{browserId}' },
|
|
10
|
+
{ method: 'GET', path: '/browsers/{browserId}' },
|
|
11
|
+
{ method: 'GET', path: '/browsers/{browserId}/tabs' },
|
|
12
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs' },
|
|
13
|
+
{ method: 'DELETE', path: '/browsers/{browserId}/tabs/{tabId}' },
|
|
14
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/back' },
|
|
15
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/check' },
|
|
16
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/click' },
|
|
17
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/close' },
|
|
18
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/console' },
|
|
19
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/content' },
|
|
20
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/dialog' },
|
|
21
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/dom' },
|
|
22
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/download' },
|
|
23
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/drag' },
|
|
24
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/eval' },
|
|
25
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/extract' },
|
|
26
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/fill_form' },
|
|
27
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/find' },
|
|
28
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/focus' },
|
|
29
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/forward' },
|
|
30
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/hover' },
|
|
31
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/key' },
|
|
32
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/live' },
|
|
33
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/move' },
|
|
34
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/nav' },
|
|
35
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/network' },
|
|
36
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/options' },
|
|
37
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/pdf' },
|
|
38
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/reload' },
|
|
39
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/screenshot' },
|
|
40
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/scroll' },
|
|
41
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/select' },
|
|
42
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/snapshot' },
|
|
43
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/status' },
|
|
44
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/type' },
|
|
45
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/uncheck' },
|
|
46
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/upload' },
|
|
47
|
+
{ method: 'POST', path: '/browsers/{browserId}/tabs/{tabId}/wait' },
|
|
48
|
+
{ method: 'GET', path: '/browsers/profiles' },
|
|
49
|
+
{ method: 'POST', path: '/browsers/profiles' },
|
|
50
|
+
{ method: 'DELETE', path: '/browsers/profiles/{profileId}' },
|
|
51
|
+
{ method: 'GET', path: '/computers' },
|
|
52
|
+
{ method: 'POST', path: '/computers' },
|
|
53
|
+
{ method: 'DELETE', path: '/computers/{computerId}' },
|
|
54
|
+
{ method: 'GET', path: '/computers/{computerId}' },
|
|
55
|
+
{ method: 'GET', path: '/computers/{computerId}/desktop/apps' },
|
|
56
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/capture' },
|
|
57
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/inspect' },
|
|
58
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/press' },
|
|
59
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/set_value' },
|
|
60
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/start' },
|
|
61
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/status' },
|
|
62
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/apps/stop' },
|
|
63
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/capture' },
|
|
64
|
+
{ method: 'GET', path: '/computers/{computerId}/desktop/displays' },
|
|
65
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/click' },
|
|
66
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/double_click' },
|
|
67
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/drag' },
|
|
68
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/key' },
|
|
69
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/mouse_down' },
|
|
70
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/mouse_up' },
|
|
71
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/move' },
|
|
72
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/scroll' },
|
|
73
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/input/type' },
|
|
74
|
+
{ method: 'GET', path: '/computers/{computerId}/desktop/permissions' },
|
|
75
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/screen' },
|
|
76
|
+
{ method: 'GET', path: '/computers/{computerId}/desktop/sessions' },
|
|
77
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/sessions' },
|
|
78
|
+
{ method: 'DELETE', path: '/computers/{computerId}/desktop/sessions/{sessionId}' },
|
|
79
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/windows/active' },
|
|
80
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/windows/place' },
|
|
81
|
+
{ method: 'POST', path: '/computers/{computerId}/desktop/windows/raise' },
|
|
82
|
+
{ method: 'POST', path: '/computers/{computerId}/exec' },
|
|
83
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/copy' },
|
|
84
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/delete' },
|
|
85
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/glob' },
|
|
86
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/grep' },
|
|
87
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/move' },
|
|
88
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/read' },
|
|
89
|
+
{ method: 'POST', path: '/computers/{computerId}/fs/write' },
|
|
90
|
+
{ method: 'POST', path: '/computers/{computerId}/repair' },
|
|
91
|
+
{ method: 'POST', path: '/computers/{computerId}/stop' },
|
|
92
|
+
{ method: 'GET', path: '/computers/{computerId}/terminals' },
|
|
93
|
+
{ method: 'POST', path: '/computers/{computerId}/terminals' },
|
|
94
|
+
{ method: 'DELETE', path: '/computers/{computerId}/terminals/{sessionId}' },
|
|
95
|
+
{ method: 'POST', path: '/computers/{computerId}/terminals/{sessionId}/ctrl' },
|
|
96
|
+
{ method: 'POST', path: '/computers/{computerId}/terminals/{sessionId}/read' },
|
|
97
|
+
{ method: 'POST', path: '/computers/{computerId}/terminals/{sessionId}/wait' },
|
|
98
|
+
{ method: 'POST', path: '/computers/{computerId}/terminals/{sessionId}/write' },
|
|
99
|
+
{ method: 'POST', path: '/computers/{computerId}/wake' },
|
|
100
|
+
];
|
|
101
|
+
/** URL segments under /browsers/{browserId}/tabs/{tabId}/. */
|
|
102
|
+
export const TAB_ACTION_SEGMENTS = [
|
|
103
|
+
'back',
|
|
104
|
+
'check',
|
|
105
|
+
'click',
|
|
106
|
+
'close',
|
|
107
|
+
'console',
|
|
108
|
+
'content',
|
|
109
|
+
'dialog',
|
|
110
|
+
'dom',
|
|
111
|
+
'download',
|
|
112
|
+
'drag',
|
|
113
|
+
'eval',
|
|
114
|
+
'extract',
|
|
115
|
+
'fill_form',
|
|
116
|
+
'find',
|
|
117
|
+
'focus',
|
|
118
|
+
'forward',
|
|
119
|
+
'hover',
|
|
120
|
+
'key',
|
|
121
|
+
'live',
|
|
122
|
+
'move',
|
|
123
|
+
'nav',
|
|
124
|
+
'network',
|
|
125
|
+
'options',
|
|
126
|
+
'pdf',
|
|
127
|
+
'reload',
|
|
128
|
+
'screenshot',
|
|
129
|
+
'scroll',
|
|
130
|
+
'select',
|
|
131
|
+
'snapshot',
|
|
132
|
+
'status',
|
|
133
|
+
'type',
|
|
134
|
+
'uncheck',
|
|
135
|
+
'upload',
|
|
136
|
+
'wait',
|
|
137
|
+
];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface KazzleOptions {
|
|
2
|
+
/** `kzl_` API key. Defaults to the KAZZLE_API_KEY environment variable. */
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
/** API base URL override for dev/local, e.g. http://localhost:8080. Defaults to the KAZZLE_API_URL environment variable, then https://api.kazzle.app. */
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
/** Fetch implementation override, for tests. */
|
|
7
|
+
fetch?: typeof globalThis.fetch;
|
|
8
|
+
}
|
|
9
|
+
/** A non-2xx response from the API. `status` and the body's `error` are always set. */
|
|
10
|
+
export declare class KazzleApiError extends Error {
|
|
11
|
+
readonly status: number;
|
|
12
|
+
readonly code: string | undefined;
|
|
13
|
+
readonly body: Record<string, unknown>;
|
|
14
|
+
constructor(status: number, body: Record<string, unknown>);
|
|
15
|
+
}
|
|
16
|
+
/** Template tag that URL-encodes interpolated path params: apiPath`/computers/${id}/wake`. */
|
|
17
|
+
export declare function apiPath(strings: TemplateStringsArray, ...params: string[]): string;
|
|
18
|
+
export type Query = Record<string, string | number | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* Wire casing: SDK request options are camelCase; the API's documented surface
|
|
21
|
+
* is snake_case. JSON bodies are deep-converted on the way out, so every
|
|
22
|
+
* namespace method can pass its options object straight through.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toWireBody(value: unknown): unknown;
|
|
25
|
+
export declare class KazzleHttp {
|
|
26
|
+
readonly baseUrl: string;
|
|
27
|
+
private readonly apiKey;
|
|
28
|
+
private readonly fetchImpl;
|
|
29
|
+
constructor(options?: KazzleOptions);
|
|
30
|
+
url(path: string, query?: Query): string;
|
|
31
|
+
/**
|
|
32
|
+
* Raw request. JSON bodies are re-cased to the snake_case wire shape; non-2xx
|
|
33
|
+
* responses are read as JSON and thrown as KazzleApiError.
|
|
34
|
+
*/
|
|
35
|
+
request(method: string, path: string, body?: unknown, query?: Query): Promise<Response>;
|
|
36
|
+
json<T>(method: string, path: string, body?: unknown, query?: Query): Promise<T>;
|
|
37
|
+
/** POST returning raw bytes (screenshot, pdf, capture). */
|
|
38
|
+
binary(path: string, body?: unknown): Promise<Uint8Array>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// HTTP core for the `kazzle` package: auth, URL building, JSON/binary/SSE requests.
|
|
2
|
+
//
|
|
3
|
+
// Dependency-free on purpose (uses global fetch). Every namespace call funnels
|
|
4
|
+
// through KazzleHttp, so auth and error mapping live in exactly one place.
|
|
5
|
+
/** Default API base. Source of truth: shared/types/auth.types.ts (apiUrlForEnv). */
|
|
6
|
+
const DEFAULT_BASE_URL = 'https://api.kazzle.app';
|
|
7
|
+
/** A non-2xx response from the API. `status` and the body's `error` are always set. */
|
|
8
|
+
export class KazzleApiError extends Error {
|
|
9
|
+
status;
|
|
10
|
+
code;
|
|
11
|
+
body;
|
|
12
|
+
constructor(status, body) {
|
|
13
|
+
const message = typeof body.error === 'string' ? body.error : `Kazzle API request failed with status ${status}`;
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'KazzleApiError';
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.code = typeof body.code === 'string' ? body.code : undefined;
|
|
18
|
+
this.body = body;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function envVar(name) {
|
|
22
|
+
if (typeof process === 'undefined' || !process.env)
|
|
23
|
+
return undefined;
|
|
24
|
+
return process.env[name];
|
|
25
|
+
}
|
|
26
|
+
/** Template tag that URL-encodes interpolated path params: apiPath`/computers/${id}/wake`. */
|
|
27
|
+
export function apiPath(strings, ...params) {
|
|
28
|
+
return strings.reduce((out, part, i) => out + part + (i < params.length ? encodeURIComponent(params[i]) : ''), '');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Keys whose VALUES are opaque customer data (env var names like MY_VAR, JSON
|
|
32
|
+
* eval arguments) — never re-cased. Source of truth: server/tools/tool-api.casing.ts.
|
|
33
|
+
*/
|
|
34
|
+
const OPAQUE_VALUE_KEYS = new Set(['env', 'arguments']);
|
|
35
|
+
function camelToSnakeKey(key) {
|
|
36
|
+
return key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Wire casing: SDK request options are camelCase; the API's documented surface
|
|
40
|
+
* is snake_case. JSON bodies are deep-converted on the way out, so every
|
|
41
|
+
* namespace method can pass its options object straight through.
|
|
42
|
+
*/
|
|
43
|
+
export function toWireBody(value) {
|
|
44
|
+
if (Array.isArray(value))
|
|
45
|
+
return value.map(toWireBody);
|
|
46
|
+
if (value === null || typeof value !== 'object')
|
|
47
|
+
return value;
|
|
48
|
+
const out = {};
|
|
49
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
50
|
+
out[camelToSnakeKey(key)] = OPAQUE_VALUE_KEYS.has(key) ? entry : toWireBody(entry);
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
export class KazzleHttp {
|
|
55
|
+
baseUrl;
|
|
56
|
+
apiKey;
|
|
57
|
+
fetchImpl;
|
|
58
|
+
constructor(options = {}) {
|
|
59
|
+
const apiKey = options.apiKey ?? envVar('KAZZLE_API_KEY');
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
throw new Error('Missing Kazzle API key. Pass `new Kazzle({ apiKey })` or set the KAZZLE_API_KEY environment variable.');
|
|
62
|
+
}
|
|
63
|
+
this.apiKey = apiKey;
|
|
64
|
+
const baseUrl = options.baseUrl ?? envVar('KAZZLE_API_URL') ?? DEFAULT_BASE_URL;
|
|
65
|
+
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
|
66
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
67
|
+
}
|
|
68
|
+
url(path, query) {
|
|
69
|
+
const params = Object.entries(query ?? {}).filter(([, value]) => value !== undefined);
|
|
70
|
+
const search = params.length
|
|
71
|
+
? `?${new URLSearchParams(params.map(([key, value]) => [key, String(value)])).toString()}`
|
|
72
|
+
: '';
|
|
73
|
+
return `${this.baseUrl}${path}${search}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Raw request. JSON bodies are re-cased to the snake_case wire shape; non-2xx
|
|
77
|
+
* responses are read as JSON and thrown as KazzleApiError.
|
|
78
|
+
*/
|
|
79
|
+
async request(method, path, body, query) {
|
|
80
|
+
const response = await this.fetchImpl(this.url(path, query), {
|
|
81
|
+
method,
|
|
82
|
+
headers: {
|
|
83
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
84
|
+
...(body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
85
|
+
},
|
|
86
|
+
...(body !== undefined ? { body: JSON.stringify(toWireBody(body)) } : {}),
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
90
|
+
throw new KazzleApiError(response.status, errorBody);
|
|
91
|
+
}
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
async json(method, path, body, query) {
|
|
95
|
+
const response = await this.request(method, path, body, query);
|
|
96
|
+
return await response.json();
|
|
97
|
+
}
|
|
98
|
+
/** POST returning raw bytes (screenshot, pdf, capture). */
|
|
99
|
+
async binary(path, body) {
|
|
100
|
+
const response = await this.request('POST', path, body ?? {});
|
|
101
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
102
|
+
}
|
|
103
|
+
}
|