@tameflare/cli 0.9.0 → 0.10.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/dist/commands/init.js +65 -346
- package/dist/commands/logs.js +48 -56
- package/dist/commands/run.js +10 -48
- package/dist/commands/status.js +21 -43
- package/dist/commands/stop.js +8 -23
- package/dist/index.js +3 -10
- package/dist/utils.d.ts +11 -4
- package/dist/utils.js +54 -34
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -2,195 +2,92 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.initCommand = initCommand;
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
5
|
const fs_1 = require("fs");
|
|
7
|
-
const path_1 = require("path");
|
|
8
6
|
const readline_1 = require("readline");
|
|
9
7
|
const utils_1 = require("../utils");
|
|
10
|
-
const https_1 = require("https");
|
|
11
|
-
const http_1 = require("http");
|
|
12
8
|
const login_1 = require("./login");
|
|
13
|
-
const
|
|
14
|
-
const GITHUB_REPO = "tameflare/tameflare";
|
|
9
|
+
const PROXY_HOST = "proxy.tameflare.com";
|
|
15
10
|
function initCommand() {
|
|
16
11
|
const cmd = new commander_1.Command("init")
|
|
17
|
-
.description("
|
|
18
|
-
.option("--
|
|
19
|
-
.option("--
|
|
20
|
-
.option("--
|
|
21
|
-
.option("--dashboard-url <url>", "Dashboard URL for config phone-home (e.g. https://tameflare.com)")
|
|
22
|
-
.option("--gateway-id <id>", "Gateway ID from dashboard")
|
|
23
|
-
.option("--gateway-token <token>", "Gateway auth token from dashboard")
|
|
12
|
+
.description("Connect this directory to a TameFlare cloud gateway")
|
|
13
|
+
.option("--dashboard-url <url>", "Dashboard URL (default: https://tameflare.com)")
|
|
14
|
+
.option("--gateway-id <id>", "Gateway ID from dashboard (for CI/CD)")
|
|
15
|
+
.option("--gateway-token <token>", "Gateway auth token from dashboard (for CI/CD)")
|
|
24
16
|
.option("--list", "List your gateways and pick one (requires tf login first)")
|
|
25
17
|
.action(async (opts) => {
|
|
26
18
|
const tfDir = (0, utils_1.getTfDir)();
|
|
27
|
-
// If
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
console.log(
|
|
37
|
-
|
|
38
|
-
else if (opts.list) {
|
|
39
|
-
// --list was explicit but no gateway picked
|
|
40
|
-
process.exit(1);
|
|
19
|
+
// If already initialized, show current config
|
|
20
|
+
if ((0, utils_1.isInitialized)() && !opts.list && !opts.gatewayId) {
|
|
21
|
+
const cfg = (0, utils_1.loadConfig)();
|
|
22
|
+
if (cfg) {
|
|
23
|
+
console.log("[TF] Already initialized at", tfDir);
|
|
24
|
+
console.log(`[TF] Gateway: ${cfg.gateway_id}`);
|
|
25
|
+
console.log(`[TF] Proxy: https://${PROXY_HOST}`);
|
|
26
|
+
console.log("");
|
|
27
|
+
console.log(" Run your agent: tf run -- node agent.js");
|
|
28
|
+
console.log(" Reconfigure: tf init --list");
|
|
29
|
+
return;
|
|
41
30
|
}
|
|
42
|
-
// If not --list and auto-detect failed, fall through to manual mode
|
|
43
31
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const enforcement = opts.platform
|
|
54
|
-
? "full_enforce"
|
|
55
|
-
: opts.enforcement;
|
|
56
|
-
// Write config (persist dashboard credentials so they survive restarts)
|
|
57
|
-
const configLines = [
|
|
58
|
-
"gateway:",
|
|
59
|
-
` port: ${opts.port}`,
|
|
60
|
-
" dashboard_port: 3000",
|
|
61
|
-
` enforcement_level: ${enforcement}`,
|
|
62
|
-
" proxy_mode: standard",
|
|
63
|
-
"",
|
|
64
|
-
"database:",
|
|
65
|
-
` url: "file:${(0, path_1.join)(tfDir, "local.db").replace(/\\/g, "/")}"`,
|
|
66
|
-
"",
|
|
67
|
-
"tls:",
|
|
68
|
-
` ca_cert: "${(0, path_1.join)(tfDir, "ca.crt").replace(/\\/g, "/")}"`,
|
|
69
|
-
` ca_key: "${(0, path_1.join)(tfDir, "ca.key").replace(/\\/g, "/")}"`,
|
|
70
|
-
];
|
|
71
|
-
if (opts.dashboardUrl || opts.gatewayId || opts.gatewayToken) {
|
|
72
|
-
configLines.push("");
|
|
73
|
-
configLines.push("dashboard:");
|
|
74
|
-
if (opts.dashboardUrl)
|
|
75
|
-
configLines.push(` url: "${opts.dashboardUrl}"`);
|
|
76
|
-
if (opts.gatewayId)
|
|
77
|
-
configLines.push(` gateway_id: "${opts.gatewayId}"`);
|
|
78
|
-
if (opts.gatewayToken)
|
|
79
|
-
configLines.push(` gateway_token: "${opts.gatewayToken}"`);
|
|
80
|
-
}
|
|
81
|
-
const config = configLines.join("\n");
|
|
82
|
-
(0, fs_1.writeFileSync)((0, utils_1.getConfigPath)(), config, "utf-8");
|
|
83
|
-
console.log("[TF] Config created at", (0, utils_1.getConfigPath)());
|
|
84
|
-
// Write platform template
|
|
85
|
-
if (opts.platform) {
|
|
86
|
-
const template = getPlatformTemplate(opts.platform);
|
|
87
|
-
if (template) {
|
|
88
|
-
const templatePath = (0, path_1.join)(tfDir, "platform.json");
|
|
89
|
-
(0, fs_1.writeFileSync)(templatePath, JSON.stringify(template, null, 2), "utf-8");
|
|
90
|
-
console.log(`[TF] Platform template: ${opts.platform}`);
|
|
91
|
-
console.log(`[TF] Enforcement: full_enforce (platform default)`);
|
|
92
|
-
console.log(`[TF] After gateway starts, run the setup commands below:`);
|
|
93
|
-
console.log();
|
|
94
|
-
for (const cmd of template.setup_commands) {
|
|
95
|
-
console.log(` ${cmd}`);
|
|
96
|
-
}
|
|
97
|
-
console.log();
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
console.error(`[TF] Unknown platform: ${opts.platform}`);
|
|
101
|
-
console.error(`[TF] Available: openclaw, langchain, n8n, claude-code`);
|
|
32
|
+
// Resolve gateway credentials: --list / auto-detect / manual flags
|
|
33
|
+
const creds = (0, login_1.loadCredentials)();
|
|
34
|
+
let dashUrl = opts.dashboardUrl || creds?.dashboard_url || "https://tameflare.com";
|
|
35
|
+
let gwId = opts.gatewayId;
|
|
36
|
+
let gwToken = opts.gatewayToken;
|
|
37
|
+
if (!gwId || !gwToken) {
|
|
38
|
+
if (!creds?.token) {
|
|
39
|
+
if (opts.list) {
|
|
40
|
+
console.error("[TF] Not logged in. Run 'tf login' first.");
|
|
102
41
|
process.exit(1);
|
|
103
42
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
let gatewayBin = findGatewayBinary();
|
|
108
|
-
if (!gatewayBin) {
|
|
109
|
-
console.log("[TF] Gateway binary not found. Downloading...");
|
|
110
|
-
try {
|
|
111
|
-
gatewayBin = await downloadGatewayBinary(tfDir);
|
|
112
|
-
}
|
|
113
|
-
catch (err) {
|
|
114
|
-
console.error(`[TF] Failed to download gateway: ${err.message}`);
|
|
115
|
-
console.error("[TF] You can manually download from: https://github.com/tameflare/tameflare/releases");
|
|
43
|
+
console.error("[TF] No gateway configured.");
|
|
44
|
+
console.error(" Run 'tf login' then 'tf init' to select a gateway.");
|
|
45
|
+
console.error(" Or pass --gateway-id and --gateway-token for CI/CD.");
|
|
116
46
|
process.exit(1);
|
|
117
47
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const gatewayEnv = { ...process.env };
|
|
122
|
-
let dashUrl = opts.dashboardUrl;
|
|
123
|
-
let gwId = opts.gatewayId;
|
|
124
|
-
let gwToken = opts.gatewayToken;
|
|
125
|
-
// Try reading from config file
|
|
126
|
-
if (!dashUrl || !gwId || !gwToken) {
|
|
127
|
-
try {
|
|
128
|
-
const cfgText = (0, fs_1.readFileSync)((0, utils_1.getConfigPath)(), "utf-8");
|
|
129
|
-
const dashSection = cfgText.split(/^dashboard:/m)[1] ?? "";
|
|
130
|
-
if (!dashUrl) {
|
|
131
|
-
const m = dashSection.match(/^\s+url:\s*"(.+?)"/m);
|
|
132
|
-
if (m)
|
|
133
|
-
dashUrl = m[1];
|
|
134
|
-
}
|
|
135
|
-
if (!gwId) {
|
|
136
|
-
const m = dashSection.match(/gateway_id:\s*"(.+?)"/);
|
|
137
|
-
if (m)
|
|
138
|
-
gwId = m[1];
|
|
139
|
-
}
|
|
140
|
-
if (!gwToken) {
|
|
141
|
-
const m = dashSection.match(/gateway_token:\s*"(.+?)"/);
|
|
142
|
-
if (m)
|
|
143
|
-
gwToken = m[1];
|
|
144
|
-
}
|
|
48
|
+
const picked = await pickGatewayInteractive(dashUrl, creds.token, opts.list ?? false);
|
|
49
|
+
if (!picked) {
|
|
50
|
+
process.exit(1);
|
|
145
51
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (!dashUrl && creds?.dashboard_url) {
|
|
150
|
-
dashUrl = creds.dashboard_url;
|
|
52
|
+
gwId = picked.id;
|
|
53
|
+
gwToken = picked.gateway_token;
|
|
54
|
+
console.log(`[TF] Selected gateway: ${picked.name} (${picked.id})`);
|
|
151
55
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
if (gwId)
|
|
155
|
-
gatewayEnv.TF_GATEWAY_ID = gwId;
|
|
156
|
-
if (gwToken)
|
|
157
|
-
gatewayEnv.TF_GATEWAY_TOKEN = gwToken;
|
|
158
|
-
if (dashUrl && gwId && gwToken) {
|
|
159
|
-
console.log(`[TF] Dashboard: ${dashUrl} (gateway: ${gwId})`);
|
|
160
|
-
}
|
|
161
|
-
else if (!gwId || !gwToken) {
|
|
162
|
-
console.log("[TF] No gateway credentials configured. Run 'tf login' then 'tf init --list' to select a gateway.");
|
|
163
|
-
}
|
|
164
|
-
console.log("[TF] Starting gateway...");
|
|
165
|
-
const gateway = (0, child_process_1.spawn)(gatewayBin, ["--config", (0, utils_1.getConfigPath)()], {
|
|
166
|
-
stdio: "inherit",
|
|
167
|
-
detached: false,
|
|
168
|
-
env: gatewayEnv,
|
|
169
|
-
});
|
|
170
|
-
gateway.on("error", (err) => {
|
|
171
|
-
console.error("[TF] Failed to start gateway:", err.message);
|
|
56
|
+
if (!gwToken) {
|
|
57
|
+
console.error("[TF] Gateway has no token. Recreate it in the dashboard.");
|
|
172
58
|
process.exit(1);
|
|
173
|
-
});
|
|
174
|
-
// Write PID file for stop command
|
|
175
|
-
const pidFile = (0, path_1.join)(tfDir, "gateway.pid");
|
|
176
|
-
if (gateway.pid) {
|
|
177
|
-
(0, fs_1.writeFileSync)(pidFile, String(gateway.pid), "utf-8");
|
|
178
59
|
}
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
60
|
+
// Write config
|
|
61
|
+
(0, fs_1.mkdirSync)(tfDir, { recursive: true });
|
|
62
|
+
const config = [
|
|
63
|
+
"# TameFlare gateway configuration",
|
|
64
|
+
`# Generated by tf init`,
|
|
65
|
+
"",
|
|
66
|
+
"dashboard:",
|
|
67
|
+
` url: "${dashUrl}"`,
|
|
68
|
+
` gateway_id: "${gwId}"`,
|
|
69
|
+
` gateway_token: "${gwToken}"`,
|
|
70
|
+
"",
|
|
71
|
+
"proxy:",
|
|
72
|
+
` host: "${PROXY_HOST}"`,
|
|
73
|
+
].join("\n");
|
|
74
|
+
(0, fs_1.writeFileSync)((0, utils_1.getConfigPath)(), config, "utf-8");
|
|
75
|
+
console.log("");
|
|
76
|
+
console.log("[TF] Initialized successfully!");
|
|
77
|
+
console.log(`[TF] Config: ${(0, utils_1.getConfigPath)()}`);
|
|
78
|
+
console.log(`[TF] Gateway: ${gwId}`);
|
|
79
|
+
console.log(`[TF] Proxy: https://${PROXY_HOST}`);
|
|
80
|
+
console.log("");
|
|
81
|
+
console.log(" Next steps:");
|
|
82
|
+
console.log(" tf run -- node agent.js # Run your agent through the gateway");
|
|
83
|
+
console.log(" tf status # Check gateway status");
|
|
84
|
+
console.log(" tf logs # View traffic logs");
|
|
188
85
|
});
|
|
189
86
|
return cmd;
|
|
190
87
|
}
|
|
191
88
|
async function pickGatewayInteractive(dashboardUrl, token, explicit) {
|
|
192
89
|
try {
|
|
193
|
-
const res = await fetch(`${dashboardUrl}/api/cli/gateways`, {
|
|
90
|
+
const res = await fetch(`${dashboardUrl}/api/cli/gateways?token=${encodeURIComponent(token)}`, {
|
|
194
91
|
headers: { Authorization: `Bearer ${token}` },
|
|
195
92
|
});
|
|
196
93
|
if (!res.ok) {
|
|
@@ -205,7 +102,8 @@ async function pickGatewayInteractive(dashboardUrl, token, explicit) {
|
|
|
205
102
|
const data = await res.json();
|
|
206
103
|
const gws = data.gateways ?? [];
|
|
207
104
|
if (gws.length === 0) {
|
|
208
|
-
console.log("[TF] No gateways found. Create one at the dashboard
|
|
105
|
+
console.log("[TF] No gateways found. Create one at the dashboard:");
|
|
106
|
+
console.log(` ${dashboardUrl}/dashboard/gateways/new`);
|
|
209
107
|
return null;
|
|
210
108
|
}
|
|
211
109
|
// Auto-select if only one gateway
|
|
@@ -236,187 +134,8 @@ async function pickGatewayInteractive(dashboardUrl, token, explicit) {
|
|
|
236
134
|
}
|
|
237
135
|
catch (err) {
|
|
238
136
|
if (!explicit)
|
|
239
|
-
return null;
|
|
137
|
+
return null;
|
|
240
138
|
console.error(`[TF] Failed to list gateways: ${err.message}`);
|
|
241
139
|
return null;
|
|
242
140
|
}
|
|
243
141
|
}
|
|
244
|
-
function getPlatformTemplate(platform) {
|
|
245
|
-
const templates = {
|
|
246
|
-
openclaw: {
|
|
247
|
-
name: "OpenClaw",
|
|
248
|
-
description: "AI agent framework - blocks all unknown domains, pre-allows LLM APIs",
|
|
249
|
-
connectors: ["openai", "anthropic"],
|
|
250
|
-
setup_commands: [
|
|
251
|
-
'npx tf connector add openai --token $OPENAI_API_KEY',
|
|
252
|
-
'npx tf connector add anthropic --token $ANTHROPIC_API_KEY',
|
|
253
|
-
'npx tf permissions set --gateway "default" --connector openai --action "openai.chat.*" --decision allow',
|
|
254
|
-
'npx tf permissions set --gateway "default" --connector anthropic --action "anthropic.message.*" --decision allow',
|
|
255
|
-
'# All other outbound traffic is blocked by default',
|
|
256
|
-
],
|
|
257
|
-
},
|
|
258
|
-
langchain: {
|
|
259
|
-
name: "LangChain",
|
|
260
|
-
description: "LLM framework - pre-allows LLM APIs and search, blocks file:// and localhost",
|
|
261
|
-
connectors: ["openai", "anthropic"],
|
|
262
|
-
setup_commands: [
|
|
263
|
-
'npx tf connector add openai --token $OPENAI_API_KEY',
|
|
264
|
-
'npx tf connector add anthropic --token $ANTHROPIC_API_KEY',
|
|
265
|
-
'npx tf permissions set --gateway "default" --connector openai --action "*" --decision allow',
|
|
266
|
-
'npx tf permissions set --gateway "default" --connector anthropic --action "*" --decision allow',
|
|
267
|
-
'# Add tool connectors as needed:',
|
|
268
|
-
'# npx tf connector add generic --domains serpapi.com --token $SERP_KEY',
|
|
269
|
-
],
|
|
270
|
-
},
|
|
271
|
-
n8n: {
|
|
272
|
-
name: "n8n",
|
|
273
|
-
description: "Workflow automation - pre-allows common integration endpoints",
|
|
274
|
-
connectors: ["openai", "slack", "github"],
|
|
275
|
-
setup_commands: [
|
|
276
|
-
'npx tf connector add openai --token $OPENAI_API_KEY',
|
|
277
|
-
'npx tf connector add slack --token $SLACK_BOT_TOKEN',
|
|
278
|
-
'npx tf connector add github --token $GITHUB_TOKEN',
|
|
279
|
-
'npx tf permissions set --gateway "workflow" --connector openai --action "*" --decision allow',
|
|
280
|
-
'npx tf permissions set --gateway "workflow" --connector slack --action "slack.message.*" --decision allow',
|
|
281
|
-
'npx tf permissions set --gateway "workflow" --connector github --action "github.issue.*" --decision allow',
|
|
282
|
-
'npx tf permissions set --gateway "workflow" --connector github --action "github.pr.merge" --decision require_approval',
|
|
283
|
-
],
|
|
284
|
-
},
|
|
285
|
-
"claude-code": {
|
|
286
|
-
name: "Claude Code",
|
|
287
|
-
description: "AI coding assistant - pre-allows package registries and git, blocks all other outbound",
|
|
288
|
-
connectors: ["github"],
|
|
289
|
-
setup_commands: [
|
|
290
|
-
'npx tf connector add github --token $GITHUB_TOKEN',
|
|
291
|
-
'npx tf connector add generic --domains registry.npmjs.org,pypi.org --auth-type none',
|
|
292
|
-
'npx tf permissions set --gateway "claude" --connector github --action "github.api.read" --decision allow',
|
|
293
|
-
'npx tf permissions set --gateway "claude" --connector github --action "github.pr.create" --decision allow',
|
|
294
|
-
'npx tf permissions set --gateway "claude" --connector github --action "github.pr.merge" --decision require_approval',
|
|
295
|
-
'npx tf permissions set --gateway "claude" --connector github --action "github.file.commit" --decision allow',
|
|
296
|
-
'npx tf permissions set --gateway "claude" --connector generic --action "*" --decision allow',
|
|
297
|
-
'# All other outbound traffic is blocked by default',
|
|
298
|
-
],
|
|
299
|
-
},
|
|
300
|
-
};
|
|
301
|
-
return templates[platform] ?? null;
|
|
302
|
-
}
|
|
303
|
-
function findGatewayBinary() {
|
|
304
|
-
const tfDir = (0, utils_1.getTfDir)();
|
|
305
|
-
const isWin = process.platform === "win32";
|
|
306
|
-
const binName = isWin ? "tameflare-gateway.exe" : "tameflare-gateway";
|
|
307
|
-
const candidates = [
|
|
308
|
-
// Preferred: downloaded binary in .tf/bin/
|
|
309
|
-
(0, path_1.join)(tfDir, "bin", binName),
|
|
310
|
-
// Dev: local build in monorepo
|
|
311
|
-
(0, path_1.join)(process.cwd(), "apps", "gateway-v2", binName),
|
|
312
|
-
(0, path_1.join)(process.cwd(), "apps", "gateway-v2", isWin ? "gateway.exe" : "gateway"),
|
|
313
|
-
// Fallback: cwd
|
|
314
|
-
(0, path_1.join)(process.cwd(), binName),
|
|
315
|
-
(0, path_1.join)(process.cwd(), isWin ? "gateway.exe" : "gateway"),
|
|
316
|
-
];
|
|
317
|
-
for (const candidate of candidates) {
|
|
318
|
-
if ((0, fs_1.existsSync)(candidate)) {
|
|
319
|
-
return candidate;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
function getPlatformArch() {
|
|
325
|
-
const platform = process.platform;
|
|
326
|
-
const arch = process.arch;
|
|
327
|
-
let os;
|
|
328
|
-
switch (platform) {
|
|
329
|
-
case "linux":
|
|
330
|
-
os = "linux";
|
|
331
|
-
break;
|
|
332
|
-
case "darwin":
|
|
333
|
-
os = "darwin";
|
|
334
|
-
break;
|
|
335
|
-
case "win32":
|
|
336
|
-
os = "windows";
|
|
337
|
-
break;
|
|
338
|
-
default: throw new Error(`Unsupported platform: ${platform}`);
|
|
339
|
-
}
|
|
340
|
-
let goArch;
|
|
341
|
-
switch (arch) {
|
|
342
|
-
case "x64":
|
|
343
|
-
goArch = "amd64";
|
|
344
|
-
break;
|
|
345
|
-
case "arm64":
|
|
346
|
-
goArch = "arm64";
|
|
347
|
-
break;
|
|
348
|
-
default: throw new Error(`Unsupported architecture: ${arch}`);
|
|
349
|
-
}
|
|
350
|
-
const ext = os === "windows" ? "zip" : "tar.gz";
|
|
351
|
-
return { os, arch: goArch, ext };
|
|
352
|
-
}
|
|
353
|
-
function followRedirects(url) {
|
|
354
|
-
return new Promise((resolve, reject) => {
|
|
355
|
-
const getter = url.startsWith("https") ? https_1.get : http_1.get;
|
|
356
|
-
getter(url, (res) => {
|
|
357
|
-
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
358
|
-
followRedirects(res.headers.location).then(resolve).catch(reject);
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
if (res.statusCode && res.statusCode >= 400) {
|
|
362
|
-
reject(new Error(`HTTP ${res.statusCode} downloading ${url}`));
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
const chunks = [];
|
|
366
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
367
|
-
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
368
|
-
res.on("error", reject);
|
|
369
|
-
}).on("error", reject);
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
async function downloadGatewayBinary(tfDir) {
|
|
373
|
-
const { os, arch, ext } = getPlatformArch();
|
|
374
|
-
const archiveName = `tameflare-gateway_${GATEWAY_VERSION}_${os}_${arch}.${ext}`;
|
|
375
|
-
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/gateway-v${GATEWAY_VERSION}/${archiveName}`;
|
|
376
|
-
console.log(`[TF] Downloading gateway v${GATEWAY_VERSION} for ${os}/${arch}...`);
|
|
377
|
-
console.log(`[TF] From: ${downloadUrl}`);
|
|
378
|
-
const data = await followRedirects(downloadUrl);
|
|
379
|
-
const binDir = (0, path_1.join)(tfDir, "bin");
|
|
380
|
-
(0, fs_1.mkdirSync)(binDir, { recursive: true });
|
|
381
|
-
const archivePath = (0, path_1.join)(binDir, archiveName);
|
|
382
|
-
(0, fs_1.writeFileSync)(archivePath, data);
|
|
383
|
-
const isWin = os === "windows";
|
|
384
|
-
const binName = isWin ? "tameflare-gateway.exe" : "tameflare-gateway";
|
|
385
|
-
const binPath = (0, path_1.join)(binDir, binName);
|
|
386
|
-
// Extract the binary from the archive
|
|
387
|
-
if (ext === "tar.gz") {
|
|
388
|
-
try {
|
|
389
|
-
(0, child_process_1.execSync)(`tar -xzf "${archivePath}" -C "${binDir}" ${binName}`, { stdio: "pipe" });
|
|
390
|
-
}
|
|
391
|
-
catch {
|
|
392
|
-
// Some tar versions need different syntax
|
|
393
|
-
(0, child_process_1.execSync)(`tar -xzf "${archivePath}" -C "${binDir}"`, { stdio: "pipe" });
|
|
394
|
-
}
|
|
395
|
-
(0, fs_1.chmodSync)(binPath, 0o755);
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
// Windows zip - use PowerShell
|
|
399
|
-
(0, child_process_1.execSync)(`powershell -Command "Expand-Archive -Force -Path '${archivePath}' -DestinationPath '${binDir}'"`, { stdio: "pipe" });
|
|
400
|
-
}
|
|
401
|
-
// Clean up archive
|
|
402
|
-
try {
|
|
403
|
-
(0, fs_1.unlinkSync)(archivePath);
|
|
404
|
-
}
|
|
405
|
-
catch { }
|
|
406
|
-
if (!(0, fs_1.existsSync)(binPath)) {
|
|
407
|
-
// Binary might be in a subdirectory - search for it
|
|
408
|
-
const files = (0, fs_1.readdirSync)(binDir, { recursive: true });
|
|
409
|
-
const found = files.find((f) => typeof f === "string" && f.endsWith(binName));
|
|
410
|
-
if (found) {
|
|
411
|
-
const foundPath = (0, path_1.join)(binDir, found);
|
|
412
|
-
if (foundPath !== binPath) {
|
|
413
|
-
(0, fs_1.renameSync)(foundPath, binPath);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
else {
|
|
417
|
-
throw new Error(`Binary ${binName} not found in downloaded archive`);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
console.log(`[TF] Gateway binary installed at ${binPath}`);
|
|
421
|
-
return binPath;
|
|
422
|
-
}
|
package/dist/commands/logs.js
CHANGED
|
@@ -1,69 +1,61 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.logsCommand = logsCommand;
|
|
4
37
|
const commander_1 = require("commander");
|
|
5
38
|
const utils_1 = require("../utils");
|
|
6
39
|
function logsCommand() {
|
|
7
40
|
const cmd = new commander_1.Command("logs")
|
|
8
|
-
.description("View
|
|
9
|
-
.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(
|
|
41
|
+
.description("View traffic logs in the dashboard")
|
|
42
|
+
.action(async () => {
|
|
43
|
+
const cfg = (0, utils_1.requireConfig)();
|
|
44
|
+
const url = `${cfg.dashboard_url}/dashboard/actions`;
|
|
45
|
+
console.log(`[TF] Traffic logs are available in the dashboard:`);
|
|
46
|
+
console.log(` ${url}`);
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log(` Gateway: ${cfg.gateway_id}`);
|
|
49
|
+
// Try to open the browser
|
|
13
50
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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`);
|
|
51
|
+
const { exec } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
52
|
+
const openCmd = process.platform === "win32" ? "start" :
|
|
53
|
+
process.platform === "darwin" ? "open" : "xdg-open";
|
|
54
|
+
exec(`${openCmd} ${url}`);
|
|
42
55
|
}
|
|
43
|
-
catch
|
|
44
|
-
|
|
45
|
-
console.error(" Start it with: tf init");
|
|
56
|
+
catch {
|
|
57
|
+
// Browser open is best-effort
|
|
46
58
|
}
|
|
47
59
|
});
|
|
48
60
|
return cmd;
|
|
49
61
|
}
|
|
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
|
-
}
|
package/dist/commands/run.js
CHANGED
|
@@ -6,48 +6,23 @@ const child_process_1 = require("child_process");
|
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
function runCommand() {
|
|
8
8
|
const cmd = new commander_1.Command("run")
|
|
9
|
-
.description("Run a process through the TameFlare gateway
|
|
10
|
-
.
|
|
11
|
-
.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
(0, utils_1.requireGateway)();
|
|
15
|
-
const processName = opts.gateway || opts.name;
|
|
9
|
+
.description("Run a process through the TameFlare cloud gateway")
|
|
10
|
+
.argument("<command...>", "Command to run (use -- before the command)")
|
|
11
|
+
.action(async (commandArgs) => {
|
|
12
|
+
const cfg = (0, utils_1.requireConfig)();
|
|
13
|
+
const proxyUrl = (0, utils_1.getProxyUrl)(cfg);
|
|
16
14
|
const [command, ...args] = commandArgs;
|
|
17
|
-
console.log(`[TF]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
registration = await (0, utils_1.gatewayRequest)("POST", "/internal/processes/register", {
|
|
22
|
-
name: processName,
|
|
23
|
-
pid: process.pid,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
catch (err) {
|
|
27
|
-
console.error(`[TF] Failed to register process: ${err.message}`);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
const proxyUrl = registration.proxy_url;
|
|
31
|
-
const caCert = registration.ca_cert;
|
|
32
|
-
console.log(`[TF] Process "${processName}" registered on port ${registration.port}`);
|
|
33
|
-
console.log(`[TF] Proxy: ${proxyUrl}`);
|
|
34
|
-
console.log(`[TF] Starting: ${command} ${args.join(" ")}`);
|
|
15
|
+
console.log(`[TF] Gateway: ${cfg.gateway_id}`);
|
|
16
|
+
console.log(`[TF] Proxy: https://${cfg.proxy_host}`);
|
|
17
|
+
console.log(`[TF] Running: ${command} ${args.join(" ")}`);
|
|
35
18
|
console.log("");
|
|
36
|
-
// Spawn child process with proxy environment variables
|
|
37
19
|
const env = {
|
|
38
20
|
...process.env,
|
|
39
21
|
HTTP_PROXY: proxyUrl,
|
|
40
22
|
HTTPS_PROXY: proxyUrl,
|
|
41
23
|
http_proxy: proxyUrl,
|
|
42
24
|
https_proxy: proxyUrl,
|
|
43
|
-
|
|
44
|
-
NODE_EXTRA_CA_CERTS: caCert,
|
|
45
|
-
SSL_CERT_FILE: caCert,
|
|
46
|
-
REQUESTS_CA_BUNDLE: caCert,
|
|
47
|
-
CURL_CA_BUNDLE: caCert,
|
|
48
|
-
// Mark as TameFlare-wrapped (processes can detect this if needed)
|
|
49
|
-
TF_PROCESS_NAME: processName,
|
|
50
|
-
TF_PROXY_PORT: String(registration.port),
|
|
25
|
+
TF_GATEWAY_ID: cfg.gateway_id,
|
|
51
26
|
};
|
|
52
27
|
const child = (0, child_process_1.spawn)(command, args, {
|
|
53
28
|
stdio: "inherit",
|
|
@@ -56,31 +31,18 @@ function runCommand() {
|
|
|
56
31
|
});
|
|
57
32
|
child.on("error", (err) => {
|
|
58
33
|
console.error(`[TF] Failed to start command: ${err.message}`);
|
|
59
|
-
deregister(processName);
|
|
60
34
|
process.exit(1);
|
|
61
35
|
});
|
|
62
36
|
child.on("exit", (code, signal) => {
|
|
63
37
|
console.log("");
|
|
64
|
-
console.log(`[TF] Process
|
|
65
|
-
deregister(processName);
|
|
38
|
+
console.log(`[TF] Process exited (code ${code ?? signal})`);
|
|
66
39
|
process.exit(code ?? 1);
|
|
67
40
|
});
|
|
68
|
-
// Clean shutdown on signals
|
|
69
41
|
const cleanup = () => {
|
|
70
42
|
child.kill("SIGTERM");
|
|
71
|
-
deregister(processName);
|
|
72
43
|
};
|
|
73
44
|
process.on("SIGINT", cleanup);
|
|
74
45
|
process.on("SIGTERM", cleanup);
|
|
75
46
|
});
|
|
76
47
|
return cmd;
|
|
77
48
|
}
|
|
78
|
-
async function deregister(name) {
|
|
79
|
-
try {
|
|
80
|
-
await (0, utils_1.gatewayRequest)("POST", "/internal/processes/deregister", { name });
|
|
81
|
-
console.log(`[TF] Process "${name}" deregistered`);
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// Gateway may already be down - ignore
|
|
85
|
-
}
|
|
86
|
-
}
|
package/dist/commands/status.js
CHANGED
|
@@ -5,55 +5,33 @@ const commander_1 = require("commander");
|
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
function statusCommand() {
|
|
7
7
|
const cmd = new commander_1.Command("status")
|
|
8
|
-
.description("Show gateway and
|
|
8
|
+
.description("Show gateway configuration and connection status")
|
|
9
9
|
.action(async () => {
|
|
10
|
-
(0, utils_1.
|
|
10
|
+
const cfg = (0, utils_1.requireConfig)();
|
|
11
|
+
console.log("");
|
|
12
|
+
console.log(" TameFlare Gateway");
|
|
13
|
+
console.log(" ─────────────────────────────────");
|
|
14
|
+
console.log(` Gateway ID: ${cfg.gateway_id}`);
|
|
15
|
+
console.log(` Dashboard: ${cfg.dashboard_url}`);
|
|
16
|
+
console.log(` Proxy: https://${cfg.proxy_host}`);
|
|
17
|
+
console.log("");
|
|
18
|
+
// Check cloud proxy connectivity
|
|
11
19
|
try {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
console.log(" ─────────────────────────────────");
|
|
16
|
-
if (status.gateway_name) {
|
|
17
|
-
console.log(` Gateway: ${status.gateway_name}`);
|
|
20
|
+
const res = await fetch(`${cfg.dashboard_url}/api/health`);
|
|
21
|
+
if (res.ok) {
|
|
22
|
+
console.log(` Dashboard: \x1b[32mreachable\x1b[0m`);
|
|
18
23
|
}
|
|
19
|
-
|
|
20
|
-
console.log(`
|
|
24
|
+
else {
|
|
25
|
+
console.log(` Dashboard: \x1b[31munreachable (${res.status})\x1b[0m`);
|
|
21
26
|
}
|
|
22
|
-
console.log(` Status: \x1b[32m${status.status}\x1b[0m`);
|
|
23
|
-
console.log(` Enforcement: ${status.enforcement_level}`);
|
|
24
|
-
if (status.uptime) {
|
|
25
|
-
console.log(` Uptime: ${status.uptime}`);
|
|
26
|
-
}
|
|
27
|
-
console.log(` Processes: ${status.processes_active}`);
|
|
28
|
-
if (status.connectors_active !== undefined) {
|
|
29
|
-
console.log(` Connectors: ${status.connectors_active}`);
|
|
30
|
-
}
|
|
31
|
-
console.log("");
|
|
32
|
-
console.log(" Traffic");
|
|
33
|
-
console.log(" ─────────────────────────────────");
|
|
34
|
-
console.log(` Total: ${status.traffic.total}`);
|
|
35
|
-
console.log(` Allowed: \x1b[32m${status.traffic.allowed}\x1b[0m`);
|
|
36
|
-
console.log(` Denied: \x1b[31m${status.traffic.denied}\x1b[0m`);
|
|
37
|
-
// List processes
|
|
38
|
-
const processes = await (0, utils_1.gatewayRequest)("GET", "/internal/processes");
|
|
39
|
-
if (processes && processes.length > 0) {
|
|
40
|
-
console.log("");
|
|
41
|
-
console.log(" Active Processes");
|
|
42
|
-
console.log(" ─────────────────────────────────");
|
|
43
|
-
const rows = processes.map((p) => [
|
|
44
|
-
p.name,
|
|
45
|
-
String(p.port),
|
|
46
|
-
String(p.request_count),
|
|
47
|
-
p.last_activity ? new Date(p.last_activity).toLocaleTimeString() : "-",
|
|
48
|
-
]);
|
|
49
|
-
console.log((0, utils_1.formatTable)(["Name", "Port", "Requests", "Last Activity"], rows));
|
|
50
|
-
}
|
|
51
|
-
console.log("");
|
|
52
27
|
}
|
|
53
|
-
catch
|
|
54
|
-
console.
|
|
55
|
-
console.error(" Start it with: tf init");
|
|
28
|
+
catch {
|
|
29
|
+
console.log(` Dashboard: \x1b[31munreachable\x1b[0m`);
|
|
56
30
|
}
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log(" Run your agent:");
|
|
33
|
+
console.log(" tf run -- node agent.js");
|
|
34
|
+
console.log("");
|
|
57
35
|
});
|
|
58
36
|
return cmd;
|
|
59
37
|
}
|
package/dist/commands/stop.js
CHANGED
|
@@ -3,37 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.stopCommand = stopCommand;
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
5
|
const fs_1 = require("fs");
|
|
6
|
-
const path_1 = require("path");
|
|
7
6
|
const utils_1 = require("../utils");
|
|
8
7
|
function stopCommand() {
|
|
9
|
-
const cmd = new commander_1.Command("
|
|
10
|
-
.description("
|
|
8
|
+
const cmd = new commander_1.Command("reset")
|
|
9
|
+
.description("Remove TameFlare configuration from this directory")
|
|
11
10
|
.action(async () => {
|
|
12
|
-
(0, utils_1.
|
|
13
|
-
|
|
14
|
-
if (!(0, fs_1.existsSync)(pidFile)) {
|
|
15
|
-
console.log("[TF] No running gateway found (no PID file).");
|
|
11
|
+
if (!(0, utils_1.isInitialized)()) {
|
|
12
|
+
console.log("[TF] Not initialized in this directory.");
|
|
16
13
|
return;
|
|
17
14
|
}
|
|
18
|
-
const pid = parseInt((0, fs_1.readFileSync)(pidFile, "utf-8").trim(), 10);
|
|
19
15
|
try {
|
|
20
|
-
|
|
21
|
-
console.log(
|
|
16
|
+
(0, fs_1.rmSync)((0, utils_1.getTfDir)(), { recursive: true, force: true });
|
|
17
|
+
console.log("[TF] Configuration removed.");
|
|
18
|
+
console.log(" Run 'tf init' to set up again.");
|
|
22
19
|
}
|
|
23
20
|
catch (err) {
|
|
24
|
-
|
|
25
|
-
console.log("[TF] Gateway process not found (already stopped).");
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.error(`[TF] Failed to stop gateway: ${err.message}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
// Clean up PID file
|
|
32
|
-
try {
|
|
33
|
-
(0, fs_1.unlinkSync)(pidFile);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Ignore
|
|
21
|
+
console.error(`[TF] Failed to remove config: ${err.message}`);
|
|
37
22
|
}
|
|
38
23
|
});
|
|
39
24
|
return cmd;
|
package/dist/index.js
CHANGED
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
|
+
const login_1 = require("./commands/login");
|
|
5
6
|
const init_1 = require("./commands/init");
|
|
6
7
|
const run_1 = require("./commands/run");
|
|
7
8
|
const status_1 = require("./commands/status");
|
|
8
9
|
const stop_1 = require("./commands/stop");
|
|
9
10
|
const logs_1 = require("./commands/logs");
|
|
10
|
-
const kill_switch_1 = require("./commands/kill-switch");
|
|
11
|
-
const connector_1 = require("./commands/connector");
|
|
12
|
-
const approvals_1 = require("./commands/approvals");
|
|
13
|
-
const login_1 = require("./commands/login");
|
|
14
11
|
const program = new commander_1.Command();
|
|
15
12
|
program
|
|
16
13
|
.name("tf")
|
|
17
|
-
.description("TameFlare -
|
|
18
|
-
.version("0.
|
|
14
|
+
.description("TameFlare - Route AI agent traffic through a secure cloud gateway")
|
|
15
|
+
.version("0.10.0");
|
|
19
16
|
program.addCommand((0, login_1.loginCommand)());
|
|
20
17
|
program.addCommand((0, login_1.logoutCommand)());
|
|
21
18
|
program.addCommand((0, init_1.initCommand)());
|
|
@@ -23,8 +20,4 @@ program.addCommand((0, run_1.runCommand)());
|
|
|
23
20
|
program.addCommand((0, status_1.statusCommand)());
|
|
24
21
|
program.addCommand((0, stop_1.stopCommand)());
|
|
25
22
|
program.addCommand((0, logs_1.logsCommand)());
|
|
26
|
-
program.addCommand((0, kill_switch_1.killSwitchCommand)());
|
|
27
|
-
program.addCommand((0, connector_1.connectorCommand)());
|
|
28
|
-
program.addCommand((0, connector_1.permissionsCommand)());
|
|
29
|
-
program.addCommand((0, approvals_1.approvalsCommand)());
|
|
30
23
|
program.parse();
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
export interface TfConfig {
|
|
2
|
+
dashboard_url: string;
|
|
3
|
+
gateway_id: string;
|
|
4
|
+
gateway_token: string;
|
|
5
|
+
proxy_host: string;
|
|
6
|
+
}
|
|
1
7
|
export declare function getTfDir(): string;
|
|
2
8
|
export declare function getConfigPath(): string;
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
9
|
+
export declare function isInitialized(): boolean;
|
|
10
|
+
export declare function loadConfig(): TfConfig | null;
|
|
11
|
+
export declare function requireConfig(): TfConfig;
|
|
12
|
+
export declare function getProxyUrl(cfg: TfConfig): string;
|
|
13
|
+
export declare function dashboardRequest(cfg: TfConfig, method: string, path: string, body?: any): Promise<any>;
|
|
7
14
|
export declare function formatTable(headers: string[], rows: string[][]): string;
|
package/dist/utils.js
CHANGED
|
@@ -2,57 +2,77 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTfDir = getTfDir;
|
|
4
4
|
exports.getConfigPath = getConfigPath;
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
5
|
+
exports.isInitialized = isInitialized;
|
|
6
|
+
exports.loadConfig = loadConfig;
|
|
7
|
+
exports.requireConfig = requireConfig;
|
|
8
|
+
exports.getProxyUrl = getProxyUrl;
|
|
9
|
+
exports.dashboardRequest = dashboardRequest;
|
|
9
10
|
exports.formatTable = formatTable;
|
|
10
11
|
const fs_1 = require("fs");
|
|
11
12
|
const path_1 = require("path");
|
|
12
|
-
const GATEWAY_PORT = 9443;
|
|
13
13
|
function getTfDir() {
|
|
14
14
|
return (0, path_1.join)(process.cwd(), ".tf");
|
|
15
15
|
}
|
|
16
16
|
function getConfigPath() {
|
|
17
17
|
return (0, path_1.join)(getTfDir(), "config.yaml");
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
return `http://127.0.0.1:${GATEWAY_PORT}`;
|
|
21
|
-
}
|
|
22
|
-
function isGatewayInitialized() {
|
|
19
|
+
function isInitialized() {
|
|
23
20
|
return (0, fs_1.existsSync)(getConfigPath());
|
|
24
21
|
}
|
|
25
|
-
|
|
26
|
-
const url = `${getGatewayUrl()}${path}`;
|
|
27
|
-
const headers = {
|
|
28
|
-
"Content-Type": "application/json",
|
|
29
|
-
};
|
|
22
|
+
function loadConfig() {
|
|
30
23
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
24
|
+
const text = (0, fs_1.readFileSync)(getConfigPath(), "utf-8");
|
|
25
|
+
const get = (key) => {
|
|
26
|
+
const m = text.match(new RegExp(`${key}:\\s*"(.+?)"`));
|
|
27
|
+
return m?.[1] ?? "";
|
|
28
|
+
};
|
|
29
|
+
const cfg = {
|
|
30
|
+
dashboard_url: get("url"),
|
|
31
|
+
gateway_id: get("gateway_id"),
|
|
32
|
+
gateway_token: get("gateway_token"),
|
|
33
|
+
proxy_host: get("host") || "proxy.tameflare.com",
|
|
34
|
+
};
|
|
35
|
+
if (!cfg.gateway_id || !cfg.gateway_token)
|
|
36
|
+
return null;
|
|
37
|
+
return cfg;
|
|
43
38
|
}
|
|
44
|
-
catch
|
|
45
|
-
|
|
46
|
-
throw new Error("Gateway is not running. Start it with 'tf init' first.");
|
|
47
|
-
}
|
|
48
|
-
throw err;
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
49
41
|
}
|
|
50
42
|
}
|
|
51
|
-
function
|
|
52
|
-
if (!
|
|
53
|
-
console.error("
|
|
43
|
+
function requireConfig() {
|
|
44
|
+
if (!isInitialized()) {
|
|
45
|
+
console.error("[TF] Not initialized. Run 'tf init' first.");
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
const cfg = loadConfig();
|
|
49
|
+
if (!cfg) {
|
|
50
|
+
console.error("[TF] Invalid config. Run 'tf init' to reconfigure.");
|
|
54
51
|
process.exit(1);
|
|
55
52
|
}
|
|
53
|
+
return cfg;
|
|
54
|
+
}
|
|
55
|
+
function getProxyUrl(cfg) {
|
|
56
|
+
return `https://${cfg.gateway_token}@${cfg.proxy_host}`;
|
|
57
|
+
}
|
|
58
|
+
async function dashboardRequest(cfg, method, path, body) {
|
|
59
|
+
const url = `${cfg.dashboard_url}${path}`;
|
|
60
|
+
const headers = {
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
"X-Gateway-Token": cfg.gateway_token,
|
|
63
|
+
};
|
|
64
|
+
const response = await fetch(url, {
|
|
65
|
+
method,
|
|
66
|
+
headers,
|
|
67
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const error = await response.json().catch(() => ({
|
|
71
|
+
error: response.statusText,
|
|
72
|
+
}));
|
|
73
|
+
throw new Error(`Dashboard error (${response.status}): ${error.error || error.message || response.statusText}`);
|
|
74
|
+
}
|
|
75
|
+
return response.json();
|
|
56
76
|
}
|
|
57
77
|
function formatTable(headers, rows) {
|
|
58
78
|
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
|