@opentrust/cli 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @opentrust/cli
2
+
3
+ CLI tool to manage [OpenTrust](https://github.com/opentrust-dev/opentrust) — an open-source AI Agent Runtime Security Platform.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @opentrust/cli
9
+ ```
10
+
11
+ Or run directly with `npx`:
12
+
13
+ ```bash
14
+ npx @opentrust/cli <command>
15
+ ```
16
+
17
+ ## Prerequisites
18
+
19
+ - **Node.js** >= 18
20
+ - **pnpm** >= 9 (Dashboard uses pnpm monorepo)
21
+ - Clone the [opentrust](https://github.com/opentrust-dev/opentrust) repo first
22
+
23
+ ## Usage
24
+
25
+ Navigate to your opentrust project directory, then:
26
+
27
+ ```bash
28
+ # Install all dependencies, build, and initialize databases
29
+ opentrust setup
30
+
31
+ # Start all services (core + dashboard + gateway)
32
+ opentrust start
33
+
34
+ # Start a single service
35
+ opentrust start core
36
+
37
+ # Check status of all services
38
+ opentrust status
39
+
40
+ # Stop all services
41
+ opentrust stop
42
+
43
+ # Stop a single service
44
+ opentrust stop gateway
45
+
46
+ # View logs
47
+ opentrust logs core
48
+ opentrust logs dashboard -f # follow mode
49
+ opentrust logs gateway -n 100 # last 100 lines
50
+ ```
51
+
52
+ ## Services
53
+
54
+ | Service | Port | Description |
55
+ |---------|------|-------------|
56
+ | **Core** | 53666 | Security engine — content detection (S01-S10), behavior assessment, Agent registry |
57
+ | **Dashboard** | 53667/53668 | Management panel — Agent assets, risk graph, policy management, detection logs |
58
+ | **Gateway** | 8900 | AI security gateway — LLM API proxy with automatic PII/credential sanitization |
59
+
60
+ ## Environment Variables
61
+
62
+ | Variable | Description |
63
+ |----------|-------------|
64
+ | `OPENTRUST_HOME` | Override the project root directory (defaults to `cwd`) |
65
+
66
+ ## License
67
+
68
+ Apache-2.0
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerLogsCommand(program: Command): void;
@@ -0,0 +1,35 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import { SERVICES, getAllServiceKeys } from "../lib/process-manager.js";
4
+ export function registerLogsCommand(program) {
5
+ program
6
+ .command("logs <service>")
7
+ .description("Tail logs for a service (core, dashboard, gateway)")
8
+ .option("-n, --lines <n>", "Number of lines to show", "50")
9
+ .option("-f, --follow", "Follow log output", false)
10
+ .action(async (service, options) => {
11
+ const svc = SERVICES[service];
12
+ if (!svc) {
13
+ console.error(`Unknown service: ${service}`);
14
+ console.error(`Available: ${getAllServiceKeys().join(", ")}`);
15
+ process.exit(1);
16
+ }
17
+ if (!fs.existsSync(svc.logFile)) {
18
+ console.log(`No log file found for ${svc.name}.`);
19
+ console.log(`Start it first: opentrust start ${service}`);
20
+ return;
21
+ }
22
+ const args = ["-n", options.lines];
23
+ if (options.follow)
24
+ args.push("-f");
25
+ args.push(svc.logFile);
26
+ const tail = spawn("tail", args, { stdio: "inherit" });
27
+ tail.on("error", (err) => {
28
+ console.error(`Failed to tail logs: ${err.message}`);
29
+ });
30
+ process.on("SIGINT", () => {
31
+ tail.kill();
32
+ process.exit(0);
33
+ });
34
+ });
35
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerSetupCommand(program: Command): void;
@@ -0,0 +1,57 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import { paths } from "../lib/paths.js";
4
+ export function registerSetupCommand(program) {
5
+ program
6
+ .command("setup")
7
+ .description("Install dependencies, build, and initialize databases")
8
+ .action(async () => {
9
+ console.log("OpenTrust Setup\n");
10
+ const steps = [
11
+ {
12
+ label: "Core",
13
+ cwd: paths.core,
14
+ commands: ["npm install"],
15
+ },
16
+ {
17
+ label: "Dashboard",
18
+ cwd: paths.dashboard,
19
+ commands: [
20
+ "pnpm install",
21
+ "pnpm build",
22
+ "pnpm db:generate",
23
+ "pnpm db:migrate",
24
+ "pnpm db:seed",
25
+ ],
26
+ },
27
+ {
28
+ label: "Gateway",
29
+ cwd: paths.gateway,
30
+ commands: ["npm install"],
31
+ },
32
+ ];
33
+ for (const step of steps) {
34
+ if (!fs.existsSync(step.cwd)) {
35
+ console.log(` ⚠ ${step.label}: directory not found, skipping`);
36
+ continue;
37
+ }
38
+ console.log(`Setting up ${step.label}...`);
39
+ for (const cmd of step.commands) {
40
+ try {
41
+ console.log(` $ ${cmd}`);
42
+ execSync(cmd, { cwd: step.cwd, stdio: "inherit" });
43
+ }
44
+ catch (err) {
45
+ console.error(` Failed: ${cmd}`);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ console.log(` ${step.label} ready.\n`);
50
+ }
51
+ console.log("-------------------------------------------");
52
+ console.log("Setup complete!");
53
+ console.log("-------------------------------------------");
54
+ console.log("\nRun 'opentrust start' to launch all services.");
55
+ console.log("Or start individually: 'opentrust start core'");
56
+ });
57
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerStartCommand(program: Command): void;
@@ -0,0 +1,24 @@
1
+ import { startService, getAllServiceKeys, SERVICES } from "../lib/process-manager.js";
2
+ export function registerStartCommand(program) {
3
+ program
4
+ .command("start [service]")
5
+ .description("Start services (core, dashboard, gateway, or all)")
6
+ .action(async (service) => {
7
+ const keys = service ? [service] : getAllServiceKeys();
8
+ for (const key of keys) {
9
+ if (!SERVICES[key]) {
10
+ console.error(`Unknown service: ${key}`);
11
+ console.error(`Available: ${getAllServiceKeys().join(", ")}`);
12
+ process.exit(1);
13
+ }
14
+ }
15
+ console.log(service
16
+ ? `Starting ${SERVICES[service].name}...`
17
+ : "Starting all OpenTrust services...");
18
+ for (const key of keys) {
19
+ startService(key);
20
+ }
21
+ console.log("\nUse 'opentrust status' to check health.");
22
+ console.log("Use 'opentrust logs <service>' to view output.");
23
+ });
24
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerStatusCommand(program: Command): void;
@@ -0,0 +1,23 @@
1
+ import { getStatus, checkHealth, getAllServiceKeys, SERVICES } from "../lib/process-manager.js";
2
+ export function registerStatusCommand(program) {
3
+ program
4
+ .command("status")
5
+ .description("Show status of all OpenTrust services")
6
+ .action(async () => {
7
+ console.log("OpenTrust Service Status\n");
8
+ const keys = getAllServiceKeys();
9
+ for (const key of keys) {
10
+ const svc = SERVICES[key];
11
+ const { running, pid } = getStatus(key);
12
+ const healthy = running ? await checkHealth(key) : false;
13
+ const statusIcon = running ? (healthy ? "\x1b[32m●\x1b[0m" : "\x1b[33m●\x1b[0m") : "\x1b[31m●\x1b[0m";
14
+ const statusText = running
15
+ ? healthy
16
+ ? `running (PID ${pid})`
17
+ : `running, not healthy yet (PID ${pid})`
18
+ : "stopped";
19
+ console.log(` ${statusIcon} ${svc.name.padEnd(12)} ${statusText.padEnd(35)} port ${svc.port}`);
20
+ }
21
+ console.log("");
22
+ });
23
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerStopCommand(program: Command): void;
@@ -0,0 +1,22 @@
1
+ import { stopService, getAllServiceKeys, SERVICES } from "../lib/process-manager.js";
2
+ export function registerStopCommand(program) {
3
+ program
4
+ .command("stop [service]")
5
+ .description("Stop services (core, dashboard, gateway, or all)")
6
+ .action(async (service) => {
7
+ const keys = service ? [service] : getAllServiceKeys();
8
+ for (const key of keys) {
9
+ if (!SERVICES[key]) {
10
+ console.error(`Unknown service: ${key}`);
11
+ console.error(`Available: ${getAllServiceKeys().join(", ")}`);
12
+ process.exit(1);
13
+ }
14
+ }
15
+ console.log(service
16
+ ? `Stopping ${SERVICES[service].name}...`
17
+ : "Stopping all OpenTrust services...");
18
+ for (const key of keys) {
19
+ stopService(key);
20
+ }
21
+ });
22
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { readFileSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ import { dirname, join } from "node:path";
6
+ import { registerStartCommand } from "./commands/start.js";
7
+ import { registerStopCommand } from "./commands/stop.js";
8
+ import { registerStatusCommand } from "./commands/status.js";
9
+ import { registerSetupCommand } from "./commands/setup.js";
10
+ import { registerLogsCommand } from "./commands/logs.js";
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
13
+ const program = new Command();
14
+ program
15
+ .name("opentrust")
16
+ .description("OpenTrust — manage local AI security services")
17
+ .version(pkg.version);
18
+ registerStartCommand(program);
19
+ registerStopCommand(program);
20
+ registerStatusCommand(program);
21
+ registerSetupCommand(program);
22
+ registerLogsCommand(program);
23
+ program.parse();
@@ -0,0 +1,15 @@
1
+ export declare const paths: {
2
+ projectRoot: string;
3
+ runtime: string;
4
+ logs: string;
5
+ core: string;
6
+ dashboard: string;
7
+ gateway: string;
8
+ guards: string;
9
+ corePid: string;
10
+ dashboardPid: string;
11
+ gatewayPid: string;
12
+ coreLog: string;
13
+ dashboardLog: string;
14
+ gatewayLog: string;
15
+ };
@@ -0,0 +1,63 @@
1
+ import path from "node:path";
2
+ import os from "node:os";
3
+ import fs from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ function findProjectRoot() {
7
+ // 1. OPENTRUST_HOME env var takes priority
8
+ if (process.env.OPENTRUST_HOME) {
9
+ return path.resolve(process.env.OPENTRUST_HOME);
10
+ }
11
+ // 2. Check if cwd looks like the opentrust project root
12
+ const cwd = process.cwd();
13
+ if (isOpenTrustRoot(cwd)) {
14
+ return cwd;
15
+ }
16
+ // 3. Walk up from cwd looking for the project root
17
+ let dir = cwd;
18
+ while (true) {
19
+ const parent = path.dirname(dir);
20
+ if (parent === dir)
21
+ break;
22
+ dir = parent;
23
+ if (isOpenTrustRoot(dir))
24
+ return dir;
25
+ }
26
+ // 4. Fallback: resolve relative to __dirname (works inside monorepo)
27
+ const fallback = path.resolve(__dirname, "../../..");
28
+ if (isOpenTrustRoot(fallback)) {
29
+ return fallback;
30
+ }
31
+ // 5. Last resort: use cwd and let commands fail with meaningful errors
32
+ return cwd;
33
+ }
34
+ function isOpenTrustRoot(dir) {
35
+ try {
36
+ const pkgPath = path.join(dir, "package.json");
37
+ if (!fs.existsSync(pkgPath))
38
+ return false;
39
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
40
+ return pkg.name === "opentrust";
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ const projectRoot = findProjectRoot();
47
+ /** ~/.opentrust/ — runtime data (pid files, logs) */
48
+ const runtimeDir = path.join(os.homedir(), ".opentrust");
49
+ export const paths = {
50
+ projectRoot,
51
+ runtime: runtimeDir,
52
+ logs: path.join(runtimeDir, "logs"),
53
+ core: path.join(projectRoot, "core"),
54
+ dashboard: path.join(projectRoot, "dashboard"),
55
+ gateway: path.join(projectRoot, "gateway"),
56
+ guards: path.join(projectRoot, "guards"),
57
+ corePid: path.join(runtimeDir, "core.pid"),
58
+ dashboardPid: path.join(runtimeDir, "dashboard.pid"),
59
+ gatewayPid: path.join(runtimeDir, "gateway.pid"),
60
+ coreLog: path.join(runtimeDir, "logs", "core.log"),
61
+ dashboardLog: path.join(runtimeDir, "logs", "dashboard.log"),
62
+ gatewayLog: path.join(runtimeDir, "logs", "gateway.log"),
63
+ };
@@ -0,0 +1,19 @@
1
+ export interface ServiceDef {
2
+ name: string;
3
+ cwd: string;
4
+ command: string;
5
+ args: string[];
6
+ pidFile: string;
7
+ logFile: string;
8
+ port: number;
9
+ healthUrl?: string;
10
+ }
11
+ export declare const SERVICES: Record<string, ServiceDef>;
12
+ export declare function startService(key: string): boolean;
13
+ export declare function stopService(key: string): boolean;
14
+ export declare function getStatus(key: string): {
15
+ running: boolean;
16
+ pid?: number;
17
+ };
18
+ export declare function checkHealth(key: string): Promise<boolean>;
19
+ export declare function getAllServiceKeys(): string[];
@@ -0,0 +1,146 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import { paths } from "./paths.js";
4
+ export const SERVICES = {
5
+ core: {
6
+ name: "Core",
7
+ cwd: paths.core,
8
+ command: "npx",
9
+ args: ["tsx", "src/index.ts"],
10
+ pidFile: paths.corePid,
11
+ logFile: paths.coreLog,
12
+ port: 53666,
13
+ healthUrl: "http://localhost:53666/health",
14
+ },
15
+ dashboard: {
16
+ name: "Dashboard",
17
+ cwd: paths.dashboard,
18
+ command: "npx",
19
+ args: ["turbo", "dev"],
20
+ pidFile: paths.dashboardPid,
21
+ logFile: paths.dashboardLog,
22
+ port: 53667,
23
+ healthUrl: "http://localhost:53667/health",
24
+ },
25
+ gateway: {
26
+ name: "Gateway",
27
+ cwd: paths.gateway,
28
+ command: "npx",
29
+ args: ["tsx", "src/index.ts"],
30
+ pidFile: paths.gatewayPid,
31
+ logFile: paths.gatewayLog,
32
+ port: 8900,
33
+ healthUrl: "http://localhost:8900/health",
34
+ },
35
+ };
36
+ function ensureDirs() {
37
+ for (const dir of [paths.runtime, paths.logs]) {
38
+ if (!fs.existsSync(dir))
39
+ fs.mkdirSync(dir, { recursive: true });
40
+ }
41
+ }
42
+ export function startService(key) {
43
+ const svc = SERVICES[key];
44
+ if (!svc) {
45
+ console.error(`Unknown service: ${key}`);
46
+ return false;
47
+ }
48
+ if (!fs.existsSync(svc.cwd)) {
49
+ console.error(` ${svc.name}: directory not found at ${svc.cwd}`);
50
+ return false;
51
+ }
52
+ const status = getStatus(key);
53
+ if (status.running) {
54
+ console.log(` ${svc.name}: already running (PID ${status.pid})`);
55
+ return true;
56
+ }
57
+ ensureDirs();
58
+ const logFd = fs.openSync(svc.logFile, "a");
59
+ const child = spawn(svc.command, svc.args, {
60
+ cwd: svc.cwd,
61
+ env: { ...process.env, FORCE_COLOR: "0" },
62
+ stdio: ["ignore", logFd, logFd],
63
+ detached: true,
64
+ });
65
+ if (child.pid) {
66
+ fs.writeFileSync(svc.pidFile, String(child.pid), "utf-8");
67
+ child.unref();
68
+ console.log(` ${svc.name}: started (PID ${child.pid}, port ${svc.port})`);
69
+ fs.closeSync(logFd);
70
+ return true;
71
+ }
72
+ fs.closeSync(logFd);
73
+ console.error(` ${svc.name}: failed to start`);
74
+ return false;
75
+ }
76
+ export function stopService(key) {
77
+ const svc = SERVICES[key];
78
+ if (!svc) {
79
+ console.error(`Unknown service: ${key}`);
80
+ return false;
81
+ }
82
+ const status = getStatus(key);
83
+ if (!status.running) {
84
+ console.log(` ${svc.name}: not running`);
85
+ return false;
86
+ }
87
+ try {
88
+ // Kill the process group (negative PID) to stop all children
89
+ process.kill(-status.pid, "SIGTERM");
90
+ }
91
+ catch (err) {
92
+ try {
93
+ process.kill(status.pid, "SIGTERM");
94
+ }
95
+ catch {
96
+ // already dead
97
+ }
98
+ }
99
+ try {
100
+ fs.unlinkSync(svc.pidFile);
101
+ }
102
+ catch { }
103
+ console.log(` ${svc.name}: stopped (was PID ${status.pid})`);
104
+ return true;
105
+ }
106
+ export function getStatus(key) {
107
+ const svc = SERVICES[key];
108
+ if (!svc)
109
+ return { running: false };
110
+ if (!fs.existsSync(svc.pidFile))
111
+ return { running: false };
112
+ const pid = parseInt(fs.readFileSync(svc.pidFile, "utf-8").trim(), 10);
113
+ if (isNaN(pid)) {
114
+ try {
115
+ fs.unlinkSync(svc.pidFile);
116
+ }
117
+ catch { }
118
+ return { running: false };
119
+ }
120
+ try {
121
+ process.kill(pid, 0);
122
+ return { running: true, pid };
123
+ }
124
+ catch {
125
+ try {
126
+ fs.unlinkSync(svc.pidFile);
127
+ }
128
+ catch { }
129
+ return { running: false };
130
+ }
131
+ }
132
+ export async function checkHealth(key) {
133
+ const svc = SERVICES[key];
134
+ if (!svc?.healthUrl)
135
+ return false;
136
+ try {
137
+ const res = await fetch(svc.healthUrl);
138
+ return res.ok;
139
+ }
140
+ catch {
141
+ return false;
142
+ }
143
+ }
144
+ export function getAllServiceKeys() {
145
+ return Object.keys(SERVICES);
146
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@opentrust/cli",
3
+ "version": "7.0.0",
4
+ "description": "CLI tool to manage OpenTrust AI Agent Runtime Security Platform — setup, start, stop, status, logs",
5
+ "type": "module",
6
+ "bin": {
7
+ "opentrust": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsx src/index.ts",
15
+ "typecheck": "tsc --noEmit",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "dependencies": {
19
+ "commander": "^12.1.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "tsx": "^4.19.0",
24
+ "typescript": "^5.7.0"
25
+ },
26
+ "keywords": [
27
+ "opentrust",
28
+ "ai-security",
29
+ "agent-security",
30
+ "cli",
31
+ "pii",
32
+ "prompt-injection",
33
+ "llm-security",
34
+ "ai-agent"
35
+ ],
36
+ "homepage": "https://github.com/opentrust-dev/opentrust#readme",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/opentrust-dev/opentrust.git",
40
+ "directory": "cli"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/opentrust-dev/opentrust/issues"
44
+ },
45
+ "author": "OpenTrust",
46
+ "license": "Apache-2.0",
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ }
50
+ }