@kazzle/app 0.1.927 → 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.
Files changed (56) hide show
  1. package/dist/auth-react.d.ts +20 -0
  2. package/dist/auth-react.js +52 -0
  3. package/dist/auth-schema.d.ts +12 -0
  4. package/dist/auth-schema.js +66 -0
  5. package/dist/auth.d.ts +50 -0
  6. package/dist/auth.js +87 -0
  7. package/dist/connections.d.ts +11 -0
  8. package/dist/connections.js +56 -0
  9. package/dist/index.d.ts +7 -0
  10. package/dist/sdk.d.ts +1 -0
  11. package/dist/sdk.js +13 -0
  12. package/dist/vendor/kazzle/browsers.d.ts +56 -0
  13. package/dist/vendor/kazzle/browsers.js +88 -0
  14. package/dist/vendor/kazzle/browsers.profiles.d.ts +16 -0
  15. package/dist/vendor/kazzle/browsers.profiles.js +26 -0
  16. package/dist/vendor/kazzle/browsers.tabs.d.ts +62 -0
  17. package/dist/vendor/kazzle/browsers.tabs.js +132 -0
  18. package/dist/vendor/kazzle/computers.d.ts +35 -0
  19. package/dist/vendor/kazzle/computers.desktop.d.ts +152 -0
  20. package/dist/vendor/kazzle/computers.desktop.js +144 -0
  21. package/dist/vendor/kazzle/computers.fs.d.ts +44 -0
  22. package/dist/vendor/kazzle/computers.fs.js +37 -0
  23. package/dist/vendor/kazzle/computers.js +55 -0
  24. package/dist/vendor/kazzle/computers.terminals.d.ts +36 -0
  25. package/dist/vendor/kazzle/computers.terminals.js +38 -0
  26. package/dist/vendor/kazzle/generated/operations.d.ts +8 -0
  27. package/dist/vendor/kazzle/generated/operations.js +137 -0
  28. package/dist/vendor/kazzle/http.d.ts +39 -0
  29. package/dist/vendor/kazzle/http.js +103 -0
  30. package/dist/vendor/kazzle/index.d.ts +11 -0
  31. package/dist/vendor/kazzle/index.js +12 -0
  32. package/dist/vendor/kazzle/kazzle.d.ts +10 -0
  33. package/dist/vendor/kazzle/kazzle.js +18 -0
  34. package/dist/vendor/kazzle/sdk.types.d.ts +134 -0
  35. package/dist/vendor/kazzle/sdk.types.js +6 -0
  36. package/dist/vendor/kazzle/sse.d.ts +45 -0
  37. package/dist/vendor/kazzle/sse.js +112 -0
  38. package/package.json +43 -1
  39. package/templates/login-app/KAZZLE.md +18 -0
  40. package/templates/login-app/components/server/index.ts +93 -0
  41. package/templates/login-app/components/server/package.json +25 -0
  42. package/templates/login-app/components/server/tsconfig.json +11 -0
  43. package/templates/login-app/components/ui/index.html +12 -0
  44. package/templates/login-app/components/ui/package.json +29 -0
  45. package/templates/login-app/components/ui/public/favicon.svg +4 -0
  46. package/templates/login-app/components/ui/src/App.tsx +107 -0
  47. package/templates/login-app/components/ui/src/api.ts +37 -0
  48. package/templates/login-app/components/ui/src/main.tsx +13 -0
  49. package/templates/login-app/components/ui/src/styles/theme.css +181 -0
  50. package/templates/login-app/components/ui/src/vite-env.d.ts +1 -0
  51. package/templates/login-app/components/ui/tsconfig.json +21 -0
  52. package/templates/login-app/components/ui/vite.config.ts +19 -0
  53. package/templates/login-app/kazzle.config.ts +13 -0
  54. package/templates/login-app/package.json +17 -0
  55. package/templates/manifest.json +1 -0
  56. package/templates/ui-db-app/components/ui/tsconfig.json +1 -1
