feat: tf code

This commit is contained in:
Gab 2026-03-24 15:40:48 +11:00
parent 1539afc803
commit 1c0c0e6a50
5 changed files with 105 additions and 4 deletions

View File

@ -24,8 +24,13 @@ class SyncedTool(BaseModel):
description: Optional[str] = None description: Optional[str] = None
tool_type: ToolType tool_type: ToolType
is_mcp_server: bool = False
is_agent_skill: bool = False
is_database_script: bool = False
request_type: Optional[FunctionRequestType] = None request_type: Optional[FunctionRequestType] = None
url: Optional[str] = None url: Optional[str] = None
tools: list[str] = []
authorisation_type: Optional[str] = None authorisation_type: Optional[str] = None
@ -96,10 +101,34 @@ def parse_function(func: AgentFunction) -> SyncedTool:
) )
def parse_agent(agent) -> SyncedTool:
"""
Parse Agent from SDK into SyncedTool.
Args:
agent: Agent from TF SDK
Returns:
SyncedTool instance
"""
return SyncedTool(
id=agent.id,
name=agent.label or f"agent_{agent.id[:8]}",
description=agent.description,
tool_type=ToolType.AGENT_SKILL,
is_agent_skill=True,
auth_via="tf_agent",
)
def sync_tools(config: TFConfig) -> ToolSyncResult: def sync_tools(config: TFConfig) -> ToolSyncResult:
""" """
Sync all tools from ToothFairyAI workspace using SDK. Sync all tools from ToothFairyAI workspace using SDK.
Includes:
- Agent Functions (API Functions)
- Coder Agents (agents with mode='coder')
Args: Args:
config: TFConfig instance config: TFConfig instance
@ -108,9 +137,19 @@ def sync_tools(config: TFConfig) -> ToolSyncResult:
""" """
try: try:
client = config.get_client() client = config.get_client()
result = client.agent_functions.list()
tools = [parse_function(f) for f in result.items] # Sync agent functions
func_result = client.agent_functions.list()
tools = [parse_function(f) for f in func_result.items]
# Sync coder agents
try:
agents_result = client.agents.list()
for agent in agents_result.items:
if getattr(agent, 'mode', None) == 'coder':
tools.append(parse_agent(agent))
except Exception as e:
pass
by_type = {} by_type = {}
for tool in tools: for tool in tools:

View File

@ -255,10 +255,50 @@ export namespace Agent {
return state().then((x) => x[agent]) return state().then((x) => x[agent])
} }
async function loadTFCoderAgents(): Promise<Info[]> {
const toolsPath = path.join(Global.Path.data, ".tfcode", "tools.json")
console.log("[TF Agents] Loading from:", toolsPath)
try {
const content = await Bun.file(toolsPath).text()
const data = JSON.parse(content)
console.log("[TF Agents] File loaded, success:", data.success, "tools:", data.tools?.length)
if (!data.success || !data.tools) {
console.log("[TF Agents] No tools data, returning []")
return []
}
const tfAgents = data.tools
.filter((t: any) => t.tool_type === "agent_skill")
console.log("[TF Agents] Found agent_skill items:", tfAgents.length)
console.log("[TF Agents] Names:", tfAgents.map((t: any) => t.name))
return tfAgents.map((t: any): Info => ({
name: t.name,
description: t.description,
mode: "subagent" as const,
permission: Permission.fromConfig({ "*": "allow" }),
native: false,
options: {
tf_agent_id: t.id,
tf_auth_via: t.auth_via,
},
}))
} catch (e) {
console.log("[TF Agents] Error:", e)
return []
}
}
export async function list() { export async function list() {
const cfg = await Config.get() const cfg = await Config.get()
const localAgents = await state()
const tfAgents = await loadTFCoderAgents()
console.log("[TF Agents] list() - local:", Object.keys(localAgents).length, "tf:", tfAgents.length)
return pipe( return pipe(
await state(), { ...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"],

View File

@ -62,6 +62,8 @@ async function runPythonSync(
method: string, method: string,
args: Record<string, unknown> = {}, args: Record<string, unknown> = {},
): Promise<unknown> { ): Promise<unknown> {
const credentials = await loadCredentials()
const pythonCode = ` const pythonCode = `
import json import json
import sys import sys
@ -162,6 +164,9 @@ sys.exit(0)
env: { env: {
...process.env, ...process.env,
PYTHONPATH: getPythonSyncPath(), PYTHONPATH: getPythonSyncPath(),
TF_WORKSPACE_ID: credentials?.workspace_id || "",
TF_API_KEY: credentials?.api_key || "",
TF_REGION: credentials?.region || "au",
}, },
}) })
@ -204,6 +209,23 @@ function getToolsFilePath(): string {
return path.join(getConfigPath(), TFCODE_TOOLS_FILE) return path.join(getConfigPath(), TFCODE_TOOLS_FILE)
} }
function getCredentialsFilePath(): string {
return path.join(getConfigPath(), "credentials.json")
}
async function loadCredentials(): Promise<{ workspace_id: string; api_key: string; region: string } | null> {
const credFile = getCredentialsFilePath()
if (!(await Filesystem.exists(credFile))) {
return null
}
try {
const content = await Bun.file(credFile).text()
return JSON.parse(content)
} catch {
return null
}
}
async function loadCachedTools(): Promise<ToolSyncResult | null> { async function loadCachedTools(): Promise<ToolSyncResult | null> {
const toolsFile = getToolsFilePath() const toolsFile = getToolsFilePath()
if (!(await Filesystem.exists(toolsFile))) { if (!(await Filesystem.exists(toolsFile))) {

View File

@ -44,7 +44,7 @@ export namespace UI {
const result: string[] = [] const result: string[] = []
const reset = "\x1b[0m" const reset = "\x1b[0m"
const left = { const left = {
fg: Bun.color("gray", "ansi") ?? "", fg: "\x1b[38;2;29;184;198m",
shadow: "\x1b[38;5;235m", shadow: "\x1b[38;5;235m",
bg: "\x1b[48;5;235m", bg: "\x1b[48;5;235m",
} }