mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 05:43:55 +00:00
feat: releases
This commit is contained in:
parent
d716f9a6d6
commit
2ae12f8d6b
2
bun.lock
2
bun.lock
@ -381,7 +381,7 @@
|
|||||||
},
|
},
|
||||||
"packages/tfcode": {
|
"packages/tfcode": {
|
||||||
"name": "tfcode",
|
"name": "tfcode",
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tfcode": "./bin/tfcode",
|
"tfcode": "./bin/tfcode",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"name": "tfcode",
|
"name": "tfcode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@ -6,22 +6,34 @@ import os from "os"
|
|||||||
import { spawnSync } from "child_process"
|
import { spawnSync } from "child_process"
|
||||||
|
|
||||||
const GITEA_HOST = process.env.TFCODE_GITEA_HOST || "gitea.toothfairyai.com"
|
const GITEA_HOST = process.env.TFCODE_GITEA_HOST || "gitea.toothfairyai.com"
|
||||||
const GITEA_REPO = process.env.TFCODE_GITEA_REPO || "ToothFairyAI/tfcode"
|
const GITEA_REPO = process.env.TFCODE_GITEA_REPO || "ToothFairyAI/tf_code"
|
||||||
|
|
||||||
function detectPlatform() {
|
function detectPlatform() {
|
||||||
let platform
|
let platform
|
||||||
switch (os.platform()) {
|
switch (os.platform()) {
|
||||||
case "darwin": platform = "darwin"; break
|
case "darwin":
|
||||||
case "linux": platform = "linux"; break
|
platform = "darwin"
|
||||||
case "win32": platform = "windows"; break
|
break
|
||||||
default: platform = os.platform()
|
case "linux":
|
||||||
|
platform = "linux"
|
||||||
|
break
|
||||||
|
case "win32":
|
||||||
|
platform = "windows"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
platform = os.platform()
|
||||||
}
|
}
|
||||||
|
|
||||||
let arch
|
let arch
|
||||||
switch (os.arch()) {
|
switch (os.arch()) {
|
||||||
case "x64": arch = "x64"; break
|
case "x64":
|
||||||
case "arm64": arch = "arm64"; break
|
arch = "x64"
|
||||||
default: arch = os.arch()
|
break
|
||||||
|
case "arm64":
|
||||||
|
arch = "arm64"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
arch = os.arch()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for AVX2 on x64
|
// Check for AVX2 on x64
|
||||||
@ -170,9 +182,13 @@ async function main() {
|
|||||||
|
|
||||||
// Install Python SDK
|
// Install Python SDK
|
||||||
console.log("Installing ToothFairyAI Python SDK...")
|
console.log("Installing ToothFairyAI Python SDK...")
|
||||||
const pipResult = spawnSync("python3", ["-m", "pip", "install", "--user", "--break-system-packages", "toothfairyai", "pydantic", "httpx", "rich"], {
|
const pipResult = spawnSync(
|
||||||
stdio: "inherit"
|
"python3",
|
||||||
})
|
["-m", "pip", "install", "--user", "--break-system-packages", "toothfairyai", "pydantic", "httpx", "rich"],
|
||||||
|
{
|
||||||
|
stdio: "inherit",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if (pipResult.status === 0) {
|
if (pipResult.status === 0) {
|
||||||
console.log("✓ Python SDK installed")
|
console.log("✓ Python SDK installed")
|
||||||
@ -197,8 +213,8 @@ async function main() {
|
|||||||
console.log("Quick Start:")
|
console.log("Quick Start:")
|
||||||
console.log("")
|
console.log("")
|
||||||
console.log(" 1. Set credentials:")
|
console.log(" 1. Set credentials:")
|
||||||
console.log(" export TF_WORKSPACE_ID=\"your-workspace-id\"")
|
console.log(' export TF_WORKSPACE_ID="your-workspace-id"')
|
||||||
console.log(" export TF_API_KEY=\"your-api-key\"")
|
console.log(' export TF_API_KEY="your-api-key"')
|
||||||
console.log("")
|
console.log("")
|
||||||
console.log(" 2. Validate:")
|
console.log(" 2. Validate:")
|
||||||
console.log(" tfcode validate")
|
console.log(" tfcode validate")
|
||||||
|
|||||||
@ -105,20 +105,45 @@ async function uploadToGitea() {
|
|||||||
|
|
||||||
// Create main npm package
|
// Create main npm package
|
||||||
async function createMainPackage() {
|
async function createMainPackage() {
|
||||||
await $`mkdir -p ./dist/tfcode`
|
await $`mkdir -p ./dist/tfcode/bin`
|
||||||
await $`cp -r ./bin ./dist/tfcode/bin`
|
|
||||||
await Bun.file(`./dist/tfcode/postinstall.mjs`).write(await Bun.file("./script/postinstall-tfcode.mjs").text())
|
await Bun.file(`./dist/tfcode/postinstall.mjs`).write(await Bun.file("./script/postinstall-tfcode.mjs").text())
|
||||||
await Bun.file(`./dist/tfcode/LICENSE`).write(await Bun.file("../../LICENSE").text())
|
await Bun.file(`./dist/tfcode/LICENSE`).write(await Bun.file("../../LICENSE").text())
|
||||||
|
|
||||||
|
// Create a simple wrapper script that runs the installed binary
|
||||||
|
const wrapper = `#!/usr/bin/env node
|
||||||
|
const { spawn } = require('child_process')
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const binDir = path.join(__dirname, 'bin')
|
||||||
|
const binary = process.platform === 'win32' ? 'tfcode.exe' : 'tfcode'
|
||||||
|
const binaryPath = path.join(binDir, binary)
|
||||||
|
|
||||||
|
if (!fs.existsSync(binaryPath)) {
|
||||||
|
console.error('tfcode binary not found. Run: npm install @toothfairyai/tfcode')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: process.env
|
||||||
|
})
|
||||||
|
|
||||||
|
child.on('exit', (code) => process.exit(code || 0))
|
||||||
|
`
|
||||||
|
await Bun.file(`./dist/tfcode/bin/tfcode.js`).write(wrapper)
|
||||||
|
|
||||||
|
// Create package.json
|
||||||
await Bun.file(`./dist/tfcode/package.json`).write(
|
await Bun.file(`./dist/tfcode/package.json`).write(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
name: "@toothfairyai/tfcode",
|
name: "@toothfairyai/tfcode",
|
||||||
version: TFCODE_VERSION,
|
version: TFCODE_VERSION,
|
||||||
bin: { tfcode: "./bin/tfcode" },
|
bin: { tfcode: "./bin/tfcode.js" },
|
||||||
scripts: { postinstall: "node ./postinstall.mjs" },
|
scripts: { postinstall: "node ./postinstall.mjs" },
|
||||||
license: pkg.license,
|
license: pkg.license,
|
||||||
optionalDependencies: binaries,
|
optionalDependencies: binaries,
|
||||||
|
engines: { node: ">=18" },
|
||||||
homepage: "https://toothfairyai.com/developers/tfcode",
|
homepage: "https://toothfairyai.com/developers/tfcode",
|
||||||
repository: {
|
repository: {
|
||||||
type: "git",
|
type: "git",
|
||||||
|
|||||||
@ -251,8 +251,15 @@ export namespace Agent {
|
|||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function get(agent: string) {
|
export async function get(agent: string): Promise<Info> {
|
||||||
return state().then((x) => x[agent])
|
const localAgents = await state()
|
||||||
|
if (localAgents[agent]) return localAgents[agent]
|
||||||
|
|
||||||
|
const tfAgents = await loadTFCoderAgents()
|
||||||
|
const tfAgent = tfAgents.find((a) => a.name === agent)
|
||||||
|
if (tfAgent) return tfAgent
|
||||||
|
|
||||||
|
return localAgents.build
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadTFCoderAgents(): Promise<Info[]> {
|
async function loadTFCoderAgents(): Promise<Info[]> {
|
||||||
@ -264,17 +271,19 @@ export namespace Agent {
|
|||||||
|
|
||||||
return data.tools
|
return data.tools
|
||||||
.filter((t: any) => t.tool_type === "coder_agent")
|
.filter((t: any) => t.tool_type === "coder_agent")
|
||||||
.map((t: any): Info => ({
|
.map(
|
||||||
name: t.name,
|
(t: any): Info => ({
|
||||||
description: t.description,
|
name: t.name,
|
||||||
mode: "primary" as const,
|
description: t.description,
|
||||||
permission: Permission.fromConfig({ "*": "allow" }),
|
mode: "primary" as const,
|
||||||
native: false,
|
permission: Permission.fromConfig({ "*": "allow" }),
|
||||||
options: {
|
native: false,
|
||||||
tf_agent_id: t.id,
|
options: {
|
||||||
tf_auth_via: t.auth_via,
|
tf_agent_id: t.id,
|
||||||
},
|
tf_auth_via: t.auth_via,
|
||||||
}))
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -286,7 +295,7 @@ export namespace Agent {
|
|||||||
const tfAgents = await loadTFCoderAgents()
|
const tfAgents = await loadTFCoderAgents()
|
||||||
|
|
||||||
return pipe(
|
return pipe(
|
||||||
{ ...localAgents, ...Object.fromEntries(tfAgents.map(a => [a.name, a])) },
|
{ ...localAgents, ...Object.fromEntries(tfAgents.map((a) => [a.name, a])) },
|
||||||
values(),
|
values(),
|
||||||
sortBy(
|
sortBy(
|
||||||
[(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"],
|
[(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"],
|
||||||
|
|||||||
@ -58,10 +58,7 @@ function getPythonSyncPath(): string {
|
|||||||
return "tf_sync"
|
return "tf_sync"
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runPythonSync(
|
async function runPythonSync(method: string, args: Record<string, unknown> = {}): Promise<unknown> {
|
||||||
method: string,
|
|
||||||
args: Record<string, unknown> = {},
|
|
||||||
): Promise<unknown> {
|
|
||||||
const credentials = await loadCredentials()
|
const credentials = await loadCredentials()
|
||||||
|
|
||||||
const pythonCode = `
|
const pythonCode = `
|
||||||
@ -458,8 +455,7 @@ const ToolsCredentialsSetCommand = cmd({
|
|||||||
const ToolsCommand = cmd({
|
const ToolsCommand = cmd({
|
||||||
command: "tools",
|
command: "tools",
|
||||||
describe: "manage ToothFairyAI tools",
|
describe: "manage ToothFairyAI tools",
|
||||||
builder: (yargs) =>
|
builder: (yargs) => yargs.command(ToolsListCommand).command(ToolsCredentialsSetCommand).demandCommand(),
|
||||||
yargs.command(ToolsListCommand).command(ToolsCredentialsSetCommand).demandCommand(),
|
|
||||||
async handler() {},
|
async handler() {},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -561,8 +557,106 @@ export const ToolsMainCommand = cmd({
|
|||||||
command: "tools",
|
command: "tools",
|
||||||
describe: "manage ToothFairyAI tools",
|
describe: "manage ToothFairyAI tools",
|
||||||
builder: (yargs) =>
|
builder: (yargs) =>
|
||||||
yargs.command(ToolsListCommand).command(ToolsCredentialsSetCommand).command(ToolsDebugCommand).command(ToolsTestCommand).demandCommand(),
|
yargs
|
||||||
|
.command(ToolsListCommand)
|
||||||
|
.command(ToolsCredentialsSetCommand)
|
||||||
|
.command(ToolsDebugCommand)
|
||||||
|
.command(ToolsTestCommand)
|
||||||
|
.demandCommand(),
|
||||||
async handler() {},
|
async handler() {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const SetupCommand = cmd({
|
||||||
|
command: "setup",
|
||||||
|
describe: "configure ToothFairyAI credentials",
|
||||||
|
builder: (yargs) =>
|
||||||
|
yargs
|
||||||
|
.option("api-key", {
|
||||||
|
type: "string",
|
||||||
|
describe: "API key",
|
||||||
|
})
|
||||||
|
.option("workspace-id", {
|
||||||
|
type: "string",
|
||||||
|
describe: "Workspace ID",
|
||||||
|
})
|
||||||
|
.option("region", {
|
||||||
|
type: "string",
|
||||||
|
describe: "Region (dev, au, eu, us)",
|
||||||
|
default: "au",
|
||||||
|
}),
|
||||||
|
handler: async (args) => {
|
||||||
|
const configPath = getConfigPath()
|
||||||
|
const credFile = getCredentialsFilePath()
|
||||||
|
|
||||||
|
// Load existing credentials
|
||||||
|
let existing: { api_key?: string; workspace_id?: string; region?: string } = {}
|
||||||
|
if (await Filesystem.exists(credFile)) {
|
||||||
|
try {
|
||||||
|
existing = await Bun.file(credFile).json()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get credentials from args or prompt
|
||||||
|
let apiKey = args["api-key"]
|
||||||
|
let workspaceId = args["workspace-id"]
|
||||||
|
let region = args.region || existing.region || "au"
|
||||||
|
|
||||||
|
if (!apiKey || !workspaceId) {
|
||||||
|
info("")
|
||||||
|
info("ToothFairyAI Credentials Setup")
|
||||||
|
info("━".repeat(40))
|
||||||
|
info("")
|
||||||
|
|
||||||
|
if (!workspaceId) {
|
||||||
|
process.stdout.write(`Workspace ID [${existing.workspace_id || ""}]: `)
|
||||||
|
const input = await new Promise<string>((resolve) => {
|
||||||
|
process.stdin.once("data", (data) => resolve(data.toString().trim()))
|
||||||
|
})
|
||||||
|
workspaceId = input || existing.workspace_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
process.stdout.write(`API Key [${existing.api_key ? "****" + existing.api_key.slice(-4) : ""}]: `)
|
||||||
|
const input = await new Promise<string>((resolve) => {
|
||||||
|
process.stdin.once("data", (data) => resolve(data.toString().trim()))
|
||||||
|
})
|
||||||
|
apiKey = input || existing.api_key
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(`Region [${region}]: `)
|
||||||
|
const regionInput = await new Promise<string>((resolve) => {
|
||||||
|
process.stdin.once("data", (data) => resolve(data.toString().trim()))
|
||||||
|
})
|
||||||
|
if (regionInput) region = regionInput
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apiKey || !workspaceId) {
|
||||||
|
printError("API key and workspace ID are required")
|
||||||
|
process.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save credentials
|
||||||
|
await mkdir(configPath, { recursive: true })
|
||||||
|
await Bun.write(
|
||||||
|
credFile,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
api_key: apiKey,
|
||||||
|
workspace_id: workspaceId,
|
||||||
|
region,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
success(`✓ Credentials saved to ${credFile}`)
|
||||||
|
info(` Workspace: ${workspaceId}`)
|
||||||
|
info(` Region: ${region}`)
|
||||||
|
info("")
|
||||||
|
info("Run 'tfcode validate' to test your credentials")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export { ValidateCommand, SyncCommand, ToolsCommand }
|
export { ValidateCommand, SyncCommand, ToolsCommand }
|
||||||
@ -11,6 +11,17 @@ interface ChangelogEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CHANGELOG: ChangelogEntry[] = [
|
const CHANGELOG: ChangelogEntry[] = [
|
||||||
|
{
|
||||||
|
version: "1.0.2",
|
||||||
|
date: "2026-03-24",
|
||||||
|
changes: [
|
||||||
|
"Fixed TF coder agents not loading correctly",
|
||||||
|
"Fixed content:null error in ToothFairyAI predictions API",
|
||||||
|
"Added reasoning_content support for thinking models",
|
||||||
|
"Added tfcode setup command to persist credentials",
|
||||||
|
"Removed TF-specific options for non-TF providers",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: "1.0.0-beta",
|
version: "1.0.0-beta",
|
||||||
date: "2026-03-24",
|
date: "2026-03-24",
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import { WebCommand } from "./cli/cmd/web"
|
|||||||
import { PrCommand } from "./cli/cmd/pr"
|
import { PrCommand } from "./cli/cmd/pr"
|
||||||
import { SessionCommand } from "./cli/cmd/session"
|
import { SessionCommand } from "./cli/cmd/session"
|
||||||
import { DbCommand } from "./cli/cmd/db"
|
import { DbCommand } from "./cli/cmd/db"
|
||||||
import { ValidateCommand, SyncCommand, ToolsMainCommand } from "./cli/cmd/tools"
|
import { ValidateCommand, SyncCommand, ToolsMainCommand, SetupCommand } from "./cli/cmd/tools"
|
||||||
import { QuickstartCommand } from "./cli/cmd/quickstart"
|
import { QuickstartCommand } from "./cli/cmd/quickstart"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Global } from "./global"
|
import { Global } from "./global"
|
||||||
@ -149,6 +149,7 @@ let cli = yargs(hideBin(process.argv))
|
|||||||
.command(DbCommand)
|
.command(DbCommand)
|
||||||
.command(ValidateCommand)
|
.command(ValidateCommand)
|
||||||
.command(SyncCommand)
|
.command(SyncCommand)
|
||||||
|
.command(SetupCommand)
|
||||||
.command(ToolsMainCommand)
|
.command(ToolsMainCommand)
|
||||||
.command(QuickstartCommand)
|
.command(QuickstartCommand)
|
||||||
|
|
||||||
|
|||||||
@ -115,10 +115,11 @@ export function convertToOpenAICompatibleChatMessages(prompt: LanguageModelV2Pro
|
|||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content: text || null,
|
content: text || undefined,
|
||||||
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
||||||
reasoning_text: reasoningOpaque ? reasoningText : undefined,
|
reasoning_text: reasoningOpaque ? reasoningText : undefined,
|
||||||
reasoning_opaque: reasoningOpaque,
|
reasoning_opaque: reasoningOpaque,
|
||||||
|
reasoning_content: reasoningText,
|
||||||
...metadata,
|
...metadata,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -109,6 +109,13 @@ export namespace LLM {
|
|||||||
mergeDeep(input.agent.options),
|
mergeDeep(input.agent.options),
|
||||||
mergeDeep(variant),
|
mergeDeep(variant),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Remove TF-specific options for non-ToothFairyAI providers
|
||||||
|
if (input.model.providerID !== "toothfairyai") {
|
||||||
|
delete options.tf_agent_id
|
||||||
|
delete options.tf_auth_via
|
||||||
|
}
|
||||||
|
|
||||||
if (isOpenaiOauth) {
|
if (isOpenaiOauth) {
|
||||||
options.instructions = system.join("\n")
|
options.instructions = system.join("\n")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user