@@ -0,0 +1,20 @@
1
+ import type { AppUser } from './auth-schema';
2
+ export type { AppUser } from './auth-schema';
3
+ /** Login methods the Kazzle broker supports. Email arrives with the email primitive. */
4
+ export type KazzleLoginMethod = 'google';
5
+ /**
6
+ * Start sign-in. Redirects the browser through the Kazzle broker to the named
7
+ * method's login, then back to `callbackURL` (default: the page root).
8
+ */
9
+ export declare function signInWithKazzle(_method?: KazzleLoginMethod, opts?: {
10
+ callbackURL?: string;
11
+ }): Promise<unknown>;
12
+ /** Sign the current user out of this app. */
13
+ export declare function signOutOfApp(): Promise<unknown>;
14
+ /**
15
+ * The signed-in end user, or null. Reactive: re-renders on sign-in/out.
16
+ * While the session is still loading, returns null (render logged-out UI).
17
+ */
18
+ export declare function useAppUser(): AppUser | null;
19
+ /** Session loading state, for UIs that want a spinner instead of a flash of logged-out. */
20
+ export declare function useAppUserPending(): boolean;
@@ -0,0 +1,52 @@
1
+ // @kazzle/app/auth/react — app login, browser half.
2
+ //
3
+ // All the agent touches: `useAppUser()` for who's signed in, and
4
+ // `signInWithKazzle()` / `signOutOfApp()` on buttons. The client talks to the
5
+ // app's own /api/auth/* routes (same origin — the UI dev server proxies them
6
+ // to the process; in prod both share one host), so there is no base URL to
7
+ // configure and no auth code to write.
8
+ import { createAuthClient } from 'better-auth/react';
9
+ import { genericOAuthClient } from 'better-auth/client/plugins';
10
+ // Module-private: Better Auth's inferred client type is not portable across
11
+ // d.ts emit. The SDK contract is the functions below, not the raw client.
12
+ const authClient = createAuthClient({
13
+ plugins: [genericOAuthClient()],
14
+ });
15
+ /**
16
+ * Start sign-in. Redirects the browser through the Kazzle broker to the named
17
+ * method's login, then back to `callbackURL` (default: the page root).
18
+ */
19
+ export function signInWithKazzle(_method = 'google', opts) {
20
+ // The method is fixed server-side today (authorizationUrlParams). The
21
+ // parameter exists so login buttons already state their intent — when the
22
+ // broker gains methods, this maps per-call without app code changing shape.
23
+ return authClient.signIn.oauth2({
24
+ providerId: 'kazzle',
25
+ callbackURL: opts?.callbackURL ?? '/',
26
+ });
27
+ }
28
+ /** Sign the current user out of this app. */
29
+ export function signOutOfApp() {
30
+ return authClient.signOut();
31
+ }
32
+ /**
33
+ * The signed-in end user, or null. Reactive: re-renders on sign-in/out.
34
+ * While the session is still loading, returns null (render logged-out UI).
35
+ */
36
+ export function useAppUser() {
37
+ const { data } = authClient.useSession();
38
+ const user = data?.user;
39
+ if (!user)
40
+ return null;
41
+ return {
42
+ id: user.id,
43
+ email: user.email,
44
+ emailVerified: user.emailVerified,
45
+ name: user.name ?? null,
46
+ image: user.image ?? null,
47
+ };
48
+ }
49
+ /** Session loading state, for UIs that want a spinner instead of a flash of logged-out. */
50
+ export function useAppUserPending() {
51
+ return authClient.useSession().isPending;
52
+ }
@@ -0,0 +1,12 @@
1
+ /** Identity claims the app sees for a signed-in end user. */
2
+ export interface AppUser {
3
+ id: string;
4
+ email: string;
5
+ emailVerified: boolean;
6
+ name: string | null;
7
+ image: string | null;
8
+ }
9
+ /** Postgres schema the auth tables live in. */
10
+ export declare const AUTH_PG_SCHEMA = "auth";
11
+ /** Idempotent DDL for the auth tables. Run against the app's own database. */
12
+ export declare const AUTH_SCHEMA_SQL = "\nCREATE SCHEMA IF NOT EXISTS auth;\n\nCREATE TABLE IF NOT EXISTS auth.\"user\" (\n id text PRIMARY KEY,\n name text,\n email text NOT NULL UNIQUE,\n \"emailVerified\" boolean NOT NULL DEFAULT false,\n image text,\n \"createdAt\" timestamptz NOT NULL DEFAULT now(),\n \"updatedAt\" timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE TABLE IF NOT EXISTS auth.\"session\" (\n id text PRIMARY KEY,\n \"expiresAt\" timestamptz NOT NULL,\n token text NOT NULL UNIQUE,\n \"ipAddress\" text,\n \"userAgent\" text,\n \"userId\" text NOT NULL REFERENCES auth.\"user\"(id) ON DELETE CASCADE,\n \"createdAt\" timestamptz NOT NULL DEFAULT now(),\n \"updatedAt\" timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS session_user_id_idx ON auth.\"session\"(\"userId\");\n\nCREATE TABLE IF NOT EXISTS auth.\"account\" (\n id text PRIMARY KEY,\n \"accountId\" text NOT NULL,\n \"providerId\" text NOT NULL,\n \"userId\" text NOT NULL REFERENCES auth.\"user\"(id) ON DELETE CASCADE,\n \"accessToken\" text,\n \"refreshToken\" text,\n \"idToken\" text,\n \"accessTokenExpiresAt\" timestamptz,\n \"refreshTokenExpiresAt\" timestamptz,\n scope text,\n password text,\n \"createdAt\" timestamptz NOT NULL DEFAULT now(),\n \"updatedAt\" timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS account_user_id_idx ON auth.\"account\"(\"userId\");\n\nCREATE TABLE IF NOT EXISTS auth.\"verification\" (\n id text PRIMARY KEY,\n identifier text NOT NULL,\n value text NOT NULL,\n \"expiresAt\" timestamptz NOT NULL,\n \"createdAt\" timestamptz NOT NULL DEFAULT now(),\n \"updatedAt\" timestamptz NOT NULL DEFAULT now()\n);\n";
@@ -0,0 +1,66 @@
1
+ // @kazzle/app/auth/schema — the app-login auth tables, as SQL.
2
+ //
3
+ // Dependency-free on purpose: the server's login template hook runs this SQL
4
+ // at checkout (and the agent runs it when adding `appLogin` to an existing
5
+ // app) without pulling better-auth/pg into the importer.
6
+ //
7
+ // The tables live in a dedicated `auth` Postgres schema inside the app's own
8
+ // database — Supabase convention: app code never modifies these tables, it
9
+ // extends by referencing `auth."user"(id)` from its own tables in the public
10
+ // schema. Shapes match Better Auth core (user/session/account/verification,
11
+ // camelCase quoted columns); `@kazzle/app/auth` connects with
12
+ // `search_path=auth` so Better Auth's unqualified table names resolve here.
13
+ /** Postgres schema the auth tables live in. */
14
+ export const AUTH_PG_SCHEMA = 'auth';
15
+ /** Idempotent DDL for the auth tables. Run against the app's own database. */
16
+ export const AUTH_SCHEMA_SQL = `
17
+ CREATE SCHEMA IF NOT EXISTS auth;
18
+
19
+ CREATE TABLE IF NOT EXISTS auth."user" (
20
+ id text PRIMARY KEY,
21
+ name text,
22
+ email text NOT NULL UNIQUE,
23
+ "emailVerified" boolean NOT NULL DEFAULT false,
24
+ image text,
25
+ "createdAt" timestamptz NOT NULL DEFAULT now(),
26
+ "updatedAt" timestamptz NOT NULL DEFAULT now()
27
+ );
28
+
29
+ CREATE TABLE IF NOT EXISTS auth."session" (
30
+ id text PRIMARY KEY,
31
+ "expiresAt" timestamptz NOT NULL,
32
+ token text NOT NULL UNIQUE,
33
+ "ipAddress" text,
34
+ "userAgent" text,
35
+ "userId" text NOT NULL REFERENCES auth."user"(id) ON DELETE CASCADE,
36
+ "createdAt" timestamptz NOT NULL DEFAULT now(),
37
+ "updatedAt" timestamptz NOT NULL DEFAULT now()
38
+ );
39
+ CREATE INDEX IF NOT EXISTS session_user_id_idx ON auth."session"("userId");
40
+
41
+ CREATE TABLE IF NOT EXISTS auth."account" (
42
+ id text PRIMARY KEY,
43
+ "accountId" text NOT NULL,
44
+ "providerId" text NOT NULL,
45
+ "userId" text NOT NULL REFERENCES auth."user"(id) ON DELETE CASCADE,
46
+ "accessToken" text,
47
+ "refreshToken" text,
48
+ "idToken" text,
49
+ "accessTokenExpiresAt" timestamptz,
50
+ "refreshTokenExpiresAt" timestamptz,
51
+ scope text,
52
+ password text,
53
+ "createdAt" timestamptz NOT NULL DEFAULT now(),
54
+ "updatedAt" timestamptz NOT NULL DEFAULT now()
55
+ );
56
+ CREATE INDEX IF NOT EXISTS account_user_id_idx ON auth."account"("userId");
57
+
58
+ CREATE TABLE IF NOT EXISTS auth."verification" (
59
+ id text PRIMARY KEY,
60
+ identifier text NOT NULL,
61
+ value text NOT NULL,
62
+ "expiresAt" timestamptz NOT NULL,
63
+ "createdAt" timestamptz NOT NULL DEFAULT now(),
64
+ "updatedAt" timestamptz NOT NULL DEFAULT now()
65
+ );
66
+ `;
package/dist/auth.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ import { type AppUser } from './auth-schema';
2
+ export type { AppUser } from './auth-schema';
3
+ export { AUTH_SCHEMA_SQL, AUTH_PG_SCHEMA } from './auth-schema';
4
+ /**
5
+ * Minimal structural view of the Better Auth instance. Kept narrow on purpose:
6
+ * Better Auth's inferred generics are not portable across d.ts emit, and the
7
+ * SDK contract is the helpers, not the raw instance.
8
+ */
9
+ interface AuthInstance {
10
+ handler(request: Request): Promise<Response>;
11
+ api: {
12
+ getSession(opts: {
13
+ headers: Headers;
14
+ }): Promise<{
15
+ user: {
16
+ id: string;
17
+ email: string;
18
+ emailVerified: boolean;
19
+ name?: string | null;
20
+ image?: string | null;
21
+ };
22
+ } | null>;
23
+ };
24
+ }
25
+ export interface KazzleAuth {
26
+ /** The underlying Better Auth instance (escape hatch; prefer the helpers). */
27
+ auth: AuthInstance;
28
+ /** Request handler for /api/auth/* — see mountKazzleAuth. */
29
+ handler(request: Request): Promise<Response>;
30
+ /** Resolve the signed-in end user from a request's cookies, or null. */
31
+ getAppUser(request: Request): Promise<AppUser | null>;
32
+ }
33
+ /**
34
+ * Create the app's login runtime. Reads platform-injected env; no options in
35
+ * the common case. `databaseUrl` override exists for tests only.
36
+ */
37
+ export declare function createKazzleAuth(options?: {
38
+ databaseUrl?: string;
39
+ }): KazzleAuth;
40
+ /**
41
+ * Mount /api/auth/* on a Hono app. Structural typing on purpose so this
42
+ * module doesn't depend on hono.
43
+ */
44
+ export declare function mountKazzleAuth(app: {
45
+ on(methods: string[], path: string, handler: (c: {
46
+ req: {
47
+ raw: Request;
48
+ };
49
+ }) => Promise<Response>): unknown;
50
+ }, kazzleAuth: KazzleAuth): void;
package/dist/auth.js ADDED
@@ -0,0 +1,87 @@
1
+ // @kazzle/app/auth — app login, server half.
2
+ //
3
+ // One provider, `kazzle`: the app's Better Auth does standard OIDC against
4
+ // Kazzle's app-login broker, which runs Google (and later other methods)
5
+ // behind it. App code never touches Google — no secrets, no callbacks, no
6
+ // consent config. The platform injects everything this module reads:
7
+ //
8
+ // DATABASE_URL the app's own Postgres (users live HERE, not at Kazzle)
9
+ // KAZZLE_LOGIN_ID the app's broker client id (= its app id)
10
+ // KAZZLE_LOGIN_SECRET the app's broker client secret (server-only)
11
+ // KAZZLE_APP_PUBLIC_URL the app's browser-facing origin (preview tunnel or prod host)
12
+ // KAZZLE_API_URL Kazzle platform API base (the broker lives under /app-login)
13
+ //
14
+ // Auth rides the app's UI origin: the browser only ever calls relative
15
+ // /api/auth/* paths (the UI dev server proxies them to this process; in prod
16
+ // both surfaces share one host), so cookies are plain same-origin.
17
+ //
18
+ // The auth tables live in the `auth` Postgres schema (see ./auth-schema.ts);
19
+ // this module connects with search_path=auth so Better Auth's unqualified
20
+ // table names resolve there. App tables in the public schema reference
21
+ // auth."user"(id) — never add columns to the auth tables themselves.
22
+ import { betterAuth } from 'better-auth';
23
+ import { genericOAuth } from 'better-auth/plugins';
24
+ import { Pool } from 'pg';
25
+ import { AUTH_PG_SCHEMA } from './auth-schema';
26
+ export { AUTH_SCHEMA_SQL, AUTH_PG_SCHEMA } from './auth-schema';
27
+ function requiredEnv(name) {
28
+ const value = process.env[name];
29
+ if (!value) {
30
+ throw new Error(`${name} is not set — app login requires the platform-injected env (is appLogin enabled for this app?)`);
31
+ }
32
+ return value;
33
+ }
34
+ /**
35
+ * Create the app's login runtime. Reads platform-injected env; no options in
36
+ * the common case. `databaseUrl` override exists for tests only.
37
+ */
38
+ export function createKazzleAuth(options) {
39
+ const databaseUrl = options?.databaseUrl ?? requiredEnv('DATABASE_URL');
40
+ const publicUrl = requiredEnv('KAZZLE_APP_PUBLIC_URL');
41
+ const apiUrl = requiredEnv('KAZZLE_API_URL');
42
+ const clientId = requiredEnv('KAZZLE_LOGIN_ID');
43
+ const clientSecret = requiredEnv('KAZZLE_LOGIN_SECRET');
44
+ const pool = new Pool({
45
+ connectionString: databaseUrl,
46
+ options: `-c search_path=${AUTH_PG_SCHEMA}`,
47
+ });
48
+ const auth = betterAuth({
49
+ database: pool,
50
+ secret: clientSecret,
51
+ baseURL: publicUrl,
52
+ basePath: '/api/auth',
53
+ trustedOrigins: [publicUrl],
54
+ plugins: [
55
+ genericOAuth({
56
+ config: [{
57
+ providerId: 'kazzle',
58
+ clientId,
59
+ clientSecret,
60
+ discoveryUrl: `${apiUrl}/app-login/.well-known/openid-configuration`,
61
+ scopes: ['openid', 'email', 'profile'],
62
+ pkce: true,
63
+ // The app states the method; the broker executes it. Today: google.
64
+ authorizationUrlParams: { method: 'google' },
65
+ }],
66
+ }),
67
+ ],
68
+ });
69
+ return {
70
+ auth,
71
+ handler: (request) => auth.handler(request),
72
+ getAppUser: async (request) => {
73
+ const session = await auth.api.getSession({ headers: request.headers });
74
+ if (!session?.user)
75
+ return null;
76
+ const { id, email, emailVerified, name, image } = session.user;
77
+ return { id, email, emailVerified, name: name ?? null, image: image ?? null };
78
+ },
79
+ };
80
+ }
81
+ /**
82
+ * Mount /api/auth/* on a Hono app. Structural typing on purpose so this
83
+ * module doesn't depend on hono.
84
+ */
85
+ export function mountKazzleAuth(app, kazzleAuth) {
86
+ app.on(['GET', 'POST'], '/api/auth/*', (c) => kazzleAuth.handler(c.req.raw));
87
+ }
@@ -0,0 +1,11 @@
1
+ export declare class ConnectionNotConnectedError extends Error {
2
+ readonly connectionId: string;
3
+ constructor(connectionId: string, message?: string);
4
+ }
5
+ /**
6
+ * A fresh vendor access token for a connection, e.g.
7
+ * `new Stripe(await getConnectionToken('8f2a…'))`.
8
+ */
9
+ export declare function getConnectionToken(connectionId: string): Promise<string>;
10
+ /** Forget cached tokens (tests, or after a reconnect). */
11
+ export declare function clearConnectionTokenCache(): void;
@@ -0,0 +1,56 @@
1
+ // @kazzle/app/connections — use the accounts a person connected in Kazzle
2
+ // (Stripe, Gmail, Slack, …) from app code.
3
+ //
4
+ // Kazzle keeps the OAuth tokens; this asks Kazzle for a fresh one and caches it
5
+ // until it expires. Identify the connection by its vault secret id (the AI that
6
+ // built the app has it from the vault). Requires KAZZLE_API_KEY and
7
+ // KAZZLE_API_URL, both injected into every component at deploy and `kazzle run`.
8
+ //
9
+ // Dependency-free on purpose (uses global fetch). Do not add zod/vite here.
10
+ import { kazzleApiUrl } from './client';
11
+ export class ConnectionNotConnectedError extends Error {
12
+ connectionId;
13
+ constructor(connectionId, message) {
14
+ super(message ?? `Connection ${connectionId} is not connected. Connect it on the Integrations page in Kazzle.`);
15
+ this.connectionId = connectionId;
16
+ this.name = 'ConnectionNotConnectedError';
17
+ }
18
+ }
19
+ const DEFAULT_TTL_MS = 60 * 60_000;
20
+ const REFRESH_MARGIN_MS = 60_000;
21
+ const cache = new Map();
22
+ function apiKey() {
23
+ const key = process.env['KAZZLE_API_KEY'];
24
+ if (!key) {
25
+ throw new Error('KAZZLE_API_KEY is not set — connection tokens are only available to deployed Kazzle app components.');
26
+ }
27
+ return key;
28
+ }
29
+ /**
30
+ * A fresh vendor access token for a connection, e.g.
31
+ * `new Stripe(await getConnectionToken('8f2a…'))`.
32
+ */
33
+ export async function getConnectionToken(connectionId) {
34
+ const hit = cache.get(connectionId);
35
+ if (hit && hit.expiresAt - Date.now() > REFRESH_MARGIN_MS)
36
+ return hit.token;
37
+ const res = await fetch(`${kazzleApiUrl()}/connections/${encodeURIComponent(connectionId)}/token`, {
38
+ headers: { authorization: `Bearer ${apiKey()}` },
39
+ });
40
+ if (res.status === 404 || res.status === 409) {
41
+ const body = await res.json().catch(() => ({}));
42
+ cache.delete(connectionId);
43
+ throw new ConnectionNotConnectedError(connectionId, body.message);
44
+ }
45
+ if (!res.ok) {
46
+ throw new Error(`Kazzle connection token request failed: HTTP ${res.status}`);
47
+ }
48
+ const body = await res.json();
49
+ const expiresAt = body.expiresAt ? Date.parse(body.expiresAt) : Date.now() + DEFAULT_TTL_MS;
50
+ cache.set(connectionId, { token: body.token, expiresAt: Number.isFinite(expiresAt) ? expiresAt : Date.now() + DEFAULT_TTL_MS });
51
+ return body.token;
52
+ }
53
+ /** Forget cached tokens (tests, or after a reconnect). */
54
+ export function clearConnectionTokenCache() {
55
+ cache.clear();
56
+ }
package/dist/index.d.ts CHANGED
@@ -89,6 +89,13 @@ export interface KazzleConfig {
89
89
  target?: 'local' | 'remote';
90
90
  /** Path to the app icon file (png, jpg, svg, webp, ico) */
91
91
  icon?: string;
92
+ /**
93
+ * End-user login for this app (via @kazzle/app/auth — Google today).
94
+ * Distinct from `kazzleAuth`, which gates the launch surface by Kazzle
95
+ * account; appLogin is the app's OWN users, stored in its own database.
96
+ * Requires a process + database.
97
+ */
98
+ appLogin?: boolean;
92
99
  /** Executable components — UI frontends or background processes */
93
100
  components?: KazzleComponent[];
94
101
  /** AI skill definitions — markdown files the AI reads for domain knowledge */
package/dist/sdk.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './vendor/kazzle/index.js';
package/dist/sdk.js ADDED
@@ -0,0 +1,13 @@
1
+ // @kazzle/app/sdk — the Kazzle REST API client, re-exported for app code.
2
+ //
3
+ // Deployed app components get KAZZLE_API_KEY and KAZZLE_API_URL injected
4
+ // (server/apps/apps.runtime-env.ts), so `new Kazzle()` needs no arguments
5
+ // there: the key scopes every call to the app's space.
6
+ //
7
+ // The implementation lives in the `kazzle` package (packages/kazzle-sdk). In
8
+ // the monorepo this resolves to the workspace package; the PUBLISHED build
9
+ // vendors the SDK's dist under dist/vendor/kazzle and rewrites this entry to
10
+ // point at it (scripts/build.ts), so app code installed from npm never needs a
11
+ // separate `kazzle` install.
12
+ // Published build: the SDK is vendored (see scripts/build.ts).
13
+ export * from './vendor/kazzle/index.js';
@@ -0,0 +1,56 @@
1
+ import { type KazzleHttp } from './http';
2
+ import { TabsApi } from './browsers.tabs';
3
+ import { ProfilesApi } from './browsers.profiles';
4
+ import type { ActionResult, BrowserCreateOptions, BrowserCreateResponse, BrowserRow, BrowserState, ListResponse, TabOpenResponse, TabRow } from './sdk.types';
5
+ /**
6
+ * Handle for one browser session, bound to the first tab from the create
7
+ * response. Use `kazzle.browsers.tabs.*` with other tab ids for multi-tab work.
8
+ */
9
+ export declare class Browser {
10
+ private readonly api;
11
+ /** Browser session id. */
12
+ readonly id: string;
13
+ /** The session's first tab — the target of nav()/screenshot() below. */
14
+ readonly tabId: string;
15
+ /** Host computer, or null for a stealth cloud browser. */
16
+ readonly computerId: string | null;
17
+ readonly provider: string;
18
+ /** Open this URL in a browser of your own to watch the session live. */
19
+ readonly liveViewUrl: string | null;
20
+ constructor(api: BrowsersApi, created: BrowserCreateResponse);
21
+ /** Navigate the first tab. */
22
+ nav(url: string): Promise<ActionResult>;
23
+ /** Screenshot the first tab. Returns PNG (or JPEG) bytes. */
24
+ screenshot(): Promise<Uint8Array>;
25
+ /** Any tab action on the first tab by its URL segment, e.g. action('click', { ref }). */
26
+ action(segment: string, body?: Record<string, unknown>): Promise<ActionResult>;
27
+ /** List the session's open tabs. */
28
+ tabs(): Promise<ListResponse<TabRow>>;
29
+ /** Open another tab. The new tab id is the response's tab_id. */
30
+ openTab(options?: {
31
+ url?: string;
32
+ }): Promise<TabOpenResponse>;
33
+ /** Close the browser session. The profile (logins, cookies) survives. */
34
+ close(): Promise<ActionResult>;
35
+ }
36
+ export declare class BrowsersApi {
37
+ private readonly http;
38
+ readonly tabs: TabsApi;
39
+ readonly profiles: ProfilesApi;
40
+ constructor(http: KazzleHttp);
41
+ /**
42
+ * Open a browser session. No options means a stealth cloud browser
43
+ * (anti-bot, proxies) for the real web; pass computerId for that computer's
44
+ * built-in browser (its own pages: app previews, localhost). Returns a
45
+ * Browser handle bound to the first tab.
46
+ */
47
+ create(options?: BrowserCreateOptions): Promise<Browser>;
48
+ /** List browser sessions in the API key's space. */
49
+ list(options?: {
50
+ computerId?: string;
51
+ }): Promise<ListResponse<BrowserRow>>;
52
+ /** State of a browser session plus its live_view_url and tabs. */
53
+ get(browserId: string): Promise<BrowserState>;
54
+ /** Close the browser session and delete its tabs. The profile survives. */
55
+ close(browserId: string): Promise<ActionResult>;
56
+ }
@@ -0,0 +1,88 @@
1
+ // kazzle.browsers.* — browser sessions (/browsers). A browser id is a session
2
+ // id; the durable identity is the profile. create() returns a Browser handle
3
+ // bound to the session's first tab for the common drive-one-page flow.
4
+ import { apiPath } from './http';
5
+ import { TabsApi } from './browsers.tabs';
6
+ import { ProfilesApi } from './browsers.profiles';
7
+ /**
8
+ * Handle for one browser session, bound to the first tab from the create
9
+ * response. Use `kazzle.browsers.tabs.*` with other tab ids for multi-tab work.
10
+ */
11
+ export class Browser {
12
+ api;
13
+ /** Browser session id. */
14
+ id;
15
+ /** The session's first tab — the target of nav()/screenshot() below. */
16
+ tabId;
17
+ /** Host computer, or null for a stealth cloud browser. */
18
+ computerId;
19
+ provider;
20
+ /** Open this URL in a browser of your own to watch the session live. */
21
+ liveViewUrl;
22
+ constructor(api, created) {
23
+ this.api = api;
24
+ this.id = created.id;
25
+ this.tabId = created.tab_id;
26
+ this.computerId = created.computer_id;
27
+ this.provider = created.provider;
28
+ this.liveViewUrl = created.live_view_url;
29
+ }
30
+ /** Navigate the first tab. */
31
+ nav(url) {
32
+ return this.api.tabs.nav(this.id, this.tabId, { url });
33
+ }
34
+ /** Screenshot the first tab. Returns PNG (or JPEG) bytes. */
35
+ screenshot() {
36
+ return this.api.tabs.screenshot(this.id, this.tabId);
37
+ }
38
+ /** Any tab action on the first tab by its URL segment, e.g. action('click', { ref }). */
39
+ action(segment, body = {}) {
40
+ return this.api.tabs.action(this.id, this.tabId, segment, body);
41
+ }
42
+ /** List the session's open tabs. */
43
+ tabs() {
44
+ return this.api.tabs.list(this.id);
45
+ }
46
+ /** Open another tab. The new tab id is the response's tab_id. */
47
+ openTab(options = {}) {
48
+ return this.api.tabs.open(this.id, options);
49
+ }
50
+ /** Close the browser session. The profile (logins, cookies) survives. */
51
+ close() {
52
+ return this.api.close(this.id);
53
+ }
54
+ }
55
+ export class BrowsersApi {
56
+ http;
57
+ tabs;
58
+ profiles;
59
+ constructor(http) {
60
+ this.http = http;
61
+ this.tabs = new TabsApi(http);
62
+ this.profiles = new ProfilesApi(http);
63
+ }
64
+ /**
65
+ * Open a browser session. No options means a stealth cloud browser
66
+ * (anti-bot, proxies) for the real web; pass computerId for that computer's
67
+ * built-in browser (its own pages: app previews, localhost). Returns a
68
+ * Browser handle bound to the first tab.
69
+ */
70
+ async create(options = {}) {
71
+ const created = await this.http.json('POST', '/browsers', options);
72
+ return new Browser(this, created);
73
+ }
74
+ /** List browser sessions in the API key's space. */
75
+ list(options = {}) {
76
+ return this.http.json('GET', '/browsers', undefined, {
77
+ computer_id: options.computerId,
78
+ });
79
+ }
80
+ /** State of a browser session plus its live_view_url and tabs. */
81
+ get(browserId) {
82
+ return this.http.json('GET', apiPath `/browsers/${browserId}`);
83
+ }
84
+ /** Close the browser session and delete its tabs. The profile survives. */
85
+ close(browserId) {
86
+ return this.http.json('DELETE', apiPath `/browsers/${browserId}`);
87
+ }
88
+ }
@@ -0,0 +1,16 @@
1
+ import { type KazzleHttp } from './http';
2
+ import type { ListResponse, OkResponse, ProfileCreateOptions, ProfileCreateResponse, ProfileRow } from './sdk.types';
3
+ export declare class ProfilesApi {
4
+ private readonly http;
5
+ constructor(http: KazzleHttp);
6
+ /**
7
+ * Ensure the durable profile for a browser backend. No computerId means the
8
+ * space's stealth profile. Idempotent: an existing profile comes back with
9
+ * `created: false`.
10
+ */
11
+ create(options?: ProfileCreateOptions): Promise<ProfileCreateResponse>;
12
+ /** List the browser profiles in the API key's space. */
13
+ list(): Promise<ListResponse<ProfileRow>>;
14
+ /** Delete a profile. Cascades its sessions and tabs; saved logins and cookies are gone. */
15
+ delete(profileId: string): Promise<OkResponse>;
16
+ }
@@ -0,0 +1,26 @@
1
+ // kazzle.browsers.profiles.* — durable browsing identities (/browsers/profiles).
2
+ // Profiles hold logins and cookies; they outlive browser sessions and are
3
+ // shared across them.
4
+ import { apiPath } from './http';
5
+ export class ProfilesApi {
6
+ http;
7
+ constructor(http) {
8
+ this.http = http;
9
+ }
10
+ /**
11
+ * Ensure the durable profile for a browser backend. No computerId means the
12
+ * space's stealth profile. Idempotent: an existing profile comes back with
13
+ * `created: false`.
14
+ */
15
+ create(options = {}) {
16
+ return this.http.json('POST', '/browsers/profiles', options);
17
+ }
18
+ /** List the browser profiles in the API key's space. */
19
+ list() {
20
+ return this.http.json('GET', '/browsers/profiles');
21
+ }
22
+ /** Delete a profile. Cascades its sessions and tabs; saved logins and cookies are gone. */
23
+ delete(profileId) {
24
+ return this.http.json('DELETE', apiPath `/browsers/profiles/${profileId}`);
25
+ }
26
+ }
@@ -0,0 +1,62 @@
1
+ import { type KazzleHttp } from './http';
2
+ import type { ActionResult, ListResponse, TabOpenResponse, TabRow } from './sdk.types';
3
+ type Body = Record<string, unknown>;
4
+ export declare class TabsApi {
5
+ private readonly http;
6
+ constructor(http: KazzleHttp);
7
+ /** List the open tabs of a browser session. */
8
+ list(browserId: string): Promise<ListResponse<TabRow>>;
9
+ /** Open a new tab. The new tab id is the response's tab_id. */
10
+ open(browserId: string, options?: {
11
+ url?: string;
12
+ }): Promise<TabOpenResponse>;
13
+ /** POST one tab action by its URL segment. The named methods below all funnel through this. */
14
+ action(browserId: string, tabId: string, segment: string, body?: Body): Promise<ActionResult>;
15
+ private binary;
16
+ nav(browserId: string, tabId: string, body: {
17
+ url: string;
18
+ }): Promise<ActionResult>;
19
+ back(browserId: string, tabId: string): Promise<ActionResult>;
20
+ forward(browserId: string, tabId: string): Promise<ActionResult>;
21
+ reload(browserId: string, tabId: string): Promise<ActionResult>;
22
+ /** Returns PNG (or JPEG) bytes. */
23
+ screenshot(browserId: string, tabId: string, body?: Body): Promise<Uint8Array>;
24
+ /** Returns PDF bytes. */
25
+ pdf(browserId: string, tabId: string, body?: Body): Promise<Uint8Array>;
26
+ /** Accessibility snapshot. Element refs from it target later click/type calls. */
27
+ snapshot(browserId: string, tabId: string, body?: Body): Promise<ActionResult>;
28
+ eval(browserId: string, tabId: string, body: {
29
+ function: string;
30
+ arguments?: unknown[];
31
+ timeoutSeconds?: number;
32
+ }): Promise<ActionResult>;
33
+ click(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
34
+ type(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
35
+ select(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
36
+ fillForm(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
37
+ check(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
38
+ uncheck(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
39
+ hover(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
40
+ drag(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
41
+ upload(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
42
+ dialog(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
43
+ key(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
44
+ scroll(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
45
+ content(browserId: string, tabId: string, body?: Body): Promise<ActionResult>;
46
+ dom(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
47
+ extract(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
48
+ find(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
49
+ options(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
50
+ console(browserId: string, tabId: string, body?: Body): Promise<ActionResult>;
51
+ network(browserId: string, tabId: string, body?: Body): Promise<ActionResult>;
52
+ wait(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
53
+ status(browserId: string, tabId: string, body?: Body): Promise<ActionResult>;
54
+ download(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
55
+ /** Live view URL for the tab. */
56
+ live(browserId: string, tabId: string, body?: Body): Promise<ActionResult>;
57
+ focus(browserId: string, tabId: string): Promise<ActionResult>;
58
+ move(browserId: string, tabId: string, body: Body): Promise<ActionResult>;
59
+ /** Close the tab (DELETE /browsers/{id}/tabs/{tid}; POST .../close does the same). */
60
+ close(browserId: string, tabId: string): Promise<ActionResult>;
61
+ }
62
+ export {};