@kavel/kavel 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.
- package/LICENSE +21 -0
- package/README.md +91 -2
- package/dist/mod.d.ts +76 -0
- package/dist/mod.js +170 -0
- package/package.json +22 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kavel (https://www.kavel.ai)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @kavel/kavel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Generate **and edit** images from TypeScript with no API key, no account and no card. Runs in Deno,
|
|
4
|
+
Node 18+ and Bun — anything with `fetch`, zero dependencies.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @kavel/kavel # Deno: deno add jsr:@kavel/kavel
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { generate, edit, KavelError } from "@kavel/kavel";
|
|
12
|
+
|
|
13
|
+
const img = await generate(
|
|
14
|
+
"matte black ceramic mug on pale oak, soft window light from the left, shallow depth of field",
|
|
15
|
+
{ aspectRatio: "16:9" },
|
|
16
|
+
);
|
|
17
|
+
console.log(img.url); // https://cdn.kavel.ai/uploads/kie/image/....webp
|
|
18
|
+
|
|
19
|
+
const edited = await edit(img.url, "place the mug on a white marble surface");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Every other image client wants a key from OpenAI, fal or Replicate before it runs once. This one talks
|
|
23
|
+
to the free tier of [Kavel](https://www.kavel.ai/?utm_source=npm&utm_medium=package) — a client id
|
|
24
|
+
the module invents rather than an account you register — so a script works on a machine with nothing
|
|
25
|
+
configured, including CI and edge functions.
|
|
26
|
+
|
|
27
|
+
## Why it is shaped this way
|
|
28
|
+
|
|
29
|
+
- **Zero dependencies, fully typed.** Just `fetch` and Web Crypto.
|
|
30
|
+
- **An edit lane.** `edit` takes a photo you already have and one sentence describing the change.
|
|
31
|
+
- **One error class with a `kind`.** `quota` means wait or sign in, `rejected` means reword,
|
|
32
|
+
`sign_in` means the request is off the free shelf.
|
|
33
|
+
- **`AbortSignal` support and a deadline.** Pass `signal` to cancel from a UI.
|
|
34
|
+
- **It keeps polling through a dropped connection.** A free run waits in a queue and only reaches the
|
|
35
|
+
model at the end of it; giving up on one failed poll would throw away a job about to run.
|
|
36
|
+
|
|
37
|
+
## Limits, measured against the running service
|
|
38
|
+
|
|
39
|
+
- The free grant is **15 credits**. An image costs **5**, an edit costs **15**. The crate mints a new
|
|
40
|
+
client id per call, so a loop is not capped at three.
|
|
41
|
+
- **30 credits per IP per day** sits on top — about six images from one machine — and surfaces as
|
|
42
|
+
a `quota` error.
|
|
43
|
+
- Free output is **1K and watermarked**; `img.watermarked` tells you instead of leaving you to guess.
|
|
44
|
+
- Free runs queue **25–80 seconds** before the model is called, which is why the default deadline is
|
|
45
|
+
six minutes.
|
|
46
|
+
- **Video does not run anonymously** — the cheapest clip costs more than the grant. The
|
|
47
|
+
[AI video generator](https://www.kavel.ai/video?utm_source=npm&utm_medium=package) has a browser
|
|
48
|
+
lane for that.
|
|
49
|
+
|
|
50
|
+
Signing in removes the watermark and opens the full shelf:
|
|
51
|
+
[Nano Banana 2](https://www.kavel.ai/image/nano-banana-2?utm_source=npm&utm_medium=package) for up to
|
|
52
|
+
14 reference photos, [GPT Image 2](https://www.kavel.ai/image/gpt-image-2?utm_source=npm&utm_medium=package)
|
|
53
|
+
for 4K output, [Qwen Image 3](https://www.kavel.ai/image/qwen-image-3?utm_source=npm&utm_medium=package)
|
|
54
|
+
when the picture has to contain correctly spelled text, and
|
|
55
|
+
[Seedream 5.0 Pro](https://www.kavel.ai/image/seedream-5-pro?utm_source=npm&utm_medium=package) for
|
|
56
|
+
photoreal people. [Pricing](https://www.kavel.ai/pricing?utm_source=npm&utm_medium=package) has every tier.
|
|
57
|
+
|
|
58
|
+
## Past the free tier: an API key
|
|
59
|
+
|
|
60
|
+
When the free allowance runs out, the error tells you where to go next. Create a key at
|
|
61
|
+
[kavel.ai/settings/apikeys](https://www.kavel.ai/settings/apikeys?utm_source=npm&utm_medium=package) — the same account
|
|
62
|
+
you use on the site — and pass it (or set `KAVEL_API_KEY`):
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const img = await generate(prompt, { apiKey: process.env.KAVEL_API_KEY, model: "gpt-image-2" });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
With a key every call runs on your account, exactly as it would on the site: your
|
|
69
|
+
[credits and plan](https://www.kavel.ai/pricing?utm_source=npm&utm_medium=package), no per-IP ceiling, no watermark on a
|
|
70
|
+
paid plan, and any image model your plan includes. A `KavelError` with `kind === "auth"` means the key is wrong; `"quota"` with a key means the account is out of credits.
|
|
71
|
+
|
|
72
|
+
## Prompts that work
|
|
73
|
+
|
|
74
|
+
Name the light, the material and the composition — that moves the result more than adjectives do.
|
|
75
|
+
For edits, say what changes and the model keeps the rest; it is the same
|
|
76
|
+
[Nano Banana 2 Lite](https://www.kavel.ai/image/nano-banana-2-lite?utm_source=npm&utm_medium=package)
|
|
77
|
+
lane behind the [AI hairstyle changer](https://www.kavel.ai/image/ai-hairstyle-changer?utm_source=npm&utm_medium=package)
|
|
78
|
+
and the [AI outfit generator](https://www.kavel.ai/image/ai-outfit-generator?utm_source=npm&utm_medium=package).
|
|
79
|
+
The [free AI image generator](https://www.kavel.ai/image?utm_source=npm&utm_medium=package) runs the
|
|
80
|
+
same engines in a browser, and the [showcase](https://www.kavel.ai/showcases?utm_source=npm&utm_medium=package)
|
|
81
|
+
has finished output.
|
|
82
|
+
|
|
83
|
+
## Tests
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
deno test # from the repository
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Offline: argument validation, client id uniqueness, the quota wall (an HTTP 200 recognised only by a
|
|
90
|
+
field), the sign-in refusal, and polling through a dropped connection.
|
|
91
|
+
|
|
92
|
+
MIT
|
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** The service this module talks to. */
|
|
2
|
+
export declare const BASE_URL = "https://www.kavel.ai";
|
|
3
|
+
/** Credit balance a signed-out caller starts with. */
|
|
4
|
+
export declare const ANON_GRANT = 15;
|
|
5
|
+
/** What one generated image costs, so the grant pays for three. */
|
|
6
|
+
export declare const COST_TEXT_TO_IMAGE = 5;
|
|
7
|
+
/** What one edit costs: the whole grant. */
|
|
8
|
+
export declare const COST_EDIT = 15;
|
|
9
|
+
/** Credits one machine may spend per day regardless of client ids. */
|
|
10
|
+
export declare const IP_DAILY_CEILING = 30;
|
|
11
|
+
/** The free text-to-image engine. */
|
|
12
|
+
export declare const MODEL_GENERATE = "kavel-image-v1";
|
|
13
|
+
/** The free image-to-image engine. */
|
|
14
|
+
export declare const MODEL_EDIT = "nano-banana-2-lite";
|
|
15
|
+
/** One finished picture. */
|
|
16
|
+
export interface Image {
|
|
17
|
+
/** A permanent, cacheable CDN link. */
|
|
18
|
+
url: string;
|
|
19
|
+
/** Whether a mark was actually drawn on it. Signing in removes it. */
|
|
20
|
+
watermarked: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Tunes a single call. Every field is optional. */
|
|
23
|
+
export interface Options {
|
|
24
|
+
/** "1:1" (default), "16:9", "9:16", "4:3" or "3:4". Ignored by {@link edit}. */
|
|
25
|
+
aspectRatio?: string;
|
|
26
|
+
/** Poll interval in milliseconds. Default 5000. */
|
|
27
|
+
pollEveryMs?: number;
|
|
28
|
+
/** Deadline for the whole call in milliseconds. Default 360000. */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
/** Aborts the call. */
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
/**
|
|
33
|
+
* An API key from https://www.kavel.ai/settings/apikeys. With a key the call
|
|
34
|
+
* runs on your account — your credits, your plan's models, no watermark on a
|
|
35
|
+
* paid plan. Without one it runs on the free anonymous tier. Defaults to the
|
|
36
|
+
* `KAVEL_API_KEY` environment variable when it can be read.
|
|
37
|
+
*/
|
|
38
|
+
apiKey?: string;
|
|
39
|
+
/** Model to run. Only honoured with an API key; the free tier has one model per lane. */
|
|
40
|
+
model?: string;
|
|
41
|
+
/** Override for tests or a proxy. */
|
|
42
|
+
baseUrl?: string;
|
|
43
|
+
/** Override for tests. */
|
|
44
|
+
fetch?: typeof fetch;
|
|
45
|
+
}
|
|
46
|
+
/** Why a call failed, split by what the caller should do next. */
|
|
47
|
+
export type KavelErrorKind = "invalid" | "quota" | "rejected" | "sign_in" | "auth" | "timeout" | "service";
|
|
48
|
+
/** The only error this module throws on purpose. Branch on `kind`. */
|
|
49
|
+
export declare class KavelError extends Error {
|
|
50
|
+
/** What went wrong. */
|
|
51
|
+
readonly kind: KavelErrorKind;
|
|
52
|
+
/** Creates an error of a given kind. */
|
|
53
|
+
constructor(kind: KavelErrorKind, message: string);
|
|
54
|
+
}
|
|
55
|
+
interface Envelope {
|
|
56
|
+
code: number;
|
|
57
|
+
message?: string;
|
|
58
|
+
data?: Record<string, unknown> | null;
|
|
59
|
+
}
|
|
60
|
+
/** A fresh client id. A reused id walls partway through a loop. */
|
|
61
|
+
export declare function anonId(): string;
|
|
62
|
+
/**
|
|
63
|
+
* Interpret the submit response. The quota wall answers HTTP 200 with code 0,
|
|
64
|
+
* so it can only be recognised by a field.
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseSubmit(env: Envelope): string;
|
|
67
|
+
/** Turn a prompt into a new image. Name the light, the material and the composition. */
|
|
68
|
+
export declare function generate(prompt: string, opts?: Options): Promise<Image>;
|
|
69
|
+
/** Rewrite an existing image. `sourceUrl` must be a public http(s) url. */
|
|
70
|
+
export declare function edit(sourceUrl: string, instruction: string, opts?: Options): Promise<Image>;
|
|
71
|
+
/** What a client id has left. Costs nothing. Omit `clientId` to ask about a fresh one. */
|
|
72
|
+
export declare function credits(clientId?: string): Promise<{
|
|
73
|
+
remaining: number;
|
|
74
|
+
grant: number;
|
|
75
|
+
}>;
|
|
76
|
+
export {};
|
package/dist/mod.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Generate and edit images with no API key and no account. See README.md.
|
|
2
|
+
/** The service this module talks to. */
|
|
3
|
+
export const BASE_URL = "https://www.kavel.ai";
|
|
4
|
+
/** Credit balance a signed-out caller starts with. */
|
|
5
|
+
export const ANON_GRANT = 15;
|
|
6
|
+
/** What one generated image costs, so the grant pays for three. */
|
|
7
|
+
export const COST_TEXT_TO_IMAGE = 5;
|
|
8
|
+
/** What one edit costs: the whole grant. */
|
|
9
|
+
export const COST_EDIT = 15;
|
|
10
|
+
/** Credits one machine may spend per day regardless of client ids. */
|
|
11
|
+
export const IP_DAILY_CEILING = 30;
|
|
12
|
+
/** The free text-to-image engine. */
|
|
13
|
+
export const MODEL_GENERATE = "kavel-image-v1";
|
|
14
|
+
/** The free image-to-image engine. */
|
|
15
|
+
export const MODEL_EDIT = "nano-banana-2-lite";
|
|
16
|
+
/** The only error this module throws on purpose. Branch on `kind`. */
|
|
17
|
+
export class KavelError extends Error {
|
|
18
|
+
/** What went wrong. */
|
|
19
|
+
kind;
|
|
20
|
+
/** Creates an error of a given kind. */
|
|
21
|
+
constructor(kind, message) {
|
|
22
|
+
super(`kavel: ${message}`);
|
|
23
|
+
this.name = "KavelError";
|
|
24
|
+
this.kind = kind;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** A fresh client id. A reused id walls partway through a loop. */
|
|
28
|
+
export function anonId() {
|
|
29
|
+
const b = new Uint8Array(8);
|
|
30
|
+
crypto.getRandomValues(b);
|
|
31
|
+
return "ts-" + Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Interpret the submit response. The quota wall answers HTTP 200 with code 0,
|
|
35
|
+
* so it can only be recognised by a field.
|
|
36
|
+
*/
|
|
37
|
+
export function parseSubmit(env) {
|
|
38
|
+
if (env.code !== 0) {
|
|
39
|
+
const m = env.message ?? "request refused";
|
|
40
|
+
const lower = m.toLowerCase();
|
|
41
|
+
if (lower.includes("invalid api key"))
|
|
42
|
+
throw new KavelError("auth", m);
|
|
43
|
+
if (lower.includes("insufficient credits"))
|
|
44
|
+
throw new KavelError("quota", m);
|
|
45
|
+
if (lower.includes("sign in") || lower.includes("subscription"))
|
|
46
|
+
throw new KavelError("sign_in", m);
|
|
47
|
+
throw new KavelError("service", m);
|
|
48
|
+
}
|
|
49
|
+
const d = env.data ?? {};
|
|
50
|
+
if (d.wall === true) {
|
|
51
|
+
if (d.reason === "anon_ip_daily") {
|
|
52
|
+
throw new KavelError("quota", `this machine has used its ${IP_DAILY_CEILING} free credits for today — pass an apiKey from ${BASE_URL}/settings/apikeys to keep going`);
|
|
53
|
+
}
|
|
54
|
+
if (d.reason === "anon_unmetered_video")
|
|
55
|
+
throw new KavelError("sign_in", "video");
|
|
56
|
+
throw new KavelError("quota", `free allowance spent — pass an apiKey from ${BASE_URL}/settings/apikeys to keep going`);
|
|
57
|
+
}
|
|
58
|
+
if (typeof d.id !== "string" || d.id === "") {
|
|
59
|
+
throw new KavelError("service", "the service returned no task id");
|
|
60
|
+
}
|
|
61
|
+
return d.id;
|
|
62
|
+
}
|
|
63
|
+
/** Turn a prompt into a new image. Name the light, the material and the composition. */
|
|
64
|
+
export function generate(prompt, opts = {}) {
|
|
65
|
+
if (!prompt?.trim())
|
|
66
|
+
return Promise.reject(new KavelError("invalid", "prompt is required"));
|
|
67
|
+
return run(opts, {
|
|
68
|
+
provider: "kie",
|
|
69
|
+
mediaType: "image",
|
|
70
|
+
model: keyFor(opts) && opts.model ? opts.model : MODEL_GENERATE,
|
|
71
|
+
scene: "text-to-image",
|
|
72
|
+
prompt,
|
|
73
|
+
options: { aspect_ratio: opts.aspectRatio || "1:1" },
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** Rewrite an existing image. `sourceUrl` must be a public http(s) url. */
|
|
77
|
+
export function edit(sourceUrl, instruction, opts = {}) {
|
|
78
|
+
if (!/^https?:\/\//.test(sourceUrl ?? "")) {
|
|
79
|
+
return Promise.reject(new KavelError("invalid", "sourceUrl must be a public http(s) url"));
|
|
80
|
+
}
|
|
81
|
+
if (!instruction?.trim())
|
|
82
|
+
return Promise.reject(new KavelError("invalid", "instruction is required"));
|
|
83
|
+
return run(opts, {
|
|
84
|
+
provider: "kie",
|
|
85
|
+
mediaType: "image",
|
|
86
|
+
model: keyFor(opts) && opts.model ? opts.model : MODEL_EDIT,
|
|
87
|
+
scene: "image-to-image",
|
|
88
|
+
prompt: instruction,
|
|
89
|
+
options: { image_input: [sourceUrl] },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const wait = (ms, signal) => new Promise((resolve, reject) => {
|
|
93
|
+
const t = setTimeout(resolve, ms);
|
|
94
|
+
signal?.addEventListener("abort", () => {
|
|
95
|
+
clearTimeout(t);
|
|
96
|
+
reject(signal.reason);
|
|
97
|
+
}, { once: true });
|
|
98
|
+
});
|
|
99
|
+
function keyFor(opts) {
|
|
100
|
+
if (opts.apiKey)
|
|
101
|
+
return opts.apiKey;
|
|
102
|
+
try {
|
|
103
|
+
return globalThis.Deno?.env.get("KAVEL_API_KEY") ??
|
|
104
|
+
globalThis.process?.env.KAVEL_API_KEY;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return undefined; // Deno without --allow-env
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function run(opts, payload) {
|
|
111
|
+
const f = opts.fetch ?? fetch;
|
|
112
|
+
const base = opts.baseUrl ?? BASE_URL;
|
|
113
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 360_000);
|
|
114
|
+
const key = keyFor(opts);
|
|
115
|
+
// With a key the account is the identity; without one, a fresh anonymous id.
|
|
116
|
+
const auth = key ? { authorization: `Bearer ${key}` } : { "x-anon-id": anonId() };
|
|
117
|
+
const res = await f(`${base}/api/ai/generate`, {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: { "content-type": "application/json", ...auth },
|
|
120
|
+
body: JSON.stringify(payload),
|
|
121
|
+
signal: opts.signal,
|
|
122
|
+
});
|
|
123
|
+
const task = parseSubmit(await res.json());
|
|
124
|
+
// Queued runs only reach the model on the poll that crosses the end of the
|
|
125
|
+
// wait, so polling is what starts the work.
|
|
126
|
+
const poll = () => key
|
|
127
|
+
? f(`${base}/api/ai/query`, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: { "content-type": "application/json", ...auth },
|
|
130
|
+
body: JSON.stringify({ taskId: task }),
|
|
131
|
+
signal: opts.signal,
|
|
132
|
+
})
|
|
133
|
+
: f(`${base}/api/ai/anon-query?taskId=${encodeURIComponent(task)}&provider=kie&mediaType=image`, {
|
|
134
|
+
headers: auth,
|
|
135
|
+
signal: opts.signal,
|
|
136
|
+
});
|
|
137
|
+
while (Date.now() < deadline) {
|
|
138
|
+
await wait(opts.pollEveryMs ?? 5000, opts.signal);
|
|
139
|
+
let env;
|
|
140
|
+
try {
|
|
141
|
+
env = await (await poll()).json();
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
if (opts.signal?.aborted)
|
|
145
|
+
throw e;
|
|
146
|
+
continue; // a dropped poll is not a failed generation
|
|
147
|
+
}
|
|
148
|
+
if (env.code !== 0)
|
|
149
|
+
continue;
|
|
150
|
+
const d = env.data ?? {};
|
|
151
|
+
const clean = d.cleanImages;
|
|
152
|
+
if (clean?.length)
|
|
153
|
+
return { url: clean[0], watermarked: false };
|
|
154
|
+
const images = d.images;
|
|
155
|
+
if (images?.length) {
|
|
156
|
+
return { url: images[0], watermarked: Boolean(d.watermarked?.[0]) };
|
|
157
|
+
}
|
|
158
|
+
if (d.status === "failed" || d.status === "error")
|
|
159
|
+
throw new KavelError("rejected", "prompt refused");
|
|
160
|
+
}
|
|
161
|
+
throw new KavelError("timeout", "deadline passed");
|
|
162
|
+
}
|
|
163
|
+
/** What a client id has left. Costs nothing. Omit `clientId` to ask about a fresh one. */
|
|
164
|
+
export async function credits(clientId = anonId()) {
|
|
165
|
+
const env = await (await fetch(`${BASE_URL}/api/ai/anon-credits`, { headers: { "x-anon-id": clientId } }))
|
|
166
|
+
.json();
|
|
167
|
+
if (env.code !== 0)
|
|
168
|
+
throw new KavelError("service", env.message ?? "failed");
|
|
169
|
+
return { remaining: Number(env.data?.remaining ?? 0), grant: Number(env.data?.grant ?? 0) };
|
|
170
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kavel/kavel",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Generate and edit images from JavaScript and TypeScript with no API key, no account and no card. Zero dependencies; Node 18+, Bun, Deno and edge runtimes.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Kavel (https://www.kavel.ai)",
|
|
7
|
+
"homepage": "https://www.kavel.ai",
|
|
8
|
+
"repository": { "type": "git", "url": "git+https://github.com/hanshs474/kavel-ts.git" },
|
|
9
|
+
"bugs": { "url": "https://github.com/hanshs474/kavel-ts/issues" },
|
|
10
|
+
"keywords": ["kavel", "ai", "image-generation", "text-to-image", "image-to-image", "image-editing", "no-api-key", "sdk"],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/mod.js",
|
|
13
|
+
"types": "./dist/mod.d.ts",
|
|
14
|
+
"exports": { ".": { "types": "./dist/mod.d.ts", "import": "./dist/mod.js" } },
|
|
15
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.npm.json",
|
|
19
|
+
"prepack": "npm run build && node scripts/npm-readme.mjs to-npm",
|
|
20
|
+
"postpack": "node scripts/npm-readme.mjs restore"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": { "typescript": "^5.6.0" },
|
|
23
|
+
"engines": { "node": ">=18" }
|
|
24
|
+
}
|