xtra-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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/dist/bin/xtra.js +124 -0
  4. package/dist/commands/access.js +107 -0
  5. package/dist/commands/admin.js +118 -0
  6. package/dist/commands/audit.js +67 -0
  7. package/dist/commands/branch.js +216 -0
  8. package/dist/commands/checkout.js +74 -0
  9. package/dist/commands/ci.js +330 -0
  10. package/dist/commands/completion.js +227 -0
  11. package/dist/commands/diff.js +163 -0
  12. package/dist/commands/doctor.js +176 -0
  13. package/dist/commands/env.js +70 -0
  14. package/dist/commands/export.js +84 -0
  15. package/dist/commands/generate.js +180 -0
  16. package/dist/commands/history.js +77 -0
  17. package/dist/commands/import.js +122 -0
  18. package/dist/commands/init.js +162 -0
  19. package/dist/commands/integration.js +188 -0
  20. package/dist/commands/local.js +198 -0
  21. package/dist/commands/login.js +176 -0
  22. package/dist/commands/login.test.js +51 -0
  23. package/dist/commands/logs.js +121 -0
  24. package/dist/commands/profile.js +184 -0
  25. package/dist/commands/project.js +98 -0
  26. package/dist/commands/rollback.js +96 -0
  27. package/dist/commands/rotate.js +94 -0
  28. package/dist/commands/run.js +215 -0
  29. package/dist/commands/scan.js +127 -0
  30. package/dist/commands/secrets.js +265 -0
  31. package/dist/commands/simulate.js +92 -0
  32. package/dist/commands/status.js +94 -0
  33. package/dist/commands/template.js +276 -0
  34. package/dist/commands/ui.js +218 -0
  35. package/dist/commands/watch.js +121 -0
  36. package/dist/lib/api.js +172 -0
  37. package/dist/lib/api.test.js +89 -0
  38. package/dist/lib/audit.js +136 -0
  39. package/dist/lib/config.js +42 -0
  40. package/dist/lib/config.test.js +47 -0
  41. package/dist/lib/crypto.js +50 -0
  42. package/dist/lib/manifest.js +52 -0
  43. package/dist/lib/profiles.js +103 -0
  44. package/package.json +67 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 XtraSecurity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # XtraSync CLI
