From 1c0c0e6a50fafa99165799d10d6b9167a30df3f2 Mon Sep 17 00:00:00 2001 From: Gab Date: Tue, 24 Mar 2026 15:40:48 +1100 Subject: [PATCH] feat: tf code --- .../tf_sync/__pycache__/tools.cpython-313.pyc | Bin 5556 -> 6847 bytes packages/tf-sync/src/tf_sync/tools.py | 43 +++++++++++++++++- packages/tfcode/src/agent/agent.ts | 42 ++++++++++++++++- packages/tfcode/src/cli/cmd/tools.ts | 22 +++++++++ packages/tfcode/src/cli/ui.ts | 2 +- 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/packages/tf-sync/src/tf_sync/__pycache__/tools.cpython-313.pyc b/packages/tf-sync/src/tf_sync/__pycache__/tools.cpython-313.pyc index 3f0405583942afb3786d1dbc77f86f789a37c549..33690be8469b2414754aaa2581797d24216cb354 100644 GIT binary patch delta 2784 zcmZuyS!^4}8J^`HBzJjV5+zEc6dhcd7Uf8CY`0RQNOI~%WDKpCI1pQ!qLwyOijrqn zZX6VlP!y=#hepQsaGt852BHFeD4?P!8YD#$zZS5{G$a;AQRJz4XrjP+C>j*#{Ig^% z82N$yX8vRUIsRk*aOy9Uo=0xChzLyvKd7vP?sz(x&+ZKW^1D*rHDOacUFcN~`xI~9 zsYrSE1gH3@#NUgQFqH=KY(9_|dEx;+Y9%N^uzHoj!4rRm=%1l$emQdBVs0|bN5(h! zKI5KE-g00uFXaWrISeiw*hM+9#knabQI5x+yd&?0rHH+&CG!y(-;%jOmPXi5w$+w- zT6Vz`c3vv;;ZUCU5m$c725|TqF9(fY_G8@ynwbDjGQ}Im|n;yazucNh_S_xM+bkl{^8=I=87xY^js>yERs>$o?>s8IT!=7Ry#=GqGjLEGK z2@a5b;vhf>zzGlrh!8NjYDLp=2k=qg!-ayT7xhZ1P}Fr?S=rQ8tx&*S#v-}rpBV3Q z!{6zOZ)bKV>Ri0po!HLq&egd@Gd}Qui`?bv+`vDdKD)iJn|#ncdKgI8xwFk^&-Uz2 z=k8Otd+J+ zne4~iu*(=g4?r(K9H1Yd4*<@Dp8}8oP7yG<)k;an37E2#nvPST;Q$5zz&RcS81eun z+mJLg;W>e7k_Ybyd1jhkpo8d5*UaThg^RiD?9!zdvoeFzM5r+Y5_kyo?ozd=X_YcL z2*hBV$j%r!$0##+@MFinnM@}U$PVl#1E#BbVdev%i_idl6%|>e9v%nK6kjSm>^{B$g@$zB!9}AwUS<0ui^7VJ8$$$e;O&lzS{)p z!IJOMK7n7L<*#%(v}iMCoZ&5S`vgrfaA`oRB-;{5VZhpwoJ7T@upIKBaU0PKCpFf5 z8Sa+SBJCAz&Kea?;T0itUKl{K{q;5RG8`x|1C?TY?i_N_nz_&tB*pZWDOhEc{S^Ez znGU^d6_zY~?ak9lfvY2Wnm!O7ldl$6)GDR{0zalS^Za~vsi3@YX<9{@@W|Y+vqriT#=^ad*j^gb4_>Pjpf_R4fpun@?P%kD{o!dU)jI0m*2}i zh>bTx(MD+SFf{lu(A5YG9R`M)9oUHWGjsn{xq?> z@>6J^))EP{3=M&jBILN7L$MnPch43{otiz46kE+cW6!>9jb)~crr0RE+7hYNOo`G}<*yzM`vZ z8f^}(>d{O)`3oKcBG}kaYpXP)P3Qb;C3SA$r59$g(hH4X$wCGTA{V7 z1nU*Bs%=(vlPO#M-ff+@CC1Of4~*Js-q1fyCp>e7T5(O)pfI7Uwa992qIQe8r4Jw` z`HOx?K5dj-$@UgHip6=c>F;Rx`w#v7b^pMQa3r9Po<<~f7)doEXAUE0>XGpsshN7F zk(&BjYO3y=Cb)Ow=e>K($K`sPJc7j|Le?~nMQ2%Fg99`jqPNb(V<2( z^-(mn%RdZu-JRSU+8?L~)4zY_@4*YChNHh)xU+Dtdp}q28~->o@db~%P9M2YFl;E^ zOm`phf9pTTKIDr&LX6Y@(B=E`h$WLRk7LH$-qHLkV+9PEeFFu%|8WPabl!dw#PCfWPp@Z1=Ce}sYg7(X|8VE3hM zwTonhCHy==*-r27LW1CfUmvZdWi4ST4Eze)suHRMpA1TV47WaTi9 z8@bTR3$PvQU4*xcW~Fri`u$gH8P($1i4a delta 1518 zcmYjRO>7%g5Z>qAwb%b%@7jr-#CDT3$*NWowG@&H4EZHR!GSuV619{j&N?`_PWpCT zHNB)DAz2imRu5IJ#32WS)Ewv`910hd+z^*Wky^9}DI7T_A3E_g!c<2pYL6=kpra4AT`i?R`7OX5|{2~hKT-L&jkdv(Qh#5%3AX6X!x zuu2KTy`ShrwMbyQimaPc%x; zq!dlFefc5jU?01)YmRI$FIO$cGr!7XdT)l@L-0=$@tpxCG50+kOlkY{WC{PIGbdj;Ba+ zD)I_|6Bv5y;_UcLeq{RMRNf#o3rdRsWVjP2SgOuhR;3JA!Q}d3C(BNIdr5k8+WQ-M zQwN2i&>$={gb+rEAn+h3N(wr|$2}ZwVaXleJ9E{#ImYXwu}}TY3R)Nv&9bljtvxXi zEWBa{=rE(u${e-K*>b&BvMb9q`XcPU#(wudVOyb;>l4C$34PJkFVy52NfRa@;hKBK zeTnxloIF970&Fg#iJlpmk%-P(y?_|DcUxK>8@>ldQ{Ya#p zu=4k&UF>Q!9^f)QZQk&+52C5d2JnttvCV51zbvn=Bj3QnIpU&e^OttKV>e6Y3NHz^ zh~cF#p`p7}t(Z02aZ}T(S8a!skAwAV`kF&b3*Q0D%jvMjK5MmB;xBH|*I~N^pY=V! zZDC)J$-W=ersrz=j@rJfb~fBx>fTYipR3s&HM^_!Hr#vJvyUe?CU+x)8IWH&ZR-}hE7}9RAPRdk1fvG;bUX@DYgLjPtopp(t*o> z=qN8=TVAMF%^WR&h<#-}X8$BIfgnlWO&kcY9AsEe@|>0?zPmT~1z6Ve2e|A{uv^I? H33K=lVaiIU diff --git a/packages/tf-sync/src/tf_sync/tools.py b/packages/tf-sync/src/tf_sync/tools.py index 9bc92678d..053a5083b 100644 --- a/packages/tf-sync/src/tf_sync/tools.py +++ b/packages/tf-sync/src/tf_sync/tools.py @@ -24,8 +24,13 @@ class SyncedTool(BaseModel): description: Optional[str] = None tool_type: ToolType + is_mcp_server: bool = False + is_agent_skill: bool = False + is_database_script: bool = False + request_type: Optional[FunctionRequestType] = None url: Optional[str] = None + tools: list[str] = [] 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: """ Sync all tools from ToothFairyAI workspace using SDK. + Includes: + - Agent Functions (API Functions) + - Coder Agents (agents with mode='coder') + Args: config: TFConfig instance @@ -108,9 +137,19 @@ def sync_tools(config: TFConfig) -> ToolSyncResult: """ try: 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 = {} for tool in tools: diff --git a/packages/tfcode/src/agent/agent.ts b/packages/tfcode/src/agent/agent.ts index 60d8d04b3..2ce3f41af 100644 --- a/packages/tfcode/src/agent/agent.ts +++ b/packages/tfcode/src/agent/agent.ts @@ -255,10 +255,50 @@ export namespace Agent { return state().then((x) => x[agent]) } + async function loadTFCoderAgents(): Promise { + 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() { 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( - await state(), + { ...localAgents, ...Object.fromEntries(tfAgents.map(a => [a.name, a])) }, values(), sortBy( [(x) => (cfg.default_agent ? x.name === cfg.default_agent : x.name === "build"), "desc"], diff --git a/packages/tfcode/src/cli/cmd/tools.ts b/packages/tfcode/src/cli/cmd/tools.ts index 2d1e07647..a966739e3 100644 --- a/packages/tfcode/src/cli/cmd/tools.ts +++ b/packages/tfcode/src/cli/cmd/tools.ts @@ -62,6 +62,8 @@ async function runPythonSync( method: string, args: Record = {}, ): Promise { + const credentials = await loadCredentials() + const pythonCode = ` import json import sys @@ -162,6 +164,9 @@ sys.exit(0) env: { ...process.env, 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) } +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 { const toolsFile = getToolsFilePath() if (!(await Filesystem.exists(toolsFile))) { diff --git a/packages/tfcode/src/cli/ui.ts b/packages/tfcode/src/cli/ui.ts index 0ea5c0c5a..545f6e329 100644 --- a/packages/tfcode/src/cli/ui.ts +++ b/packages/tfcode/src/cli/ui.ts @@ -44,7 +44,7 @@ export namespace UI { const result: string[] = [] const reset = "\x1b[0m" const left = { - fg: Bun.color("gray", "ansi") ?? "", + fg: "\x1b[38;2;29;184;198m", shadow: "\x1b[38;5;235m", bg: "\x1b[48;5;235m", }