@chainvue/cli 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,106 @@
1
+ # ChainVue CLI
2
+
3
+ The official command-line interface for [ChainVue](https://chainvue.io) - manage your blockchain infrastructure from the terminal.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @chainvue/cli
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Login to your ChainVue account
15
+ chainvue login
16
+
17
+ # Check your current session
18
+ chainvue whoami
19
+
20
+ # Query blockchain data
21
+ chainvue query '{ blocks(limit: 5) { height hash } }'
22
+ ```
23
+
24
+ ## Commands
25
+
26
+ ### Authentication
27
+
28
+ ```bash
29
+ chainvue login # Login via browser (OAuth Device Flow)
30
+ chainvue login --api-key # Login with an API key (for CI/CD)
31
+ chainvue logout # Log out and clear credentials
32
+ chainvue whoami # Show current user and organization
33
+ ```
34
+
35
+ ### API Keys
36
+
37
+ ```bash
38
+ chainvue keys list # List all API keys
39
+ chainvue keys create --name "My Key" # Create a new API key
40
+ chainvue keys create --name "Test" --env test --type agent
41
+ chainvue keys revoke <id> # Revoke an API key
42
+ ```
43
+
44
+ ### Webhooks
45
+
46
+ ```bash
47
+ chainvue webhooks list # List all webhooks
48
+ chainvue webhooks create \
49
+ --url https://example.com/hook \
50
+ --events address.received,address.sent \
51
+ --chain VRSC
52
+ chainvue webhooks test <id> # Send a test event
53
+ chainvue webhooks delete <id> # Delete a webhook
54
+ ```
55
+
56
+ ### Organizations
57
+
58
+ ```bash
59
+ chainvue org list # List your organizations
60
+ chainvue org switch <id> # Switch to a different org
61
+ ```
62
+
63
+ ### GraphQL Queries
64
+
65
+ ```bash
66
+ # Inline query
67
+ chainvue query '{ blocks(limit: 1) { height } }'
68
+
69
+ # From file
70
+ chainvue query -f query.graphql
71
+
72
+ # Output as JSON
73
+ chainvue query '{ blocks(limit: 5) { height hash } }' --json
74
+ ```
75
+
76
+ ## Configuration
77
+
78
+ Credentials are stored securely in your system keychain:
79
+ - **macOS**: Keychain Access
80
+ - **Windows**: Credential Manager
81
+ - **Linux**: libsecret
82
+
83
+ Config file location: `~/.config/chainvue/config.json`
84
+
85
+ ## Environment Variables
86
+
87
+ For CI/CD pipelines, you can use environment variables:
88
+
89
+ ```bash
90
+ export CHAINVUE_API_KEY=cv_api_live_xxxxx
91
+ chainvue query '{ blocks(limit: 1) { height } }'
92
+ ```
93
+
94
+ ## Requirements
95
+
96
+ - Node.js 18.0.0 or higher
97
+
98
+ ## Links
99
+
100
+ - [Documentation](https://chainvue.io/docs)
101
+ - [GraphQL API Reference](https://chainvue.io/docs/graphql)
102
+ - [Dashboard](https://chainvue.io/dashboard)
103
+
104
+ ## License
105
+
106
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js'
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,693 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command as Command8 } from "commander";
5
+
6
+ // src/commands/login.ts
7
+ import { Command } from "commander";
8
+ import ora from "ora";
9
+ import chalk2 from "chalk";
10
+
11
+ // src/lib/api.ts
12
+ import { request } from "undici";
13
+
14
+ // src/lib/config.ts
15
+ import Conf from "conf";
16
+ import os from "os";
17
+ import path from "path";
18
+ var defaults = {
19
+ currentProfile: "default",
20
+ profiles: {},
21
+ apiEndpoint: "https://chainvue.io"
22
+ };
23
+ var config = new Conf({
24
+ projectName: "chainvue",
25
+ defaults,
26
+ cwd: path.join(os.homedir(), ".config", "chainvue")
27
+ });
28
+ function getCurrentProfile() {
29
+ const profileName = config.get("currentProfile");
30
+ const profiles = config.get("profiles");
31
+ return profiles[profileName] || null;
32
+ }
33
+ function setCurrentProfile(name, profile) {
34
+ const profiles = config.get("profiles");
35
+ profiles[name] = profile;
36
+ config.set("profiles", profiles);
37
+ config.set("currentProfile", name);
38
+ }
39
+ function deleteProfile(name) {
40
+ const profiles = config.get("profiles");
41
+ delete profiles[name];
42
+ config.set("profiles", profiles);
43
+ if (config.get("currentProfile") === name) {
44
+ const remaining = Object.keys(profiles);
45
+ config.set("currentProfile", remaining[0] || "default");
46
+ }
47
+ }
48
+ function getApiEndpoint() {
49
+ return config.get("apiEndpoint");
50
+ }
51
+
52
+ // src/lib/keychain.ts
53
+ import keytar from "keytar";
54
+ var SERVICE_NAME = "chainvue-cli";
55
+ async function saveCredentials(profile, credentials) {
56
+ await keytar.setPassword(
57
+ SERVICE_NAME,
58
+ profile,
59
+ JSON.stringify(credentials)
60
+ );
61
+ }
62
+ async function getCredentials(profile) {
63
+ const data = await keytar.getPassword(SERVICE_NAME, profile);
64
+ if (!data) return null;
65
+ try {
66
+ return JSON.parse(data);
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+ async function deleteCredentials(profile) {
72
+ return keytar.deletePassword(SERVICE_NAME, profile);
73
+ }
74
+ async function getAuthHeader(profile) {
75
+ const creds = await getCredentials(profile);
76
+ if (!creds) return null;
77
+ if (creds.apiKey) {
78
+ return `Bearer ${creds.apiKey}`;
79
+ }
80
+ if (creds.accessToken) {
81
+ return `Bearer ${creds.accessToken}`;
82
+ }
83
+ return null;
84
+ }
85
+
86
+ // src/lib/api.ts
87
+ async function apiRequest(method, path2, body, authRequired = true) {
88
+ const endpoint = getApiEndpoint();
89
+ const url = `${endpoint}${path2}`;
90
+ const headers = {
91
+ "Content-Type": "application/json",
92
+ "User-Agent": "ChainVue-CLI/0.1.0"
93
+ };
94
+ if (authRequired) {
95
+ const profile = getCurrentProfile();
96
+ if (!profile) {
97
+ return {
98
+ ok: false,
99
+ status: 401,
100
+ error: "Not logged in. Run: chainvue login"
101
+ };
102
+ }
103
+ const auth = await getAuthHeader("default");
104
+ if (!auth) {
105
+ return {
106
+ ok: false,
107
+ status: 401,
108
+ error: "No credentials found. Run: chainvue login"
109
+ };
110
+ }
111
+ headers["Authorization"] = auth;
112
+ }
113
+ try {
114
+ const response = await request(url, {
115
+ method,
116
+ headers,
117
+ body: body ? JSON.stringify(body) : void 0
118
+ });
119
+ const text = await response.body.text();
120
+ let data;
121
+ try {
122
+ data = JSON.parse(text);
123
+ } catch {
124
+ }
125
+ if (response.statusCode >= 400) {
126
+ return {
127
+ ok: false,
128
+ status: response.statusCode,
129
+ error: data?.error || `HTTP ${response.statusCode}`,
130
+ data
131
+ };
132
+ }
133
+ return {
134
+ ok: true,
135
+ status: response.statusCode,
136
+ data
137
+ };
138
+ } catch (err) {
139
+ return {
140
+ ok: false,
141
+ status: 0,
142
+ error: err.message || "Network error"
143
+ };
144
+ }
145
+ }
146
+ async function graphqlQuery(query, variables) {
147
+ const endpoint = "https://api.chainvue.io/graphql";
148
+ const profile = getCurrentProfile();
149
+ if (!profile) {
150
+ return {
151
+ ok: false,
152
+ status: 401,
153
+ error: "Not logged in. Run: chainvue login"
154
+ };
155
+ }
156
+ const auth = await getAuthHeader("default");
157
+ if (!auth) {
158
+ return {
159
+ ok: false,
160
+ status: 401,
161
+ error: "No credentials found. Run: chainvue login"
162
+ };
163
+ }
164
+ try {
165
+ const response = await request(endpoint, {
166
+ method: "POST",
167
+ headers: {
168
+ "Content-Type": "application/json",
169
+ "Authorization": auth,
170
+ "User-Agent": "ChainVue-CLI/0.1.0"
171
+ },
172
+ body: JSON.stringify({ query, variables })
173
+ });
174
+ const data = await response.body.json();
175
+ if (data.errors) {
176
+ return {
177
+ ok: false,
178
+ status: response.statusCode,
179
+ error: data.errors[0]?.message || "GraphQL error",
180
+ data
181
+ };
182
+ }
183
+ return {
184
+ ok: true,
185
+ status: response.statusCode,
186
+ data: data.data
187
+ };
188
+ } catch (err) {
189
+ return {
190
+ ok: false,
191
+ status: 0,
192
+ error: err.message || "Network error"
193
+ };
194
+ }
195
+ }
196
+ async function requestDeviceCode() {
197
+ return apiRequest("POST", "/api/v1/auth/device", void 0, false);
198
+ }
199
+ async function pollDeviceToken(deviceCode) {
200
+ return apiRequest("POST", "/api/v1/auth/device/token", { deviceCode }, false);
201
+ }
202
+
203
+ // src/lib/output.ts
204
+ import chalk from "chalk";
205
+ import Table from "cli-table3";
206
+ function success(message) {
207
+ console.log(chalk.green("\u2713") + " " + message);
208
+ }
209
+ function error(message) {
210
+ console.log(chalk.red("\u2717") + " " + message);
211
+ }
212
+ function warn(message) {
213
+ console.log(chalk.yellow("!") + " " + message);
214
+ }
215
+ function info(message) {
216
+ console.log(chalk.blue("\u2192") + " " + message);
217
+ }
218
+ function dim(message) {
219
+ console.log(chalk.dim(message));
220
+ }
221
+ function json(data) {
222
+ console.log(JSON.stringify(data, null, 2));
223
+ }
224
+ function table(headers, rows, options = {}) {
225
+ const t = new Table({
226
+ head: headers.map((h) => chalk.bold(h)),
227
+ style: {
228
+ head: [],
229
+ border: ["dim"]
230
+ }
231
+ });
232
+ for (const row of rows) {
233
+ t.push(row);
234
+ }
235
+ console.log(t.toString());
236
+ }
237
+ function keyValue(data) {
238
+ const maxKeyLength = Math.max(...Object.keys(data).map((k) => k.length));
239
+ for (const [key, value] of Object.entries(data)) {
240
+ const paddedKey = key.padEnd(maxKeyLength);
241
+ const displayValue = value === null || value === void 0 ? chalk.dim("\u2014") : String(value);
242
+ console.log(`${chalk.bold(paddedKey)} ${displayValue}`);
243
+ }
244
+ }
245
+ function formatTimeAgo(date) {
246
+ if (!date) return "Never";
247
+ const d = typeof date === "string" ? new Date(date) : date;
248
+ const now = /* @__PURE__ */ new Date();
249
+ const seconds = Math.floor((now.getTime() - d.getTime()) / 1e3);
250
+ if (seconds < 60) return "Just now";
251
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} min ago`;
252
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
253
+ if (seconds < 604800) return `${Math.floor(seconds / 86400)} days ago`;
254
+ return d.toLocaleDateString();
255
+ }
256
+
257
+ // src/commands/login.ts
258
+ var loginCommand = new Command("login").description("Log in to ChainVue").option("--api-key <key>", "Login with an API key (for CI/CD)").action(async (options) => {
259
+ const existing = getCurrentProfile();
260
+ if (existing) {
261
+ warn(`Already logged in as ${existing.email}`);
262
+ dim('Run "chainvue logout" first to switch accounts');
263
+ return;
264
+ }
265
+ if (options.apiKey) {
266
+ await loginWithApiKey(options.apiKey);
267
+ return;
268
+ }
269
+ await loginWithDeviceFlow();
270
+ });
271
+ async function loginWithApiKey(apiKey) {
272
+ const spinner = ora("Validating API key...").start();
273
+ if (!apiKey.startsWith("cv_api_") && !apiKey.startsWith("cv_agent_")) {
274
+ spinner.fail("Invalid API key format");
275
+ dim("API keys should start with cv_api_ or cv_agent_");
276
+ return;
277
+ }
278
+ await saveCredentials("default", { apiKey, accessToken: "" });
279
+ setCurrentProfile("default", {
280
+ orgId: "unknown",
281
+ orgName: "API Key Auth",
282
+ email: "api-key",
283
+ clerkUserId: "",
284
+ environment: apiKey.includes("_live_") ? "live" : "test"
285
+ });
286
+ spinner.succeed("Logged in with API key");
287
+ dim("Note: Some commands may have limited functionality with API key auth");
288
+ }
289
+ async function loginWithDeviceFlow() {
290
+ const spinner = ora("Requesting device code...").start();
291
+ const deviceResponse = await requestDeviceCode();
292
+ if (!deviceResponse.ok || !deviceResponse.data) {
293
+ spinner.fail("Failed to start login");
294
+ error(deviceResponse.error || "Unknown error");
295
+ return;
296
+ }
297
+ const { deviceCode, userCode, verificationUri, expiresIn, interval } = deviceResponse.data;
298
+ spinner.stop();
299
+ console.log();
300
+ console.log(chalk2.bold(" To complete login, visit:"));
301
+ console.log();
302
+ console.log(chalk2.cyan(` ${verificationUri}`));
303
+ console.log();
304
+ console.log(chalk2.bold(" And enter this code:"));
305
+ console.log();
306
+ console.log(chalk2.yellow.bold(` ${userCode}`));
307
+ console.log();
308
+ const pollSpinner = ora("Waiting for authorization...").start();
309
+ const expiresAt = Date.now() + expiresIn * 1e3;
310
+ const pollInterval = Math.max(interval, 5) * 1e3;
311
+ while (Date.now() < expiresAt) {
312
+ await sleep(pollInterval);
313
+ const tokenResponse = await pollDeviceToken(deviceCode);
314
+ if (tokenResponse.ok && tokenResponse.data) {
315
+ const data = tokenResponse.data;
316
+ if (data.error === "authorization_pending") {
317
+ continue;
318
+ }
319
+ if (data.accessToken) {
320
+ pollSpinner.succeed("Authorized");
321
+ await saveCredentials("default", {
322
+ accessToken: data.accessToken,
323
+ refreshToken: data.refreshToken
324
+ });
325
+ const org = data.organizations[0];
326
+ setCurrentProfile("default", {
327
+ orgId: org.id,
328
+ orgName: org.name,
329
+ email: data.user.email,
330
+ clerkUserId: data.user.id,
331
+ environment: "live"
332
+ });
333
+ console.log();
334
+ success(`Logged in as ${data.user.email}`);
335
+ success(`Organization: ${org.name}`);
336
+ if (data.organizations.length > 1) {
337
+ dim(`You have access to ${data.organizations.length} organizations`);
338
+ dim('Use "chainvue org switch" to change');
339
+ }
340
+ return;
341
+ }
342
+ }
343
+ if (!tokenResponse.ok && tokenResponse.error !== "authorization_pending") {
344
+ pollSpinner.fail("Authorization failed");
345
+ error(tokenResponse.error || "Unknown error");
346
+ return;
347
+ }
348
+ }
349
+ pollSpinner.fail("Authorization timed out");
350
+ error("Please try again");
351
+ }
352
+ function sleep(ms) {
353
+ return new Promise((resolve) => setTimeout(resolve, ms));
354
+ }
355
+
356
+ // src/commands/logout.ts
357
+ import { Command as Command2 } from "commander";
358
+ var logoutCommand = new Command2("logout").description("Log out of ChainVue").action(async () => {
359
+ const profile = getCurrentProfile();
360
+ if (!profile) {
361
+ warn("Not currently logged in");
362
+ return;
363
+ }
364
+ await deleteCredentials("default");
365
+ deleteProfile("default");
366
+ success(`Logged out from ${profile.email}`);
367
+ });
368
+
369
+ // src/commands/whoami.ts
370
+ import { Command as Command3 } from "commander";
371
+ var whoamiCommand = new Command3("whoami").description("Show current user and organization").option("--json", "Output as JSON").action(async (options) => {
372
+ const profile = getCurrentProfile();
373
+ if (!profile) {
374
+ error("Not logged in");
375
+ dim("Run: chainvue login");
376
+ process.exit(1);
377
+ }
378
+ const creds = await getCredentials("default");
379
+ const authMethod = creds?.apiKey ? "API Key" : "Session";
380
+ if (options.json) {
381
+ json({
382
+ email: profile.email,
383
+ organization: {
384
+ id: profile.orgId,
385
+ name: profile.orgName
386
+ },
387
+ environment: profile.environment,
388
+ authMethod,
389
+ apiEndpoint: getApiEndpoint()
390
+ });
391
+ return;
392
+ }
393
+ console.log();
394
+ keyValue({
395
+ "Email": profile.email,
396
+ "Organization": profile.orgName,
397
+ "Org ID": profile.orgId,
398
+ "Environment": profile.environment,
399
+ "Auth Method": authMethod,
400
+ "API Endpoint": getApiEndpoint()
401
+ });
402
+ console.log();
403
+ });
404
+
405
+ // src/commands/keys.ts
406
+ import { Command as Command4 } from "commander";
407
+ import ora2 from "ora";
408
+ var keysCommand = new Command4("keys").description("Manage API keys");
409
+ keysCommand.command("list").description("List all API keys").option("--json", "Output as JSON").action(async (options) => {
410
+ const spinner = ora2("Fetching API keys...").start();
411
+ const response = await apiRequest("GET", "/api/v1/keys");
412
+ if (!response.ok) {
413
+ spinner.fail("Failed to fetch keys");
414
+ error(response.error || "Unknown error");
415
+ return;
416
+ }
417
+ spinner.stop();
418
+ const keys = response.data?.keys || [];
419
+ if (options.json) {
420
+ json(keys);
421
+ return;
422
+ }
423
+ if (keys.length === 0) {
424
+ info("No API keys found");
425
+ dim('Create one with: chainvue keys create --name "My Key"');
426
+ return;
427
+ }
428
+ table(
429
+ ["NAME", "TYPE", "ENV", "PREFIX", "LAST USED", "CREATED"],
430
+ keys.map((k) => [
431
+ k.name,
432
+ k.type,
433
+ k.environment.toLowerCase(),
434
+ k.keyPrefix,
435
+ formatTimeAgo(k.lastUsedAt),
436
+ formatTimeAgo(k.createdAt)
437
+ ])
438
+ );
439
+ });
440
+ keysCommand.command("create").description("Create a new API key").requiredOption("--name <name>", "Name for the key").option("--type <type>", "Key type: api or agent", "api").option("--env <env>", "Environment: test or live", "test").action(async (options) => {
441
+ const spinner = ora2("Creating API key...").start();
442
+ const response = await apiRequest("POST", "/api/v1/keys", {
443
+ name: options.name,
444
+ type: options.type,
445
+ environment: options.env.toUpperCase()
446
+ });
447
+ if (!response.ok) {
448
+ spinner.fail("Failed to create key");
449
+ error(response.error || "Unknown error");
450
+ return;
451
+ }
452
+ spinner.succeed("API key created");
453
+ console.log();
454
+ const { key, keyPrefix, name, environment } = response.data;
455
+ keyValue({
456
+ "Name": name,
457
+ "Environment": environment.toLowerCase(),
458
+ "Key": key
459
+ });
460
+ console.log();
461
+ warn("Save this key now! You won't be able to see it again.");
462
+ });
463
+ keysCommand.command("revoke <id>").description("Revoke an API key").action(async (id) => {
464
+ const spinner = ora2("Revoking API key...").start();
465
+ const response = await apiRequest("DELETE", `/api/v1/keys/${id}`);
466
+ if (!response.ok) {
467
+ spinner.fail("Failed to revoke key");
468
+ error(response.error || "Unknown error");
469
+ return;
470
+ }
471
+ spinner.succeed("API key revoked");
472
+ });
473
+
474
+ // src/commands/webhooks.ts
475
+ import { Command as Command5 } from "commander";
476
+ import ora3 from "ora";
477
+ var CHAIN_NAMES = {
478
+ "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq": "VRSCTEST",
479
+ "i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV": "Verus"
480
+ };
481
+ var webhooksCommand = new Command5("webhooks").description("Manage webhooks");
482
+ webhooksCommand.command("list").description("List all webhooks").option("--json", "Output as JSON").action(async (options) => {
483
+ const spinner = ora3("Fetching webhooks...").start();
484
+ const response = await apiRequest("GET", "/api/v1/webhooks");
485
+ if (!response.ok) {
486
+ spinner.fail("Failed to fetch webhooks");
487
+ error(response.error || "Unknown error");
488
+ return;
489
+ }
490
+ spinner.stop();
491
+ const webhooks = response.data?.webhooks || [];
492
+ if (options.json) {
493
+ json(webhooks);
494
+ return;
495
+ }
496
+ if (webhooks.length === 0) {
497
+ info("No webhooks found");
498
+ dim("Create one with: chainvue webhooks create --url https://...");
499
+ return;
500
+ }
501
+ table(
502
+ ["URL", "CHAIN", "EVENTS", "STATUS", "SUCCESS", "LAST"],
503
+ webhooks.map((w) => {
504
+ const successRate = w.deliveryCount > 0 ? `${((w.deliveryCount - w.failureCount) / w.deliveryCount * 100).toFixed(1)}%` : "\u2014";
505
+ return [
506
+ w.url.length > 40 ? w.url.slice(0, 37) + "..." : w.url,
507
+ CHAIN_NAMES[w.chainId] || w.chainId.slice(0, 8),
508
+ w.events.length.toString(),
509
+ w.isActive ? "active" : "disabled",
510
+ successRate,
511
+ formatTimeAgo(w.lastTriggeredAt)
512
+ ];
513
+ })
514
+ );
515
+ });
516
+ webhooksCommand.command("create").description("Create a new webhook").requiredOption("--url <url>", "Webhook endpoint URL (HTTPS)").option("--description <desc>", "Description").option("--events <events>", "Comma-separated events", "address.received").option("--chain <chain>", "Chain ID or name", "VRSCTEST").action(async (options) => {
517
+ const spinner = ora3("Creating webhook...").start();
518
+ let chainId = options.chain;
519
+ if (options.chain === "VRSCTEST") {
520
+ chainId = "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq";
521
+ } else if (options.chain === "Verus") {
522
+ chainId = "i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV";
523
+ }
524
+ const events = options.events.split(",").map((e) => e.trim());
525
+ const response = await apiRequest("POST", "/api/v1/webhooks", {
526
+ url: options.url,
527
+ description: options.description,
528
+ chainId,
529
+ events
530
+ });
531
+ if (!response.ok) {
532
+ spinner.fail("Failed to create webhook");
533
+ error(response.error || "Unknown error");
534
+ return;
535
+ }
536
+ spinner.succeed("Webhook created");
537
+ console.log();
538
+ const { url, secret, events: createdEvents } = response.data;
539
+ keyValue({
540
+ "URL": url,
541
+ "Events": createdEvents.join(", "),
542
+ "Secret": secret
543
+ });
544
+ console.log();
545
+ warn("Save this secret now! You won't be able to see it again.");
546
+ });
547
+ webhooksCommand.command("delete <id>").description("Delete a webhook").action(async (id) => {
548
+ const spinner = ora3("Deleting webhook...").start();
549
+ const response = await apiRequest("DELETE", `/api/v1/webhooks/${id}`);
550
+ if (!response.ok) {
551
+ spinner.fail("Failed to delete webhook");
552
+ error(response.error || "Unknown error");
553
+ return;
554
+ }
555
+ spinner.succeed("Webhook deleted");
556
+ });
557
+ webhooksCommand.command("test <id>").description("Send a test event to a webhook").action(async (id) => {
558
+ const spinner = ora3("Sending test event...").start();
559
+ const response = await apiRequest("POST", `/api/v1/webhooks/${id}/test`);
560
+ if (!response.ok) {
561
+ spinner.fail("Failed to send test");
562
+ error(response.error || "Unknown error");
563
+ return;
564
+ }
565
+ const result = response.data;
566
+ if (result.success) {
567
+ spinner.succeed(`Test delivered (${result.responseCode}, ${result.responseTime}ms)`);
568
+ } else {
569
+ spinner.fail(`Test failed: ${result.errorMessage}`);
570
+ }
571
+ });
572
+
573
+ // src/commands/query.ts
574
+ import { Command as Command6 } from "commander";
575
+ import { readFileSync } from "fs";
576
+ import ora4 from "ora";
577
+ var queryCommand = new Command6("query").description("Execute a GraphQL query").argument("[query]", "GraphQL query string").option("-f, --file <file>", "Read query from file").option("--json", "Output as JSON (default)").option("--pretty", "Pretty print JSON output").action(async (queryArg, options) => {
578
+ let query;
579
+ if (options.file) {
580
+ try {
581
+ query = readFileSync(options.file, "utf-8");
582
+ } catch (err) {
583
+ error(`Failed to read file: ${err.message}`);
584
+ process.exit(1);
585
+ }
586
+ } else if (queryArg) {
587
+ query = queryArg;
588
+ } else {
589
+ error("Provide a query string or use --file");
590
+ dim('Example: chainvue query "{ blocks(limit: 5) { height hash } }"');
591
+ process.exit(1);
592
+ }
593
+ const spinner = ora4("Executing query...").start();
594
+ const response = await graphqlQuery(query);
595
+ if (!response.ok) {
596
+ spinner.fail("Query failed");
597
+ error(response.error || "Unknown error");
598
+ if (response.data) {
599
+ console.log();
600
+ json(response.data);
601
+ }
602
+ process.exit(1);
603
+ }
604
+ spinner.stop();
605
+ if (options.pretty) {
606
+ console.log(JSON.stringify(response.data, null, 2));
607
+ } else {
608
+ console.log(JSON.stringify(response.data));
609
+ }
610
+ });
611
+
612
+ // src/commands/org.ts
613
+ import { Command as Command7 } from "commander";
614
+ import ora5 from "ora";
615
+ import chalk3 from "chalk";
616
+ var orgCommand = new Command7("org").description("Manage organizations");
617
+ orgCommand.command("list").description("List your organizations").option("--json", "Output as JSON").action(async (options) => {
618
+ const spinner = ora5("Fetching organizations...").start();
619
+ const response = await apiRequest("GET", "/api/v1/orgs");
620
+ if (!response.ok) {
621
+ spinner.fail("Failed to fetch organizations");
622
+ error(response.error || "Unknown error");
623
+ return;
624
+ }
625
+ spinner.stop();
626
+ const orgs = response.data?.organizations || [];
627
+ const currentProfile = getCurrentProfile();
628
+ if (options.json) {
629
+ json(orgs);
630
+ return;
631
+ }
632
+ if (orgs.length === 0) {
633
+ info("No organizations found");
634
+ return;
635
+ }
636
+ console.log();
637
+ for (const org of orgs) {
638
+ const isCurrent = org.id === currentProfile?.orgId;
639
+ const marker = isCurrent ? chalk3.green("\u25B6 ") : " ";
640
+ const name = isCurrent ? chalk3.bold(org.name) : org.name;
641
+ console.log(`${marker}${name}`);
642
+ console.log(` ID: ${org.id}`);
643
+ console.log(` Role: ${org.role} Plan: ${org.plan}`);
644
+ console.log();
645
+ }
646
+ });
647
+ orgCommand.command("switch <orgId>").description("Switch to a different organization").action(async (orgId) => {
648
+ const spinner = ora5("Switching organization...").start();
649
+ const response = await apiRequest("GET", `/api/v1/orgs/${orgId}`);
650
+ if (!response.ok) {
651
+ spinner.fail("Failed to switch organization");
652
+ error(response.error || "Organization not found or no access");
653
+ return;
654
+ }
655
+ const org = response.data;
656
+ const currentProfile = getCurrentProfile();
657
+ if (!currentProfile) {
658
+ spinner.fail("Not logged in");
659
+ return;
660
+ }
661
+ setCurrentProfile("default", {
662
+ ...currentProfile,
663
+ orgId: org.id,
664
+ orgName: org.name
665
+ });
666
+ spinner.succeed(`Switched to ${org.name}`);
667
+ });
668
+ orgCommand.command("current").description("Show current organization").action(async () => {
669
+ const profile = getCurrentProfile();
670
+ if (!profile) {
671
+ error("Not logged in");
672
+ process.exit(1);
673
+ }
674
+ console.log();
675
+ keyValue({
676
+ "Organization": profile.orgName,
677
+ "ID": profile.orgId
678
+ });
679
+ console.log();
680
+ });
681
+
682
+ // src/index.ts
683
+ var program = new Command8();
684
+ program.name("chainvue").description("ChainVue CLI - Manage your blockchain infrastructure").version("0.1.0");
685
+ program.addCommand(loginCommand);
686
+ program.addCommand(logoutCommand);
687
+ program.addCommand(whoamiCommand);
688
+ program.addCommand(keysCommand);
689
+ program.addCommand(webhooksCommand);
690
+ program.addCommand(orgCommand);
691
+ program.addCommand(queryCommand);
692
+ program.parse();
693
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/api.ts","../src/lib/config.ts","../src/lib/keychain.ts","../src/lib/output.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/keys.ts","../src/commands/webhooks.ts","../src/commands/query.ts","../src/commands/org.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander'\nimport { loginCommand } from './commands/login.js'\nimport { logoutCommand } from './commands/logout.js'\nimport { whoamiCommand } from './commands/whoami.js'\nimport { keysCommand } from './commands/keys.js'\nimport { webhooksCommand } from './commands/webhooks.js'\nimport { queryCommand } from './commands/query.js'\nimport { orgCommand } from './commands/org.js'\n\nconst program = new Command()\n\nprogram\n .name('chainvue')\n .description('ChainVue CLI - Manage your blockchain infrastructure')\n .version('0.1.0')\n\n// Auth commands\nprogram.addCommand(loginCommand)\nprogram.addCommand(logoutCommand)\nprogram.addCommand(whoamiCommand)\n\n// Management commands\nprogram.addCommand(keysCommand)\nprogram.addCommand(webhooksCommand)\nprogram.addCommand(orgCommand)\n\n// Query command\nprogram.addCommand(queryCommand)\n\n// Parse arguments\nprogram.parse()\n","import { Command } from 'commander'\nimport ora from 'ora'\nimport chalk from 'chalk'\nimport { requestDeviceCode, pollDeviceToken } from '../lib/api.js'\nimport { saveCredentials, getCredentials } from '../lib/keychain.js'\nimport { setCurrentProfile, getCurrentProfile } from '../lib/config.js'\nimport * as output from '../lib/output.js'\n\nexport const loginCommand = new Command('login')\n .description('Log in to ChainVue')\n .option('--api-key <key>', 'Login with an API key (for CI/CD)')\n .action(async (options) => {\n // Check if already logged in\n const existing = getCurrentProfile()\n if (existing) {\n output.warn(`Already logged in as ${existing.email}`)\n output.dim('Run \"chainvue logout\" first to switch accounts')\n return\n }\n\n // API key login (for CI/CD)\n if (options.apiKey) {\n await loginWithApiKey(options.apiKey)\n return\n }\n\n // OAuth Device Flow\n await loginWithDeviceFlow()\n })\n\nasync function loginWithApiKey(apiKey: string): Promise<void> {\n const spinner = ora('Validating API key...').start()\n\n // Validate key format\n if (!apiKey.startsWith('cv_api_') && !apiKey.startsWith('cv_agent_')) {\n spinner.fail('Invalid API key format')\n output.dim('API keys should start with cv_api_ or cv_agent_')\n return\n }\n\n // TODO: Validate key by calling /api/v1/auth/me\n // For now, just store it\n\n await saveCredentials('default', { apiKey, accessToken: '' })\n\n // We don't have user info with just API key, so create minimal profile\n setCurrentProfile('default', {\n orgId: 'unknown',\n orgName: 'API Key Auth',\n email: 'api-key',\n clerkUserId: '',\n environment: apiKey.includes('_live_') ? 'live' : 'test',\n })\n\n spinner.succeed('Logged in with API key')\n output.dim('Note: Some commands may have limited functionality with API key auth')\n}\n\nasync function loginWithDeviceFlow(): Promise<void> {\n const spinner = ora('Requesting device code...').start()\n\n // Step 1: Request device code\n const deviceResponse = await requestDeviceCode()\n\n if (!deviceResponse.ok || !deviceResponse.data) {\n spinner.fail('Failed to start login')\n output.error(deviceResponse.error || 'Unknown error')\n return\n }\n\n const { deviceCode, userCode, verificationUri, expiresIn, interval } = deviceResponse.data\n\n spinner.stop()\n\n // Step 2: Display code to user\n console.log()\n console.log(chalk.bold(' To complete login, visit:'))\n console.log()\n console.log(chalk.cyan(` ${verificationUri}`))\n console.log()\n console.log(chalk.bold(' And enter this code:'))\n console.log()\n console.log(chalk.yellow.bold(` ${userCode}`))\n console.log()\n\n // Step 3: Poll for token\n const pollSpinner = ora('Waiting for authorization...').start()\n\n const expiresAt = Date.now() + expiresIn * 1000\n const pollInterval = Math.max(interval, 5) * 1000\n\n while (Date.now() < expiresAt) {\n await sleep(pollInterval)\n\n const tokenResponse = await pollDeviceToken(deviceCode)\n\n if (tokenResponse.ok && tokenResponse.data) {\n const data = tokenResponse.data as any\n\n if (data.error === 'authorization_pending') {\n // Still waiting\n continue\n }\n\n if (data.accessToken) {\n // Success!\n pollSpinner.succeed('Authorized')\n\n // Save credentials\n await saveCredentials('default', {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n })\n\n // Save profile\n const org = data.organizations[0]\n setCurrentProfile('default', {\n orgId: org.id,\n orgName: org.name,\n email: data.user.email,\n clerkUserId: data.user.id,\n environment: 'live',\n })\n\n console.log()\n output.success(`Logged in as ${data.user.email}`)\n output.success(`Organization: ${org.name}`)\n\n if (data.organizations.length > 1) {\n output.dim(`You have access to ${data.organizations.length} organizations`)\n output.dim('Use \"chainvue org switch\" to change')\n }\n\n return\n }\n }\n\n if (!tokenResponse.ok && tokenResponse.error !== 'authorization_pending') {\n pollSpinner.fail('Authorization failed')\n output.error(tokenResponse.error || 'Unknown error')\n return\n }\n }\n\n pollSpinner.fail('Authorization timed out')\n output.error('Please try again')\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n","import { request } from 'undici'\nimport { getApiEndpoint, getCurrentProfile } from './config.js'\nimport { getAuthHeader } from './keychain.js'\n\nexport interface ApiResponse<T = unknown> {\n ok: boolean\n status: number\n data?: T\n error?: string\n}\n\nexport async function apiRequest<T = unknown>(\n method: 'GET' | 'POST' | 'PATCH' | 'DELETE',\n path: string,\n body?: unknown,\n authRequired = true\n): Promise<ApiResponse<T>> {\n const endpoint = getApiEndpoint()\n const url = `${endpoint}${path}`\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'User-Agent': 'ChainVue-CLI/0.1.0',\n }\n\n if (authRequired) {\n const profile = getCurrentProfile()\n if (!profile) {\n return {\n ok: false,\n status: 401,\n error: 'Not logged in. Run: chainvue login',\n }\n }\n\n const auth = await getAuthHeader('default')\n if (!auth) {\n return {\n ok: false,\n status: 401,\n error: 'No credentials found. Run: chainvue login',\n }\n }\n\n headers['Authorization'] = auth\n }\n\n try {\n const response = await request(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n })\n\n const text = await response.body.text()\n let data: T | undefined\n\n try {\n data = JSON.parse(text) as T\n } catch {\n // Not JSON response\n }\n\n if (response.statusCode >= 400) {\n return {\n ok: false,\n status: response.statusCode,\n error: (data as any)?.error || `HTTP ${response.statusCode}`,\n data,\n }\n }\n\n return {\n ok: true,\n status: response.statusCode,\n data,\n }\n } catch (err: any) {\n return {\n ok: false,\n status: 0,\n error: err.message || 'Network error',\n }\n }\n}\n\n// GraphQL query\nexport async function graphqlQuery<T = unknown>(\n query: string,\n variables?: Record<string, unknown>\n): Promise<ApiResponse<T>> {\n // GraphQL goes to the NestJS API, not the Next.js app\n const endpoint = 'https://api.chainvue.io/graphql'\n\n const profile = getCurrentProfile()\n if (!profile) {\n return {\n ok: false,\n status: 401,\n error: 'Not logged in. Run: chainvue login',\n }\n }\n\n const auth = await getAuthHeader('default')\n if (!auth) {\n return {\n ok: false,\n status: 401,\n error: 'No credentials found. Run: chainvue login',\n }\n }\n\n try {\n const response = await request(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': auth,\n 'User-Agent': 'ChainVue-CLI/0.1.0',\n },\n body: JSON.stringify({ query, variables }),\n })\n\n const data = await response.body.json() as any\n\n if (data.errors) {\n return {\n ok: false,\n status: response.statusCode,\n error: data.errors[0]?.message || 'GraphQL error',\n data,\n }\n }\n\n return {\n ok: true,\n status: response.statusCode,\n data: data.data as T,\n }\n } catch (err: any) {\n return {\n ok: false,\n status: 0,\n error: err.message || 'Network error',\n }\n }\n}\n\n// Device auth flow\nexport async function requestDeviceCode(): Promise<ApiResponse<{\n deviceCode: string\n userCode: string\n verificationUri: string\n expiresIn: number\n interval: number\n}>> {\n return apiRequest('POST', '/api/v1/auth/device', undefined, false)\n}\n\nexport async function pollDeviceToken(deviceCode: string): Promise<ApiResponse<{\n accessToken: string\n refreshToken: string\n user: { id: string; email: string }\n organizations: Array<{ id: string; name: string; role: string }>\n} | { error: string }>> {\n return apiRequest('POST', '/api/v1/auth/device/token', { deviceCode }, false)\n}\n","import Conf from 'conf'\nimport os from 'os'\nimport path from 'path'\n\nexport interface Profile {\n orgId: string\n orgName: string\n email: string\n clerkUserId: string\n environment: 'live' | 'test'\n}\n\nexport interface Config {\n currentProfile: string\n profiles: Record<string, Profile>\n apiEndpoint: string\n}\n\nconst defaults: Config = {\n currentProfile: 'default',\n profiles: {},\n apiEndpoint: 'https://chainvue.io',\n}\n\nexport const config = new Conf<Config>({\n projectName: 'chainvue',\n defaults,\n cwd: path.join(os.homedir(), '.config', 'chainvue'),\n})\n\nexport function getCurrentProfile(): Profile | null {\n const profileName = config.get('currentProfile')\n const profiles = config.get('profiles')\n return profiles[profileName] || null\n}\n\nexport function setCurrentProfile(name: string, profile: Profile): void {\n const profiles = config.get('profiles')\n profiles[name] = profile\n config.set('profiles', profiles)\n config.set('currentProfile', name)\n}\n\nexport function deleteProfile(name: string): void {\n const profiles = config.get('profiles')\n delete profiles[name]\n config.set('profiles', profiles)\n\n if (config.get('currentProfile') === name) {\n const remaining = Object.keys(profiles)\n config.set('currentProfile', remaining[0] || 'default')\n }\n}\n\nexport function listProfiles(): Record<string, Profile> {\n return config.get('profiles')\n}\n\nexport function getApiEndpoint(): string {\n return config.get('apiEndpoint')\n}\n","import keytar from 'keytar'\n\nconst SERVICE_NAME = 'chainvue-cli'\n\nexport interface StoredCredentials {\n accessToken: string\n refreshToken?: string\n apiKey?: string\n}\n\nexport async function saveCredentials(\n profile: string,\n credentials: StoredCredentials\n): Promise<void> {\n await keytar.setPassword(\n SERVICE_NAME,\n profile,\n JSON.stringify(credentials)\n )\n}\n\nexport async function getCredentials(\n profile: string\n): Promise<StoredCredentials | null> {\n const data = await keytar.getPassword(SERVICE_NAME, profile)\n if (!data) return null\n\n try {\n return JSON.parse(data) as StoredCredentials\n } catch {\n return null\n }\n}\n\nexport async function deleteCredentials(profile: string): Promise<boolean> {\n return keytar.deletePassword(SERVICE_NAME, profile)\n}\n\nexport async function hasCredentials(profile: string): Promise<boolean> {\n const creds = await getCredentials(profile)\n return creds !== null\n}\n\n// Get the authorization header value\nexport async function getAuthHeader(profile: string): Promise<string | null> {\n const creds = await getCredentials(profile)\n if (!creds) return null\n\n // Prefer API key if set (for CI/CD use)\n if (creds.apiKey) {\n return `Bearer ${creds.apiKey}`\n }\n\n // Otherwise use session token\n if (creds.accessToken) {\n return `Bearer ${creds.accessToken}`\n }\n\n return null\n}\n","import chalk from 'chalk'\nimport Table from 'cli-table3'\n\nexport function success(message: string): void {\n console.log(chalk.green('✓') + ' ' + message)\n}\n\nexport function error(message: string): void {\n console.log(chalk.red('✗') + ' ' + message)\n}\n\nexport function warn(message: string): void {\n console.log(chalk.yellow('!') + ' ' + message)\n}\n\nexport function info(message: string): void {\n console.log(chalk.blue('→') + ' ' + message)\n}\n\nexport function dim(message: string): void {\n console.log(chalk.dim(message))\n}\n\nexport function json(data: unknown): void {\n console.log(JSON.stringify(data, null, 2))\n}\n\nexport function table(\n headers: string[],\n rows: string[][],\n options: { head?: boolean } = {}\n): void {\n const t = new Table({\n head: headers.map(h => chalk.bold(h)),\n style: {\n head: [],\n border: ['dim'],\n },\n })\n\n for (const row of rows) {\n t.push(row)\n }\n\n console.log(t.toString())\n}\n\nexport function keyValue(data: Record<string, string | number | null | undefined>): void {\n const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length))\n\n for (const [key, value] of Object.entries(data)) {\n const paddedKey = key.padEnd(maxKeyLength)\n const displayValue = value === null || value === undefined\n ? chalk.dim('—')\n : String(value)\n console.log(`${chalk.bold(paddedKey)} ${displayValue}`)\n }\n}\n\nexport function formatTimeAgo(date: Date | string | null): string {\n if (!date) return 'Never'\n\n const d = typeof date === 'string' ? new Date(date) : date\n const now = new Date()\n const seconds = Math.floor((now.getTime() - d.getTime()) / 1000)\n\n if (seconds < 60) return 'Just now'\n if (seconds < 3600) return `${Math.floor(seconds / 60)} min ago`\n if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`\n if (seconds < 604800) return `${Math.floor(seconds / 86400)} days ago`\n\n return d.toLocaleDateString()\n}\n","import { Command } from 'commander'\nimport { deleteCredentials } from '../lib/keychain.js'\nimport { deleteProfile, getCurrentProfile } from '../lib/config.js'\nimport * as output from '../lib/output.js'\n\nexport const logoutCommand = new Command('logout')\n .description('Log out of ChainVue')\n .action(async () => {\n const profile = getCurrentProfile()\n\n if (!profile) {\n output.warn('Not currently logged in')\n return\n }\n\n // Delete credentials from keychain\n await deleteCredentials('default')\n\n // Delete profile from config\n deleteProfile('default')\n\n output.success(`Logged out from ${profile.email}`)\n })\n","import { Command } from 'commander'\nimport { getCurrentProfile, getApiEndpoint } from '../lib/config.js'\nimport { getCredentials } from '../lib/keychain.js'\nimport * as output from '../lib/output.js'\n\nexport const whoamiCommand = new Command('whoami')\n .description('Show current user and organization')\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const profile = getCurrentProfile()\n\n if (!profile) {\n output.error('Not logged in')\n output.dim('Run: chainvue login')\n process.exit(1)\n }\n\n const creds = await getCredentials('default')\n const authMethod = creds?.apiKey ? 'API Key' : 'Session'\n\n if (options.json) {\n output.json({\n email: profile.email,\n organization: {\n id: profile.orgId,\n name: profile.orgName,\n },\n environment: profile.environment,\n authMethod,\n apiEndpoint: getApiEndpoint(),\n })\n return\n }\n\n console.log()\n output.keyValue({\n 'Email': profile.email,\n 'Organization': profile.orgName,\n 'Org ID': profile.orgId,\n 'Environment': profile.environment,\n 'Auth Method': authMethod,\n 'API Endpoint': getApiEndpoint(),\n })\n console.log()\n })\n","import { Command } from 'commander'\nimport ora from 'ora'\nimport { apiRequest } from '../lib/api.js'\nimport * as output from '../lib/output.js'\n\ninterface ApiKey {\n id: string\n name: string\n keyPrefix: string\n environment: 'TEST' | 'LIVE'\n type: 'api' | 'agent'\n lastUsedAt: string | null\n createdAt: string\n}\n\nexport const keysCommand = new Command('keys')\n .description('Manage API keys')\n\nkeysCommand\n .command('list')\n .description('List all API keys')\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const spinner = ora('Fetching API keys...').start()\n\n const response = await apiRequest<{ keys: ApiKey[] }>('GET', '/api/v1/keys')\n\n if (!response.ok) {\n spinner.fail('Failed to fetch keys')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.stop()\n\n const keys = response.data?.keys || []\n\n if (options.json) {\n output.json(keys)\n return\n }\n\n if (keys.length === 0) {\n output.info('No API keys found')\n output.dim('Create one with: chainvue keys create --name \"My Key\"')\n return\n }\n\n output.table(\n ['NAME', 'TYPE', 'ENV', 'PREFIX', 'LAST USED', 'CREATED'],\n keys.map(k => [\n k.name,\n k.type,\n k.environment.toLowerCase(),\n k.keyPrefix,\n output.formatTimeAgo(k.lastUsedAt),\n output.formatTimeAgo(k.createdAt),\n ])\n )\n })\n\nkeysCommand\n .command('create')\n .description('Create a new API key')\n .requiredOption('--name <name>', 'Name for the key')\n .option('--type <type>', 'Key type: api or agent', 'api')\n .option('--env <env>', 'Environment: test or live', 'test')\n .action(async (options) => {\n const spinner = ora('Creating API key...').start()\n\n const response = await apiRequest<{\n id: string\n key: string\n keyPrefix: string\n name: string\n environment: string\n }>('POST', '/api/v1/keys', {\n name: options.name,\n type: options.type,\n environment: options.env.toUpperCase(),\n })\n\n if (!response.ok) {\n spinner.fail('Failed to create key')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.succeed('API key created')\n console.log()\n\n const { key, keyPrefix, name, environment } = response.data!\n\n output.keyValue({\n 'Name': name,\n 'Environment': environment.toLowerCase(),\n 'Key': key,\n })\n\n console.log()\n output.warn('Save this key now! You won\\'t be able to see it again.')\n })\n\nkeysCommand\n .command('revoke <id>')\n .description('Revoke an API key')\n .action(async (id) => {\n const spinner = ora('Revoking API key...').start()\n\n const response = await apiRequest('DELETE', `/api/v1/keys/${id}`)\n\n if (!response.ok) {\n spinner.fail('Failed to revoke key')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.succeed('API key revoked')\n })\n","import { Command } from 'commander'\nimport ora from 'ora'\nimport { apiRequest } from '../lib/api.js'\nimport * as output from '../lib/output.js'\n\ninterface Webhook {\n id: string\n url: string\n description?: string\n events: string[]\n chainId: string\n isActive: boolean\n deliveryCount: number\n failureCount: number\n lastTriggeredAt: string | null\n createdAt: string\n}\n\nconst CHAIN_NAMES: Record<string, string> = {\n 'iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq': 'VRSCTEST',\n 'i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV': 'Verus',\n}\n\nexport const webhooksCommand = new Command('webhooks')\n .description('Manage webhooks')\n\nwebhooksCommand\n .command('list')\n .description('List all webhooks')\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const spinner = ora('Fetching webhooks...').start()\n\n const response = await apiRequest<{ webhooks: Webhook[] }>('GET', '/api/v1/webhooks')\n\n if (!response.ok) {\n spinner.fail('Failed to fetch webhooks')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.stop()\n\n const webhooks = response.data?.webhooks || []\n\n if (options.json) {\n output.json(webhooks)\n return\n }\n\n if (webhooks.length === 0) {\n output.info('No webhooks found')\n output.dim('Create one with: chainvue webhooks create --url https://...')\n return\n }\n\n output.table(\n ['URL', 'CHAIN', 'EVENTS', 'STATUS', 'SUCCESS', 'LAST'],\n webhooks.map(w => {\n const successRate = w.deliveryCount > 0\n ? `${(((w.deliveryCount - w.failureCount) / w.deliveryCount) * 100).toFixed(1)}%`\n : '—'\n\n return [\n w.url.length > 40 ? w.url.slice(0, 37) + '...' : w.url,\n CHAIN_NAMES[w.chainId] || w.chainId.slice(0, 8),\n w.events.length.toString(),\n w.isActive ? 'active' : 'disabled',\n successRate,\n output.formatTimeAgo(w.lastTriggeredAt),\n ]\n })\n )\n })\n\nwebhooksCommand\n .command('create')\n .description('Create a new webhook')\n .requiredOption('--url <url>', 'Webhook endpoint URL (HTTPS)')\n .option('--description <desc>', 'Description')\n .option('--events <events>', 'Comma-separated events', 'address.received')\n .option('--chain <chain>', 'Chain ID or name', 'VRSCTEST')\n .action(async (options) => {\n const spinner = ora('Creating webhook...').start()\n\n // Resolve chain name to ID\n let chainId = options.chain\n if (options.chain === 'VRSCTEST') {\n chainId = 'iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq'\n } else if (options.chain === 'Verus') {\n chainId = 'i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV'\n }\n\n const events = options.events.split(',').map((e: string) => e.trim())\n\n const response = await apiRequest<{\n id: string\n url: string\n secret: string\n events: string[]\n }>('POST', '/api/v1/webhooks', {\n url: options.url,\n description: options.description,\n chainId,\n events,\n })\n\n if (!response.ok) {\n spinner.fail('Failed to create webhook')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.succeed('Webhook created')\n console.log()\n\n const { url, secret, events: createdEvents } = response.data!\n\n output.keyValue({\n 'URL': url,\n 'Events': createdEvents.join(', '),\n 'Secret': secret,\n })\n\n console.log()\n output.warn('Save this secret now! You won\\'t be able to see it again.')\n })\n\nwebhooksCommand\n .command('delete <id>')\n .description('Delete a webhook')\n .action(async (id) => {\n const spinner = ora('Deleting webhook...').start()\n\n const response = await apiRequest('DELETE', `/api/v1/webhooks/${id}`)\n\n if (!response.ok) {\n spinner.fail('Failed to delete webhook')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.succeed('Webhook deleted')\n })\n\nwebhooksCommand\n .command('test <id>')\n .description('Send a test event to a webhook')\n .action(async (id) => {\n const spinner = ora('Sending test event...').start()\n\n const response = await apiRequest<{\n success: boolean\n responseCode: number | null\n responseTime: number | null\n errorMessage: string | null\n }>('POST', `/api/v1/webhooks/${id}/test`)\n\n if (!response.ok) {\n spinner.fail('Failed to send test')\n output.error(response.error || 'Unknown error')\n return\n }\n\n const result = response.data!\n\n if (result.success) {\n spinner.succeed(`Test delivered (${result.responseCode}, ${result.responseTime}ms)`)\n } else {\n spinner.fail(`Test failed: ${result.errorMessage}`)\n }\n })\n","import { Command } from 'commander'\nimport { readFileSync } from 'fs'\nimport ora from 'ora'\nimport { graphqlQuery } from '../lib/api.js'\nimport * as output from '../lib/output.js'\n\nexport const queryCommand = new Command('query')\n .description('Execute a GraphQL query')\n .argument('[query]', 'GraphQL query string')\n .option('-f, --file <file>', 'Read query from file')\n .option('--json', 'Output as JSON (default)')\n .option('--pretty', 'Pretty print JSON output')\n .action(async (queryArg, options) => {\n let query: string\n\n if (options.file) {\n try {\n query = readFileSync(options.file, 'utf-8')\n } catch (err: any) {\n output.error(`Failed to read file: ${err.message}`)\n process.exit(1)\n }\n } else if (queryArg) {\n query = queryArg\n } else {\n output.error('Provide a query string or use --file')\n output.dim('Example: chainvue query \"{ blocks(limit: 5) { height hash } }\"')\n process.exit(1)\n }\n\n const spinner = ora('Executing query...').start()\n\n const response = await graphqlQuery(query)\n\n if (!response.ok) {\n spinner.fail('Query failed')\n output.error(response.error || 'Unknown error')\n\n if (response.data) {\n console.log()\n output.json(response.data)\n }\n\n process.exit(1)\n }\n\n spinner.stop()\n\n if (options.pretty) {\n console.log(JSON.stringify(response.data, null, 2))\n } else {\n console.log(JSON.stringify(response.data))\n }\n })\n","import { Command } from 'commander'\nimport ora from 'ora'\nimport chalk from 'chalk'\nimport { apiRequest } from '../lib/api.js'\nimport { getCurrentProfile, setCurrentProfile } from '../lib/config.js'\nimport * as output from '../lib/output.js'\n\ninterface Organization {\n id: string\n name: string\n slug: string\n role: string\n plan: string\n}\n\nexport const orgCommand = new Command('org')\n .description('Manage organizations')\n\norgCommand\n .command('list')\n .description('List your organizations')\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const spinner = ora('Fetching organizations...').start()\n\n const response = await apiRequest<{ organizations: Organization[] }>('GET', '/api/v1/orgs')\n\n if (!response.ok) {\n spinner.fail('Failed to fetch organizations')\n output.error(response.error || 'Unknown error')\n return\n }\n\n spinner.stop()\n\n const orgs = response.data?.organizations || []\n const currentProfile = getCurrentProfile()\n\n if (options.json) {\n output.json(orgs)\n return\n }\n\n if (orgs.length === 0) {\n output.info('No organizations found')\n return\n }\n\n console.log()\n for (const org of orgs) {\n const isCurrent = org.id === currentProfile?.orgId\n const marker = isCurrent ? chalk.green('▶ ') : ' '\n const name = isCurrent ? chalk.bold(org.name) : org.name\n\n console.log(`${marker}${name}`)\n console.log(` ID: ${org.id}`)\n console.log(` Role: ${org.role} Plan: ${org.plan}`)\n console.log()\n }\n })\n\norgCommand\n .command('switch <orgId>')\n .description('Switch to a different organization')\n .action(async (orgId) => {\n const spinner = ora('Switching organization...').start()\n\n // Fetch org details to verify access\n const response = await apiRequest<{\n id: string\n name: string\n slug: string\n }>('GET', `/api/v1/orgs/${orgId}`)\n\n if (!response.ok) {\n spinner.fail('Failed to switch organization')\n output.error(response.error || 'Organization not found or no access')\n return\n }\n\n const org = response.data!\n const currentProfile = getCurrentProfile()\n\n if (!currentProfile) {\n spinner.fail('Not logged in')\n return\n }\n\n // Update profile with new org\n setCurrentProfile('default', {\n ...currentProfile,\n orgId: org.id,\n orgName: org.name,\n })\n\n spinner.succeed(`Switched to ${org.name}`)\n })\n\norgCommand\n .command('current')\n .description('Show current organization')\n .action(async () => {\n const profile = getCurrentProfile()\n\n if (!profile) {\n output.error('Not logged in')\n process.exit(1)\n }\n\n console.log()\n output.keyValue({\n 'Organization': profile.orgName,\n 'ID': profile.orgId,\n })\n console.log()\n })\n"],"mappings":"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","names":["Command","chalk","path","chalk","Command","Command","Command","Command","Command","ora","Command","ora","Command","ora","Command","ora","Command","ora","Command","ora","Command","ora","chalk","Command","ora","chalk","Command"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@chainvue/cli",
3
+ "version": "0.1.0",
4
+ "description": "ChainVue CLI - Manage your blockchain infrastructure from the command line",
5
+ "type": "module",
6
+ "bin": {
7
+ "chainvue": "./bin/chainvue.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "typecheck": "tsc --noEmit",
17
+ "lint": "eslint src/",
18
+ "start": "node bin/chainvue.js"
19
+ },
20
+ "dependencies": {
21
+ "chalk": "^5.3.0",
22
+ "cli-table3": "^0.6.5",
23
+ "commander": "^12.1.0",
24
+ "conf": "^13.0.1",
25
+ "keytar": "^7.9.0",
26
+ "ora": "^8.1.0",
27
+ "undici": "^6.19.8"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.14.0",
31
+ "tsup": "^8.2.4",
32
+ "typescript": "^5.9.3"
33
+ },
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "keywords": [
38
+ "chainvue",
39
+ "verus",
40
+ "blockchain",
41
+ "cli",
42
+ "graphql"
43
+ ],
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/chainvue/cli.git"
47
+ },
48
+ "homepage": "https://chainvue.io",
49
+ "bugs": {
50
+ "url": "https://github.com/chainvue/cli/issues"
51
+ },
52
+ "license": "MIT"
53
+ }