cascrow-cli 1.1.0 → 1.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 (2) hide show
  1. package/index.js +94 -6
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -5,16 +5,25 @@
5
5
  * Usage: cascrow <command> [options]
6
6
  */
7
7
 
8
+ import { readFileSync } from "node:fs";
9
+
8
10
  const BASE_URL = process.env.CASCROW_BASE_URL ?? "https://cascrow.com";
9
11
  const API_KEY = process.env.CASCROW_API_KEY ?? "";
10
12
 
11
13
  // ─── HTTP helpers ─────────────────────────────────────────────────────────────
14
+ // Authorization header is omitted entirely when key is empty, rather than
15
+ // sending a garbage "Bearer " token — matters for `verify-work`, the one
16
+ // command that works without CASCROW_API_KEY.
17
+
18
+ function authHeader(key) {
19
+ return key ? { "Authorization": `Bearer ${key}` } : {};
20
+ }
12
21
 
13
22
  async function apiPost(path, body, key = API_KEY) {
14
23
  const res = await fetch(`${BASE_URL}${path}`, {
15
24
  method: "POST",
16
25
  headers: {
17
- "Authorization": `Bearer ${key}`,
26
+ ...authHeader(key),
18
27
  "Content-Type": "application/json",
19
28
  },
20
29
  body: JSON.stringify(body),
@@ -26,7 +35,7 @@ async function apiPost(path, body, key = API_KEY) {
26
35
 
27
36
  async function apiGet(path, key = API_KEY) {
28
37
  const res = await fetch(`${BASE_URL}${path}`, {
29
- headers: { "Authorization": `Bearer ${key}` },
38
+ headers: { ...authHeader(key) },
30
39
  });
31
40
  const data = await res.json();
32
41
  if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
@@ -40,7 +49,7 @@ async function uploadProof(milestoneId, filename, content, key = API_KEY) {
40
49
  form.append("milestoneId", milestoneId);
41
50
  const res = await fetch(`${BASE_URL}/api/proof/upload`, {
42
51
  method: "POST",
43
- headers: { "Authorization": `Bearer ${key}` },
52
+ headers: { ...authHeader(key) },
44
53
  body: form,
45
54
  });
46
55
  const data = await res.json();
@@ -58,7 +67,7 @@ async function streamVerify(proofId, key = API_KEY) {
58
67
  res = await fetch(`${BASE_URL}/api/verify`, {
59
68
  method: "POST",
60
69
  headers: {
61
- "Authorization": `Bearer ${key}`,
70
+ ...authHeader(key),
62
71
  "Content-Type": "application/json",
63
72
  "Accept": "text/event-stream",
64
73
  },
@@ -134,6 +143,7 @@ Usage:
134
143
 
135
144
  Commands:
136
145
  register Create an agent account, get an API key instantly
146
+ verify-work Verify AI-generated work — no contract, no escrow, no account needed
137
147
  verify Submit proof and get AI verdict in one call (recommended)
138
148
  create Create a milestone contract
139
149
  fund Activate a milestone (simulation — no on-chain tx)
@@ -153,6 +163,12 @@ Options for each command:
153
163
  --name <name> optional display name
154
164
  --key-name <name> optional label for API key (default: cli)
155
165
 
166
+ verify-work [NO ACCOUNT NEEDED — 3 free/day per IP]
167
+ --task <description> required what was the AI supposed to do
168
+ --pr <url> GitHub PR URL (github.com only) — or use --code / --image
169
+ --code <text> pasted code, diff, or output (max 50,000 chars)
170
+ --image <path> path to a screenshot/image (.jpg/.png/.webp/.gif, max 8 MB)
171
+
156
172
  verify [ONE-SHOT — recommended for code/text proof]
157
173
  --contract <id> required contract ID
158
174
  --proof <text> required full description of what was completed
@@ -197,10 +213,14 @@ Options for each command:
197
213
  me no options
198
214
 
199
215
  Environment:
200
- CASCROW_API_KEY Your API key — csk_... (required for most commands)
216
+ CASCROW_API_KEY Your API key — csk_... (required for every command except verify-work)
201
217
  CASCROW_BASE_URL Override API base (default: https://cascrow.com)
202
218
 
203
219
  Examples:
220
+ # Verify AI-generated work — no account, no escrow, 3 free/day per IP
221
+ cascrow verify-work --task "Fix the 3 security vulnerabilities in the audit report" --pr https://github.com/owner/repo/pull/123
222
+ cascrow verify-work --task "Build a landing page with a working contact form" --image ./screenshot.png
223
+
204
224
  # Register a new agent account (no human in the loop)
205
225
  cascrow register --email bot@example.com --password secret123
206
226
 
@@ -267,6 +287,73 @@ async function cmdRegister(args) {
267
287
  console.error("\nSave your API key and set it:\n export CASCROW_API_KEY=" + data.apiKey + "\n");
268
288
  }
269
289
 
290
+ // Verify AI-generated work with no contract, no escrow, no account required.
291
+ // Works without CASCROW_API_KEY (3 free/day per IP, same tier as cascrow.com/verify
292
+ // and the cascrow_verify_work MCP tool) — the one command in this CLI that does.
293
+ async function cmdVerifyWork(args) {
294
+ if (!args.task) err("--task is required");
295
+ if (!args.pr && !args.code && !args.image) err("--pr, --code, or --image is required");
296
+
297
+ // Validate the image up front so errors surface before the "Verifying..." message.
298
+ let imageBuffer, imageMimeType;
299
+ if (args.image) {
300
+ const ext = (args.image.split(".").pop() ?? "").toLowerCase();
301
+ const mimeByExt = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", gif: "image/gif" };
302
+ imageMimeType = mimeByExt[ext];
303
+ if (!imageMimeType) err("--image must be a .jpg, .jpeg, .png, .webp, or .gif file");
304
+ try {
305
+ imageBuffer = readFileSync(args.image);
306
+ } catch (e) {
307
+ err(`Could not read image file "${args.image}": ${e.message}`);
308
+ }
309
+ }
310
+
311
+ process.stderr.write("\nVerifying with 5 independent AI models (no escrow, no account needed)...\n\n");
312
+
313
+ let data;
314
+ if (args.image) {
315
+ const form = new FormData();
316
+ form.append("taskDescription", args.task);
317
+ form.append("image", new Blob([imageBuffer], { type: imageMimeType }), args.image.split("/").pop());
318
+
319
+ const res = await fetch(`${BASE_URL}/api/verify/standalone`, {
320
+ method: "POST",
321
+ headers: { ...authHeader(API_KEY) },
322
+ body: form,
323
+ });
324
+ data = await res.json();
325
+ if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
326
+ } else {
327
+ const body = { taskDescription: args.task };
328
+ if (args.pr) body.prUrl = args.pr;
329
+ if (args.code) body.codeText = args.code;
330
+ data = await apiPost("/api/verify/standalone", body);
331
+ }
332
+
333
+ const votes = Array.isArray(data.modelVotes) ? data.modelVotes : [];
334
+ for (const v of votes) {
335
+ const icon = v.decision === "YES" ? "✅" : "❌";
336
+ process.stderr.write(` ${icon} ${v.model}: ${v.decision} (${v.confidence}%)\n`);
337
+ }
338
+ process.stderr.write("\n");
339
+
340
+ const statusLine = data.decision === "YES"
341
+ ? `✅ VERIFIED (${data.confidence}% confidence)`
342
+ : `❌ NOT VERIFIED (${data.confidence}% confidence)`;
343
+ process.stderr.write(statusLine + "\n");
344
+ if (data.reportUrl) process.stderr.write(`Full report: ${data.reportUrl}\n\n`);
345
+
346
+ print({
347
+ decision: data.decision,
348
+ confidence: data.confidence,
349
+ reasoning: data.reasoning,
350
+ reportUrl: data.reportUrl,
351
+ publicHash: data.publicHash,
352
+ promptHash: data.promptHash,
353
+ modelVotes: votes,
354
+ });
355
+ }
356
+
270
357
  // One-shot verify: submit text proof + get verdict in a single call via /api/mcp/submit.
271
358
  // Auto-releases funds and mints NFT on approval — no separate `verify --proof <id>` needed.
272
359
  async function cmdVerify(args) {
@@ -412,7 +499,7 @@ async function cmdSubmit(args) {
412
499
  async function cmdGet(args) {
413
500
  if (!API_KEY) err("CASCROW_API_KEY is not set");
414
501
  if (!args.contract) err("--contract is required");
415
- const data = await apiGet(`/api/contracts/${args.contract}`);
502
+ const data = await apiGet(`/api/contracts/${encodeURIComponent(args.contract)}`);
416
503
  print(data);
417
504
  }
418
505
 
@@ -471,6 +558,7 @@ if (!command || command === "--help" || command === "help" || command === "-h")
471
558
  try {
472
559
  switch (command) {
473
560
  case "register": await cmdRegister(args); break;
561
+ case "verify-work": await cmdVerifyWork(args); break;
474
562
  case "verify": await cmdVerify(args); break;
475
563
  case "create": await cmdCreate(args); break;
476
564
  case "fund": await cmdFund(args); break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cascrow-cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CLI for Cascrow — agentic escrow and verification",
5
5
  "type": "module",
6
6
  "main": "index.js",