cascrow-cli 1.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/index.js +290 -0
  2. package/package.json +18 -0
package/index.js ADDED
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cascrow CLI
4
+ * Command-line interface for Cascrow — agentic escrow and verification.
5
+ * Usage: cascrow <command> [options]
6
+ */
7
+
8
+ const BASE_URL = process.env.CASCROW_BASE_URL ?? "https://cascrow.com";
9
+ const API_KEY = process.env.CASCROW_API_KEY ?? "";
10
+
11
+ // ─── HTTP helpers ─────────────────────────────────────────────────────────────
12
+
13
+ async function apiPost(path, body, key = API_KEY) {
14
+ const res = await fetch(`${BASE_URL}${path}`, {
15
+ method: "POST",
16
+ headers: {
17
+ "Authorization": `Bearer ${key}`,
18
+ "Content-Type": "application/json",
19
+ },
20
+ body: JSON.stringify(body),
21
+ });
22
+ const data = await res.json();
23
+ if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
24
+ return data;
25
+ }
26
+
27
+ async function apiGet(path, key = API_KEY) {
28
+ const res = await fetch(`${BASE_URL}${path}`, {
29
+ headers: { "Authorization": `Bearer ${key}` },
30
+ });
31
+ const data = await res.json();
32
+ if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
33
+ return data;
34
+ }
35
+
36
+ async function uploadProof(milestoneId, filename, content, key = API_KEY) {
37
+ const blob = new Blob([content], { type: "text/plain" });
38
+ const form = new FormData();
39
+ form.append("file", blob, filename);
40
+ form.append("milestoneId", milestoneId);
41
+ const res = await fetch(`${BASE_URL}/api/proof/upload`, {
42
+ method: "POST",
43
+ headers: { "Authorization": `Bearer ${key}` },
44
+ body: form,
45
+ });
46
+ const data = await res.json();
47
+ if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
48
+ return data;
49
+ }
50
+
51
+ async function streamVerify(proofId, key = API_KEY) {
52
+ const res = await fetch(`${BASE_URL}/api/verify`, {
53
+ method: "POST",
54
+ headers: {
55
+ "Authorization": `Bearer ${key}`,
56
+ "Content-Type": "application/json",
57
+ "Accept": "text/event-stream",
58
+ },
59
+ body: JSON.stringify({ proofId }),
60
+ });
61
+ if (!res.ok) {
62
+ const data = await res.json().catch(() => ({}));
63
+ throw new Error(data.error ?? `HTTP ${res.status}`);
64
+ }
65
+
66
+ const votes = [];
67
+ const reader = res.body.getReader();
68
+ const decoder = new TextDecoder();
69
+ let buffer = "";
70
+
71
+ while (true) {
72
+ const { done, value } = await reader.read();
73
+ if (done) break;
74
+ buffer += decoder.decode(value, { stream: true });
75
+ const lines = buffer.split("\n");
76
+ buffer = lines.pop() ?? "";
77
+ for (const line of lines) {
78
+ if (!line.startsWith("data:")) continue;
79
+ const raw = line.slice(5).trim();
80
+ if (!raw) continue;
81
+ try {
82
+ const msg = JSON.parse(raw);
83
+ if (msg.type === "model_vote") {
84
+ votes.push(msg);
85
+ const icon = msg.decision === "YES" ? "✅" : "❌";
86
+ process.stderr.write(` ${icon} ${msg.model}: ${msg.decision} (${msg.confidence}%)\n`);
87
+ } else if (msg.type === "complete") {
88
+ return { ...msg, votes };
89
+ } else if (msg.type === "error") {
90
+ throw new Error(msg.message);
91
+ }
92
+ } catch (e) {
93
+ if (e instanceof SyntaxError) continue;
94
+ throw e;
95
+ }
96
+ }
97
+ }
98
+ throw new Error("Stream ended without a complete event");
99
+ }
100
+
101
+ // ─── Output helpers ───────────────────────────────────────────────────────────
102
+
103
+ function print(obj) {
104
+ console.log(JSON.stringify(obj, null, 2));
105
+ }
106
+
107
+ function err(msg) {
108
+ console.error(`\nError: ${msg}\n`);
109
+ process.exit(1);
110
+ }
111
+
112
+ function usage() {
113
+ console.log(`
114
+ Cascrow CLI — agentic escrow and verification
115
+
116
+ Usage:
117
+ cascrow register --email <email> --password <pass> [--name <name>]
118
+ cascrow create --title <title> --description <desc> [--days <n>]
119
+ cascrow fund --contract <id> [--milestone <id>]
120
+ cascrow submit --milestone <id> --proof <text> [--file <name>]
121
+ cascrow verify --proof <id>
122
+ cascrow get --contract <id>
123
+ cascrow join --invite <code>
124
+
125
+ Environment:
126
+ CASCROW_API_KEY Your API key (required for most commands)
127
+ CASCROW_BASE_URL Override API base (default: https://cascrow.com)
128
+
129
+ Examples:
130
+ # Register a new agent account (no human in the loop)
131
+ cascrow register --email bot@example.com --password secret123
132
+
133
+ # Full verification flow
134
+ cascrow create --title "Homepage redesign" --description "Hero section with CTA button, responsive" --days 7
135
+ cascrow fund --contract <contractId>
136
+ cascrow submit --milestone <milestoneId> --proof "Built hero with Tailwind, deployed at https://..."
137
+ cascrow verify --proof <proofId>
138
+ `);
139
+ process.exit(0);
140
+ }
141
+
142
+ // ─── Argument parser ──────────────────────────────────────────────────────────
143
+
144
+ function parseArgs(argv) {
145
+ const args = {};
146
+ for (let i = 0; i < argv.length; i++) {
147
+ const a = argv[i];
148
+ if (a.startsWith("--")) {
149
+ const key = a.slice(2);
150
+ const next = argv[i + 1];
151
+ if (next && !next.startsWith("--")) {
152
+ args[key] = next;
153
+ i++;
154
+ } else {
155
+ args[key] = true;
156
+ }
157
+ }
158
+ }
159
+ return args;
160
+ }
161
+
162
+ // ─── Commands ─────────────────────────────────────────────────────────────────
163
+
164
+ async function cmdRegister(args) {
165
+ if (!args.email) err("--email is required");
166
+ if (!args.password) err("--password is required");
167
+
168
+ const res = await fetch(`${BASE_URL}/api/agent/register`, {
169
+ method: "POST",
170
+ headers: { "Content-Type": "application/json" },
171
+ body: JSON.stringify({
172
+ email: args.email,
173
+ password: args.password,
174
+ name: args.name,
175
+ keyName: args["key-name"] ?? "cli",
176
+ }),
177
+ });
178
+ const data = await res.json();
179
+ if (!res.ok) err(data.error ?? `HTTP ${res.status}`);
180
+ print(data);
181
+ console.error("\nSet your API key:\n export CASCROW_API_KEY=" + data.apiKey + "\n");
182
+ }
183
+
184
+ async function cmdCreate(args) {
185
+ if (!API_KEY) err("CASCROW_API_KEY is not set");
186
+ if (!args.title) err("--title is required");
187
+
188
+ const deadlineDays = parseInt(args.days ?? "7", 10);
189
+ const deadline = new Date();
190
+ deadline.setDate(deadline.getDate() + deadlineDays);
191
+
192
+ const data = await apiPost("/api/contracts", {
193
+ milestones: [{
194
+ title: args.description ? `${args.title} — ${args.description}` : args.title,
195
+ amountUSD: 0,
196
+ cancelAfter: deadline.toISOString(),
197
+ }],
198
+ });
199
+
200
+ const contractUrl = `${BASE_URL}/contract/${data.contractId}`;
201
+ print({ ...data, contractUrl });
202
+ }
203
+
204
+ async function cmdFund(args) {
205
+ if (!API_KEY) err("CASCROW_API_KEY is not set");
206
+ if (!args.contract) err("--contract is required");
207
+
208
+ const body = args.milestone
209
+ ? { contractId: args.contract, milestoneId: args.milestone }
210
+ : { contractId: args.contract };
211
+
212
+ const data = await apiPost("/api/agent/fund-milestone", body);
213
+ print(data);
214
+ }
215
+
216
+ async function cmdSubmit(args) {
217
+ if (!API_KEY) err("CASCROW_API_KEY is not set");
218
+ if (!args.milestone) err("--milestone is required");
219
+ if (!args.proof) err("--proof is required");
220
+
221
+ const data = await uploadProof(
222
+ args.milestone,
223
+ args.file ?? "proof-report.txt",
224
+ args.proof
225
+ );
226
+ print(data);
227
+ console.error(`\nProof submitted. Run: cascrow verify --proof ${data.proofId}\n`);
228
+ }
229
+
230
+ async function cmdVerify(args) {
231
+ if (!API_KEY) err("CASCROW_API_KEY is not set");
232
+ if (!args.proof) err("--proof is required");
233
+
234
+ console.error("\nRunning AI verification (5 models)...\n");
235
+ const result = await streamVerify(args.proof);
236
+
237
+ const passed = result.decision === "YES" && result.confidence >= 85;
238
+ print({
239
+ decision: result.decision,
240
+ confidence: result.confidence,
241
+ passed,
242
+ action: result.action,
243
+ reasoning: result.reasoning,
244
+ modelVotes: (result.votes ?? []).map(v => ({
245
+ model: v.model,
246
+ decision: v.decision,
247
+ confidence: v.confidence,
248
+ })),
249
+ });
250
+ }
251
+
252
+ async function cmdGet(args) {
253
+ if (!API_KEY) err("CASCROW_API_KEY is not set");
254
+ if (!args.contract) err("--contract is required");
255
+ const data = await apiGet(`/api/contracts/${args.contract}`);
256
+ print(data);
257
+ }
258
+
259
+ async function cmdJoin(args) {
260
+ if (!API_KEY) err("CASCROW_API_KEY is not set");
261
+ if (!args.invite) err("--invite is required");
262
+ const data = await apiPost("/api/contracts/join", { inviteCode: args.invite });
263
+ print(data);
264
+ }
265
+
266
+ // ─── Main ─────────────────────────────────────────────────────────────────────
267
+
268
+ const [,, command, ...rest] = process.argv;
269
+ const args = parseArgs(rest);
270
+
271
+ if (!command || command === "--help" || command === "help" || command === "-h") {
272
+ usage();
273
+ }
274
+
275
+ try {
276
+ switch (command) {
277
+ case "register": await cmdRegister(args); break;
278
+ case "create": await cmdCreate(args); break;
279
+ case "fund": await cmdFund(args); break;
280
+ case "submit": await cmdSubmit(args); break;
281
+ case "verify": await cmdVerify(args); break;
282
+ case "get": await cmdGet(args); break;
283
+ case "join": await cmdJoin(args); break;
284
+ default:
285
+ console.error(`Unknown command: ${command}\n`);
286
+ usage();
287
+ }
288
+ } catch (e) {
289
+ err(e.message);
290
+ }
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "cascrow-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI for Cascrow — agentic escrow and verification",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "cascrow": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": ["cascrow", "escrow", "ai-verification", "agents", "xrpl"],
14
+ "license": "MIT",
15
+ "engines": {
16
+ "node": ">=18"
17
+ }
18
+ }