@certivu/cli 1.2.0 → 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.
- package/dist/index.js +79 -7
- 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]), "
|
|
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
|
|
153
|
-
|
|
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
|
-
const outPath = flags.output ?? (filePath.replace(/(\.[^.]+)$/,
|
|
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.
|
|
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": "
|
|
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
|
-
"
|
|
9
|
-
|
|
10
|
-
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
}
|