diffusr 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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -2
  3. package/dist/cli/account.d.ts +19 -0
  4. package/dist/cli/account.js +250 -0
  5. package/dist/cli/account.js.map +1 -0
  6. package/dist/cli/args.d.ts +28 -0
  7. package/dist/cli/args.js +204 -0
  8. package/dist/cli/args.js.map +1 -0
  9. package/dist/cli/format.d.ts +10 -0
  10. package/dist/cli/format.js +80 -0
  11. package/dist/cli/format.js.map +1 -0
  12. package/dist/cli/io.d.ts +32 -0
  13. package/dist/cli/io.js +56 -0
  14. package/dist/cli/io.js.map +1 -0
  15. package/dist/cli/main.d.ts +3 -0
  16. package/dist/cli/main.js +176 -0
  17. package/dist/cli/main.js.map +1 -0
  18. package/dist/cli/ops.d.ts +22 -0
  19. package/dist/cli/ops.js +367 -0
  20. package/dist/cli/ops.js.map +1 -0
  21. package/dist/cli/spec.d.ts +37 -0
  22. package/dist/cli/spec.js +117 -0
  23. package/dist/cli/spec.js.map +1 -0
  24. package/dist/cli/spec.json +4207 -0
  25. package/dist/cli/test-helpers.d.ts +32 -0
  26. package/dist/cli/test-helpers.js +70 -0
  27. package/dist/cli/test-helpers.js.map +1 -0
  28. package/dist/cli.d.ts +2 -0
  29. package/dist/cli.js +9 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/client.d.ts +116 -0
  32. package/dist/client.js +281 -0
  33. package/dist/client.js.map +1 -0
  34. package/dist/config.d.ts +30 -0
  35. package/dist/config.js +71 -0
  36. package/dist/config.js.map +1 -0
  37. package/dist/errors.d.ts +21 -0
  38. package/dist/errors.js +23 -0
  39. package/dist/errors.js.map +1 -0
  40. package/dist/index.d.ts +8 -0
  41. package/dist/index.js +6 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/types.d.ts +208 -0
  44. package/dist/types.js +5 -0
  45. package/dist/types.js.map +1 -0
  46. package/dist/verify.d.ts +11 -0
  47. package/dist/verify.js +43 -0
  48. package/dist/verify.js.map +1 -0
  49. package/package.json +56 -3
