@toothfairyai/tfcode 1.0.0 → 1.0.1

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/LICENSE CHANGED
File without changes
package/bin/tfcode.js CHANGED
@@ -1,298 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ const { spawn } = require("child_process")
3
+ const path = require("path")
4
+ const fs = require("fs")
2
5
 
3
- import { spawn } from 'child_process';
4
- import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
5
- import { join, dirname } from 'path';
6
- import { homedir } from 'os';
7
- import * as readline from 'readline';
8
- import { fileURLToPath } from 'url';
6
+ const binDir = path.join(__dirname, "bin")
7
+ const binary = process.platform === "win32" ? "tfcode.exe" : "tfcode"
8
+ const binaryPath = path.join(binDir, binary)
9
9
 
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
-
13
- const TFCODE_DIR = join(homedir(), '.tfcode');
14
- const TOOLS_FILE = join(TFCODE_DIR, 'tools.json');
15
- const CREDENTIALS_FILE = join(TFCODE_DIR, 'credentials.json');
16
- const CONFIG_FILE = join(TFCODE_DIR, 'config.json');
17
-
18
- const COLORS = {
19
- reset: '\x1b[0m',
20
- bold: '\x1b[1m',
21
- green: '\x1b[32m',
22
- red: '\x1b[31m',
23
- cyan: '\x1b[36m',
24
- dim: '\x1b[90m',
25
- yellow: '\x1b[33m',
26
- magenta: '\x1b[35m'
27
- };
28
-
29
- function log(msg) { console.log(msg); }
30
- function success(msg) { console.log(`${COLORS.green}✓${COLORS.reset} ${msg}`); }
31
- function error(msg) { console.error(`${COLORS.red}✗${COLORS.reset} ${msg}`); }
32
- function info(msg) { console.log(`${COLORS.cyan}ℹ${COLORS.reset} ${msg}`); }
33
-
34
- function ensureConfigDir() {
35
- if (!existsSync(TFCODE_DIR)) mkdirSync(TFCODE_DIR, { recursive: true });
36
- }
37
-
38
- function loadConfig() {
39
- const envConfig = {
40
- workspace_id: process.env.TF_WORKSPACE_ID,
41
- api_key: process.env.TF_API_KEY,
42
- region: process.env.TF_REGION
43
- };
44
- if (envConfig.workspace_id && envConfig.api_key) return envConfig;
45
- if (existsSync(CONFIG_FILE)) {
46
- try { return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8')); } catch {}
47
- }
48
- return null;
49
- }
50
-
51
- function saveConfig(config) {
52
- ensureConfigDir();
53
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
54
- }
55
-
56
- function runPythonSync(method, config = null) {
57
- const wsId = config?.workspace_id || process.env.TF_WORKSPACE_ID || '';
58
- const apiKey = config?.api_key || process.env.TF_API_KEY || '';
59
- const region = config?.region || process.env.TF_REGION || 'au';
60
-
61
- const pythonCode = `
62
- import json, sys, os
63
- try:
64
- os.environ["TF_WORKSPACE_ID"] = "${wsId}"
65
- os.environ["TF_API_KEY"] = "${apiKey}"
66
- os.environ["TF_REGION"] = "${region}"
67
- from tf_sync.config import load_config, validate_credentials, Region
68
- from tf_sync.tools import sync_tools
69
- from tf_sync.config import get_region_urls
70
-
71
- method = "${method}"
72
- if method == "validate":
73
- config = load_config()
74
- result = validate_credentials(config)
75
- urls = get_region_urls(config.region)
76
- print(json.dumps({"success": result.success, "workspace_id": result.workspace_id, "workspace_name": result.workspace_name, "error": result.error, "base_url": urls["base_url"]}))
77
- elif method == "sync":
78
- config = load_config()
79
- result = sync_tools(config)
80
- tools_data = [{"id": t.id, "name": t.name, "description": t.description, "tool_type": t.tool_type.value, "request_type": t.request_type.value if t.request_type else None, "url": t.url, "auth_via": t.auth_via} for t in result.tools]
81
- print(json.dumps({"success": result.success, "tools": tools_data, "by_type": result.by_type, "error": result.error}))
82
- except Exception as e:
83
- print(json.dumps({"success": False, "error": str(e)}))
84
- `;
85
-
86
- return new Promise((resolve, reject) => {
87
- const proc = spawn(process.env.TFCODE_PYTHON_PATH || 'python3', ['-c', pythonCode], { env: { ...process.env } });
88
- let stdout = '', stderr = '';
89
- proc.stdout.on('data', (d) => stdout += d);
90
- proc.stderr.on('data', (d) => stderr += d);
91
- proc.on('close', (code) => {
92
- if (code !== 0 && !stdout) reject(new Error(`Python failed: ${stderr}`));
93
- else try { resolve(JSON.parse(stdout.trim())); } catch (e) { reject(new Error(`Parse error: ${stdout}`)); }
94
- });
95
- proc.on('error', reject);
96
- });
97
- }
98
-
99
- function loadCachedTools() {
100
- if (!existsSync(TOOLS_FILE)) return null;
101
- try { return JSON.parse(readFileSync(TOOLS_FILE, 'utf-8')); } catch { return null; }
102
- }
103
-
104
- function saveToolsCache(tools) {
105
- ensureConfigDir();
106
- writeFileSync(TOOLS_FILE, JSON.stringify(tools, null, 2));
107
- }
108
-
109
- async function question(prompt) {
110
- return new Promise((resolve) => {
111
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
112
- rl.question(prompt, (answer) => { rl.close(); resolve(answer.trim()); });
113
- });
114
- }
115
-
116
- async function select(prompt, options) {
117
- log('');
118
- log(prompt);
119
- log('');
120
- options.forEach((opt, i) => log(` ${COLORS.cyan}${i + 1}.${COLORS.reset} ${opt}`));
121
- log('');
122
- const answer = await question('Select (1-' + options.length + '): ');
123
- const idx = parseInt(answer) - 1;
124
- return idx >= 0 && idx < options.length ? idx : 0;
125
- }
126
-
127
- async function interactiveSetup() {
128
- log('');
129
- log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
130
- log(`${COLORS.bold}${COLORS.magenta} tfcode Setup${COLORS.reset}`);
131
- log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
132
- log('');
133
- log('This will guide you through setting up your ToothFairyAI credentials.');
134
- log('');
135
- log(`${COLORS.dim}You can find your credentials at:${COLORS.reset}`);
136
- log(`${COLORS.dim} https://app.toothfairyai.com → Settings → API Keys${COLORS.reset}`);
137
- log('');
138
-
139
- log(`${COLORS.bold}Step 1: Workspace ID${COLORS.reset}`);
140
- log(`${COLORS.dim}This is your workspace UUID${COLORS.reset}`);
141
- log('');
142
- const workspaceId = await question('Enter your Workspace ID: ');
143
- if (!workspaceId) { error('Workspace ID is required'); process.exit(1); }
144
- log('');
145
-
146
- log(`${COLORS.bold}Step 2: API Key${COLORS.reset}`);
147
- log(`${COLORS.dim}Paste or type your API key${COLORS.reset}`);
148
- log('');
149
- const apiKey = await question('Enter your API Key: ');
150
- if (!apiKey) { error('API Key is required'); process.exit(1); }
151
- log('');
152
-
153
- log(`${COLORS.bold}Step 3: Region${COLORS.reset}`);
154
- const regions = ['dev (Development)', 'au (Australia)', 'eu (Europe)', 'us (United States)'];
155
- const regionIdx = await select('Select your region:', regions);
156
- const region = ['dev', 'au', 'eu', 'us'][regionIdx];
157
-
158
- log('');
159
- log(`${COLORS.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
160
- log('');
161
- log(`${COLORS.bold}Summary:${COLORS.reset}`);
162
- log(` Workspace ID: ${workspaceId}`);
163
- log(` API Key: ***${apiKey.slice(-4)}`);
164
- log(` Region: ${region}`);
165
- log('');
166
-
167
- const confirm = await question('Save these credentials? (Y/n): ');
168
- if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') { log('Setup cancelled.'); return; }
169
-
170
- const config = { workspace_id: workspaceId, api_key: apiKey, region };
171
- saveConfig(config);
172
- success('Credentials saved to ~/.tfcode/config.json');
173
- log('');
174
-
175
- const testNow = await question('Validate credentials now? (Y/n): ');
176
- if (testNow.toLowerCase() === 'n' || testNow.toLowerCase() === 'no') return;
177
-
178
- log('');
179
- info('Validating credentials...');
180
- log('');
181
-
182
- try {
183
- const result = await runPythonSync('validate', config);
184
- if (result.success) {
185
- success('Credentials valid!');
186
- log(` API URL: ${result.base_url}`);
187
- log(` Workspace ID: ${result.workspace_id}`);
188
- log('');
189
-
190
- const syncNow = await question('Sync tools now? (Y/n): ');
191
- if (syncNow.toLowerCase() === 'n' || syncNow.toLowerCase() === 'no') return;
192
-
193
- log('');
194
- info('Syncing tools...');
195
- log('');
196
-
197
- const syncResult = await runPythonSync('sync', config);
198
- if (syncResult.success) {
199
- saveToolsCache(syncResult);
200
- success(`Synced ${syncResult.tools.length} tools`);
201
- if (syncResult.by_type && Object.keys(syncResult.by_type).length > 0) {
202
- log('');
203
- log('By type:');
204
- for (const [type, count] of Object.entries(syncResult.by_type)) log(` ${type}: ${count}`);
205
- }
206
- } else {
207
- error(`Sync failed: ${syncResult.error}`);
208
- }
209
- } else {
210
- error(`Validation failed: ${result.error}`);
211
- }
212
- } catch (e) {
213
- error(`Failed: ${e.message}`);
214
- }
215
-
216
- log('');
217
- log(`${COLORS.bold}${COLORS.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
218
- log(`${COLORS.bold}${COLORS.green} Setup Complete!${COLORS.reset}`);
219
- log(`${COLORS.bold}${COLORS.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
220
- log('');
221
- log('Commands:');
222
- log(` ${COLORS.cyan}tfcode validate${COLORS.reset} Check credentials`);
223
- log(` ${COLORS.cyan}tfcode sync${COLORS.reset} Sync tools`);
224
- log(` ${COLORS.cyan}tfcode tools list${COLORS.reset} List tools`);
225
- log('');
226
- }
227
-
228
- function showHelp() {
229
- log('');
230
- log(`${COLORS.bold}tfcode${COLORS.reset} - ToothFairyAI's AI coding agent`);
231
- log('');
232
- log('Commands:');
233
- log(` ${COLORS.cyan}tfcode setup${COLORS.reset} Interactive credential setup`);
234
- log(` ${COLORS.cyan}tfcode validate${COLORS.reset} Test credentials`);
235
- log(` ${COLORS.cyan}tfcode sync${COLORS.reset} Sync tools from workspace`);
236
- log(` ${COLORS.cyan}tfcode tools list${COLORS.reset} List synced tools`);
237
- log(` ${COLORS.cyan}tfcode --help${COLORS.reset} Show this help`);
238
- log(` ${COLORS.cyan}tfcode --version${COLORS.reset} Show version`);
239
- log('');
240
- log(`${COLORS.dim}For full TUI, run from source:${COLORS.reset}`);
241
- log(`${COLORS.dim} bun run packages/tfcode/src/index.ts${COLORS.reset}`);
242
- log('');
10
+ if (!fs.existsSync(binaryPath)) {
11
+ console.error("tfcode binary not found. The postinstall script should have downloaded it.")
12
+ console.error("Try running: npm rebuild @toothfairyai/tfcode")
13
+ process.exit(1)
243
14
  }
244
15
 
245
- const args = process.argv.slice(2);
246
- const command = args[0];
16
+ const child = spawn(binaryPath, process.argv.slice(2), {
17
+ stdio: "inherit",
18
+ env: process.env
19
+ })
247
20
 
248
- if (args.includes('--help') || args.includes('-h')) {
249
- showHelp();
250
- } else if (args.includes('--version') || args.includes('-v')) {
251
- log('tfcode v1.0.0-beta.9');
252
- } else if (command === 'setup') {
253
- interactiveSetup();
254
- } else if (command === 'validate') {
255
- (async () => {
256
- const config = loadConfig();
257
- if (!config) { error('No credentials. Run: tfcode setup'); process.exit(1); }
258
- info('Validating...');
259
- try {
260
- const result = await runPythonSync('validate', config);
261
- if (result.success) { success('Credentials valid'); log(` API URL: ${result.base_url}`); }
262
- else { error(`Failed: ${result.error}`); process.exit(1); }
263
- } catch (e) { error(`Failed: ${e.message}`); process.exit(1); }
264
- })();
265
- } else if (command === 'sync') {
266
- (async () => {
267
- const config = loadConfig();
268
- if (!config) { error('No credentials. Run: tfcode setup'); process.exit(1); }
269
- info('Syncing tools...');
270
- try {
271
- const result = await runPythonSync('sync', config);
272
- if (result.success) {
273
- saveToolsCache(result);
274
- success(`Synced ${result.tools.length} tools`);
275
- if (result.by_type) { log(''); log('By type:'); for (const [t, c] of Object.entries(result.by_type)) log(` ${t}: ${c}`); }
276
- } else { error(`Failed: ${result.error}`); process.exit(1); }
277
- } catch (e) { error(`Failed: ${e.message}`); process.exit(1); }
278
- })();
279
- } else if (command === 'tools' && args[1] === 'list') {
280
- const cached = loadCachedTools();
281
- if (!cached?.success) { error('No tools. Run: tfcode sync'); process.exit(1); }
282
- let tools = cached.tools;
283
- if (args[3] === '--type' && args[4]) tools = tools.filter(t => t.tool_type === args[4]);
284
- log(`\n${tools.length} tool(s):\n`);
285
- for (const t of tools) {
286
- log(` ${COLORS.cyan}${t.name}${COLORS.reset}`);
287
- log(` Type: ${t.tool_type}`);
288
- if (t.description) log(` ${COLORS.dim}${t.description.slice(0, 60)}${COLORS.reset}`);
289
- log(` Auth: ${t.auth_via}\n`);
290
- }
291
- } else if (!command) {
292
- // Show help instead of trying TUI (TUI requires full build)
293
- showHelp();
294
- } else {
295
- error(`Unknown command: ${command}`);
296
- showHelp();
297
- process.exit(1);
298
- }
21
+ child.on("exit", (code) => process.exit(code || 0))
package/package.json CHANGED
@@ -1,30 +1 @@
1
- {
2
- "name": "@toothfairyai/tfcode",
3
- "version": "1.0.0",
4
- "bin": {
5
- "tfcode": "./bin/tfcode"
6
- },
7
- "scripts": {
8
- "postinstall": "node ./postinstall.mjs"
9
- },
10
- "license": "MIT",
11
- "optionalDependencies": {
12
- "@toothfairyai/tfcode-linux-arm64": "1.0.0",
13
- "@toothfairyai/tfcode-windows-x64": "1.0.0",
14
- "@toothfairyai/tfcode-linux-x64-baseline-musl": "1.0.0",
15
- "@toothfairyai/tfcode-darwin-x64-baseline": "1.0.0",
16
- "@toothfairyai/tfcode-linux-x64-musl": "1.0.0",
17
- "@toothfairyai/tfcode-windows-x64-baseline": "1.0.0",
18
- "@toothfairyai/tfcode-linux-arm64-musl": "1.0.0",
19
- "@toothfairyai/tfcode-windows-arm64": "1.0.0",
20
- "@toothfairyai/tfcode-linux-x64": "1.0.0",
21
- "@toothfairyai/tfcode-darwin-x64": "1.0.0",
22
- "@toothfairyai/tfcode-linux-x64-baseline": "1.0.0",
23
- "@toothfairyai/tfcode-darwin-arm64": "1.0.0"
24
- },
25
- "homepage": "https://toothfairyai.com/developers/tfcode",
26
- "repository": {
27
- "type": "git",
28
- "url": "https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git"
29
- }
30
- }
1
+ {"name":"@toothfairyai/tfcode","version":"1.0.1","bin":{"tfcode":"./bin/tfcode.js"},"scripts":{"postinstall":"node ./postinstall.mjs"},"license":"MIT","optionalDependencies":{"@toothfairyai/tfcode-linux-arm64":"1.0.0","@toothfairyai/tfcode-windows-x64":"1.0.0","@toothfairyai/tfcode-linux-x64-baseline-musl":"1.0.0","@toothfairyai/tfcode-darwin-x64-baseline":"1.0.0","@toothfairyai/tfcode-linux-x64-musl":"1.0.0","@toothfairyai/tfcode-windows-x64-baseline":"1.0.0","@toothfairyai/tfcode-linux-arm64-musl":"1.0.0","@toothfairyai/tfcode-windows-arm64":"1.0.0","@toothfairyai/tfcode-linux-x64":"1.0.0","@toothfairyai/tfcode-darwin-x64":"1.0.0","@toothfairyai/tfcode-linux-x64-baseline":"1.0.0","@toothfairyai/tfcode-darwin-arm64":"1.0.0"},"engines":{"node":">=18"},"homepage":"https://toothfairyai.com/developers/tfcode","repository":{"type":"git","url":"https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git"}}
package/postinstall.mjs CHANGED
File without changes
Binary file
package/bin/tfcode DELETED
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { spawn } = require("child_process")
4
- const path = require("path")
5
-
6
- const scriptDir = __dirname
7
- const srcPath = path.join(scriptDir, "..", "src", "index.ts")
8
-
9
- const child = spawn("npx", ["tsx", srcPath, ...process.argv.slice(2)], {
10
- stdio: "inherit",
11
- shell: process.platform === "win32",
12
- cwd: scriptDir
13
- })
14
-
15
- child.on("exit", (code) => {
16
- process.exit(code || 0)
17
- })