@certivu/cli 1.2.1 → 2.0.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 (2) hide show
  1. package/dist/index.js +78 -6
  2. package/package.json +20 -23
package/dist/index.js CHANGED
@@ -135,9 +135,11 @@ function toUint8Array(content) {
135
135
  async function signContent(baseUrl, apiKey, input) {
136
136
  const bytes = toUint8Array(input.content);
137
137
  const form = new FormData();
138
- form.append("image", new Blob([bytes]), "image");
138
+ form.append("image", new Blob([bytes]), "content");
139
139
  form.append("model", input.model);
140
140
  form.append("generator_id", input.generatorId);
141
+ if (input.format)
142
+ form.append("format", input.format);
141
143
  const res = await fetch(`${baseUrl}/v1/sign`, {
142
144
  method: "POST",
143
145
  headers: { Authorization: `Bearer ${apiKey}` },
@@ -149,8 +151,15 @@ async function signContent(baseUrl, apiKey, input) {
149
151
  }
150
152
  const token = res.headers.get("X-Certivu-Token") ?? "";
151
153
  const record_id = res.headers.get("X-Certivu-Record-Id") ?? "";
152
- const watermarkedContent = new Uint8Array(await res.arrayBuffer());
153
- return { token, record_id, watermarkedContent };
154
+ const formatHeader = res.headers.get("X-Certivu-Format");
155
+ const signedContent = new Uint8Array(await res.arrayBuffer());
156
+ return {
157
+ token,
158
+ record_id,
159
+ signedContent,
160
+ watermarkedContent: signedContent,
161
+ ...formatHeader ? { format: formatHeader } : {}
162
+ };
154
163
  }
155
164
  function toUint8Array2(content) {
156
165
  if (typeof content === "string")
@@ -234,11 +243,71 @@ var CertivuClient = class {
234
243
  }
235
244
  return res.json();
236
245
  }
246
+ async getAnalyticsOverview(days) {
247
+ const url = new URL(`${this.baseUrl}/v1/analytics/overview`);
248
+ if (days !== void 0)
249
+ url.searchParams.set("days", String(days));
250
+ const res = await fetch(url.toString(), { headers: { Authorization: `Bearer ${this.apiKey}` } });
251
+ if (!res.ok) {
252
+ const err2 = await res.json().catch(() => ({}));
253
+ throw new Error(err2.error ?? `HTTP ${res.status}`);
254
+ }
255
+ return res.json();
256
+ }
257
+ async getRecordAnalytics(recordId) {
258
+ const res = await fetch(`${this.baseUrl}/v1/analytics/records/${encodeURIComponent(recordId)}`, {
259
+ headers: { Authorization: `Bearer ${this.apiKey}` }
260
+ });
261
+ if (!res.ok) {
262
+ const err2 = await res.json().catch(() => ({}));
263
+ throw new Error(err2.error ?? `HTTP ${res.status}`);
264
+ }
265
+ return res.json();
266
+ }
267
+ async listWebhooks() {
268
+ const res = await fetch(`${this.baseUrl}/v1/webhooks`, {
269
+ headers: { Authorization: `Bearer ${this.apiKey}` }
270
+ });
271
+ if (!res.ok) {
272
+ const err2 = await res.json().catch(() => ({}));
273
+ throw new Error(err2.error ?? `HTTP ${res.status}`);
274
+ }
275
+ return res.json();
276
+ }
277
+ async createWebhook(input) {
278
+ const res = await fetch(`${this.baseUrl}/v1/webhooks`, {
279
+ method: "POST",
280
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` },
281
+ body: JSON.stringify(input)
282
+ });
283
+ if (!res.ok) {
284
+ const err2 = await res.json().catch(() => ({}));
285
+ throw new Error(err2.error ?? `HTTP ${res.status}`);
286
+ }
287
+ return res.json();
288
+ }
289
+ async deleteWebhook(webhookId) {
290
+ const res = await fetch(`${this.baseUrl}/v1/webhooks/${encodeURIComponent(webhookId)}`, {
291
+ method: "DELETE",
292
+ headers: { Authorization: `Bearer ${this.apiKey}` }
293
+ });
294
+ if (!res.ok) {
295
+ const err2 = await res.json().catch(() => ({}));
296
+ throw new Error(err2.error ?? `HTTP ${res.status}`);
297
+ }
298
+ }
237
299
  };
238
300
 
239
301
  // src/commands/sign.ts
302
+ function inferFormat(filePath) {
303
+ const ext = (0, import_node_path2.extname)(filePath).toLowerCase();
304
+ if ([".jpg", ".jpeg", ".png", ".webp"].includes(ext)) return "image";
305
+ if ([".mp3", ".flac", ".wav", ".ogg", ".aac", ".m4a", ".aiff"].includes(ext)) return "audio";
306
+ if ([".pdf", ".docx", ".html", ".htm", ".txt", ".md"].includes(ext)) return "text";
307
+ return void 0;
308
+ }
240
309
  async function signCommand(filePath, flags) {
241
- if (!filePath) die("Usage: certivu sign <file> --model <model>");
310
+ if (!filePath) die("Usage: certivu sign <file> --model <model> [--format image|audio|text]");
242
311
  const config = await loadConfig();
243
312
  const apiKey = flags.apiKey ?? config.apiKey;
244
313
  const generatorId = flags.generatorId ?? config.generatorId;
@@ -250,6 +319,7 @@ async function signCommand(filePath, flags) {
250
319
  } catch {
251
320
  die(`Cannot read file: ${filePath}`);
252
321
  }
322
+ const format = flags.format ?? inferFormat(filePath);
253
323
  const clientConfig = {
254
324
  apiKey,
255
325
  generatorId,
@@ -258,20 +328,21 @@ async function signCommand(filePath, flags) {
258
328
  const client = new CertivuClient(clientConfig);
259
329
  let result;
260
330
  try {
261
- result = await client.sign({ content, model: flags.model, generatorId });
331
+ result = await client.sign({ content, model: flags.model, generatorId, format });
262
332
  } catch (e) {
263
333
  const msg = e instanceof Error ? e.message : String(e);
264
334
  die(`Sign failed: ${msg}`);
265
335
  }
266
336
  const outPath = flags.output ?? (filePath.replace(/(\.[^.]+)$/, ".signed$1") || `${filePath}.signed${(0, import_node_path2.extname)(filePath)}`);
267
337
  try {
268
- (0, import_node_fs2.writeFileSync)(outPath, result.watermarkedContent);
338
+ (0, import_node_fs2.writeFileSync)(outPath, result.signedContent);
269
339
  } catch {
270
340
  die(`Could not write output file: ${outPath}`);
271
341
  }
272
342
  console.log(ok("Signed"));
273
343
  row("Token", result.token);
274
344
  row("Record ID", result.record_id);
345
+ row("Format", result.format ?? format ?? "auto-detected");
275
346
  row("Output", outPath);
276
347
  if (result.deduplicated) {
277
348
  console.log(" (content already signed \u2014 existing token returned, no quota consumed)");
@@ -374,6 +445,7 @@ ${bold("Commands:")}
374
445
 
375
446
  ${bold("Sign flags:")}
376
447
  --model <name> AI model name, e.g. stable-diffusion-xl ${dim("(required)")}
448
+ --format <fmt> Content format: image | audio | text ${dim("(auto-detected if omitted)")}
377
449
  --generator-id <id> Override generator ID
378
450
  --private-key <key> Override ML-DSA private key (base64)
379
451
  --api-key <key> Override API key
package/package.json CHANGED
@@ -1,29 +1,26 @@
1
1
  {
2
2
  "name": "@certivu/cli",
3
- "version": "1.2.1",
3
+ "version": "2.0.0",
4
4
  "description": "Certivu CLI — sign and verify AI-generated content",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://certivu.ai",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/art-emini/certivu"
10
- },
11
- "bin": {
12
- "certivu": "./bin/certivu"
13
- },
14
- "files": ["bin", "dist", "README.md"],
15
- "scripts": {
16
- "dev": "bun run src/index.ts",
17
- "build": "tsup",
18
- "typecheck": "tsc --noEmit",
19
- "lint": "biome check src"
20
- },
21
- "devDependencies": {
22
- "@biomejs/biome": "^1.9.4",
23
- "@certivu/crypto": "0.0.1",
24
- "@certivu/sdk": "1.0.0",
25
- "@types/node": "^20.0.0",
26
- "tsup": "^8.0.0",
27
- "typescript": "^5.7.3"
28
- }
7
+ "repository": { "type": "git", "url": "https://github.com/art-emini/certivu" },
8
+ "bin": {
9
+ "certivu": "./bin/certivu"
10
+ },
11
+ "files": ["bin", "dist", "README.md"],
12
+ "scripts": {
13
+ "dev": "bun run src/index.ts",
14
+ "build": "tsup",
15
+ "typecheck": "tsc --noEmit",
16
+ "lint": "biome check src"
17
+ },
18
+ "devDependencies": {
19
+ "@biomejs/biome": "^1.9.4",
20
+ "@certivu/crypto": "workspace:*",
21
+ "@certivu/sdk": "workspace:*",
22
+ "@types/node": "^20.0.0",
23
+ "tsup": "^8.0.0",
24
+ "typescript": "^5.7.3"
25
+ }
29
26
  }