@clawvard/sdk 0.1.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/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @clawvard/sdk
2
+
3
+ Typed TypeScript client for **Clawvard's non-LLM service layer** —
4
+ composed workflows, video / image / data jobs, third-party API
5
+ integrations. Same `sk-xxx` key works for the OpenAI SDK against
6
+ [token.clawvard.school](https://token.clawvard.school) and for this
7
+ SDK against [clawvard.school](https://clawvard.school).
8
+
9
+ ```bash
10
+ pnpm add @clawvard/sdk
11
+ # or: npm i @clawvard/sdk / yarn add @clawvard/sdk
12
+ ```
13
+
14
+ ## Quick start
15
+
16
+ ```ts
17
+ import { Clawvard } from "@clawvard/sdk";
18
+
19
+ const cv = new Clawvard({ apiKey: "sk-xxx" });
20
+
21
+ // Local services (instant, charged per call)
22
+ const { hex } = await cv.text.hash({ text: "hello", algorithm: "sha256" });
23
+ const { dataUri } = await cv.url.qrCode({ text: "https://clawvard.school" });
24
+
25
+ // Long-running jobs (auto-poll, refundable on failure)
26
+ const result = await cv.video.removeSilence({ inputUrl: "https://…" })
27
+ .onProgress((pct, note) => console.log(`${(pct * 100).toFixed(0)}% — ${note}`))
28
+ .wait();
29
+
30
+ // Untyped escape hatch — call any registered service by id
31
+ const out = await cv.workflow.run<MyOutput>("my.service", input).wait();
32
+ ```
33
+
34
+ For LLM calls (chat / embeddings / Whisper / DALL·E), point the
35
+ OpenAI SDK at Token Relay with the same `sk-xxx`:
36
+
37
+ ```ts
38
+ import { OpenAI } from "openai";
39
+ const ai = new OpenAI({
40
+ apiKey: "sk-xxx",
41
+ baseURL: "https://token.clawvard.school/v1",
42
+ });
43
+ await ai.chat.completions.create({ model: "claude-opus-4-7", messages });
44
+ ```
45
+
46
+ ## What's in the box
47
+
48
+ | Class | Purpose |
49
+ |---|---|
50
+ | `Clawvard` | Main client — composes generated namespaces with platform helpers |
51
+ | `Job<T>` | Handle for long-running jobs — `.wait()`, `.onProgress(cb)`, `.cancel()`, `.id()` |
52
+ | `HttpClient` | Lower-level: `.invoke()`, `.invokeJob()`, raw `.raw()` — exposed via `cv.client` |
53
+ | Generated namespaces | One per service group (`cv.util.*`, `cv.text.*`, `cv.url.*`, `cv.video.*`, …) |
54
+ | `cv.workflow.run(id, input)` | Untyped invoker — works for any registered service without bumping the SDK |
55
+ | `cv.catalog()` | Public catalog with pricing per service |
56
+ | `cv.usage()` / `cv.usageFor(g, m)` | Caller's usage stats per service |
57
+
58
+ ## Per-call options
59
+
60
+ ```ts
61
+ // Idempotency: same key → server returns the original outcome (no double-charge)
62
+ await cv.client.invoke("video", "render", input, {
63
+ idempotencyKey: crypto.randomUUID(),
64
+ });
65
+
66
+ // Webhooks (job services only): platform POSTs the terminal state
67
+ // to your URL with HMAC signature header `X-Clawvard-Signature`
68
+ await cv.client.invokeJob("video", "removeSilence", input, {
69
+ idempotencyKey: "...",
70
+ webhookUrl: "https://you.com/webhook",
71
+ });
72
+ ```
73
+
74
+ ## Configuration
75
+
76
+ ```ts
77
+ const cv = new Clawvard({
78
+ apiKey: "sk-xxx", // Required for remote calls
79
+ baseUrl: "https://clawvard.school", // Default; override for staging
80
+ pollIntervalMs: 2000, // Job polling cadence
81
+ retry: { maxRetries: 3, baseDelayMs: 250 },
82
+ fetch: customFetch, // For testing / edge runtimes
83
+ plugins: [], // School / capability plugins
84
+ });
85
+ ```
86
+
87
+ ### Auto-retry policy
88
+
89
+ - **GET**: always retry on 5xx + network errors
90
+ - **POST/PUT/DELETE**: only retry on 5xx if `Idempotency-Key` is set;
91
+ always retry on network errors (request never reached the server)
92
+ - **4xx**: never retry (caller's bug)
93
+
94
+ ## Errors
95
+
96
+ ```ts
97
+ import { ClawvardError, MissingApiKeyError } from "@clawvard/sdk";
98
+
99
+ try {
100
+ await cv.video.render(input);
101
+ } catch (err) {
102
+ if (err instanceof MissingApiKeyError) { /* config issue */ }
103
+ // Server errors carry .status + .hint:
104
+ // (err as Error & { status?: number; hint?: string }).status
105
+ }
106
+ ```
107
+
108
+ ## Plugins
109
+
110
+ Capability packages can attach namespaces:
111
+
112
+ ```ts
113
+ import { Clawvard } from "@clawvard/sdk";
114
+ import { mediaPlugin } from "@clawvard/sdk-media";
115
+
116
+ const cv = new Clawvard({
117
+ apiKey: "sk-xxx",
118
+ plugins: [mediaPlugin()],
119
+ });
120
+ await cv.video.extractFrames({ url, every: "1s" });
121
+ ```
122
+
123
+ ## Resources
124
+
125
+ - Docs: [clawvard.school/docs](https://clawvard.school/docs)
126
+ - Service catalog: [clawvard.school/services](https://clawvard.school/services)
127
+ - Get an API key: [clawvard.school/token-relay](https://clawvard.school/token-relay)
128
+ - Issues: [github.com/THEZIONLABS/clawvard](https://github.com/THEZIONLABS/clawvard)
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,18 @@
1
+ /**
2
+ * SDK-side error classes. Server-side `error: "..."` strings come back
3
+ * via `toApiError` in http-client.ts; these are for client-side preflight
4
+ * errors raised before any HTTP call goes out.
5
+ */
6
+ export declare class ClawvardError extends Error {
7
+ readonly code: string;
8
+ constructor(message: string, code: string);
9
+ }
10
+ /**
11
+ * Raised when a remote method is called but no API key was supplied at
12
+ * construction. Local-only methods (e.g., `cv.video.extractFrames()`) do
13
+ * not raise this — they don't need a key.
14
+ */
15
+ export declare class MissingApiKeyError extends ClawvardError {
16
+ constructor(operation: string);
17
+ }
18
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,qBAAa,aAAc,SAAQ,KAAK;aACO,IAAI,EAAE,MAAM;gBAA7C,OAAO,EAAE,MAAM,EAAkB,IAAI,EAAE,MAAM;CAI1D;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,aAAa;gBACvC,SAAS,EAAE,MAAM;CAS9B"}
package/dist/errors.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SDK-side error classes. Server-side `error: "..."` strings come back
3
+ * via `toApiError` in http-client.ts; these are for client-side preflight
4
+ * errors raised before any HTTP call goes out.
5
+ */
6
+ export class ClawvardError extends Error {
7
+ code;
8
+ constructor(message, code) {
9
+ super(message);
10
+ this.code = code;
11
+ this.name = "ClawvardError";
12
+ }
13
+ }
14
+ /**
15
+ * Raised when a remote method is called but no API key was supplied at
16
+ * construction. Local-only methods (e.g., `cv.video.extractFrames()`) do
17
+ * not raise this — they don't need a key.
18
+ */
19
+ export class MissingApiKeyError extends ClawvardError {
20
+ constructor(operation) {
21
+ super(`Clawvard: apiKey is required to call \`${operation}\`. Pass it to ` +
22
+ `\`new Clawvard({ apiKey: "sk-xxx" })\` or call only local ` +
23
+ `helpers (e.g. \`cv.video.*\`, \`cv.subtitle.*\`).`, "missing_api_key");
24
+ this.name = "MissingApiKeyError";
25
+ }
26
+ }
27
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACO;IAA7C,YAAY,OAAe,EAAkB,IAAY;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QAD4B,SAAI,GAAJ,IAAI,CAAQ;QAEvD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IACnD,YAAY,SAAiB;QAC3B,KAAK,CACH,0CAA0C,SAAS,iBAAiB;YAClE,4DAA4D;YAC5D,mDAAmD,EACrD,iBAAiB,CAClB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * AUTO-GENERATED — do not edit by hand.
3
+ *
4
+ * Run `pnpm sdk:gen` to regenerate from `src/lib/services/registry.ts`.
5
+ */
6
+ import type { HttpClient } from "./http-client";
7
+ import type { Job } from "./job";
8
+ import type { InputArgs, OutputOf } from "./service-types";
9
+ export declare class TextNamespace {
10
+ private readonly c;
11
+ constructor(c: HttpClient);
12
+ /** text.word-count (local) — generated. */
13
+ wordCount(...args: InputArgs<"text.word-count">): Promise<OutputOf<"text.word-count">>;
14
+ /** text.hash (local) — generated. */
15
+ hash(...args: InputArgs<"text.hash">): Promise<OutputOf<"text.hash">>;
16
+ }
17
+ export declare class UrlNamespace {
18
+ private readonly c;
19
+ constructor(c: HttpClient);
20
+ /** url.qr-code (local) — generated. */
21
+ qrCode(...args: InputArgs<"url.qr-code">): Promise<OutputOf<"url.qr-code">>;
22
+ /** url.preview (local) — generated. */
23
+ preview(...args: InputArgs<"url.preview">): Promise<OutputOf<"url.preview">>;
24
+ }
25
+ export declare class UtilNamespace {
26
+ private readonly c;
27
+ constructor(c: HttpClient);
28
+ /** util.ip-check (proxy) — generated. */
29
+ ipCheck(...args: InputArgs<"util.ip-check">): Promise<OutputOf<"util.ip-check">>;
30
+ /** util.delay-echo (job) — generated. */
31
+ delayEcho(...args: InputArgs<"util.delay-echo">): Job<OutputOf<"util.delay-echo">>;
32
+ }
33
+ export declare class GeneratedClient {
34
+ readonly text: TextNamespace;
35
+ readonly url: UrlNamespace;
36
+ readonly util: UtilNamespace;
37
+ constructor(client: HttpClient);
38
+ }
39
+ //# sourceMappingURL=generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../src/generated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3D,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAAD,CAAC,EAAE,UAAU;IAE1C,2CAA2C;IAC3C,SAAS,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,iBAAiB,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;IAItF,qCAAqC;IACrC,IAAI,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;CAGtE;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAAD,CAAC,EAAE,UAAU;IAE1C,uCAAuC;IACvC,MAAM,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAI3E,uCAAuC;IACvC,OAAO,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;CAG7E;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAAD,CAAC,EAAE,UAAU;IAE1C,yCAAyC;IACzC,OAAO,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAIhF,yCAAyC;IACzC,SAAS,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,iBAAiB,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;CAGnF;AAED,qBAAa,eAAe;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;gBAEjB,MAAM,EAAE,UAAU;CAK/B"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * AUTO-GENERATED — do not edit by hand.
3
+ *
4
+ * Run `pnpm sdk:gen` to regenerate from `src/lib/services/registry.ts`.
5
+ */
6
+ export class TextNamespace {
7
+ c;
8
+ constructor(c) {
9
+ this.c = c;
10
+ }
11
+ /** text.word-count (local) — generated. */
12
+ wordCount(...args) {
13
+ return this.c.invoke("text", "wordCount", args[0]);
14
+ }
15
+ /** text.hash (local) — generated. */
16
+ hash(...args) {
17
+ return this.c.invoke("text", "hash", args[0]);
18
+ }
19
+ }
20
+ export class UrlNamespace {
21
+ c;
22
+ constructor(c) {
23
+ this.c = c;
24
+ }
25
+ /** url.qr-code (local) — generated. */
26
+ qrCode(...args) {
27
+ return this.c.invoke("url", "qrCode", args[0]);
28
+ }
29
+ /** url.preview (local) — generated. */
30
+ preview(...args) {
31
+ return this.c.invoke("url", "preview", args[0]);
32
+ }
33
+ }
34
+ export class UtilNamespace {
35
+ c;
36
+ constructor(c) {
37
+ this.c = c;
38
+ }
39
+ /** util.ip-check (proxy) — generated. */
40
+ ipCheck(...args) {
41
+ return this.c.invoke("util", "ipCheck", args[0]);
42
+ }
43
+ /** util.delay-echo (job) — generated. */
44
+ delayEcho(...args) {
45
+ return this.c.invokeJob("util", "delayEcho", args[0]);
46
+ }
47
+ }
48
+ export class GeneratedClient {
49
+ text;
50
+ url;
51
+ util;
52
+ constructor(client) {
53
+ this.text = new TextNamespace(client);
54
+ this.url = new UrlNamespace(client);
55
+ this.util = new UtilNamespace(client);
56
+ }
57
+ }
58
+ //# sourceMappingURL=generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generated.js","sourceRoot":"","sources":["../src/generated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,CAAa;QAAb,MAAC,GAAD,CAAC,CAAY;IAAG,CAAC;IAE9C,2CAA2C;IAC3C,SAAS,CAAC,GAAG,IAAkC;QAC7C,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAA8B,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,qCAAqC;IACrC,IAAI,CAAC,GAAG,IAA4B;QAClC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAwB,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;CACF;AAED,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,CAAa;QAAb,MAAC,GAAD,CAAC,CAAY;IAAG,CAAC;IAE9C,uCAAuC;IACvC,MAAM,CAAC,GAAG,IAA8B;QACtC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAA0B,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,uCAAuC;IACvC,OAAO,CAAC,GAAG,IAA8B;QACvC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAA0B,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,CAAa;QAAb,MAAC,GAAD,CAAC,CAAY;IAAG,CAAC;IAE9C,yCAAyC;IACzC,OAAO,CAAC,GAAG,IAAgC;QACzC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAA4B,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,yCAAyC;IACzC,SAAS,CAAC,GAAG,IAAkC;QAC7C,OAAO,IAAI,CAAC,CAAC,CAAC,SAAS,CAA8B,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,CAAC;CACF;AAED,MAAM,OAAO,eAAe;IACjB,IAAI,CAAgB;IACpB,GAAG,CAAe;IAClB,IAAI,CAAgB;IAE7B,YAAY,MAAkB;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;CACF"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Internal HTTP client used by every namespace method.
3
+ * Not part of the public surface — exported only so the
4
+ * auto-generated client can construct one.
5
+ */
6
+ import { Job } from "./job";
7
+ export interface ClawvardHttpConfig {
8
+ /** Optional — local-only callers (e.g. `cv.video.extractFrames`) work without it. */
9
+ apiKey?: string;
10
+ baseUrl: string;
11
+ pollIntervalMs: number;
12
+ fetchImpl: typeof globalThis.fetch;
13
+ /** Auto-retry policy for transient failures (5xx + network errors).
14
+ * Default is 3 attempts with exponential backoff. Set `maxRetries:0`
15
+ * to disable. */
16
+ retry?: {
17
+ maxRetries: number;
18
+ baseDelayMs: number;
19
+ };
20
+ }
21
+ export declare class HttpClient {
22
+ private readonly baseUrl;
23
+ private readonly apiKey;
24
+ private readonly fetchImpl;
25
+ private readonly retry;
26
+ readonly pollIntervalMs: number;
27
+ constructor(config: ClawvardHttpConfig);
28
+ /** Throws `MissingApiKeyError` if no key was supplied. Used by every
29
+ * remote method before issuing the HTTP call. Local-only callers
30
+ * (e.g. cv.video.extractFrames) skip this check entirely. */
31
+ requireApiKey(operation: string): string;
32
+ /** Fetch wrapper with optional bearer auth + transparent retry on
33
+ * 5xx + network errors. Retries are only safe for idempotent
34
+ * operations: GET/HEAD always retried; POST/PUT/DELETE only when the
35
+ * caller provides an `Idempotency-Key` header (the dispatcher dedups
36
+ * on the server side). Non-idempotent POSTs without the header are
37
+ * retried only on network errors (where we know the request never
38
+ * reached the server).
39
+ *
40
+ * Authorization header is added only when an `apiKey` was configured;
41
+ * endpoints that need auth respond 401 and the caller surfaces that. */
42
+ raw(path: string, init?: RequestInit): Promise<Response>;
43
+ invoke<T>(group: string, method: string, body: unknown, options?: InvokeOptions): Promise<T>;
44
+ invokeJob<T>(group: string, method: string, body: unknown, options?: InvokeOptions): Job<T>;
45
+ }
46
+ /** Per-call overrides passed to namespace methods. Most callers can
47
+ * ignore this — it's used by `cv.client.invoke(...)` and the
48
+ * retry-friendly forms. */
49
+ export interface InvokeOptions {
50
+ /** Sent as the `Idempotency-Key` header. Same key + same caller =
51
+ * the dispatcher returns the original invocation's outcome instead
52
+ * of re-charging / re-creating. Setting this also unlocks SDK auto-
53
+ * retry on 5xx for the call (otherwise only network errors retry). */
54
+ idempotencyKey?: string;
55
+ /** (Job services only) Sent as the `X-Webhook-Url` header. The
56
+ * platform POSTs the terminal job state to this URL so callers
57
+ * don't have to poll. Body is HMAC-signed with the server's
58
+ * webhook secret — verify via the `X-Clawvard-Signature` header. */
59
+ webhookUrl?: string;
60
+ }
61
+ export declare function toApiError(res: Response): Promise<Error>;
62
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACnC;;sBAEkB;IAClB,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8C;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,MAAM,EAAE,kBAAkB;IAQtC;;kEAE8D;IAC9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAKxC;;;;;;;;;6EASyE;IACnE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IA4BxD,MAAM,CAAC,CAAC,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,CAAC,CAAC;IAcb,SAAS,CAAC,CAAC,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,aAAa,GACtB,GAAG,CAAC,CAAC,CAAC;CAIV;AAED;;4BAE4B;AAC5B,MAAM,WAAW,aAAa;IAC5B;;;2EAGuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;yEAGqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD,wBAAsB,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAW9D"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Internal HTTP client used by every namespace method.
3
+ * Not part of the public surface — exported only so the
4
+ * auto-generated client can construct one.
5
+ */
6
+ import { MissingApiKeyError } from "./errors";
7
+ import { Job } from "./job";
8
+ export class HttpClient {
9
+ baseUrl;
10
+ apiKey;
11
+ fetchImpl;
12
+ retry;
13
+ pollIntervalMs;
14
+ constructor(config) {
15
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
16
+ this.apiKey = config.apiKey;
17
+ this.fetchImpl = config.fetchImpl;
18
+ this.pollIntervalMs = config.pollIntervalMs;
19
+ this.retry = config.retry ?? { maxRetries: 3, baseDelayMs: 250 };
20
+ }
21
+ /** Throws `MissingApiKeyError` if no key was supplied. Used by every
22
+ * remote method before issuing the HTTP call. Local-only callers
23
+ * (e.g. cv.video.extractFrames) skip this check entirely. */
24
+ requireApiKey(operation) {
25
+ if (!this.apiKey)
26
+ throw new MissingApiKeyError(operation);
27
+ return this.apiKey;
28
+ }
29
+ /** Fetch wrapper with optional bearer auth + transparent retry on
30
+ * 5xx + network errors. Retries are only safe for idempotent
31
+ * operations: GET/HEAD always retried; POST/PUT/DELETE only when the
32
+ * caller provides an `Idempotency-Key` header (the dispatcher dedups
33
+ * on the server side). Non-idempotent POSTs without the header are
34
+ * retried only on network errors (where we know the request never
35
+ * reached the server).
36
+ *
37
+ * Authorization header is added only when an `apiKey` was configured;
38
+ * endpoints that need auth respond 401 and the caller surfaces that. */
39
+ async raw(path, init) {
40
+ const method = (init?.method || "GET").toUpperCase();
41
+ const headersIn = (init?.headers || {});
42
+ const hasIdemKey = "Idempotency-Key" in headersIn || "idempotency-key" in headersIn;
43
+ const isReadOnly = method === "GET" || method === "HEAD";
44
+ const fullySafe = isReadOnly || hasIdemKey;
45
+ const url = `${this.baseUrl}${path}`;
46
+ const headers = { ...headersIn };
47
+ if (this.apiKey)
48
+ headers.Authorization = `Bearer ${this.apiKey}`;
49
+ const merged = { ...init, headers };
50
+ let lastError;
51
+ for (let attempt = 0; attempt <= this.retry.maxRetries; attempt++) {
52
+ try {
53
+ const res = await this.fetchImpl(url, merged);
54
+ if (res.status < 500)
55
+ return res;
56
+ if (!fullySafe)
57
+ return res;
58
+ if (attempt === this.retry.maxRetries)
59
+ return res;
60
+ }
61
+ catch (err) {
62
+ lastError = err;
63
+ if (attempt === this.retry.maxRetries)
64
+ throw err;
65
+ }
66
+ await sleep(this.retry.baseDelayMs * 2 ** attempt);
67
+ }
68
+ throw lastError ?? new Error("retry exhausted");
69
+ }
70
+ async invoke(group, method, body, options) {
71
+ this.requireApiKey(`${group}.${method}`);
72
+ const headers = { "Content-Type": "application/json" };
73
+ if (options?.idempotencyKey)
74
+ headers["Idempotency-Key"] = options.idempotencyKey;
75
+ if (options?.webhookUrl)
76
+ headers["X-Webhook-Url"] = options.webhookUrl;
77
+ const res = await this.raw(`/api/services/invoke/${group}/${method}`, {
78
+ method: "POST",
79
+ headers,
80
+ body: JSON.stringify(body ?? {}),
81
+ });
82
+ if (!res.ok)
83
+ throw await toApiError(res);
84
+ return (await res.json());
85
+ }
86
+ invokeJob(group, method, body, options) {
87
+ this.requireApiKey(`${group}.${method}`);
88
+ return new Job(this, group, method, body, options);
89
+ }
90
+ }
91
+ function sleep(ms) {
92
+ return new Promise((r) => setTimeout(r, ms));
93
+ }
94
+ export async function toApiError(res) {
95
+ let payload = {};
96
+ try {
97
+ payload = await res.json();
98
+ }
99
+ catch {
100
+ /* non-JSON body */
101
+ }
102
+ const err = new Error(payload.error || res.statusText);
103
+ err.status = res.status;
104
+ err.hint = payload.hint;
105
+ return err;
106
+ }
107
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @clawvard/sdk — unified service client for Clawvard's composed /
3
+ * long-running / external-API-integrated services.
4
+ *
5
+ * ⚠️ NOT FOR LLM PASSTHROUGHS. Chat, embeddings, Whisper, TTS, DALL·E
6
+ * etc. are served by Token Relay at `https://token.clawvard.school`
7
+ * with `sk-xxx` keys. Use the OpenAI SDK (or any OpenAI-compatible
8
+ * client) pointed at that base URL for those.
9
+ *
10
+ * This SDK covers:
11
+ * - Composed workflows (multi-step recipes)
12
+ * - Long-running jobs (video render, batch processing)
13
+ * - External-API integrations (Shotstack, Mux, Replicate, ...)
14
+ *
15
+ * @example
16
+ * import { Clawvard } from "@clawvard/sdk";
17
+ *
18
+ * const cv = new Clawvard({ apiKey: "sk-xxx" });
19
+ *
20
+ * // Smoke test (proxy)
21
+ * const { ip } = await cv.util.ipCheck();
22
+ *
23
+ * // Job — auto-polls to completion with progress callback
24
+ * const result = await cv.util.delayEcho({ seconds: 5 })
25
+ * .onProgress((pct, note) => console.log(`${(pct * 100).toFixed(0)}% — ${note}`))
26
+ * .wait();
27
+ *
28
+ * // Arbitrary registered service by id (untyped escape hatch)
29
+ * const post = await cv.workflow.run("workflow.podcast2blog", { audioUrl: "..." }).wait();
30
+ */
31
+ import { HttpClient } from "./http-client";
32
+ import { Job } from "./job";
33
+ import { GeneratedClient } from "./generated";
34
+ import type { ClawvardPlugin } from "./plugin";
35
+ export { Job } from "./job";
36
+ export { HttpClient } from "./http-client";
37
+ export type { InvokeOptions } from "./http-client";
38
+ export type { ServiceTypeMap, InputOf, OutputOf, InputArgs, } from "./service-types";
39
+ export type { ClawvardPlugin, ClawvardInstance } from "./plugin";
40
+ export { ClawvardError, MissingApiKeyError } from "./errors";
41
+ export interface ClawvardConfig {
42
+ /** Token Relay-issued `sk-xxx` API key. Mint or list yours at
43
+ * https://clawvard.school/token-relay. The same key works for the
44
+ * OpenAI SDK pointed at `https://token.clawvard.school` AND for
45
+ * this SDK — there's no separate "Clawvard key" any more.
46
+ *
47
+ * Optional — local-only methods (e.g. `cv.video.extractFrames` from
48
+ * `@clawvard/sdk-media`) work without it. Remote calls throw
49
+ * `MissingApiKeyError` when no key is configured. */
50
+ apiKey?: string;
51
+ /** Override base URL. Defaults to production. */
52
+ baseUrl?: string;
53
+ /** Poll interval for jobs, ms. Defaults to 2000. */
54
+ pollIntervalMs?: number;
55
+ /** Fetch implementation override (for testing / edge runtimes). */
56
+ fetch?: typeof globalThis.fetch;
57
+ /** Auto-retry policy for transient failures. Default = 3 attempts
58
+ * with exponential backoff (250ms base). Set `{maxRetries: 0}` to
59
+ * disable. POST/PUT/DELETE only retry on 5xx when the call carries
60
+ * an Idempotency-Key, but ALL methods retry on network errors. */
61
+ retry?: {
62
+ maxRetries: number;
63
+ baseDelayMs?: number;
64
+ };
65
+ /** School / capability plugins. Each plugin's `install()` runs once
66
+ * at construction and may attach namespaces to the instance. */
67
+ plugins?: ClawvardPlugin[];
68
+ }
69
+ /** The main SDK client. Composes the auto-generated namespace methods
70
+ * (one per registered service `group`) with hand-written platform
71
+ * helpers (`catalog`, `usage`, `usageFor`, `workflow.run`).
72
+ *
73
+ * When backend adds a new service to `src/lib/services/registry.ts`
74
+ * and runs `pnpm sdk:gen`, a new typed method appears on the
75
+ * appropriate namespace automatically. */
76
+ export declare class Clawvard extends GeneratedClient {
77
+ /** Untyped invocation by id — escape hatch for newly-registered
78
+ * services not yet in `ServiceTypeMap`, or for callers that don't
79
+ * want to bump their SDK version. */
80
+ readonly workflow: WorkflowNamespace;
81
+ /** Lower-level HTTP client. Use this when you need per-call options
82
+ * the generated namespace methods don't expose, e.g. an
83
+ * `Idempotency-Key` header:
84
+ *
85
+ * cv.client.invoke("video", "render", input, { idempotencyKey: "..." })
86
+ * cv.client.invokeJob("video", "removeSilence", input, { idempotencyKey: "..." })
87
+ * */
88
+ readonly client: HttpClient;
89
+ constructor(config?: ClawvardConfig);
90
+ /**
91
+ * Fetch the service catalog. Each entry carries pricing + runtime +
92
+ * access flags — enough to render a marketplace tile without any
93
+ * other request. Public endpoint, no auth required.
94
+ */
95
+ catalog(): Promise<ServiceCatalogEntry[]>;
96
+ /**
97
+ * Caller's usage across every registered service. Auth required.
98
+ * Services never called show zero stats so the UI can render a
99
+ * complete table.
100
+ */
101
+ usage(): Promise<UsageReport>;
102
+ /**
103
+ * Caller's usage of a single service. Pass `includeHistory: true`
104
+ * to also get the N most recent invocation rows.
105
+ */
106
+ usageFor(group: string, method: string, options?: {
107
+ includeHistory?: boolean;
108
+ limit?: number;
109
+ }): Promise<SingleServiceUsage>;
110
+ }
111
+ /** Untyped invocation by service id — for services not yet in
112
+ * `ServiceTypeMap` or for callers who prefer string-keyed access. */
113
+ declare class WorkflowNamespace {
114
+ private readonly c;
115
+ constructor(c: HttpClient);
116
+ /** Composable workflow / generic invoke. Returns a `Job<T>` so the
117
+ * caller can await `.wait()` for both proxy and job services
118
+ * (proxy services complete on the first poll). */
119
+ run<T>(id: string, input: unknown): Job<T>;
120
+ }
121
+ /** Price display bundled with every catalog entry. */
122
+ export interface PriceDisplay {
123
+ /** Authoritative price — what gets deducted on success. */
124
+ credits: number;
125
+ /** ¥ at the published rate (10 credits = ¥1). 2dp. */
126
+ cny: number;
127
+ /** $ at the published rate (100 credits = $1). 4dp. */
128
+ usd: number;
129
+ }
130
+ export interface ServiceCatalogEntry {
131
+ id: string;
132
+ group: string;
133
+ method: string;
134
+ summary: string;
135
+ description?: string;
136
+ costCredits: number;
137
+ approxCostHintUsd?: number;
138
+ runtime: "proxy" | "job" | "local";
139
+ pricing: PriceDisplay;
140
+ access?: {
141
+ beta?: boolean;
142
+ requiresCoursePurchase?: string;
143
+ minAccountTier?: string;
144
+ };
145
+ rateLimit?: {
146
+ perMinute?: number;
147
+ perHour?: number;
148
+ perDay?: number;
149
+ };
150
+ }
151
+ /** One row of per-service usage for a specific user. */
152
+ export interface ServiceUsage {
153
+ serviceId: string;
154
+ calls: number;
155
+ refunded: number;
156
+ netCalls: number;
157
+ creditsSpent: number;
158
+ lastCalledAt: string | null;
159
+ }
160
+ /** Shape of `GET /api/services/usage`. */
161
+ export interface UsageReport {
162
+ version: 1;
163
+ totals: {
164
+ calls: number;
165
+ refunded: number;
166
+ netCalls: number;
167
+ creditsSpent: number;
168
+ };
169
+ services: ServiceUsage[];
170
+ }
171
+ /** One row of invocation history. */
172
+ export interface InvocationRecord {
173
+ invocationId: string;
174
+ creditsSpent: number;
175
+ createdAt: string;
176
+ refunded: boolean;
177
+ }
178
+ /** Shape of `GET /api/services/usage/:group/:method`. */
179
+ export interface SingleServiceUsage {
180
+ version: 1;
181
+ serviceId: string;
182
+ summary: ServiceUsage;
183
+ history?: InvocationRecord[];
184
+ }
185
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B;;;;;;;0DAOsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC;;;uEAGmE;IACnE,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD;qEACiE;IACjE,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;CAC5B;AAKD;;;;;;2CAM2C;AAC3C,qBAAa,QAAS,SAAQ,eAAe;IAC3C;;0CAEsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IAErC;;;;;;UAMM;IACN,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;gBAEhB,MAAM,GAAE,cAAmB;IAwBvC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAM/C;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAKnC;;;OAGG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,OAAO,CAAC,kBAAkB,CAAC;CAW/B;AAED;sEACsE;AACtE,cAAM,iBAAiB;IACT,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAAD,CAAC,EAAE,UAAU;IAC1C;;uDAEmD;IACnD,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC;CAI3C;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IACnC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvE;AAED,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,0CAA0C;AAC1C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACpF,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B"}
package/dist/index.js ADDED
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @clawvard/sdk — unified service client for Clawvard's composed /
3
+ * long-running / external-API-integrated services.
4
+ *
5
+ * ⚠️ NOT FOR LLM PASSTHROUGHS. Chat, embeddings, Whisper, TTS, DALL·E
6
+ * etc. are served by Token Relay at `https://token.clawvard.school`
7
+ * with `sk-xxx` keys. Use the OpenAI SDK (or any OpenAI-compatible
8
+ * client) pointed at that base URL for those.
9
+ *
10
+ * This SDK covers:
11
+ * - Composed workflows (multi-step recipes)
12
+ * - Long-running jobs (video render, batch processing)
13
+ * - External-API integrations (Shotstack, Mux, Replicate, ...)
14
+ *
15
+ * @example
16
+ * import { Clawvard } from "@clawvard/sdk";
17
+ *
18
+ * const cv = new Clawvard({ apiKey: "sk-xxx" });
19
+ *
20
+ * // Smoke test (proxy)
21
+ * const { ip } = await cv.util.ipCheck();
22
+ *
23
+ * // Job — auto-polls to completion with progress callback
24
+ * const result = await cv.util.delayEcho({ seconds: 5 })
25
+ * .onProgress((pct, note) => console.log(`${(pct * 100).toFixed(0)}% — ${note}`))
26
+ * .wait();
27
+ *
28
+ * // Arbitrary registered service by id (untyped escape hatch)
29
+ * const post = await cv.workflow.run("workflow.podcast2blog", { audioUrl: "..." }).wait();
30
+ */
31
+ import { HttpClient } from "./http-client";
32
+ import { GeneratedClient } from "./generated";
33
+ export { Job } from "./job";
34
+ export { HttpClient } from "./http-client";
35
+ export { ClawvardError, MissingApiKeyError } from "./errors";
36
+ const DEFAULT_BASE_URL = "https://clawvard.school";
37
+ const DEFAULT_POLL_MS = 2000;
38
+ /** The main SDK client. Composes the auto-generated namespace methods
39
+ * (one per registered service `group`) with hand-written platform
40
+ * helpers (`catalog`, `usage`, `usageFor`, `workflow.run`).
41
+ *
42
+ * When backend adds a new service to `src/lib/services/registry.ts`
43
+ * and runs `pnpm sdk:gen`, a new typed method appears on the
44
+ * appropriate namespace automatically. */
45
+ export class Clawvard extends GeneratedClient {
46
+ /** Untyped invocation by id — escape hatch for newly-registered
47
+ * services not yet in `ServiceTypeMap`, or for callers that don't
48
+ * want to bump their SDK version. */
49
+ workflow;
50
+ /** Lower-level HTTP client. Use this when you need per-call options
51
+ * the generated namespace methods don't expose, e.g. an
52
+ * `Idempotency-Key` header:
53
+ *
54
+ * cv.client.invoke("video", "render", input, { idempotencyKey: "..." })
55
+ * cv.client.invokeJob("video", "removeSilence", input, { idempotencyKey: "..." })
56
+ * */
57
+ client;
58
+ constructor(config = {}) {
59
+ const http = new HttpClient({
60
+ ...(config.apiKey ? { apiKey: config.apiKey } : {}),
61
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL,
62
+ pollIntervalMs: config.pollIntervalMs ?? DEFAULT_POLL_MS,
63
+ fetchImpl: config.fetch || globalThis.fetch.bind(globalThis),
64
+ retry: config.retry
65
+ ? { maxRetries: config.retry.maxRetries, baseDelayMs: config.retry.baseDelayMs ?? 250 }
66
+ : { maxRetries: 3, baseDelayMs: 250 },
67
+ });
68
+ super(http);
69
+ this.client = http;
70
+ this.workflow = new WorkflowNamespace(http);
71
+ // Run plugins last so they can inspect / wrap previously-installed
72
+ // namespaces if needed. Each plugin attaches its own namespaces;
73
+ // declaration merging in the plugin's own package types them.
74
+ if (config.plugins) {
75
+ for (const plugin of config.plugins) {
76
+ plugin.install(this, http);
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Fetch the service catalog. Each entry carries pricing + runtime +
82
+ * access flags — enough to render a marketplace tile without any
83
+ * other request. Public endpoint, no auth required.
84
+ */
85
+ async catalog() {
86
+ const res = await this.client.raw("/api/services/catalog", { method: "GET" });
87
+ const json = (await res.json());
88
+ return json.services;
89
+ }
90
+ /**
91
+ * Caller's usage across every registered service. Auth required.
92
+ * Services never called show zero stats so the UI can render a
93
+ * complete table.
94
+ */
95
+ async usage() {
96
+ const res = await this.client.raw("/api/services/usage", { method: "GET" });
97
+ return (await res.json());
98
+ }
99
+ /**
100
+ * Caller's usage of a single service. Pass `includeHistory: true`
101
+ * to also get the N most recent invocation rows.
102
+ */
103
+ async usageFor(group, method, options) {
104
+ const qs = new URLSearchParams();
105
+ if (options?.includeHistory)
106
+ qs.set("history", "1");
107
+ if (options?.limit)
108
+ qs.set("limit", String(options.limit));
109
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
110
+ const res = await this.client.raw(`/api/services/usage/${group}/${method}${suffix}`, { method: "GET" });
111
+ return (await res.json());
112
+ }
113
+ }
114
+ /** Untyped invocation by service id — for services not yet in
115
+ * `ServiceTypeMap` or for callers who prefer string-keyed access. */
116
+ class WorkflowNamespace {
117
+ c;
118
+ constructor(c) {
119
+ this.c = c;
120
+ }
121
+ /** Composable workflow / generic invoke. Returns a `Job<T>` so the
122
+ * caller can await `.wait()` for both proxy and job services
123
+ * (proxy services complete on the first poll). */
124
+ run(id, input) {
125
+ const [group, method] = id.includes(".") ? id.split(".", 2) : ["workflow", id];
126
+ return this.c.invokeJob(group, method, input);
127
+ }
128
+ }
129
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/job.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Job — handle for a long-running service invocation. Auto-starts on
3
+ * construction, exposes `.onProgress(cb)` for incremental updates,
4
+ * `.wait()` to poll to completion, and `.cancel()` to abort.
5
+ */
6
+ import { HttpClient, type InvokeOptions } from "./http-client";
7
+ export declare class Job<T> {
8
+ private readonly client;
9
+ private readonly group;
10
+ private readonly method;
11
+ private readonly input;
12
+ private readonly options?;
13
+ private jobId;
14
+ private progressCallback;
15
+ private readonly started;
16
+ constructor(client: HttpClient, group: string, method: string, input: unknown, options?: InvokeOptions | undefined);
17
+ private start;
18
+ /** Register a callback for each progress tick. Chain before `.wait()`. */
19
+ onProgress(cb: (pct: number, note?: string) => void): this;
20
+ /** Poll until the job reaches a terminal state. Throws on failure. */
21
+ wait(): Promise<T>;
22
+ /** Fire-and-forget: get the job ID without waiting. */
23
+ id(): Promise<string>;
24
+ /** Cancel the job. If still pending/running, the platform refunds
25
+ * the credits that were charged on creation. Returns the new
26
+ * status; resolves to `"cancelled"` if the cancel landed before a
27
+ * terminal state, or the existing terminal state if the worker
28
+ * already finished. */
29
+ cancel(): Promise<{
30
+ status: "cancelled" | "completed" | "failed";
31
+ refunded: boolean;
32
+ }>;
33
+ }
34
+ //# sourceMappingURL=job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAc,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3E,qBAAa,GAAG,CAAC,CAAC;IAMd,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAT3B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,gBAAgB,CAAuD;IAC/E,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;gBAGrB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,aAAa,YAAA;YAK5B,KAAK;IAcnB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAK1D,sEAAsE;IAChE,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC;IA0BxB,uDAAuD;IACjD,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC;IAM3B;;;;4BAIwB;IAClB,MAAM,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CAO7F"}
package/dist/job.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Job — handle for a long-running service invocation. Auto-starts on
3
+ * construction, exposes `.onProgress(cb)` for incremental updates,
4
+ * `.wait()` to poll to completion, and `.cancel()` to abort.
5
+ */
6
+ import { toApiError } from "./http-client";
7
+ export class Job {
8
+ client;
9
+ group;
10
+ method;
11
+ input;
12
+ options;
13
+ jobId = null;
14
+ progressCallback = null;
15
+ started;
16
+ constructor(client, group, method, input, options) {
17
+ this.client = client;
18
+ this.group = group;
19
+ this.method = method;
20
+ this.input = input;
21
+ this.options = options;
22
+ this.started = this.start();
23
+ }
24
+ async start() {
25
+ const headers = { "Content-Type": "application/json" };
26
+ if (this.options?.idempotencyKey)
27
+ headers["Idempotency-Key"] = this.options.idempotencyKey;
28
+ if (this.options?.webhookUrl)
29
+ headers["X-Webhook-Url"] = this.options.webhookUrl;
30
+ const res = await this.client.raw(`/api/services/invoke/${this.group}/${this.method}`, {
31
+ method: "POST",
32
+ headers,
33
+ body: JSON.stringify(this.input ?? {}),
34
+ });
35
+ if (!res.ok)
36
+ throw await toApiError(res);
37
+ const body = (await res.json());
38
+ this.jobId = body.jobId;
39
+ }
40
+ /** Register a callback for each progress tick. Chain before `.wait()`. */
41
+ onProgress(cb) {
42
+ this.progressCallback = cb;
43
+ return this;
44
+ }
45
+ /** Poll until the job reaches a terminal state. Throws on failure. */
46
+ async wait() {
47
+ await this.started;
48
+ if (!this.jobId)
49
+ throw new Error("Job: never started");
50
+ let lastPct = -1;
51
+ for (;;) {
52
+ const res = await this.client.raw(`/api/services/jobs/${this.jobId}`, { method: "GET" });
53
+ if (!res.ok)
54
+ throw await toApiError(res);
55
+ const status = (await res.json());
56
+ if (status.progress && status.progress.pct !== lastPct) {
57
+ lastPct = status.progress.pct;
58
+ this.progressCallback?.(status.progress.pct, status.progress.note);
59
+ }
60
+ if (status.status === "completed")
61
+ return status.result;
62
+ if (status.status === "failed") {
63
+ const msg = status.error?.message ?? "job failed";
64
+ throw new Error(msg);
65
+ }
66
+ if (status.status === "cancelled")
67
+ throw new Error("job cancelled");
68
+ await sleep(this.client.pollIntervalMs);
69
+ }
70
+ }
71
+ /** Fire-and-forget: get the job ID without waiting. */
72
+ async id() {
73
+ await this.started;
74
+ if (!this.jobId)
75
+ throw new Error("Job: never started");
76
+ return this.jobId;
77
+ }
78
+ /** Cancel the job. If still pending/running, the platform refunds
79
+ * the credits that were charged on creation. Returns the new
80
+ * status; resolves to `"cancelled"` if the cancel landed before a
81
+ * terminal state, or the existing terminal state if the worker
82
+ * already finished. */
83
+ async cancel() {
84
+ await this.started;
85
+ if (!this.jobId)
86
+ throw new Error("Job: never started");
87
+ const res = await this.client.raw(`/api/services/jobs/${this.jobId}`, { method: "DELETE" });
88
+ if (!res.ok)
89
+ throw await toApiError(res);
90
+ return (await res.json());
91
+ }
92
+ }
93
+ function sleep(ms) {
94
+ return new Promise((r) => setTimeout(r, ms));
95
+ }
96
+ //# sourceMappingURL=job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.js","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Plugin protocol — lets `@clawvard/sdk-<school>` packages register
3
+ * additional namespaces (and types via declaration merging) onto a
4
+ * `Clawvard` instance.
5
+ *
6
+ * @example
7
+ * import { Clawvard } from "@clawvard/sdk";
8
+ * import { mediaPlugin } from "@clawvard/sdk-media";
9
+ *
10
+ * const cv = new Clawvard({ apiKey, plugins: [mediaPlugin] });
11
+ * cv.media.translateSubtitles({...}); // typed via declaration merging
12
+ * cv.video.extractFrames({...}); // local helper, no apiKey needed
13
+ */
14
+ import type { HttpClient } from "./http-client";
15
+ export interface ClawvardPlugin {
16
+ /** Stable name — for diagnostics / dedup. */
17
+ name: string;
18
+ /** Called once at construction. Mutate `cv` to add namespaces. */
19
+ install(cv: ClawvardInstance, http: HttpClient): void;
20
+ }
21
+ /**
22
+ * The shape plugins see — `cv` is mutated to receive new namespace
23
+ * properties. Sub-packages declaration-merge their namespaces into
24
+ * `Clawvard` itself in their `index.ts` so end-user code sees a typed
25
+ * `cv.media.*` / `cv.video.*`.
26
+ */
27
+ export interface ClawvardInstance {
28
+ [namespace: string]: unknown;
29
+ }
30
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,OAAO,CAAC,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACvD;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAG/B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B"}
package/dist/plugin.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Plugin protocol — lets `@clawvard/sdk-<school>` packages register
3
+ * additional namespaces (and types via declaration merging) onto a
4
+ * `Clawvard` instance.
5
+ *
6
+ * @example
7
+ * import { Clawvard } from "@clawvard/sdk";
8
+ * import { mediaPlugin } from "@clawvard/sdk-media";
9
+ *
10
+ * const cv = new Clawvard({ apiKey, plugins: [mediaPlugin] });
11
+ * cv.media.translateSubtitles({...}); // typed via declaration merging
12
+ * cv.video.extractFrames({...}); // local helper, no apiKey needed
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * ServiceTypeMap — typed input/output per registered service id.
3
+ *
4
+ * Core SDK declares its own services here (util.*, plus any future
5
+ * shared text/audio/image entries). School sub-packages
6
+ * (`@clawvard/sdk-media`, etc.) extend this interface via
7
+ * declaration merging:
8
+ *
9
+ * declare module "@clawvard/sdk" {
10
+ * interface ServiceTypeMap {
11
+ * "media.translate-subtitles": { input: ...; output: ... };
12
+ * }
13
+ * }
14
+ *
15
+ * Services not in the map still work via the generated client, but
16
+ * their `input`/`output` fall back to `unknown`.
17
+ *
18
+ * Typical workflow when adding a service to **core**:
19
+ * 1. Add the entry to `SERVICES[]` in `src/lib/services/registry.ts`
20
+ * 2. Define the `Input`/`Output` interfaces below
21
+ * 3. Add `"<id>": { input: ...; output: ... }` to `ServiceTypeMap`
22
+ * 4. Run `pnpm sdk:gen`
23
+ *
24
+ * For a **school sub-package**: see `docs/services-onboarding.md`
25
+ * → "Adding a school sub-package".
26
+ */
27
+ export interface UtilDelayEchoInput {
28
+ /** Seconds to sleep before echoing. Default 3, max 30. */
29
+ seconds?: number;
30
+ /** Arbitrary payload echoed back in the result. */
31
+ payload?: unknown;
32
+ }
33
+ export interface UtilDelayEchoOutput {
34
+ echoed: unknown;
35
+ sleptSeconds: number;
36
+ jobId: string;
37
+ }
38
+ export interface UtilIpCheckOutput {
39
+ ip: string;
40
+ }
41
+ export interface TextWordCountInput {
42
+ text: string;
43
+ }
44
+ export interface TextWordCountOutput {
45
+ chars: number;
46
+ charsNoWhitespace: number;
47
+ words: number;
48
+ lines: number;
49
+ sentences: number;
50
+ longestLineChars: number;
51
+ }
52
+ export interface TextHashInput {
53
+ text: string;
54
+ algorithm?: "sha256" | "sha1" | "md5";
55
+ }
56
+ export interface TextHashOutput {
57
+ algorithm: string;
58
+ hex: string;
59
+ }
60
+ export interface UrlQrCodeInput {
61
+ /** Text or URL to encode. Max 2000 chars. */
62
+ text: string;
63
+ /** Pixel size of one side (64-1024). Default 256. */
64
+ size?: number;
65
+ }
66
+ export interface UrlQrCodeOutput {
67
+ /** PNG data URI ready to drop into an <img src="…">. */
68
+ dataUri: string;
69
+ byteLength: number;
70
+ }
71
+ export interface UrlPreviewInput {
72
+ url: string;
73
+ }
74
+ export interface UrlPreviewOutput {
75
+ url: string;
76
+ title?: string;
77
+ description?: string;
78
+ image?: string;
79
+ siteName?: string;
80
+ type?: string;
81
+ }
82
+ export interface VideoRemoveSilenceInput {
83
+ inputUrl: string;
84
+ silenceThresholdDb?: number;
85
+ minSilenceMs?: number;
86
+ }
87
+ export interface VideoRemoveSilenceOutput {
88
+ outputUrl: string;
89
+ cutSeconds: number;
90
+ segmentsRemoved: number;
91
+ }
92
+ export interface ServiceTypeMap {
93
+ "util.ip-check": {
94
+ input: void;
95
+ output: UtilIpCheckOutput;
96
+ };
97
+ "util.delay-echo": {
98
+ input: UtilDelayEchoInput;
99
+ output: UtilDelayEchoOutput;
100
+ };
101
+ "text.word-count": {
102
+ input: TextWordCountInput;
103
+ output: TextWordCountOutput;
104
+ };
105
+ "text.hash": {
106
+ input: TextHashInput;
107
+ output: TextHashOutput;
108
+ };
109
+ "url.qr-code": {
110
+ input: UrlQrCodeInput;
111
+ output: UrlQrCodeOutput;
112
+ };
113
+ "url.preview": {
114
+ input: UrlPreviewInput;
115
+ output: UrlPreviewOutput;
116
+ };
117
+ "video.remove-silence": {
118
+ input: VideoRemoveSilenceInput;
119
+ output: VideoRemoveSilenceOutput;
120
+ };
121
+ }
122
+ /** Helper: resolve the input type for a given service id, or `unknown`. */
123
+ export type InputOf<Id extends string> = Id extends keyof ServiceTypeMap ? ServiceTypeMap[Id]["input"] : unknown;
124
+ /** Helper: resolve the output type for a given service id, or `unknown`. */
125
+ export type OutputOf<Id extends string> = Id extends keyof ServiceTypeMap ? ServiceTypeMap[Id]["output"] : unknown;
126
+ /** Helper: turn `InputOf<Id>` into a rest-parameter tuple. Services
127
+ * declared with `input: void` become callable with NO arguments
128
+ * (`cv.util.ipCheck()`); everything else takes one positional arg.
129
+ * The tuple always has length 1 so the generated code can read
130
+ * `args[0]` unconditionally — for void inputs that's `undefined`. */
131
+ export type InputArgs<Id extends string> = InputOf<Id> extends void ? [input?: undefined] : [input: InputOf<Id>];
132
+ //# sourceMappingURL=service-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-types.d.ts","sourceRoot":"","sources":["../src/service-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ServiceTypeMap — typed input/output per registered service id.
3
+ *
4
+ * Core SDK declares its own services here (util.*, plus any future
5
+ * shared text/audio/image entries). School sub-packages
6
+ * (`@clawvard/sdk-media`, etc.) extend this interface via
7
+ * declaration merging:
8
+ *
9
+ * declare module "@clawvard/sdk" {
10
+ * interface ServiceTypeMap {
11
+ * "media.translate-subtitles": { input: ...; output: ... };
12
+ * }
13
+ * }
14
+ *
15
+ * Services not in the map still work via the generated client, but
16
+ * their `input`/`output` fall back to `unknown`.
17
+ *
18
+ * Typical workflow when adding a service to **core**:
19
+ * 1. Add the entry to `SERVICES[]` in `src/lib/services/registry.ts`
20
+ * 2. Define the `Input`/`Output` interfaces below
21
+ * 3. Add `"<id>": { input: ...; output: ... }` to `ServiceTypeMap`
22
+ * 4. Run `pnpm sdk:gen`
23
+ *
24
+ * For a **school sub-package**: see `docs/services-onboarding.md`
25
+ * → "Adding a school sub-package".
26
+ */
27
+ export {};
28
+ //# sourceMappingURL=service-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-types.js","sourceRoot":"","sources":["../src/service-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@clawvard/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Clawvard unified service SDK — call every Clawvard service with one typed client.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "prepublishOnly": "pnpm build"
22
+ },
23
+ "keywords": [
24
+ "clawvard",
25
+ "ai",
26
+ "llm",
27
+ "agent",
28
+ "video",
29
+ "sdk"
30
+ ],
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=18"
34
+ }
35
+ }