@tameflare/cli 0.10.2 → 0.10.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @tameflare/cli
2
2
 
3
- The official CLI for [TameFlare](https://tameflare.com) - secure and govern AI agent traffic through a transparent proxy gateway.
3
+ The official CLI for [TameFlare](https://tameflare.com) - secure and govern AI agent traffic through a cloud proxy gateway.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,75 +8,53 @@ The official CLI for [TameFlare](https://tameflare.com) - secure and govern AI a
8
8
  npm i -g @tameflare/cli
9
9
  ```
10
10
 
11
- Or run directly with npx:
12
-
13
- ```bash
14
- npx @tameflare/cli init
15
- ```
16
-
17
11
  ## Quick Start
18
12
 
19
13
  ```bash
20
- # 1. Initialize TameFlare in your project
21
- tf init
14
+ # 1. Log in (opens browser)
15
+ tf login
22
16
 
23
- # 2. Add a connector (e.g. GitHub)
24
- tf connector add github --token ghp_xxx
17
+ # 2. Pick a gateway
18
+ tf init --list
25
19
 
26
- # 3. Run your agent through the gateway
27
- tf run --name my-agent -- python agent.py
20
+ # 3. Run your agent through the cloud proxy
21
+ tf run -- python agent.py
28
22
  ```
29
23
 
30
24
  ## Commands
31
25
 
32
26
  | Command | Description |
33
27
  |---------|-------------|
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 |
28
+ | `tf login` | Authenticate via browser (saves credentials to `~/.tameflare/`) |
29
+ | `tf logout` | Remove saved credentials |
30
+ | `tf init` | Select a gateway and save config to `.tf/config.yaml` |
31
+ | `tf run -- <cmd>` | Run a process through the cloud proxy |
32
+ | `tf status` | Show gateway config and connectivity |
33
+ | `tf logs` | Open dashboard traffic view in browser |
34
+ | `tf reset` | Remove `.tf/` directory |
35
+
36
+ Connectors, permissions, approvals, and kill switch are managed in the [dashboard](https://tameflare.com/dashboard).
48
37
 
49
38
  ## How It Works
50
39
 
51
- TameFlare acts as a transparent HTTP/HTTPS proxy between your AI agent and external APIs. When you run `tf run`, the CLI:
40
+ When you run `tf run -- python agent.py`, the CLI:
52
41
 
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
42
+ 1. Sets `HTTPS_PROXY=https://{gateway_token}@proxy.tameflare.com`
43
+ 2. Spawns your process with the proxy environment variable
44
+ 3. All outbound HTTP traffic routes through the cloud gateway
45
+ 4. The gateway parses requests, checks permissions, and logs everything
57
46
 
58
- The gateway matches requests against connectors (GitHub, OpenAI, Stripe, etc.), applies permission rules, and either allows, denies, or holds requests for human approval.
47
+ No local binary, no Docker, no server to manage. The gateway runs in the cloud at `proxy.tameflare.com`.
59
48
 
60
49
  ## Requirements
61
50
 
62
51
  - 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
52
 
75
53
  ## Links
76
54
 
77
55
  - [Documentation](https://tameflare.com/docs)
78
56
  - [GitHub](https://github.com/tameflare/tameflare)
79
- - [Website](https://tameflare.com)
57
+ - [Dashboard](https://tameflare.com/dashboard)
80
58
 
81
59
  ## License
82
60
 
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runCommand = runCommand;
4
4
  const commander_1 = require("commander");
5
5
  const child_process_1 = require("child_process");
6
+ const path_1 = require("path");
7
+ const fs_1 = require("fs");
6
8
  const utils_1 = require("../utils");
7
9
  function runCommand() {
8
10
  const cmd = new commander_1.Command("run")
@@ -16,13 +18,32 @@ function runCommand() {
16
18
  console.log(`[TF] Proxy: https://${cfg.proxy_host}`);
17
19
  console.log(`[TF] Running: ${command} ${args.join(" ")}`);
18
20
  console.log("");
21
+ // Register heartbeat so dashboard shows gateway as online
22
+ try {
23
+ await fetch(`${cfg.dashboard_url}/api/gateway/v2/verify`, {
24
+ method: "POST",
25
+ headers: { "Content-Type": "application/json" },
26
+ body: JSON.stringify({ gateway_token: cfg.gateway_token }),
27
+ });
28
+ }
29
+ catch {
30
+ // Non-fatal — gateway still works even if dashboard is temporarily unreachable
31
+ }
32
+ // Locate the proxy bootstrap script (shipped alongside the CLI)
33
+ // Use forward slashes to avoid Windows shell escaping issues with --require
34
+ const bootstrapPath = (0, path_1.resolve)((0, path_1.dirname)(__filename), "..", "proxy-bootstrap.js").replace(/\\/g, "/");
35
+ let nodeOptions = process.env.NODE_OPTIONS || "";
36
+ if ((0, fs_1.existsSync)(bootstrapPath)) {
37
+ const requireFlag = `--require "${bootstrapPath}"`;
38
+ nodeOptions = nodeOptions ? `${nodeOptions} ${requireFlag}` : requireFlag;
39
+ }
19
40
  const env = {
20
41
  ...process.env,
21
42
  HTTP_PROXY: proxyUrl,
22
43
  HTTPS_PROXY: proxyUrl,
23
44
  http_proxy: proxyUrl,
24
45
  https_proxy: proxyUrl,
25
- TF_GATEWAY_ID: cfg.gateway_id,
46
+ NODE_OPTIONS: nodeOptions,
26
47
  };
27
48
  const child = (0, child_process_1.spawn)(command, args, {
28
49
  stdio: "inherit",
@@ -15,14 +15,23 @@ function statusCommand() {
15
15
  console.log(` Dashboard: ${cfg.dashboard_url}`);
16
16
  console.log(` Proxy: https://${cfg.proxy_host}`);
17
17
  console.log("");
18
- // Check cloud proxy connectivity
18
+ // Ping dashboard and register gateway heartbeat
19
19
  try {
20
- const res = await fetch(`${cfg.dashboard_url}/api/health`);
20
+ const res = await fetch(`${cfg.dashboard_url}/api/gateway/v2/verify`, {
21
+ method: "POST",
22
+ headers: { "Content-Type": "application/json" },
23
+ body: JSON.stringify({ gateway_token: cfg.gateway_token }),
24
+ });
21
25
  if (res.ok) {
22
26
  console.log(` Dashboard: \x1b[32mreachable\x1b[0m`);
27
+ console.log(` Gateway: \x1b[32monline\x1b[0m`);
28
+ }
29
+ else if (res.status === 401) {
30
+ console.log(` Dashboard: \x1b[32mreachable\x1b[0m`);
31
+ console.log(` Gateway: \x1b[31minvalid token\x1b[0m`);
23
32
  }
24
33
  else {
25
- console.log(` Dashboard: \x1b[31munreachable (${res.status})\x1b[0m`);
34
+ console.log(` Dashboard: \x1b[31merror (${res.status})\x1b[0m`);
26
35
  }
27
36
  }
28
37
  catch {
@@ -0,0 +1,68 @@
1
+ /**
2
+ * TameFlare Proxy Bootstrap
3
+ *
4
+ * This script is injected via NODE_OPTIONS="--require <this-file>" by `tf run`.
5
+ * It overrides globalThis.fetch to route all HTTP/HTTPS requests through the
6
+ * TameFlare cloud proxy.
7
+ *
8
+ * Architecture: The cloud proxy runs behind Railway's TLS termination, so
9
+ * standard HTTP CONNECT tunneling doesn't work. Instead, we send requests
10
+ * directly to the proxy with the target URL in the X-TF-Target-URL header.
11
+ * The proxy reads this header, forwards the request to the real upstream,
12
+ * and returns the response.
13
+ */
14
+
15
+ "use strict";
16
+
17
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
18
+
19
+ if (proxyUrl) {
20
+ const originalFetch = globalThis.fetch;
21
+
22
+ // Parse proxy URL: https://token@proxy.tameflare.com
23
+ let proxyOrigin, proxyToken;
24
+ try {
25
+ const parsed = new URL(proxyUrl);
26
+ proxyToken = parsed.username || "";
27
+ parsed.username = "";
28
+ parsed.password = "";
29
+ proxyOrigin = parsed.origin;
30
+ } catch (_) {}
31
+
32
+ if (proxyOrigin && originalFetch) {
33
+ globalThis.fetch = function tfProxyFetch(input, init) {
34
+ try {
35
+ const req = new Request(input, init);
36
+ const targetUrl = req.url;
37
+
38
+ // Only proxy http/https requests
39
+ if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) {
40
+ return originalFetch(input, init);
41
+ }
42
+
43
+ // Extract the path from the target URL to build the proxy request URL
44
+ // e.g. https://api.openai.com/v1/models → proxy sends to proxy.tameflare.com/v1/models
45
+ const targetParsed = new URL(targetUrl);
46
+ const proxyRequestUrl = proxyOrigin + targetParsed.pathname + targetParsed.search;
47
+
48
+ // Build headers
49
+ const headers = new Headers(req.headers);
50
+ headers.set("X-TF-Target-URL", targetUrl);
51
+ headers.set("X-TF-Target-Host", targetParsed.host);
52
+ if (proxyToken) {
53
+ headers.set("X-TF-Gateway-Token", proxyToken);
54
+ }
55
+
56
+ return originalFetch(proxyRequestUrl, {
57
+ method: req.method,
58
+ headers: headers,
59
+ body: req.body,
60
+ redirect: "manual",
61
+ duplex: "half",
62
+ });
63
+ } catch (_) {
64
+ return originalFetch(input, init);
65
+ }
66
+ };
67
+ }
68
+ }
package/package.json CHANGED
@@ -1,21 +1,22 @@
1
1
  {
2
2
  "name": "@tameflare/cli",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "TameFlare CLI - secure and govern AI agent traffic through a transparent proxy gateway",
5
5
  "bin": {
6
6
  "tf": "dist/index.js"
7
7
  },
8
8
  "scripts": {
9
- "build": "tsc",
9
+ "build": "tsc && node -e \"require('fs').copyFileSync('src/proxy-bootstrap.js','dist/proxy-bootstrap.js')\"",
10
10
  "dev": "tsc --watch",
11
- "prepublishOnly": "tsc"
11
+ "prepublishOnly": "tsc && node -e \"require('fs').copyFileSync('src/proxy-bootstrap.js','dist/proxy-bootstrap.js')\""
12
12
  },
13
13
  "dependencies": {
14
- "commander": "^12.1.0"
14
+ "commander": "^12.1.0",
15
+ "undici": "^7.21.0"
15
16
  },
16
17
  "devDependencies": {
17
- "typescript": "^5.7.0",
18
- "@types/node": "^22.0.0"
18
+ "@types/node": "^22.0.0",
19
+ "typescript": "^5.7.0"
19
20
  },
20
21
  "files": [
21
22
  "dist",