2
+
3
+ **Secure Runtime Secret Injection**
4
+
5
+ XtraSync CLI is a powerful command-line tool designed to seamlessly and securely inject environment variables into your application at runtime. Say goodbye to scattered `.env` files and leaked secrets—XtraSync brings central secret management directly to your local development and CI/CD pipelines.
6
+
7
+ ## Installation
8
+
9
+ Install the CLI globally via npm:
10
+
11
+ ```bash
12
+ npm install -g xtra-cli
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ 1. **Login and Authenticate:**
18
+ Authenticate your machine with the XtraSecurity platform. You can log in via Single Sign-On (SSO), email, or an Access Key.
19
+ ```bash
20
+ xtra login
21
+ ```
22
+
23
+ 2. **Initialize a Project:**
24
+ Bootstrap your project by creating an `.xtrarc` configuration file.
25
+ ```bash
26
+ xtra init
27
+ ```
28
+
29
+ 3. **Run your Application securely:**
30
+ Inject secrets directly into your running process without ever touching a disk.
31
+ ```bash
32
+ xtra run npm start
33
+ ```
34
+
35
+ ## Core Commands
36
+
37
+ ### `xtra run [command]`
38
+ Run a command with secrets injected at runtime.
39
+ ```bash
40
+ # Example: Inject production secrets and run the build script
41
+ xtra run -e production npm run build
42
+ ```
43
+
44
+ ### `xtra secrets list`
45
+ List all secrets available for a given project and environment.
46
+ ```bash
47
+ xtra secrets list -e staging
48
+ ```
49
+
50
+ ### `xtra secrets set [KEY=VALUE...]`
51
+ Set one or more secrets.
52
+ ```bash
53
+ xtra secrets set API_KEY=123x DB_PASS=secret
54
+ ```
55
+
56
+ ### `xtra env clone`
57
+ Clone secrets from one environment to another.
58
+ ```bash
59
+ xtra env clone --from development --to production
60
+ ```
61
+
62
+ ### `xtra local sync`
63
+ Pull cloud secrets to a `.env.local` file for offline development.
64
+ ```bash
65
+ xtra local sync
66
+ ```
67
+
68
+ ## Advanced Usage
69
+
70
+ - **CI/CD Integration (`xtra ci`)**: Run headless operations in your pipelines.
71
+ - **Auditing (`xtra logs`)**: View your local audit trails and access history.
72
+ - **Profiles (`xtra profile`)**: Manage multiple connection setups and environments.
73
+ - **TUI Dashboard (`xtra ui`)**: Launch an interactive terminal user interface to manage secrets visually.
74
+
75
+ ## Help & Documentation
76
+
77
+ For a complete list of commands and options, run:
78
+
79
+ ```bash
80
+ xtra --help
81
+ ```
82
+
83
+ To see help for a specific command, use:
84
+
85
+ ```bash
86
+ xtra <command> --help
87
+ ```
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commander_1 = require("commander");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const login_1 = require("../commands/login");
41
+ const run_1 = require("../commands/run");
42
+ const secrets_1 = require("../commands/secrets");
43
+ const generate_1 = require("../commands/generate");
44
+ const status_1 = require("../commands/status");
45
+ const diff_1 = require("../commands/diff");
46
+ const rollback_1 = require("../commands/rollback");
47
+ const scan_1 = require("../commands/scan");
48
+ const logs_1 = require("../commands/logs");
49
+ const export_1 = require("../commands/export");
50
+ const import_1 = require("../commands/import");
51
+ const rotate_1 = require("../commands/rotate");
52
+ const audit_1 = require("../commands/audit");
53
+ const access_1 = require("../commands/access");
54
+ const branch_1 = require("../commands/branch");
55
+ const checkout_1 = require("../commands/checkout");
56
+ const project_1 = require("../commands/project");
57
+ const admin_1 = require("../commands/admin");
58
+ const integration_1 = require("../commands/integration");
59
+ const history_1 = require("../commands/history");
60
+ const env_1 = require("../commands/env");
61
+ const ui_1 = require("../commands/ui");
62
+ const completion_1 = require("../commands/completion");
63
+ const profile_1 = require("../commands/profile");
64
+ const profiles_1 = require("../lib/profiles");
65
+ const ci_1 = require("../commands/ci");
66
+ const template_1 = require("../commands/template");
67
+ const doctor_1 = require("../commands/doctor");
68
+ const init_1 = require("../commands/init");
69
+ const watch_1 = require("../commands/watch");
70
+ const simulate_1 = require("../commands/simulate");
71
+ const local_1 = require("../commands/local");
72
+ const program = new commander_1.Command();
73
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), "utf-8"));
74
+ program
75
+ .name("xtra")
76
+ .description("XtraSync CLI - Secure Runtime Secret Injection")
77
+ .version(pkg.version)
78
+ .option("--profile <name>", "Use a specific named profile for this command");
79
+ // Apply profile before any subcommand runs
80
+ program.hook("preSubcommand", (thisCommand) => {
81
+ const profileName = thisCommand.opts().profile;
82
+ if (profileName) {
83
+ try {
84
+ (0, profiles_1.useProfile)(profileName);
85
+ }
86
+ catch (e) {
87
+ console.error(`\x1b[31mError: ${e.message}\x1b[0m`);
88
+ process.exit(1);
89
+ }
90
+ }
91
+ });
92
+ program.addCommand(login_1.loginCommand);
93
+ program.addCommand(run_1.runCommand);
94
+ program.addCommand(secrets_1.secretsCommand);
95
+ program.addCommand(generate_1.generateCommand);
96
+ program.addCommand(status_1.statusCommand);
97
+ program.addCommand(diff_1.diffCommand);
98
+ program.addCommand(rollback_1.rollbackCommand);
99
+ program.addCommand(scan_1.scanCommand);
100
+ program.addCommand(logs_1.logsCommand);
101
+ program.addCommand(export_1.exportCommand);
102
+ program.addCommand(import_1.importCommand);
103
+ program.addCommand(rotate_1.rotateCommand);
104
+ program.addCommand(audit_1.auditCommand);
105
+ program.addCommand(access_1.accessCommand);
106
+ program.addCommand(branch_1.branchCommand);
107
+ program.addCommand(checkout_1.checkoutCommand);
108
+ program.addCommand(project_1.projectCommand);
109
+ program.addCommand(admin_1.adminCommand);
110
+ program.addCommand(integration_1.integrationCommand);
111
+ program.addCommand(integration_1.kubernetesCommand);
112
+ program.addCommand(history_1.historyCommand);
113
+ program.addCommand(env_1.envCommand);
114
+ program.addCommand(ui_1.uiCommand);
115
+ program.addCommand(completion_1.completionCommand);
116
+ program.addCommand(profile_1.profileCommand);
117
+ program.addCommand(ci_1.ciCommand);
118
+ program.addCommand(template_1.templateCommand);
119
+ program.addCommand(doctor_1.doctorCommand);
120
+ program.addCommand(init_1.initCommand);
121
+ program.addCommand(watch_1.watchCommand);
122
+ program.addCommand(simulate_1.simulateCommand);
123
+ program.addCommand(local_1.localCommand);
124
+ program.parse(process.argv);
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.accessCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const api_1 = require("../lib/api");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const table_1 = require("table");
12
+ const config_1 = require("../lib/config");
13
+ const logAuditSafe = (event, projectId, meta) => {
14
+ try {
15
+ const { logAudit } = require("../lib/audit");
16
+ logAudit(event, projectId, null, meta);
17
+ }
18
+ catch (e) { }
19
+ };
20
+ const safeError = (error) => error?.response?.data?.error || error?.message || "Unknown error";
21
+ exports.accessCommand = new commander_1.Command("access")
22
+ .description("Manage Just-in-Time access requests")
23
+ .addCommand(new commander_1.Command("request")
24
+ .description("Request access to a secret or project")
25
+ .option("-p, --project <projectId>", "Project ID")
26
+ .option("-s, --secret <secretId>", "Specific Secret ID (optional)")
27
+ .requiredOption("-d, --duration <minutes>", "Duration in minutes")
28
+ .requiredOption("-r, --reason <text>", "Reason for access")
29
+ .action(async (options) => {
30
+ let { project, secret, duration, reason } = options;
31
+ if (!project) {
32
+ project = (0, config_1.getConfigValue)("project");
33
+ }
34
+ if (!project) {
35
+ console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
36
+ process.exit(1);
37
+ }
38
+ const spinner = (0, ora_1.default)("Submitting access request...").start();
39
+ try {
40
+ const result = await api_1.api.requestAccess(project, reason, parseInt(duration), secret);
41
+ spinner.succeed(chalk_1.default.green(`Access request submitted! (ID: ${result.requestId})`));
42
+ console.log(chalk_1.default.yellow(`Status: ${result.status}`));
43
+ // Audit log
44
+ logAuditSafe("ACCESS_REQUESTED", project, { requestId: result.requestId, secretId: secret, duration, reason });
45
+ }
46
+ catch (error) {
47
+ spinner.fail("Request failed");
48
+ console.error(chalk_1.default.red(safeError(error)));
49
+ }
50
+ }))
51
+ .addCommand(new commander_1.Command("list")
52
+ .description("List access requests")
53
+ .option("--pending", "Show pending approvals (for admins)", false)
54
+ .action(async (options) => {
55
+ try {
56
+ const mode = options.pending ? "pending" : "my";
57
+ const requests = await api_1.api.listAccessRequests(mode);
58
+ if (requests.length === 0) {
59
+ console.log(chalk_1.default.yellow("No requests found."));
60
+ return;
61
+ }
62
+ const data = [
63
+ [chalk_1.default.bold("ID"), chalk_1.default.bold("User"), chalk_1.default.bold("Target"), chalk_1.default.bold("Duration"), chalk_1.default.bold("Reason"), chalk_1.default.bold("Status")]
64
+ ];
65
+ requests.forEach((r) => {
66
+ const target = r.secret ? `Secret: ${r.secret.key}` : `Project: ${r.projectId}`;
67
+ data.push([
68
+ r.id,
69
+ r.user.email,
70
+ target,
71
+ `${r.duration}m`,
72
+ r.reason,
73
+ r.status === "pending" ? chalk_1.default.yellow(r.status) : r.status === "approved" ? chalk_1.default.green(r.status) : chalk_1.default.red(r.status)
74
+ ]);
75
+ });
76
+ console.log((0, table_1.table)(data));
77
+ }
78
+ catch (error) {
79
+ console.error(chalk_1.default.red("Failed to list requests: " + safeError(error)));
80
+ }
81
+ }))
82
+ .addCommand(new commander_1.Command("approve")
83
+ .description("Approve or reject a request")
84
+ .argument("<requestId>", "Request ID")
85
+ .requiredOption("--decision <decision>", "approved or rejected")
86
+ .action(async (requestId, options) => {
87
+ // Validate decision value
88
+ const validDecisions = ["approved", "rejected"];
89
+ if (!validDecisions.includes(options.decision.toLowerCase())) {
90
+ console.error(chalk_1.default.red(`Invalid decision: '${options.decision}'. Must be 'approved' or 'rejected'.`));
91
+ process.exit(1);
92
+ }
93
+ const spinner = (0, ora_1.default)("Processing decision...").start();
94
+ try {
95
+ const result = await api_1.api.approveAccess(requestId, options.decision);
96
+ spinner.succeed(chalk_1.default.green(`Request ${options.decision}!`));
97
+ if (result.expiresAt) {
98
+ console.log(chalk_1.default.blue(`Access granted until: ${new Date(result.expiresAt).toLocaleString()}`));
99
+ }
100
+ // Audit log — privileged action
101
+ logAuditSafe(options.decision === "approved" ? "ACCESS_APPROVED" : "ACCESS_REJECTED", null, { requestId, decision: options.decision, expiresAt: result.expiresAt });
102
+ }
103
+ catch (error) {
104
+ spinner.fail("Operation failed");
105
+ console.error(chalk_1.default.red(safeError(error)));
106
+ }
107
+ }));
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.adminCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const api_1 = require("../lib/api");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const table_1 = require("table");
12
+ exports.adminCommand = new commander_1.Command("admin")
13
+ .description("Admin commands for user and role management");
14
+ // Role Management
15
+ const roleCommand = new commander_1.Command("role")
16
+ .description("Manage user roles and permissions");
17
+ const listRoles = async () => {
18
+ const spinner = (0, ora_1.default)("Fetching roles from server...").start();
19
+ try {
20
+ const roles = await api_1.api.getRoles();
21
+ spinner.stop();
22
+ console.log(chalk_1.default.bold("\nAvailable Roles:\n"));
23
+ const tableData = [
24
+ [chalk_1.default.bold("Role"), chalk_1.default.bold("Description"), chalk_1.default.bold("Permissions")]
25
+ ];
26
+ roles.forEach((role) => {
27
+ tableData.push([
28
+ chalk_1.default.cyan(role.name),
29
+ role.description || "-",
30
+ Array.isArray(role.permissions) ? role.permissions.join(", ") : role.permissions || "-",
31
+ ]);
32
+ });
33
+ console.log((0, table_1.table)(tableData));
34
+ }
35
+ catch (error) {
36
+ spinner.fail("Failed to fetch roles");
37
+ const safeErr = error.response?.data?.error || error.message || "Unknown error";
38
+ console.error(chalk_1.default.red(safeErr));
39
+ }
40
+ };
41
+ roleCommand
42
+ .command("list")
43
+ .description("List all available roles")
44
+ .alias("ls")
45
+ .action(listRoles);
46
+ exports.adminCommand.addCommand(roleCommand);
47
+ // Legacy/Short support: `xtra admin roles` -> alias to `xtra admin role list`
48
+ exports.adminCommand
49
+ .command("roles")
50
+ .description("Alias for 'role list'")
51
+ .action(listRoles);
52
+ // List all users (admin only)
53
+ exports.adminCommand
54
+ .command("users")
55
+ .description("List all users with their roles")
56
+ .option("-t, --team <teamId>", "Filter by team ID")
57
+ .action(async (options) => {
58
+ const spinner = (0, ora_1.default)("Fetching users...").start();
59
+ try {
60
+ const users = await api_1.api.getUsers(options.team);
61
+ spinner.stop();
62
+ if (!users || users.length === 0) {
63
+ console.log(chalk_1.default.yellow("No users found."));
64
+ return;
65
+ }
66
+ console.log(chalk_1.default.bold("\nUsers:\n"));
67
+ const tableData = [
68
+ [chalk_1.default.bold("Email"), chalk_1.default.bold("Name"), chalk_1.default.bold("Role"), chalk_1.default.bold("Status")]
69
+ ];
70
+ users.forEach((user) => {
71
+ const roleColor = user.role === "owner" || user.role === "admin"
72
+ ? chalk_1.default.magenta
73
+ : user.role === "viewer"
74
+ ? chalk_1.default.gray
75
+ : chalk_1.default.cyan;
76
+ tableData.push([
77
+ user.email,
78
+ user.name || "-",
79
+ roleColor(user.role),
80
+ user.status || "active"
81
+ ]);
82
+ });
83
+ console.log((0, table_1.table)(tableData));
84
+ }
85
+ catch (error) {
86
+ spinner.fail("Failed to fetch users");
87
+ console.error(chalk_1.default.red(error.message));
88
+ }
89
+ });
90
+ // Set user role
91
+ exports.adminCommand
92
+ .command("set-role <email> <role>")
93
+ .description("Change a user's role (owner, admin, developer, viewer, guest)")
94
+ .option("-t, --team <teamId>", "Team ID (for team-specific role)")
95
+ .action(async (email, role, options) => {
96
+ const validRoles = ["owner", "admin", "developer", "viewer", "guest"];
97
+ if (!validRoles.includes(role.toLowerCase())) {
98
+ console.error(chalk_1.default.red(`Invalid role: ${role}`));
99
+ console.log(chalk_1.default.yellow(`Valid roles: ${validRoles.join(", ")}`));
100
+ process.exit(1);
101
+ }
102
+ const spinner = (0, ora_1.default)(`Updating role for ${email}...`).start();
103
+ try {
104
+ await api_1.api.setUserRole(email, role.toLowerCase(), options.team);
105
+ spinner.succeed(chalk_1.default.green(`Role updated: ${email} → ${role}`));
106
+ // Audit log this privileged action
107
+ try {
108
+ const { logAudit } = require("../lib/audit");
109
+ logAudit("ADMIN_ROLE_CHANGE", null, null, { email, newRole: role, teamId: options.team });
110
+ }
111
+ catch (e) { }
112
+ }
113
+ catch (error) {
114
+ spinner.fail("Failed to update role");
115
+ const safeErr = error.response?.data?.error || error.message || "Unknown error";
116
+ console.error(chalk_1.default.red(safeErr));
117
+ }
118
+ });
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.auditCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const api_1 = require("../lib/api");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ exports.auditCommand = new commander_1.Command("audit")
13
+ .description("Manage and verify audit logs")
14
+ // Subcommand: Verify
15
+ .addCommand(new commander_1.Command("verify")
16
+ .description("Verify the integrity of the audit log chain (Tamper-Evident)")
17
+ .action(async () => {
18
+ const spinner = (0, ora_1.default)("Verifying audit chain integrity...").start();
19
+ try {
20
+ const result = await api_1.api.verifyAuditLogs();
21
+ if (result.valid) {
22
+ spinner.succeed(chalk_1.default.green(result.message));
23
+ }
24
+ else {
25
+ spinner.fail(chalk_1.default.red("Verification Failed!"));
26
+ console.error(chalk_1.default.red(`Broken at Log ID: ${result.details?.brokenAtId}`));
27
+ console.error(chalk_1.default.red(`Reason: ${result.details?.reason}`));
28
+ }
29
+ }
30
+ catch (error) {
31
+ spinner.fail("Verification failed");
32
+ if (error.response?.data?.details) {
33
+ console.error(chalk_1.default.red(`Chain Broken: ${error.response.data.message}`));
34
+ console.error(chalk_1.default.yellow(`Details: ${JSON.stringify(error.response.data.details, null, 2)}`));
35
+ }
36
+ else {
37
+ console.error(chalk_1.default.red(error.message));
38
+ }
39
+ }
40
+ }))
41
+ // Subcommand: Export
42
+ .addCommand(new commander_1.Command("export")
43
+ .description("Export audit logs for compliance (SOC2/ISO)")
44
+ .option("-f, --format <format>", "Output format (json, csv)", "json")
45
+ .option("--start <date>", "Start date (YYYY-MM-DD)")
46
+ .option("--end <date>", "End date (YYYY-MM-DD)")
47
+ .option("-o, --output <file>", "Output file path")
48
+ .action(async (options) => {
49
+ const { format, start, end, output } = options;
50
+ const spinner = (0, ora_1.default)(`Exporting audit logs (${format})...`).start();
51
+ try {
52
+ const data = await api_1.api.exportAuditLogs(format, start, end);
53
+ const content = typeof data === 'object' ? JSON.stringify(data, null, 2) : data;
54
+ if (output) {
55
+ fs_1.default.writeFileSync(output, content);
56
+ spinner.succeed(chalk_1.default.green(`Export saved to ${output}`));
57
+ }
58
+ else {
59
+ spinner.stop();
60
+ console.log(content);
61
+ }
62
+ }
63
+ catch (error) {
64
+ spinner.fail("Export failed");
65
+ console.error(chalk_1.default.red(error.message));
66
+ }
67
+ }));