@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.
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initCommand = initCommand;
4
+ const commander_1 = require("commander");
5
+ const child_process_1 = require("child_process");
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const utils_1 = require("../utils");
9
+ function initCommand() {
10
+ const cmd = new commander_1.Command("init")
11
+ .description("Initialize TameFlare gateway in the current directory")
12
+ .option("--port <port>", "Gateway port", "9443")
13
+ .option("--enforcement <level>", "Enforcement level: monitor, soft_enforce, full_enforce", "monitor")
14
+ .option("--platform <name>", "Platform template: openclaw, langchain, n8n, claude-code")
15
+ .action(async (opts) => {
16
+ const tfDir = (0, utils_1.getTfDir)();
17
+ if ((0, utils_1.isGatewayInitialized)()) {
18
+ console.log("[TF] Already initialized at", tfDir);
19
+ console.log("[TF] Starting gateway...");
20
+ }
21
+ else {
22
+ console.log("[TF] Initializing TameFlare...");
23
+ // Create .tf directory
24
+ (0, fs_1.mkdirSync)(tfDir, { recursive: true });
25
+ // Apply platform template enforcement level
26
+ const enforcement = opts.platform
27
+ ? "full_enforce"
28
+ : opts.enforcement;
29
+ // Write config
30
+ const config = [
31
+ "gateway:",
32
+ ` port: ${opts.port}`,
33
+ " dashboard_port: 3000",
34
+ ` enforcement_level: ${enforcement}`,
35
+ " proxy_mode: standard",
36
+ "",
37
+ "database:",
38
+ ` url: "file:${(0, path_1.join)(tfDir, "local.db").replace(/\\/g, "/")}"`,
39
+ "",
40
+ "tls:",
41
+ ` ca_cert: "${(0, path_1.join)(tfDir, "ca.crt").replace(/\\/g, "/")}"`,
42
+ ` ca_key: "${(0, path_1.join)(tfDir, "ca.key").replace(/\\/g, "/")}"`,
43
+ ].join("\n");
44
+ (0, fs_1.writeFileSync)((0, utils_1.getConfigPath)(), config, "utf-8");
45
+ console.log("[TF] Config created at", (0, utils_1.getConfigPath)());
46
+ // Write platform template
47
+ if (opts.platform) {
48
+ const template = getPlatformTemplate(opts.platform);
49
+ if (template) {
50
+ const templatePath = (0, path_1.join)(tfDir, "platform.json");
51
+ (0, fs_1.writeFileSync)(templatePath, JSON.stringify(template, null, 2), "utf-8");
52
+ console.log(`[TF] Platform template: ${opts.platform}`);
53
+ console.log(`[TF] Enforcement: full_enforce (platform default)`);
54
+ console.log(`[TF] After gateway starts, run the setup commands below:`);
55
+ console.log();
56
+ for (const cmd of template.setup_commands) {
57
+ console.log(` ${cmd}`);
58
+ }
59
+ console.log();
60
+ }
61
+ else {
62
+ console.error(`[TF] Unknown platform: ${opts.platform}`);
63
+ console.error(`[TF] Available: openclaw, langchain, n8n, claude-code`);
64
+ process.exit(1);
65
+ }
66
+ }
67
+ }
68
+ // Find and start the gateway binary
69
+ const gatewayBin = findGatewayBinary();
70
+ if (!gatewayBin) {
71
+ console.error("[TF] Gateway binary not found.");
72
+ console.error("[TF] Build it with: cd apps/gateway-v2 && go build -o gateway ./cmd/gateway");
73
+ process.exit(1);
74
+ }
75
+ console.log("[TF] Starting gateway...");
76
+ const gateway = (0, child_process_1.spawn)(gatewayBin, ["--config", (0, utils_1.getConfigPath)()], {
77
+ stdio: "inherit",
78
+ detached: false,
79
+ });
80
+ gateway.on("error", (err) => {
81
+ console.error("[TF] Failed to start gateway:", err.message);
82
+ process.exit(1);
83
+ });
84
+ // Write PID file for stop command
85
+ const pidFile = (0, path_1.join)(tfDir, "gateway.pid");
86
+ if (gateway.pid) {
87
+ (0, fs_1.writeFileSync)(pidFile, String(gateway.pid), "utf-8");
88
+ }
89
+ // Handle clean shutdown
90
+ process.on("SIGINT", () => {
91
+ gateway.kill("SIGTERM");
92
+ process.exit(0);
93
+ });
94
+ process.on("SIGTERM", () => {
95
+ gateway.kill("SIGTERM");
96
+ process.exit(0);
97
+ });
98
+ });
99
+ return cmd;
100
+ }
101
+ function getPlatformTemplate(platform) {
102
+ const templates = {
103
+ openclaw: {
104
+ name: "OpenClaw",
105
+ description: "AI agent framework — blocks all unknown domains, pre-allows LLM APIs",
106
+ connectors: ["openai", "anthropic"],
107
+ setup_commands: [
108
+ 'npx tf connector add openai --token $OPENAI_API_KEY',
109
+ 'npx tf connector add anthropic --token $ANTHROPIC_API_KEY',
110
+ 'npx tf permissions set --gateway "default" --connector openai --action "openai.chat.*" --decision allow',
111
+ 'npx tf permissions set --gateway "default" --connector anthropic --action "anthropic.message.*" --decision allow',
112
+ '# All other outbound traffic is blocked by default',
113
+ ],
114
+ },
115
+ langchain: {
116
+ name: "LangChain",
117
+ description: "LLM framework — pre-allows LLM APIs and search, blocks file:// and localhost",
118
+ connectors: ["openai", "anthropic"],
119
+ setup_commands: [
120
+ 'npx tf connector add openai --token $OPENAI_API_KEY',
121
+ 'npx tf connector add anthropic --token $ANTHROPIC_API_KEY',
122
+ 'npx tf permissions set --gateway "default" --connector openai --action "*" --decision allow',
123
+ 'npx tf permissions set --gateway "default" --connector anthropic --action "*" --decision allow',
124
+ '# Add tool connectors as needed:',
125
+ '# npx tf connector add generic --domains serpapi.com --token $SERP_KEY',
126
+ ],
127
+ },
128
+ n8n: {
129
+ name: "n8n",
130
+ description: "Workflow automation — pre-allows common integration endpoints",
131
+ connectors: ["openai", "slack", "github"],
132
+ setup_commands: [
133
+ 'npx tf connector add openai --token $OPENAI_API_KEY',
134
+ 'npx tf connector add slack --token $SLACK_BOT_TOKEN',
135
+ 'npx tf connector add github --token $GITHUB_TOKEN',
136
+ 'npx tf permissions set --gateway "workflow" --connector openai --action "*" --decision allow',
137
+ 'npx tf permissions set --gateway "workflow" --connector slack --action "slack.message.*" --decision allow',
138
+ 'npx tf permissions set --gateway "workflow" --connector github --action "github.issue.*" --decision allow',
139
+ 'npx tf permissions set --gateway "workflow" --connector github --action "github.pr.merge" --decision require_approval',
140
+ ],
141
+ },
142
+ "claude-code": {
143
+ name: "Claude Code",
144
+ description: "AI coding assistant — pre-allows package registries and git, blocks all other outbound",
145
+ connectors: ["github"],
146
+ setup_commands: [
147
+ 'npx tf connector add github --token $GITHUB_TOKEN',
148
+ 'npx tf connector add generic --domains registry.npmjs.org,pypi.org --auth-type none',
149
+ 'npx tf permissions set --gateway "claude" --connector github --action "github.api.read" --decision allow',
150
+ 'npx tf permissions set --gateway "claude" --connector github --action "github.pr.create" --decision allow',
151
+ 'npx tf permissions set --gateway "claude" --connector github --action "github.pr.merge" --decision require_approval',
152
+ 'npx tf permissions set --gateway "claude" --connector github --action "github.file.commit" --decision allow',
153
+ 'npx tf permissions set --gateway "claude" --connector generic --action "*" --decision allow',
154
+ '# All other outbound traffic is blocked by default',
155
+ ],
156
+ },
157
+ };
158
+ return templates[platform] ?? null;
159
+ }
160
+ function findGatewayBinary() {
161
+ const candidates = [
162
+ (0, path_1.join)(process.cwd(), "apps", "gateway-v2", "gateway.exe"),
163
+ (0, path_1.join)(process.cwd(), "apps", "gateway-v2", "gateway"),
164
+ (0, path_1.join)(process.cwd(), "gateway.exe"),
165
+ (0, path_1.join)(process.cwd(), "gateway"),
166
+ ];
167
+ for (const candidate of candidates) {
168
+ if ((0, fs_1.existsSync)(candidate)) {
169
+ return candidate;
170
+ }
171
+ }
172
+ return null;
173
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function killSwitchCommand(): Command;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.killSwitchCommand = killSwitchCommand;
4
+ const commander_1 = require("commander");
5
+ const utils_1 = require("../utils");
6
+ function killSwitchCommand() {
7
+ const cmd = new commander_1.Command("kill-switch")
8
+ .description("Emergency: block all gateway traffic");
9
+ cmd
10
+ .command("activate")
11
+ .description("Activate kill switch — block ALL traffic immediately")
12
+ .option("--reason <reason>", "Reason for activation", "Manual activation")
13
+ .action(async (opts) => {
14
+ (0, utils_1.requireGateway)();
15
+ try {
16
+ await (0, utils_1.gatewayRequest)("POST", "/internal/kill-switch", {
17
+ active: true,
18
+ reason: opts.reason,
19
+ });
20
+ console.log("[TF] KILL SWITCH ACTIVATED");
21
+ console.log(` Reason: ${opts.reason}`);
22
+ console.log(" All traffic is now blocked.");
23
+ console.log(" Deactivate with: tf kill-switch deactivate");
24
+ }
25
+ catch (err) {
26
+ console.error(`[TF] Failed: ${err.message}`);
27
+ }
28
+ });
29
+ cmd
30
+ .command("deactivate")
31
+ .description("Deactivate kill switch — resume normal enforcement")
32
+ .action(async () => {
33
+ (0, utils_1.requireGateway)();
34
+ try {
35
+ await (0, utils_1.gatewayRequest)("POST", "/internal/kill-switch", {
36
+ active: false,
37
+ });
38
+ console.log("[TF] Kill switch deactivated. Normal enforcement resumed.");
39
+ }
40
+ catch (err) {
41
+ console.error(`[TF] Failed: ${err.message}`);
42
+ }
43
+ });
44
+ return cmd;
45
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function licenseCommand(): Command;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.licenseCommand = licenseCommand;
4
+ const commander_1 = require("commander");
5
+ const utils_1 = require("../utils");
6
+ function licenseCommand() {
7
+ const cmd = new commander_1.Command("license")
8
+ .description("Manage license and plan");
9
+ cmd
10
+ .command("status")
11
+ .description("Show current license tier, usage, and limits")
12
+ .action(async () => {
13
+ try {
14
+ const baseUrl = (0, utils_1.getBaseUrl)();
15
+ const apiKey = (0, utils_1.getApiKey)();
16
+ const response = await fetch(`${baseUrl}/api/license/activate`, {
17
+ method: "GET",
18
+ headers: {
19
+ Authorization: `Bearer ${apiKey}`,
20
+ "Content-Type": "application/json",
21
+ "User-Agent": "@agentfirewall/cli/0.1.0",
22
+ },
23
+ });
24
+ const data = (await response.json());
25
+ if (!response.ok) {
26
+ console.error(`Error ${response.status}: ${data.error ?? "Unknown error"}`);
27
+ process.exit(1);
28
+ }
29
+ console.log(`\nPlan: ${data.tier_name} (${data.tier})`);
30
+ console.log(`License ID: ${data.license_id}`);
31
+ console.log(`Valid: ${data.valid ? "Yes" : "No"}`);
32
+ console.log(`Expires: ${new Date(data.expires_at).toLocaleDateString()}`);
33
+ const limits = data.limits;
34
+ console.log("\nLimits:");
35
+ console.log(` Agents: ${limits.maxAgents === -1 ? "Unlimited" : limits.maxAgents}`);
36
+ console.log(` Actions/mo: ${limits.maxActionsPerMonth === -1 ? "Unlimited" : limits.maxActionsPerMonth.toLocaleString()}`);
37
+ console.log(` Audit retain: ${limits.auditRetentionDays === -1 ? "Unlimited" : `${limits.auditRetentionDays} days`}`);
38
+ const features = data.features;
39
+ if (features.length > 0) {
40
+ console.log("\nFeatures:");
41
+ features.forEach((f) => console.log(` + ${f.replace(/_/g, " ")}`));
42
+ }
43
+ else {
44
+ console.log("\nFeatures: (none — upgrade for more)");
45
+ }
46
+ if (data.price !== null && data.price !== undefined) {
47
+ console.log(`\nPrice: ${data.price === 0 ? "Free" : `$${data.price}/mo`}`);
48
+ }
49
+ console.log("");
50
+ }
51
+ catch (err) {
52
+ console.error("Failed to fetch license status:", err instanceof Error ? err.message : err);
53
+ process.exit(1);
54
+ }
55
+ });
56
+ cmd
57
+ .command("activate <key>")
58
+ .description("Activate a license key on this instance")
59
+ .action(async (key) => {
60
+ try {
61
+ const baseUrl = (0, utils_1.getBaseUrl)();
62
+ const apiKey = (0, utils_1.getApiKey)();
63
+ console.log("Activating license key...");
64
+ const response = await fetch(`${baseUrl}/api/license/activate`, {
65
+ method: "POST",
66
+ headers: {
67
+ Authorization: `Bearer ${apiKey}`,
68
+ "Content-Type": "application/json",
69
+ "User-Agent": "@agentfirewall/cli/0.1.0",
70
+ },
71
+ body: JSON.stringify({ license_key: key }),
72
+ });
73
+ const data = (await response.json());
74
+ if (!response.ok) {
75
+ console.error(`Activation failed: ${data.error ?? "Unknown error"}`);
76
+ if (data.details)
77
+ console.error(`Details: ${data.details}`);
78
+ process.exit(1);
79
+ }
80
+ console.log(`\nLicense activated successfully!`);
81
+ console.log(` Tier: ${data.tier}`);
82
+ console.log(` License ID: ${data.license_id}`);
83
+ console.log(` Expires: ${new Date(data.expires_at).toLocaleDateString()}`);
84
+ const limits = data.limits;
85
+ console.log(` Agents: ${limits.maxAgents === -1 ? "Unlimited" : limits.maxAgents}`);
86
+ console.log(` Actions/mo: ${limits.maxActionsPerMonth === -1 ? "Unlimited" : limits.maxActionsPerMonth.toLocaleString()}`);
87
+ const features = data.features;
88
+ if (features.length > 0) {
89
+ console.log(` Features: ${features.join(", ")}`);
90
+ }
91
+ console.log("");
92
+ }
93
+ catch (err) {
94
+ console.error("Activation failed:", err instanceof Error ? err.message : err);
95
+ process.exit(1);
96
+ }
97
+ });
98
+ cmd
99
+ .command("usage")
100
+ .description("Show current usage against plan limits")
101
+ .action(async () => {
102
+ try {
103
+ const baseUrl = (0, utils_1.getBaseUrl)();
104
+ const apiKey = (0, utils_1.getApiKey)();
105
+ const response = await fetch(`${baseUrl}/api/license/activate`, {
106
+ method: "GET",
107
+ headers: {
108
+ Authorization: `Bearer ${apiKey}`,
109
+ "Content-Type": "application/json",
110
+ "User-Agent": "@agentfirewall/cli/0.1.0",
111
+ },
112
+ });
113
+ const data = (await response.json());
114
+ if (!response.ok) {
115
+ console.error(`Error: ${data.error ?? "Unknown error"}`);
116
+ process.exit(1);
117
+ }
118
+ console.log(`\nPlan: ${data.tier_name}`);
119
+ console.log("");
120
+ const table = (0, utils_1.formatTable)(["Resource", "Current", "Limit", "Usage"], [
121
+ [
122
+ "Agents",
123
+ String(data.usage?.agents ?? "?"),
124
+ data.limits.maxAgents === -1 ? "Unlimited" : String(data.limits.maxAgents),
125
+ data.limits.maxAgents === -1 ? "-" : `${Math.round(((data.usage?.agents ?? 0) / data.limits.maxAgents) * 100)}%`,
126
+ ],
127
+ [
128
+ "Actions/mo",
129
+ String(data.usage?.actions ?? "?"),
130
+ data.limits.maxActionsPerMonth === -1 ? "Unlimited" : String(data.limits.maxActionsPerMonth),
131
+ data.limits.maxActionsPerMonth === -1 ? "-" : `${Math.round(((data.usage?.actions ?? 0) / data.limits.maxActionsPerMonth) * 100)}%`,
132
+ ],
133
+ ]);
134
+ console.log(table);
135
+ console.log("");
136
+ }
137
+ catch (err) {
138
+ console.error("Failed to fetch usage:", err instanceof Error ? err.message : err);
139
+ process.exit(1);
140
+ }
141
+ });
142
+ return cmd;
143
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function logsCommand(): Command;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logsCommand = logsCommand;
4
+ const commander_1 = require("commander");
5
+ const utils_1 = require("../utils");
6
+ function logsCommand() {
7
+ const cmd = new commander_1.Command("logs")
8
+ .description("View recent traffic logs")
9
+ .option("--gateway <name>", "Filter by gateway name")
10
+ .option("-n, --limit <count>", "Number of entries", "25")
11
+ .action(async (opts) => {
12
+ (0, utils_1.requireGateway)();
13
+ try {
14
+ const entries = await (0, utils_1.gatewayRequest)("GET", "/internal/traffic");
15
+ if (!entries || entries.length === 0) {
16
+ console.log("[TF] No traffic logged yet.");
17
+ console.log(" Run a process with: tf run --name <name> <command>");
18
+ return;
19
+ }
20
+ let filtered = entries;
21
+ if (opts.gateway) {
22
+ filtered = entries.filter((e) => e.gateway_name === opts.gateway);
23
+ }
24
+ const limit = parseInt(opts.limit, 10);
25
+ filtered = filtered.slice(0, limit);
26
+ console.log("[TF] Recent Traffic");
27
+ console.log("");
28
+ for (const entry of filtered) {
29
+ const time = new Date(entry.timestamp).toLocaleTimeString();
30
+ const symbol = getSymbol(entry.decision);
31
+ const gwName = entry.gateway_name.padEnd(15);
32
+ const method = entry.method.padEnd(7);
33
+ const url = truncate(entry.url, 55);
34
+ const decision = entry.decision;
35
+ console.log(` ${time} ${symbol} ${gwName} ${method} ${url} ${decision}`);
36
+ if (entry.reason && entry.decision !== "allowed") {
37
+ console.log(` ${" ".repeat(15)} ${"".padEnd(7)} -> ${entry.reason}`);
38
+ }
39
+ }
40
+ console.log("");
41
+ console.log(` Showing ${filtered.length} of ${entries.length} entries`);
42
+ }
43
+ catch (err) {
44
+ console.error("[TF] Gateway is not running.");
45
+ console.error(" Start it with: tf init");
46
+ }
47
+ });
48
+ return cmd;
49
+ }
50
+ function getSymbol(decision) {
51
+ switch (decision) {
52
+ case "allowed":
53
+ case "would_allow":
54
+ return "\x1b[32m✓\x1b[0m";
55
+ case "denied":
56
+ return "\x1b[31m✗\x1b[0m";
57
+ case "would_deny":
58
+ return "\x1b[33m~\x1b[0m";
59
+ case "error":
60
+ return "\x1b[31m!\x1b[0m";
61
+ default:
62
+ return "?";
63
+ }
64
+ }
65
+ function truncate(s, maxLen) {
66
+ if (s.length <= maxLen)
67
+ return s;
68
+ return s.substring(0, maxLen - 3) + "...";
69
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function policiesCommand(): Command;
@@ -0,0 +1,113 @@
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.policiesCommand = policiesCommand;
37
+ const commander_1 = require("commander");
38
+ const fs = __importStar(require("fs"));
39
+ const utils_1 = require("../utils");
40
+ function policiesCommand() {
41
+ const cmd = new commander_1.Command("policies").description("Manage policies");
42
+ cmd
43
+ .command("list")
44
+ .description("List active policies")
45
+ .action(async () => {
46
+ const data = await (0, utils_1.apiRequest)("GET", "/v1/policies");
47
+ const policies = (data.policies ?? []);
48
+ if (policies.length === 0) {
49
+ console.log("No policies found.");
50
+ return;
51
+ }
52
+ console.log((0, utils_1.formatTable)(["Name", "Priority", "Tags", "Status"], policies.map((p) => [
53
+ String(p.name),
54
+ String(p.priority),
55
+ Array.isArray(p.tags) ? p.tags.join(", ") : "—",
56
+ p.enabled ? "active" : "disabled",
57
+ ])));
58
+ });
59
+ cmd
60
+ .command("create")
61
+ .description("Create a policy from a YAML file")
62
+ .requiredOption("--file <path>", "Path to YAML policy file")
63
+ .action(async (opts) => {
64
+ const filePath = opts.file;
65
+ if (!fs.existsSync(filePath)) {
66
+ console.error(`File not found: ${filePath}`);
67
+ process.exit(1);
68
+ }
69
+ const yaml = fs.readFileSync(filePath, "utf-8");
70
+ const data = await (0, utils_1.apiRequest)("POST", "/v1/policies", {
71
+ policy_yaml: yaml,
72
+ });
73
+ console.log("Policy created successfully!");
74
+ console.log(` ID: ${data.policy_id}`);
75
+ console.log(` Name: ${data.name}`);
76
+ const validation = data.validation;
77
+ if (validation?.warnings && Array.isArray(validation.warnings) && validation.warnings.length > 0) {
78
+ console.log(" Warnings:");
79
+ for (const w of validation.warnings) {
80
+ console.log(` - ${w}`);
81
+ }
82
+ }
83
+ });
84
+ cmd
85
+ .command("simulate")
86
+ .description("Simulate policy evaluation")
87
+ .requiredOption("--action-type <type>", "Action type (e.g. github.pr.merge)")
88
+ .requiredOption("--provider <provider>", "Provider (e.g. github)")
89
+ .requiredOption("--target <target>", "Resource target")
90
+ .option("--environment <env>", "Environment", "production")
91
+ .action(async (opts) => {
92
+ const data = await (0, utils_1.apiRequest)("POST", "/v1/policies/simulate", {
93
+ action_spec: {
94
+ type: opts.actionType,
95
+ resource: {
96
+ provider: opts.provider,
97
+ account: "cli",
98
+ target: opts.target,
99
+ environment: opts.environment,
100
+ },
101
+ parameters: {},
102
+ context: { reason: "CLI simulation" },
103
+ risk_hints: {},
104
+ },
105
+ });
106
+ const decision = data.simulated_decision;
107
+ console.log(`Outcome: ${decision?.outcome}`);
108
+ console.log(`Reason: ${decision?.reason}`);
109
+ console.log(`Risk: ${decision?.risk_score}`);
110
+ console.log(`Policies: ${decision?.matched_policies?.join(", ") ?? "none"}`);
111
+ });
112
+ return cmd;
113
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function runCommand(): Command;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runCommand = runCommand;
4
+ const commander_1 = require("commander");
5
+ const child_process_1 = require("child_process");
6
+ const utils_1 = require("../utils");
7
+ function runCommand() {
8
+ const cmd = new commander_1.Command("run")
9
+ .description("Run a process through the TameFlare proxy")
10
+ .requiredOption("--name <name>", "Process name")
11
+ .argument("<command...>", "Command to run")
12
+ .action(async (commandArgs, opts) => {
13
+ (0, utils_1.requireGateway)();
14
+ const processName = opts.name;
15
+ const [command, ...args] = commandArgs;
16
+ console.log(`[TF] Registering process "${processName}"...`);
17
+ // Register process with gateway
18
+ let registration;
19
+ try {
20
+ registration = await (0, utils_1.gatewayRequest)("POST", "/internal/processes/register", {
21
+ name: processName,
22
+ pid: process.pid,
23
+ });
24
+ }
25
+ catch (err) {
26
+ console.error(`[TF] Failed to register process: ${err.message}`);
27
+ process.exit(1);
28
+ }
29
+ const proxyUrl = registration.proxy_url;
30
+ const caCert = registration.ca_cert;
31
+ console.log(`[TF] Process "${processName}" registered on port ${registration.port}`);
32
+ console.log(`[TF] Proxy: ${proxyUrl}`);
33
+ console.log(`[TF] Starting: ${command} ${args.join(" ")}`);
34
+ console.log("");
35
+ // Spawn child process with proxy environment variables
36
+ const env = {
37
+ ...process.env,
38
+ HTTP_PROXY: proxyUrl,
39
+ HTTPS_PROXY: proxyUrl,
40
+ http_proxy: proxyUrl,
41
+ https_proxy: proxyUrl,
42
+ // Trust the TameFlare CA certificate for various runtimes
43
+ NODE_EXTRA_CA_CERTS: caCert,
44
+ SSL_CERT_FILE: caCert,
45
+ REQUESTS_CA_BUNDLE: caCert,
46
+ CURL_CA_BUNDLE: caCert,
47
+ // Mark as TameFlare-wrapped (processes can detect this if needed)
48
+ TF_PROCESS_NAME: processName,
49
+ TF_PROXY_PORT: String(registration.port),
50
+ };
51
+ const child = (0, child_process_1.spawn)(command, args, {
52
+ stdio: "inherit",
53
+ env,
54
+ shell: true,
55
+ });
56
+ child.on("error", (err) => {
57
+ console.error(`[TF] Failed to start command: ${err.message}`);
58
+ deregister(processName);
59
+ process.exit(1);
60
+ });
61
+ child.on("exit", (code, signal) => {
62
+ console.log("");
63
+ console.log(`[TF] Process "${processName}" exited (code ${code ?? signal})`);
64
+ deregister(processName);
65
+ process.exit(code ?? 1);
66
+ });
67
+ // Clean shutdown on signals
68
+ const cleanup = () => {
69
+ child.kill("SIGTERM");
70
+ deregister(processName);
71
+ };
72
+ process.on("SIGINT", cleanup);
73
+ process.on("SIGTERM", cleanup);
74
+ });
75
+ return cmd;
76
+ }
77
+ async function deregister(name) {
78
+ try {
79
+ await (0, utils_1.gatewayRequest)("POST", "/internal/processes/deregister", { name });
80
+ console.log(`[TF] Process "${name}" deregistered`);
81
+ }
82
+ catch {
83
+ // Gateway may already be down — ignore
84
+ }
85
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function statusCommand(): Command;