@tameflare/cli 0.8.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,83 @@
1
+ # @tameflare/cli
2
+
3
+ The official CLI for [TameFlare](https://tameflare.com) — secure and govern AI agent traffic through a transparent proxy gateway.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -g @tameflare/cli
9
+ ```
10
+
11
+ Or run directly with npx:
12
+
13
+ ```bash
14
+ npx @tameflare/cli init
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # 1. Initialize TameFlare in your project
21
+ tf init
22
+
23
+ # 2. Add a connector (e.g. GitHub)
24
+ tf connector add github --token ghp_xxx
25
+
26
+ # 3. Run your agent through the gateway
27
+ tf run --name my-agent -- python agent.py
28
+ ```
29
+
30
+ ## Commands
31
+
32
+ | Command | Description |
33
+ |---------|-------------|
34
+ | `tf init` | Initialize TameFlare — downloads gateway, creates config |
35
+ | `tf run --name <n> <cmd>` | Run a process through the proxy gateway |
36
+ | `tf status` | Show gateway status and active processes |
37
+ | `tf stop` | Stop the gateway |
38
+ | `tf logs` | View recent traffic logs |
39
+ | `tf kill-switch` | Emergency stop — block all traffic |
40
+ | `tf connector add <type>` | Add a connector (github, openai, slack, stripe, generic) |
41
+ | `tf connector list` | List active connectors |
42
+ | `tf connector remove <id>` | Remove a connector |
43
+ | `tf permissions set` | Set access rules for a gateway |
44
+ | `tf permissions list` | List access rules |
45
+ | `tf approvals list` | List pending approval requests |
46
+ | `tf approvals approve <id>` | Approve a pending request |
47
+ | `tf approvals deny <id>` | Deny a pending request |
48
+
49
+ ## How It Works
50
+
51
+ TameFlare acts as a transparent HTTP/HTTPS proxy between your AI agent and external APIs. When you run `tf run`, the CLI:
52
+
53
+ 1. Starts (or connects to) the TameFlare gateway
54
+ 2. Registers your process and gets a dedicated proxy port
55
+ 3. Sets `HTTP_PROXY` and `HTTPS_PROXY` env vars on your process
56
+ 4. All outbound traffic flows through the gateway for inspection and enforcement
57
+
58
+ The gateway matches requests against connectors (GitHub, OpenAI, Stripe, etc.), applies permission rules, and either allows, denies, or holds requests for human approval.
59
+
60
+ ## Requirements
61
+
62
+ - Node.js >= 18
63
+ - The TameFlare gateway binary (downloaded automatically by `tf init`)
64
+
65
+ ## Dashboard
66
+
67
+ TameFlare includes a web dashboard for real-time traffic monitoring, policy management, and approval workflows. Start it with:
68
+
69
+ ```bash
70
+ docker compose up
71
+ ```
72
+
73
+ Then open [http://localhost:3000](http://localhost:3000).
74
+
75
+ ## Links
76
+
77
+ - [Documentation](https://tameflare.com/docs)
78
+ - [GitHub](https://github.com/agentfirewall/agentfirewall)
79
+ - [Website](https://tameflare.com)
80
+
81
+ ## License
82
+
83
+ [ELv2](https://github.com/agentfirewall/agentfirewall/blob/main/LICENSE)
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function actionsCommand(): Command;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.actionsCommand = actionsCommand;
4
+ const commander_1 = require("commander");
5
+ const utils_1 = require("../utils");
6
+ function actionsCommand() {
7
+ const cmd = new commander_1.Command("actions").description("View action requests");
8
+ cmd
9
+ .command("list")
10
+ .description("List recent action requests")
11
+ .option("--limit <n>", "Number of results", "20")
12
+ .action(async (opts) => {
13
+ const data = await (0, utils_1.apiRequest)("GET", `/v1/audit/events?limit=${opts.limit}`);
14
+ const events = (data.events ?? []);
15
+ const actionEvents = events.filter((e) => e.type?.startsWith("action."));
16
+ if (actionEvents.length === 0) {
17
+ console.log("No action events found.");
18
+ return;
19
+ }
20
+ console.log((0, utils_1.formatTable)(["Time", "Type", "Agent", "Action"], actionEvents.map((e) => [
21
+ e.timestamp ? new Date(e.timestamp).toLocaleString() : "—",
22
+ e.type,
23
+ e.agent_id ? e.agent_id.slice(0, 12) + "..." : "—",
24
+ e.action_type ?? "—",
25
+ ])));
26
+ });
27
+ cmd
28
+ .command("get")
29
+ .description("Get action request details")
30
+ .requiredOption("--id <id>", "Action request ID")
31
+ .action(async (opts) => {
32
+ const data = await (0, utils_1.apiRequest)("GET", `/v1/actions/${opts.id}`);
33
+ console.log(`ID: ${data.action_request_id}`);
34
+ console.log(`Status: ${data.status}`);
35
+ const decision = data.decision;
36
+ if (decision) {
37
+ console.log(`Outcome: ${decision.outcome}`);
38
+ console.log(`Reason: ${decision.reason}`);
39
+ console.log(`Risk: ${decision.risk_score}`);
40
+ }
41
+ if (data.decision_token) {
42
+ console.log(`Token: ${String(data.decision_token).slice(0, 20)}...`);
43
+ }
44
+ const approval = data.approval;
45
+ if (approval) {
46
+ console.log(`Approval: ${approval.status}`);
47
+ }
48
+ });
49
+ return cmd;
50
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function agentsCommand(): Command;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentsCommand = agentsCommand;
4
+ const commander_1 = require("commander");
5
+ const utils_1 = require("../utils");
6
+ function agentsCommand() {
7
+ const cmd = new commander_1.Command("agents").description("Manage agents");
8
+ cmd
9
+ .command("list")
10
+ .description("List registered agents")
11
+ .action(async () => {
12
+ const data = await (0, utils_1.apiRequest)("GET", "/v1/agents");
13
+ const agents = (data.agents ?? []);
14
+ if (agents.length === 0) {
15
+ console.log("No agents registered.");
16
+ return;
17
+ }
18
+ console.log((0, utils_1.formatTable)(["Name", "ID", "Environment", "Status", "Last Seen"], agents.map((a) => [
19
+ a.name,
20
+ a.id?.slice(0, 12) + "...",
21
+ a.environment,
22
+ a.status,
23
+ a.last_seen_at ?? "Never",
24
+ ])));
25
+ });
26
+ cmd
27
+ .command("register")
28
+ .description("Register a new agent")
29
+ .requiredOption("--name <name>", "Agent name")
30
+ .option("--runtime <runtime>", "Runtime (e.g. node, python)")
31
+ .option("--environment <env>", "Environment", "development")
32
+ .action(async (opts) => {
33
+ const data = await (0, utils_1.apiRequest)("POST", "/v1/agents/register", {
34
+ name: opts.name,
35
+ runtime: opts.runtime,
36
+ environment: opts.environment,
37
+ });
38
+ console.log("Agent registered successfully!");
39
+ console.log(` ID: ${data.agent_id}`);
40
+ console.log(` Prefix: ${data.api_key_prefix}`);
41
+ console.log(` Key: ${data.api_key}`);
42
+ console.log("");
43
+ console.log("Store this API key securely. It will not be shown again.");
44
+ });
45
+ return cmd;
46
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function approvalsCommand(): Command;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.approvalsCommand = approvalsCommand;
4
+ const commander_1 = require("commander");
5
+ const GATEWAY_URL = "http://127.0.0.1:9443";
6
+ function approvalsCommand() {
7
+ const cmd = new commander_1.Command("approvals")
8
+ .description("Manage pending approval requests");
9
+ cmd
10
+ .command("list")
11
+ .description("List pending approval requests")
12
+ .option("--all", "Show all recent approvals (not just pending)")
13
+ .action(async (opts) => {
14
+ try {
15
+ const url = opts.all
16
+ ? `${GATEWAY_URL}/internal/approvals/recent`
17
+ : `${GATEWAY_URL}/internal/approvals`;
18
+ const res = await fetch(url);
19
+ if (!res.ok) {
20
+ console.error(`Error: ${res.statusText}`);
21
+ process.exit(1);
22
+ }
23
+ const data = await res.json();
24
+ const items = opts.all ? data : data.pending;
25
+ if (!items || items.length === 0) {
26
+ console.log(opts.all ? "No recent approvals." : "No pending approvals.");
27
+ return;
28
+ }
29
+ if (!opts.all) {
30
+ console.log(`\n${data.count} pending approval(s):\n`);
31
+ }
32
+ for (const req of items) {
33
+ const status = req.status === "pending"
34
+ ? "\x1b[33m⏳ pending\x1b[0m"
35
+ : req.status === "approved"
36
+ ? "\x1b[32m✓ approved\x1b[0m"
37
+ : req.status === "denied"
38
+ ? "\x1b[31m✗ denied\x1b[0m"
39
+ : `\x1b[90m${req.status}\x1b[0m`;
40
+ console.log(` ${status} ${req.id}`);
41
+ console.log(` Gateway: ${req.gateway_name}`);
42
+ console.log(` Action: ${req.action_type} (${req.connector_type})`);
43
+ console.log(` Request: ${req.method} ${req.url}`);
44
+ console.log(` Created: ${req.created_at}`);
45
+ if (req.status === "pending") {
46
+ console.log(` Expires: ${req.expires_at}`);
47
+ }
48
+ if (req.responded_by) {
49
+ console.log(` By: ${req.responded_by}`);
50
+ }
51
+ if (req.response_note) {
52
+ console.log(` Note: ${req.response_note}`);
53
+ }
54
+ console.log();
55
+ }
56
+ }
57
+ catch (err) {
58
+ console.error("Failed to connect to gateway. Is it running?");
59
+ console.error(` Run: tf init`);
60
+ process.exit(1);
61
+ }
62
+ });
63
+ cmd
64
+ .command("approve <id>")
65
+ .description("Approve a pending request")
66
+ .option("--by <name>", "Who is approving", "cli-user")
67
+ .option("--note <text>", "Optional note")
68
+ .action(async (id, opts) => {
69
+ try {
70
+ const res = await fetch(`${GATEWAY_URL}/internal/approvals/respond`, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify({
74
+ id,
75
+ approved: true,
76
+ by: opts.by,
77
+ note: opts.note ?? "",
78
+ }),
79
+ });
80
+ if (!res.ok) {
81
+ const text = await res.text();
82
+ console.error(`Error: ${text}`);
83
+ process.exit(1);
84
+ }
85
+ console.log(`\x1b[32m✓\x1b[0m Approved: ${id}`);
86
+ }
87
+ catch (err) {
88
+ console.error("Failed to connect to gateway. Is it running?");
89
+ process.exit(1);
90
+ }
91
+ });
92
+ cmd
93
+ .command("deny <id>")
94
+ .description("Deny a pending request")
95
+ .option("--by <name>", "Who is denying", "cli-user")
96
+ .option("--note <text>", "Reason for denial")
97
+ .action(async (id, opts) => {
98
+ try {
99
+ const res = await fetch(`${GATEWAY_URL}/internal/approvals/respond`, {
100
+ method: "POST",
101
+ headers: { "Content-Type": "application/json" },
102
+ body: JSON.stringify({
103
+ id,
104
+ approved: false,
105
+ by: opts.by,
106
+ note: opts.note ?? "",
107
+ }),
108
+ });
109
+ if (!res.ok) {
110
+ const text = await res.text();
111
+ console.error(`Error: ${text}`);
112
+ process.exit(1);
113
+ }
114
+ console.log(`\x1b[31m✗\x1b[0m Denied: ${id}`);
115
+ }
116
+ catch (err) {
117
+ console.error("Failed to connect to gateway. Is it running?");
118
+ process.exit(1);
119
+ }
120
+ });
121
+ return cmd;
122
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function connectorCommand(): Command;
3
+ export declare function permissionsCommand(): Command;
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.connectorCommand = connectorCommand;
37
+ exports.permissionsCommand = permissionsCommand;
38
+ const commander_1 = require("commander");
39
+ const readline = __importStar(require("readline"));
40
+ const utils_1 = require("../utils");
41
+ function connectorCommand() {
42
+ const cmd = new commander_1.Command("connector")
43
+ .description("Manage connectors (API integrations)");
44
+ // tf connector add <type>
45
+ cmd
46
+ .command("add <type>")
47
+ .description("Add a connector (github, generic)")
48
+ .option("--token <token>", "API token or credential (visible in shell history — prefer --token-stdin or --token-env)")
49
+ .option("--token-stdin", "Read token from stdin (avoids shell history exposure)")
50
+ .option("--token-env <var>", "Read token from environment variable")
51
+ .option("--name <name>", "Display name")
52
+ .option("--domains <domains>", "Comma-separated domains (for generic type)")
53
+ .option("--auth-type <type>", "Auth type: bearer, api_key, basic, none", "bearer")
54
+ .option("--auth-header <header>", "Custom auth header name (for api_key type)")
55
+ .action(async (type, opts) => {
56
+ (0, utils_1.requireGateway)();
57
+ let token = opts.token;
58
+ if (opts.tokenEnv) {
59
+ token = process.env[opts.tokenEnv];
60
+ if (!token) {
61
+ console.error(`[TF] Environment variable ${opts.tokenEnv} is not set.`);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ else if (opts.tokenStdin) {
66
+ token = await readStdin();
67
+ if (!token) {
68
+ console.error("[TF] No token received from stdin.");
69
+ process.exit(1);
70
+ }
71
+ }
72
+ else if (!token) {
73
+ token = await prompt(`Paste your ${type} token: `);
74
+ }
75
+ const body = {
76
+ type,
77
+ display_name: opts.name || type,
78
+ token,
79
+ };
80
+ if (type === "generic") {
81
+ if (!opts.domains) {
82
+ const domainsStr = await prompt("Domains (comma-separated): ");
83
+ body.domains = domainsStr.split(",").map((d) => d.trim());
84
+ }
85
+ else {
86
+ body.domains = opts.domains.split(",").map((d) => d.trim());
87
+ }
88
+ body.auth_type = opts.authType;
89
+ if (opts.authHeader) {
90
+ body.auth_header = opts.authHeader;
91
+ }
92
+ }
93
+ try {
94
+ const result = await (0, utils_1.gatewayRequest)("POST", "/internal/connectors/add", body);
95
+ console.log(`[TF] Connector added: ${result.name} (${result.type})`);
96
+ console.log(` ID: ${result.id}`);
97
+ console.log(` Domains: ${result.domains.join(", ")}`);
98
+ }
99
+ catch (err) {
100
+ console.error(`[TF] Failed: ${err.message}`);
101
+ process.exit(1);
102
+ }
103
+ });
104
+ // tf connector list
105
+ cmd
106
+ .command("list")
107
+ .description("List all connectors")
108
+ .action(async () => {
109
+ (0, utils_1.requireGateway)();
110
+ try {
111
+ const connectors = await (0, utils_1.gatewayRequest)("GET", "/internal/connectors");
112
+ if (!connectors || connectors.length === 0) {
113
+ console.log("[TF] No connectors configured.");
114
+ console.log(" Add one with: tf connector add github --token <PAT>");
115
+ return;
116
+ }
117
+ console.log("[TF] Connectors");
118
+ const rows = connectors.map((c) => [
119
+ c.id,
120
+ c.type,
121
+ c.display_name,
122
+ (c.domains || []).join(", "),
123
+ ]);
124
+ console.log((0, utils_1.formatTable)(["ID", "Type", "Name", "Domains"], rows));
125
+ }
126
+ catch (err) {
127
+ console.error(`[TF] Failed: ${err.message}`);
128
+ }
129
+ });
130
+ // tf connector remove <id>
131
+ cmd
132
+ .command("remove <id>")
133
+ .description("Remove a connector")
134
+ .action(async (id) => {
135
+ (0, utils_1.requireGateway)();
136
+ try {
137
+ await (0, utils_1.gatewayRequest)("POST", "/internal/connectors/remove", { id });
138
+ console.log(`[TF] Connector ${id} removed.`);
139
+ }
140
+ catch (err) {
141
+ console.error(`[TF] Failed: ${err.message}`);
142
+ }
143
+ });
144
+ return cmd;
145
+ }
146
+ // tf permissions command (separate top-level for clarity)
147
+ function permissionsCommand() {
148
+ const cmd = new commander_1.Command("permissions")
149
+ .description("Manage gateway permissions on connectors");
150
+ // tf permissions set
151
+ cmd
152
+ .command("set")
153
+ .description("Set a permission rule")
154
+ .requiredOption("--gateway <name>", "Gateway name")
155
+ .requiredOption("--connector <type>", "Connector type (github, generic, or *)")
156
+ .requiredOption("--action <pattern>", "Action pattern (e.g. github.pr.*, *)")
157
+ .requiredOption("--decision <decision>", "Decision: allow, deny, require_approval")
158
+ .action(async (opts) => {
159
+ (0, utils_1.requireGateway)();
160
+ try {
161
+ await (0, utils_1.gatewayRequest)("POST", "/internal/permissions/set", {
162
+ gateway_name: opts.gateway,
163
+ connector_type: opts.connector,
164
+ action_pattern: opts.action,
165
+ decision: opts.decision,
166
+ });
167
+ console.log(`[TF] Permission set: ${opts.gateway} → ${opts.connector}.${opts.action} = ${opts.decision}`);
168
+ }
169
+ catch (err) {
170
+ console.error(`[TF] Failed: ${err.message}`);
171
+ }
172
+ });
173
+ // tf permissions list
174
+ cmd
175
+ .command("list")
176
+ .description("List permission rules")
177
+ .option("--gateway <name>", "Filter by gateway name")
178
+ .action(async (opts) => {
179
+ (0, utils_1.requireGateway)();
180
+ try {
181
+ const url = opts.gateway
182
+ ? `/internal/permissions?gateway=${encodeURIComponent(opts.gateway)}`
183
+ : "/internal/permissions";
184
+ const rules = await (0, utils_1.gatewayRequest)("GET", url);
185
+ if (!rules || rules.length === 0) {
186
+ console.log("[TF] No permissions configured.");
187
+ console.log(" Default: all actions denied (deny-all).");
188
+ console.log(" Set with: tf permissions set --gateway <name> --connector <type> --action '*' --decision allow");
189
+ return;
190
+ }
191
+ console.log("[TF] Permissions");
192
+ const rows = rules.map((r) => [
193
+ r.gateway_name,
194
+ r.connector_type,
195
+ r.action_pattern,
196
+ r.decision,
197
+ ]);
198
+ console.log((0, utils_1.formatTable)(["Gateway", "Connector", "Action Pattern", "Decision"], rows));
199
+ }
200
+ catch (err) {
201
+ console.error(`[TF] Failed: ${err.message}`);
202
+ }
203
+ });
204
+ return cmd;
205
+ }
206
+ function readStdin() {
207
+ return new Promise((resolve) => {
208
+ let data = "";
209
+ process.stdin.setEncoding("utf8");
210
+ process.stdin.on("data", (chunk) => { data += chunk; });
211
+ process.stdin.on("end", () => { resolve(data.trim()); });
212
+ if (process.stdin.isTTY) {
213
+ resolve("");
214
+ }
215
+ });
216
+ }
217
+ function prompt(question) {
218
+ const rl = readline.createInterface({
219
+ input: process.stdin,
220
+ output: process.stdout,
221
+ });
222
+ return new Promise((resolve) => {
223
+ rl.question(question, (answer) => {
224
+ rl.close();
225
+ resolve(answer.trim());
226
+ });
227
+ });
228
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function initCommand(): Command;