@@ -0,0 +1,32 @@
1
+ import type { Io } from "./io.js";
2
+ export interface Call {
3
+ method: string;
4
+ url: string;
5
+ headers: Record<string, string>;
6
+ body: unknown;
7
+ raw: unknown;
8
+ }
9
+ export type Route = (call: Call) => {
10
+ status?: number;
11
+ body?: unknown;
12
+ } | undefined;
13
+ export interface Fake {
14
+ io: Io;
15
+ home: string;
16
+ out: string[];
17
+ err: string[];
18
+ calls: Call[];
19
+ asked: string[];
20
+ opened: string[];
21
+ setRoutes(r: Route[]): void;
22
+ answers: string[];
23
+ }
24
+ /** An Io with a mocked fetch, a temp HOME and a fake clock. Never touches the network. */
25
+ export declare function fakeIo(opts?: {
26
+ env?: Record<string, string>;
27
+ tty?: boolean;
28
+ routes?: Route[];
29
+ }): Fake;
30
+ export declare const BASE = "https://app.diffusr.ai/api/v2";
31
+ export declare const KEY = "dfsr_live_testkey";
32
+ export declare const on: (method: string, path: string, body: unknown, status?: number) => Route;
@@ -0,0 +1,70 @@
1
+ import { mkdtempSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ /** An Io with a mocked fetch, a temp HOME and a fake clock. Never touches the network. */
5
+ export function fakeIo(opts = {}) {
6
+ const home = mkdtempSync(join(tmpdir(), "diffusr-cli-"));
7
+ let routes = opts.routes ?? [];
8
+ let clock = 1_700_000_000_000;
9
+ const f = {
10
+ home,
11
+ out: [],
12
+ err: [],
13
+ calls: [],
14
+ asked: [],
15
+ opened: [],
16
+ answers: [],
17
+ setRoutes: (r) => (routes = r),
18
+ io: undefined,
19
+ };
20
+ f.io = {
21
+ out: (l) => void f.out.push(l),
22
+ err: (l) => void f.err.push(l),
23
+ errRaw: () => { },
24
+ stdinIsTTY: opts.tty ?? false,
25
+ stdoutIsTTY: opts.tty ?? false,
26
+ stderrIsTTY: false,
27
+ ask: async (q) => {
28
+ f.asked.push(q);
29
+ return f.answers.shift() ?? "";
30
+ },
31
+ env: { HOME: home, ...opts.env },
32
+ platform: "linux",
33
+ hostname: "athens",
34
+ fetch: (async (input, init) => {
35
+ const url = String(input);
36
+ const headers = {};
37
+ for (const [k, v] of Object.entries((init?.headers ?? {})))
38
+ headers[k.toLowerCase()] = v;
39
+ let body = init?.body;
40
+ if (typeof body === "string") {
41
+ try {
42
+ body = JSON.parse(body);
43
+ }
44
+ catch {
45
+ /* keep text */
46
+ }
47
+ }
48
+ const call = { method: init?.method ?? "GET", url, headers, body, raw: init?.body };
49
+ f.calls.push(call);
50
+ for (const r of routes) {
51
+ const hit = r(call);
52
+ if (hit)
53
+ return new Response(hit.body === undefined ? "" : JSON.stringify(hit.body), { status: hit.status ?? 200, headers: { "content-type": "application/json" } });
54
+ }
55
+ return new Response(JSON.stringify({ error: `no route for ${call.method} ${url}`, code: "NO_ROUTE" }), { status: 599 });
56
+ }),
57
+ sleep: async (ms) => {
58
+ clock += ms;
59
+ },
60
+ now: () => clock,
61
+ openUrl: (u) => void f.opened.push(u),
62
+ liveSpec: false,
63
+ pollIntervalMs: 1,
64
+ };
65
+ return f;
66
+ }
67
+ export const BASE = "https://app.diffusr.ai/api/v2";
68
+ export const KEY = "dfsr_live_testkey";
69
+ export const on = (method, path, body, status = 200) => (c) => c.method === method && c.url.split("?")[0] === BASE + path ? { status, body } : undefined;
70
+ //# sourceMappingURL=test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../../src/cli/test-helpers.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./cli/main.js";
3
+ runCli(process.argv.slice(2)).then((code) => {
4
+ process.exitCode = code;
5
+ }, (e) => {
6
+ process.stderr.write(`error: ${e instanceof Error ? e.message : String(e)}\n`);
7
+ process.exitCode = 1;
8
+ });
9
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAChC,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,CAAU,EAAE,EAAE;IACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
@@ -0,0 +1,116 @@
1
+ import type { HeaderBag } from "./verify.js";
2
+ import type { CallbackDelivery, CallbackDeliveryList, CallbackSecret, CallOptions, CreatorVideoBody, RequestOptions, EditImageBody, MakeAnswer, MakeImageBody, MakeMusicBody, MakeVideoBody, ModelList, RecastBody, Result, ResultList, RunTemplateBody, TemplateList, UploadedFile } from "./types.js";
3
+ export declare const DEFAULT_BASE_URL = "https://app.diffusr.ai/api/v2";
4
+ export declare const SDK_VERSION = "0.2.0";
5
+ export interface DiffusrOptions {
6
+ /** Default `https://app.diffusr.ai/api/v2`. */
7
+ baseUrl?: string;
8
+ /** Your own fetch (tests, proxies). Default: the global one (Node 18+). */
9
+ fetch?: typeof fetch;
10
+ /** Automatic retries on 429 / 503 / network errors / timeouts (GETs also on 500, 502, 504). Default 3. */
11
+ maxRetries?: number;
12
+ /** First retry delay when the API sends no Retry-After; doubles each try. Default 500. */
13
+ retryBaseMs?: number;
14
+ /** Per-request timeout in ms (each attempt). Default 60 000. */
15
+ timeoutMs?: number;
16
+ }
17
+ export interface WaitOptions {
18
+ /** Give up after this long. Default 15 minutes. */
19
+ timeoutMs?: number;
20
+ /** First pause between checks. Default 3000; it grows ×1.5 up to 15 s. */
21
+ intervalMs?: number;
22
+ /** Called with every answer while waiting. */
23
+ onPoll?: (result: Result) => void;
24
+ }
25
+ export declare class Diffusr {
26
+ private readonly apiKey;
27
+ private readonly baseUrl;
28
+ private readonly fetchImpl;
29
+ private readonly maxRetries;
30
+ private readonly retryBaseMs;
31
+ private readonly timeoutMs;
32
+ /**
33
+ * @param apiKey `dfsr_live_…` or `dfsr_test_…` (free). Default: `process.env.DIFFUSR_KEY`,
34
+ * then the key `diffusr login` saved. The base URL: `opts.baseUrl`, then `DIFFUSR_BASE_URL`,
35
+ * then the saved file's `base_url` (only when the key came from the file), then the default.
36
+ */
37
+ constructor(apiKey?: string, opts?: DiffusrOptions);
38
+ makeVideo<B extends MakeVideoBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
39
+ makeImage<B extends MakeImageBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
40
+ editImage<B extends EditImageBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
41
+ recast<B extends RecastBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
42
+ makeMusic<B extends MakeMusicBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
43
+ creatorVideo<B extends CreatorVideoBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
44
+ runTemplate<B extends RunTemplateBody>(body: B, opts?: CallOptions): Promise<MakeAnswer<B>>;
45
+ /** POST /{tool}. One Idempotency-Key per call, reused by every automatic retry. */
46
+ private make;
47
+ getResult(id: string, opts?: RequestOptions): Promise<Result>;
48
+ listResults(q?: {
49
+ limit?: number;
50
+ }): Promise<ResultList>;
51
+ /**
52
+ * Stop queued work before it generates (that part is never charged). A step
53
+ * that is already generating may still finish and be charged: check
54
+ * `result.charge` on the answer. A finished job comes back unchanged.
55
+ */
56
+ cancel(id: string): Promise<Result>;
57
+ /**
58
+ * Poll until the job is over and return the result. A job that ends FAILED
59
+ * or CANCELED is returned too: check `result.status`. Throws a
60
+ * `DiffusrError` with code `WAIT_TIMEOUT` if it is still running at the
61
+ * deadline (the job itself keeps going).
62
+ */
63
+ waitFor(id: string, opts?: WaitOptions): Promise<Result>;
64
+ /**
65
+ * Upload a local file and get a `file_url` to pass to a tool. Takes a path
66
+ * (Node), a Buffer / Uint8Array, or a Blob. Two steps: ask for a signed
67
+ * URL, PUT the exact bytes to it.
68
+ */
69
+ uploadFile(file: string | Uint8Array | Blob, opts?: {
70
+ contentType?: string;
71
+ }): Promise<{
72
+ file_url: string;
73
+ kind: string;
74
+ }>;
75
+ /** Have us copy a public link in (images and short clips; use `uploadFile` for big videos). */
76
+ uploadFromUrl(sourceUrl: string): Promise<UploadedFile>;
77
+ listModels(q?: {
78
+ kind?: "video" | "image";
79
+ }): Promise<ModelList>;
80
+ listTemplates(): Promise<TemplateList>;
81
+ listCallbacks(q?: {
82
+ limit?: number;
83
+ cursor?: string;
84
+ }): Promise<CallbackDeliveryList>;
85
+ /** One more try for a FAILED event (up to 3 by hand). */
86
+ retryCallback(eventId: string): Promise<CallbackDelivery>;
87
+ /** Your signing secret (`whsec_…`) for `Diffusr.verifyCallback`. */
88
+ getCallbackSecret(): Promise<CallbackSecret>;
89
+ rotateCallbackSecret(): Promise<CallbackSecret>;
90
+ /**
91
+ * Check a callback's signature (Standard Webhooks). Pass the raw request
92
+ * body text, the request headers and your `whsec_…` secret. Rejects a
93
+ * timestamp more than 5 minutes off; accepts a rotation's two signatures.
94
+ */
95
+ static verifyCallback(rawBody: string, headers: HeaderBag, secret: string, opts?: {
96
+ toleranceSec?: number;
97
+ nowSec?: number;
98
+ }): boolean;
99
+ /**
100
+ * Call any API path the typed methods do not cover (the CLI uses this).
101
+ * Same retries, timeouts and errors as every other call. `path` is relative
102
+ * to the base URL, e.g. `/results/r1`. Add your own `Idempotency-Key` header
103
+ * for a make call.
104
+ */
105
+ call<T = unknown>(method: "GET" | "POST", path: string, opts?: {
106
+ query?: Record<string, string | number | boolean | undefined>;
107
+ body?: unknown;
108
+ headers?: Record<string, string>;
109
+ signal?: AbortSignal;
110
+ timeoutMs?: number;
111
+ }): Promise<T>;
112
+ private request;
113
+ /** 0.5 s, 1 s, 2 s… with a little jitter. */
114
+ private backoff;
115
+ private toError;
116
+ }
package/dist/client.js ADDED
@@ -0,0 +1,281 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { DiffusrError } from "./errors.js";
3
+ import { resolveKey } from "./config.js";
4
+ import { verifyCallback } from "./verify.js";
5
+ import { TERMINAL_STATUSES } from "./types.js";
6
+ export const DEFAULT_BASE_URL = "https://app.diffusr.ai/api/v2";
7
+ export const SDK_VERSION = "0.2.0";
8
+ /** Never wait longer than this on a single automatic retry; throw instead. */
9
+ const MAX_AUTO_RETRY_WAIT_SEC = 30;
10
+ const DEFAULT_TIMEOUT_MS = 60_000;
11
+ const CONTENT_TYPES = {
12
+ jpg: "image/jpeg",
13
+ jpeg: "image/jpeg",
14
+ png: "image/png",
15
+ webp: "image/webp",
16
+ gif: "image/gif",
17
+ mp4: "video/mp4",
18
+ mov: "video/quicktime",
19
+ webm: "video/webm",
20
+ mp3: "audio/mpeg",
21
+ wav: "audio/wav",
22
+ m4a: "audio/mp4",
23
+ };
24
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
25
+ function toQuery(q) {
26
+ const p = new URLSearchParams();
27
+ for (const [k, v] of Object.entries(q ?? {}))
28
+ if (v !== undefined)
29
+ p.set(k, String(v));
30
+ const s = p.toString();
31
+ return s ? `?${s}` : "";
32
+ }
33
+ export class Diffusr {
34
+ apiKey;
35
+ baseUrl;
36
+ fetchImpl;
37
+ maxRetries;
38
+ retryBaseMs;
39
+ timeoutMs;
40
+ /**
41
+ * @param apiKey `dfsr_live_…` or `dfsr_test_…` (free). Default: `process.env.DIFFUSR_KEY`,
42
+ * then the key `diffusr login` saved. The base URL: `opts.baseUrl`, then `DIFFUSR_BASE_URL`,
43
+ * then the saved file's `base_url` (only when the key came from the file), then the default.
44
+ */
45
+ constructor(apiKey, opts = {}) {
46
+ const found = resolveKey(apiKey || undefined);
47
+ if (!found) {
48
+ throw new DiffusrError({ status: 0, code: "MISSING_API_KEY", message: "Pass an API key, set DIFFUSR_KEY, or run `npx diffusr login`." });
49
+ }
50
+ const envBase = typeof process !== "undefined" ? process.env.DIFFUSR_BASE_URL : undefined;
51
+ const fileBase = found.source === "file" ? found.config?.base_url : undefined;
52
+ this.apiKey = found.key;
53
+ this.baseUrl = (opts.baseUrl || envBase || fileBase || DEFAULT_BASE_URL).replace(/\/+$/, "");
54
+ this.fetchImpl = opts.fetch ?? ((...a) => fetch(...a));
55
+ this.maxRetries = opts.maxRetries ?? 3;
56
+ this.retryBaseMs = opts.retryBaseMs ?? 500;
57
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
58
+ }
59
+ // ——— make calls (paid; `dry_run: true` is free) ———
60
+ makeVideo(body, opts) {
61
+ return this.make("make_video", body, opts);
62
+ }
63
+ makeImage(body, opts) {
64
+ return this.make("make_image", body, opts);
65
+ }
66
+ editImage(body, opts) {
67
+ return this.make("edit_image", body, opts);
68
+ }
69
+ recast(body, opts) {
70
+ return this.make("recast", body, opts);
71
+ }
72
+ makeMusic(body, opts) {
73
+ return this.make("make_music", body, opts);
74
+ }
75
+ creatorVideo(body, opts) {
76
+ return this.make("creator_video", body, opts);
77
+ }
78
+ runTemplate(body, opts) {
79
+ return this.make("run_template", body, opts);
80
+ }
81
+ /** POST /{tool}. One Idempotency-Key per call, reused by every automatic retry. */
82
+ async make(tool, body, opts = {}) {
83
+ const key = opts.idempotencyKey ?? body.idempotency_key ?? randomUUID();
84
+ const { idempotency_key: _drop, ...rest } = body;
85
+ void _drop;
86
+ return this.request("POST", `/${tool}`, { body: rest, headers: { "Idempotency-Key": key }, signal: opts.signal, timeoutMs: opts.timeoutMs });
87
+ }
88
+ // ——— results ———
89
+ getResult(id, opts) {
90
+ return this.request("GET", `/results/${encodeURIComponent(id)}`, opts);
91
+ }
92
+ listResults(q) {
93
+ return this.request("GET", `/results${toQuery(q)}`);
94
+ }
95
+ /**
96
+ * Stop queued work before it generates (that part is never charged). A step
97
+ * that is already generating may still finish and be charged: check
98
+ * `result.charge` on the answer. A finished job comes back unchanged.
99
+ */
100
+ cancel(id) {
101
+ return this.request("POST", `/results/${encodeURIComponent(id)}/cancel`, { body: {} });
102
+ }
103
+ /**
104
+ * Poll until the job is over and return the result. A job that ends FAILED
105
+ * or CANCELED is returned too: check `result.status`. Throws a
106
+ * `DiffusrError` with code `WAIT_TIMEOUT` if it is still running at the
107
+ * deadline (the job itself keeps going).
108
+ */
109
+ async waitFor(id, opts = {}) {
110
+ const deadline = Date.now() + (opts.timeoutMs ?? 15 * 60_000);
111
+ let interval = opts.intervalMs ?? 3000;
112
+ for (;;) {
113
+ const result = await this.getResult(id);
114
+ opts.onPoll?.(result);
115
+ if (TERMINAL_STATUSES.includes(result.status))
116
+ return result;
117
+ const left = deadline - Date.now();
118
+ if (left <= 0)
119
+ break;
120
+ await sleep(Math.min(interval, left));
121
+ interval = Math.min(Math.round(interval * 1.5), Math.max(15_000, opts.intervalMs ?? 0));
122
+ if (Date.now() >= deadline)
123
+ break;
124
+ }
125
+ throw new DiffusrError({
126
+ status: 0,
127
+ code: "WAIT_TIMEOUT",
128
+ message: `Result ${id} was still not finished after ${Math.round((opts.timeoutMs ?? 15 * 60_000) / 1000)} s. It keeps running: call waitFor or getResult again.`,
129
+ });
130
+ }
131
+ // ——— files ———
132
+ /**
133
+ * Upload a local file and get a `file_url` to pass to a tool. Takes a path
134
+ * (Node), a Buffer / Uint8Array, or a Blob. Two steps: ask for a signed
135
+ * URL, PUT the exact bytes to it.
136
+ */
137
+ async uploadFile(file, opts = {}) {
138
+ let bytes;
139
+ let contentType = opts.contentType;
140
+ if (typeof file === "string") {
141
+ const { readFile } = await import("node:fs/promises");
142
+ bytes = await readFile(file);
143
+ contentType ??= CONTENT_TYPES[file.split(".").pop()?.toLowerCase() ?? ""];
144
+ }
145
+ else if (typeof Blob !== "undefined" && file instanceof Blob) {
146
+ bytes = new Uint8Array(await file.arrayBuffer());
147
+ contentType ??= file.type || undefined;
148
+ }
149
+ else {
150
+ bytes = file;
151
+ }
152
+ if (!contentType) {
153
+ throw new DiffusrError({ status: 0, code: "CONTENT_TYPE_REQUIRED", message: "Pass { contentType } (e.g. \"video/mp4\"): it could not be worked out." });
154
+ }
155
+ const ticket = await this.request("POST", "/uploads", { body: { content_type: contentType, size_bytes: bytes.byteLength } });
156
+ const res = await this.fetchImpl(ticket.upload_url, { method: ticket.method, headers: ticket.headers, body: bytes });
157
+ if (!res.ok) {
158
+ throw new DiffusrError({ status: res.status, code: "UPLOAD_FAILED", message: `The upload PUT failed with HTTP ${res.status}.` });
159
+ }
160
+ return { file_url: ticket.file_url, kind: ticket.kind };
161
+ }
162
+ /** Have us copy a public link in (images and short clips; use `uploadFile` for big videos). */
163
+ uploadFromUrl(sourceUrl) {
164
+ return this.request("POST", "/uploads", { body: { source_url: sourceUrl } });
165
+ }
166
+ // ——— catalog ———
167
+ listModels(q) {
168
+ return this.request("GET", `/models${toQuery(q)}`);
169
+ }
170
+ listTemplates() {
171
+ return this.request("GET", "/templates");
172
+ }
173
+ // ——— callbacks ———
174
+ listCallbacks(q) {
175
+ return this.request("GET", `/callbacks${toQuery(q)}`);
176
+ }
177
+ /** One more try for a FAILED event (up to 3 by hand). */
178
+ retryCallback(eventId) {
179
+ return this.request("POST", `/callbacks/${encodeURIComponent(eventId)}/retry`, { body: {} });
180
+ }
181
+ /** Your signing secret (`whsec_…`) for `Diffusr.verifyCallback`. */
182
+ getCallbackSecret() {
183
+ return this.request("GET", "/callbacks/secret");
184
+ }
185
+ rotateCallbackSecret() {
186
+ return this.request("POST", "/callbacks/secret/rotate", { body: {} });
187
+ }
188
+ /**
189
+ * Check a callback's signature (Standard Webhooks). Pass the raw request
190
+ * body text, the request headers and your `whsec_…` secret. Rejects a
191
+ * timestamp more than 5 minutes off; accepts a rotation's two signatures.
192
+ */
193
+ static verifyCallback(rawBody, headers, secret, opts) {
194
+ return verifyCallback(rawBody, headers, secret, opts);
195
+ }
196
+ // ——— any operation ———
197
+ /**
198
+ * Call any API path the typed methods do not cover (the CLI uses this).
199
+ * Same retries, timeouts and errors as every other call. `path` is relative
200
+ * to the base URL, e.g. `/results/r1`. Add your own `Idempotency-Key` header
201
+ * for a make call.
202
+ */
203
+ call(method, path, opts = {}) {
204
+ return this.request(method, `${path}${toQuery(opts.query)}`, { body: opts.body, headers: opts.headers, signal: opts.signal, timeoutMs: opts.timeoutMs });
205
+ }
206
+ // ——— plumbing ———
207
+ async request(method, path, init = {}) {
208
+ const headers = {
209
+ Authorization: `Bearer ${this.apiKey}`,
210
+ Accept: "application/json",
211
+ "User-Agent": `diffusr-node/${SDK_VERSION}`,
212
+ ...init.headers,
213
+ };
214
+ if (init.body !== undefined)
215
+ headers["Content-Type"] = "application/json";
216
+ const payload = init.body === undefined ? undefined : JSON.stringify(init.body);
217
+ const timeoutMs = init.timeoutMs ?? this.timeoutMs;
218
+ for (let attempt = 0;; attempt++) {
219
+ let res;
220
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
221
+ const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
222
+ try {
223
+ res = await this.fetchImpl(this.baseUrl + path, { method, headers, signal, ...(payload !== undefined ? { body: payload } : {}) });
224
+ }
225
+ catch (cause) {
226
+ // The caller's own abort is theirs: never retry or rewrap it.
227
+ if (init.signal?.aborted)
228
+ throw cause;
229
+ const timedOut = timeoutSignal.aborted;
230
+ if (attempt < this.maxRetries) {
231
+ await sleep(this.backoff(attempt));
232
+ continue;
233
+ }
234
+ throw timedOut
235
+ ? new DiffusrError({ status: 0, code: "TIMEOUT", message: `diffusr did not answer within ${Math.round(timeoutMs / 1000)} s.` })
236
+ : new DiffusrError({ status: 0, code: "NETWORK_ERROR", message: `Could not reach diffusr: ${cause.message}` });
237
+ }
238
+ if (res.ok) {
239
+ const text = await res.text();
240
+ return (text ? JSON.parse(text) : {});
241
+ }
242
+ const err = await this.toError(res);
243
+ const retryable = res.status === 429 || res.status === 503 || (method === "GET" && (res.status === 500 || res.status === 502 || res.status === 504));
244
+ const wait = err.retryAfter ?? this.backoff(attempt) / 1000;
245
+ if (retryable && attempt < this.maxRetries && wait <= MAX_AUTO_RETRY_WAIT_SEC) {
246
+ await sleep(Math.round(wait * 1000));
247
+ continue;
248
+ }
249
+ throw err;
250
+ }
251
+ }
252
+ /** 0.5 s, 1 s, 2 s… with a little jitter. */
253
+ backoff(attempt) {
254
+ return this.retryBaseMs * 2 ** attempt * (1 + Math.random() * 0.2);
255
+ }
256
+ async toError(res) {
257
+ const text = await res.text().catch(() => "");
258
+ let body;
259
+ try {
260
+ const parsed = JSON.parse(text);
261
+ if (parsed && typeof parsed === "object")
262
+ body = parsed;
263
+ }
264
+ catch {
265
+ /* not JSON */
266
+ }
267
+ const header = Number(res.headers.get("retry-after"));
268
+ const fromBody = typeof body?.retry_after_seconds === "number" ? body.retry_after_seconds : undefined;
269
+ const retryAfter = Number.isFinite(header) && res.headers.get("retry-after") !== null ? header : fromBody;
270
+ const message = typeof body?.error === "string" ? body.error : text.slice(0, 300) || `HTTP ${res.status}`;
271
+ return new DiffusrError({
272
+ status: res.status,
273
+ code: typeof body?.code === "string" ? body.code : `HTTP_${res.status}`,
274
+ message,
275
+ requestId: (typeof body?.request_id === "string" ? body.request_id : undefined) ?? res.headers.get("x-request-id") ?? undefined,
276
+ retryAfter,
277
+ body,
278
+ });
279
+ }
280
+ }
281
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAuB/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AACnC,8EAA8E;AAC9E,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAwBlC,MAAM,aAAa,GAA2B;IAC5C,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,iBAAiB;IACtB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;CACjB,CAAC;AAEF,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAE1E,SAAS,OAAO,CAAC,CAAoE;IACnF,MAAM,CAAC,GAAG,IAAI,eAAe,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAAE,IAAI,CAAC,KAAK,SAAS;YAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvB,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,OAAO,OAAO;IACD,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,SAAS,CAAe;IACxB,UAAU,CAAS;IACnB,WAAW,CAAS;IACpB,SAAS,CAAS;IAEnC;;;;OAIG;IACH,YAAY,MAAe,EAAE,OAAuB,EAAE;QACpD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,+DAA+D,EAAE,CAAC,CAAC;QAC3I,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1F,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9E,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,QAAQ,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC7F,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC;QAC3C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACxD,CAAC;IAED,qDAAqD;IAErD,SAAS,CAA0B,IAAO,EAAE,IAAkB;QAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IACD,SAAS,CAA0B,IAAO,EAAE,IAAkB;QAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IACD,SAAS,CAA0B,IAAO,EAAE,IAAkB;QAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,CAAuB,IAAO,EAAE,IAAkB;QACtD,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IACD,SAAS,CAA0B,IAAO,EAAE,IAAkB;QAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IACD,YAAY,CAA6B,IAAO,EAAE,IAAkB;QAClE,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,WAAW,CAA4B,IAAO,EAAE,IAAkB;QAChE,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,mFAAmF;IAC3E,KAAK,CAAC,IAAI,CAChB,IAAY,EACZ,IAAO,EACP,OAAoB,EAAE;QAEtB,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,eAAe,IAAI,UAAU,EAAE,CAAC;QACxE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;QACjD,KAAK,KAAK,CAAC;QACX,OAAO,IAAI,CAAC,OAAO,CAAgB,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,iBAAiB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9J,CAAC;IAED,kBAAkB;IAElB,SAAS,CAAC,EAAU,EAAE,IAAqB;QACzC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAED,WAAW,CAAC,CAAsB;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,EAAU;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACzF,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,OAAoB,EAAE;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC;QAC9D,IAAI,QAAQ,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;QACvC,SAAS,CAAC;YACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YACtB,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;YAC7D,MAAM,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACnC,IAAI,IAAI,IAAI,CAAC;gBAAE,MAAM;YACrB,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;YACtC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC;YACxF,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,MAAM;QACpC,CAAC;QACD,MAAM,IAAI,YAAY,CAAC;YACrB,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,UAAU,EAAE,iCAAiC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,wDAAwD;SACjK,CAAC,CAAC;IACL,CAAC;IAED,gBAAgB;IAEhB;;;;OAIG;IACH,KAAK,CAAC,UAAU,CACd,IAAgC,EAChC,OAAiC,EAAE;QAEnC,IAAI,KAAiB,CAAC;QACtB,IAAI,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACnC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YACtD,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC7B,WAAW,KAAK,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YAC/D,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACjD,WAAW,KAAK,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,IAAkB,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,wEAAwE,EAAE,CAAC,CAAC;QAC1J,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAe,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC3I,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,KAAc,EAAE,CAAC,CAAC;QAC9H,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,mCAAmC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;QACnI,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IAC1D,CAAC;IAED,+FAA+F;IAC/F,aAAa,CAAC,SAAiB;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,kBAAkB;IAElB,UAAU,CAAC,CAAgC;QACzC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC;IAED,oBAAoB;IAEpB,aAAa,CAAC,CAAuC;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,yDAAyD;IACzD,aAAa,CAAC,OAAe;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,oEAAoE;IACpE,iBAAiB;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IAClD,CAAC;IAED,oBAAoB;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,0BAA0B,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACxE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,OAAe,EAAE,OAAkB,EAAE,MAAc,EAAE,IAAiD;QAC1H,OAAO,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC;IAED,wBAAwB;IAExB;;;;;OAKG;IACH,IAAI,CACF,MAAsB,EACtB,IAAY,EACZ,OAAsK,EAAE;QAExK,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9J,CAAC;IAED,mBAAmB;IAEX,KAAK,CAAC,OAAO,CAAI,MAAsB,EAAE,IAAY,EAAE,OAA+H,EAAE;QAC9L,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;YACtC,MAAM,EAAE,kBAAkB;YAC1B,YAAY,EAAE,gBAAgB,WAAW,EAAE;YAC3C,GAAG,IAAI,CAAC,OAAO;SAChB,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC;QACnD,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,IAAI,GAAa,CAAC;YAClB,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YAC3F,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACpI,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,8DAA8D;gBAC9D,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;oBAAE,MAAM,KAAK,CAAC;gBACtC,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC;gBACvC,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;oBAC9B,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;oBACnC,SAAS;gBACX,CAAC;gBACD,MAAM,QAAQ;oBACZ,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,iCAAiC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;oBAC/H,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,4BAA6B,KAAe,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC9H,CAAC;YACD,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAM,CAAC;YAC7C,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;YACrJ,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YAC5D,IAAI,SAAS,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,uBAAuB,EAAE,CAAC;gBAC9E,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;gBACrC,SAAS;YACX,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,6CAA6C;IACrC,OAAO,CAAC,OAAe;QAC7B,OAAO,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IACrE,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,GAAa;QACjC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,IAAI,IAAyC,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;gBAAE,IAAI,GAAG,MAAiC,CAAC;QACrF,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,OAAO,IAAI,EAAE,mBAAmB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS,CAAC;QACtG,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1G,MAAM,OAAO,GAAG,OAAO,IAAI,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;QAC1G,OAAO,IAAI,YAAY,CAAC;YACtB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE;YACvE,OAAO;YACP,SAAS,EAAE,CAAC,OAAO,IAAI,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,SAAS;YAC/H,UAAU;YACV,IAAI;SACL,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ /** What `diffusr login` saves. Only `key` matters to the SDK; the rest is for people. */
2
+ export interface SavedConfig {
3
+ key: string;
4
+ key_id?: string;
5
+ key_name?: string;
6
+ mode?: "live" | "test";
7
+ email?: string;
8
+ /** Only written when it is not the default base URL. */
9
+ base_url?: string;
10
+ saved_at?: string;
11
+ }
12
+ type Env = Record<string, string | undefined>;
13
+ /** `$XDG_CONFIG_HOME/diffusr/config.json`, else `~/.config/diffusr/config.json`; on Windows `%APPDATA%\diffusr\config.json`. */
14
+ export declare function configPath(env?: Env, platform?: string): string;
15
+ /** Where the CLI caches the API description. */
16
+ export declare function cacheDir(env?: Env, platform?: string): string;
17
+ /** The saved config, or undefined when there is none or it has no key. Never throws. */
18
+ export declare function readConfig(env?: Env, platform?: string): SavedConfig | undefined;
19
+ /** Write the config: folder 0700, file 0600, via a temp file in the same folder and a rename. */
20
+ export declare function writeConfig(cfg: SavedConfig, env?: Env, platform?: string): string;
21
+ /** Delete the config file (only that file). Returns true when there was one. */
22
+ export declare function deleteConfig(env?: Env, platform?: string): boolean;
23
+ export type KeySource = "argument" | "env" | "file";
24
+ /** Key lookup order: the argument, then DIFFUSR_KEY, then the saved file. */
25
+ export declare function resolveKey(arg?: string, env?: Env, platform?: string): {
26
+ key: string;
27
+ source: KeySource;
28
+ config?: SavedConfig;
29
+ } | undefined;
30
+ export {};
package/dist/config.js ADDED
@@ -0,0 +1,71 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ const processEnv = () => (typeof process !== "undefined" ? process.env : {});
5
+ const processPlatform = () => (typeof process !== "undefined" ? process.platform : "linux");
6
+ /** `$XDG_CONFIG_HOME/diffusr/config.json`, else `~/.config/diffusr/config.json`; on Windows `%APPDATA%\diffusr\config.json`. */
7
+ export function configPath(env = processEnv(), platform = processPlatform()) {
8
+ if (platform === "win32")
9
+ return join(env.APPDATA || join(homedir(), "AppData", "Roaming"), "diffusr", "config.json");
10
+ return join(env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config"), "diffusr", "config.json");
11
+ }
12
+ /** Where the CLI caches the API description. */
13
+ export function cacheDir(env = processEnv(), platform = processPlatform()) {
14
+ if (platform === "win32")
15
+ return join(env.LOCALAPPDATA || join(homedir(), "AppData", "Local"), "diffusr");
16
+ return join(env.XDG_CACHE_HOME || join(env.HOME || homedir(), ".cache"), "diffusr");
17
+ }
18
+ /** The saved config, or undefined when there is none or it has no key. Never throws. */
19
+ export function readConfig(env = processEnv(), platform) {
20
+ try {
21
+ const parsed = JSON.parse(readFileSync(configPath(env, platform), "utf8"));
22
+ if (parsed && typeof parsed === "object" && typeof parsed.key === "string" && parsed.key) {
23
+ return parsed;
24
+ }
25
+ }
26
+ catch {
27
+ /* missing or unreadable */
28
+ }
29
+ return undefined;
30
+ }
31
+ /** Write the config: folder 0700, file 0600, via a temp file in the same folder and a rename. */
32
+ export function writeConfig(cfg, env = processEnv(), platform) {
33
+ const file = configPath(env, platform);
34
+ const dir = dirname(file);
35
+ const existed = existsSync(dir);
36
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
37
+ // Only tighten a folder we made: it may hold other files that are not ours.
38
+ if (!existed)
39
+ chmodSync(dir, 0o700);
40
+ const tmp = join(dir, `.config.${process.pid}.${Date.now()}.tmp`);
41
+ try {
42
+ writeFileSync(tmp, JSON.stringify(cfg, null, 2) + "\n", { mode: 0o600 });
43
+ chmodSync(tmp, 0o600);
44
+ renameSync(tmp, file);
45
+ }
46
+ catch (e) {
47
+ rmSync(tmp, { force: true });
48
+ throw e;
49
+ }
50
+ return file;
51
+ }
52
+ /** Delete the config file (only that file). Returns true when there was one. */
53
+ export function deleteConfig(env = processEnv(), platform) {
54
+ const file = configPath(env, platform);
55
+ if (!existsSync(file))
56
+ return false;
57
+ rmSync(file, { force: true });
58
+ return true;
59
+ }
60
+ /** Key lookup order: the argument, then DIFFUSR_KEY, then the saved file. */
61
+ export function resolveKey(arg, env = processEnv(), platform) {
62
+ if (arg)
63
+ return { key: arg, source: "argument" };
64
+ if (env.DIFFUSR_KEY)
65
+ return { key: env.DIFFUSR_KEY, source: "env" };
66
+ const config = readConfig(env, platform);
67
+ if (config)
68
+ return { key: config.key, source: "file", config };
69
+ return undefined;
70
+ }
71
+ //# sourceMappingURL=config.js.map