Skip to content

Commit 88f89c5

Browse files
committed
Migrate relabel endpoint and fix agent loader
1 parent ac2530a commit 88f89c5

File tree

7 files changed

+574
-128
lines changed

7 files changed

+574
-128
lines changed

sdk/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"@vscode/tree-sitter-wasm": "0.1.4",
6565
"ai": "^5.0.0",
6666
"diff": "8.0.2",
67+
"esbuild": "^0.19.12",
6768
"ignore": "7.0.5",
6869
"micromatch": "^4.0.8",
6970
"web-tree-sitter": "0.25.6",

sdk/src/agents/load-agents.ts

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,37 @@ import fs from 'fs'
22
import os from 'os'
33
import path from 'path'
44
import { pathToFileURL } from 'url'
5+
import { build } from 'esbuild'
6+
import { createHash } from 'crypto'
7+
import { builtinModules } from 'module'
58

69
export let loadedAgents: Record<string, any> = {}
710

8-
const getAllTsFiles = (dir: string): string[] => {
11+
const agentFileExtensions = new Set([
12+
'.ts',
13+
'.tsx',
14+
'.js',
15+
'.mjs',
16+
'.cjs',
17+
])
18+
19+
const getAllAgentFiles = (dir: string): string[] => {
920
const files: string[] = []
1021
try {
1122
const entries = fs.readdirSync(dir, { withFileTypes: true })
1223
for (const entry of entries) {
1324
const fullPath = path.join(dir, entry.name)
1425
if (entry.isDirectory()) {
15-
files.push(...getAllTsFiles(fullPath))
26+
files.push(...getAllAgentFiles(fullPath))
1627
continue
1728
}
18-
const isTsFile =
29+
const extension = path.extname(entry.name).toLowerCase()
30+
const isAgentFile =
1931
entry.isFile() &&
20-
entry.name.endsWith('.ts') &&
32+
agentFileExtensions.has(extension) &&
2133
!entry.name.endsWith('.d.ts') &&
2234
!entry.name.endsWith('.test.ts')
23-
if (isTsFile) {
35+
if (isAgentFile) {
2436
files.push(fullPath)
2537
}
2638
}
@@ -47,16 +59,18 @@ export async function loadLocalAgents({
4759
loadedAgents = {}
4860

4961
const agentDirs = agentsPath ? [agentsPath] : getDefaultAgentDirs()
50-
const allTsFiles = agentDirs.flatMap((dir) => getAllTsFiles(dir))
62+
const allAgentFiles = agentDirs.flatMap((dir) => getAllAgentFiles(dir))
5163

52-
if (allTsFiles.length === 0) {
64+
if (allAgentFiles.length === 0) {
5365
return loadedAgents
5466
}
5567

56-
for (const fullPath of allTsFiles) {
68+
for (const fullPath of allAgentFiles) {
5769
try {
58-
const moduleUrl = `${pathToFileURL(fullPath).href}?update=${Date.now()}`
59-
const agentModule = await import(moduleUrl)
70+
const agentModule = await importAgentModule(fullPath, verbose)
71+
if (!agentModule) {
72+
continue
73+
}
6074
const agentDefinition = agentModule.default ?? agentModule
6175

6276
if (!agentDefinition?.id || !agentDefinition?.model) {
@@ -87,3 +101,72 @@ export async function loadLocalAgents({
87101

88102
return loadedAgents
89103
}
104+
105+
async function importAgentModule(
106+
fullPath: string,
107+
verbose: boolean,
108+
): Promise<any | null> {
109+
const extension = path.extname(fullPath).toLowerCase()
110+
const urlVersion = `?update=${Date.now()}`
111+
112+
if (extension === '.ts' || extension === '.tsx') {
113+
const compiledPath = await transpileAgent(fullPath, verbose)
114+
if (!compiledPath) {
115+
return null
116+
}
117+
return import(`${pathToFileURL(compiledPath).href}${urlVersion}`)
118+
}
119+
120+
return import(`${pathToFileURL(fullPath).href}${urlVersion}`)
121+
}
122+
123+
async function transpileAgent(
124+
fullPath: string,
125+
verbose: boolean,
126+
): Promise<string | null> {
127+
try {
128+
const result = await build({
129+
entryPoints: [fullPath],
130+
bundle: true,
131+
format: 'esm',
132+
platform: 'node',
133+
target: 'node18',
134+
write: false,
135+
logLevel: verbose ? 'info' : 'silent',
136+
sourcemap: 'inline',
137+
packages: 'external',
138+
external: [
139+
...builtinModules,
140+
...builtinModules.map((mod) => `node:${mod}`),
141+
'@codebuff/*',
142+
],
143+
})
144+
145+
const jsOutput = result.outputFiles?.find((file) =>
146+
file.path.endsWith('.js'),
147+
)
148+
if (!jsOutput?.text) {
149+
if (verbose) {
150+
console.error(`Failed to transpile agent (no output): ${fullPath}`)
151+
}
152+
return null
153+
}
154+
155+
const hash = createHash('sha1').update(fullPath).digest('hex')
156+
const tempDir = path.join(os.tmpdir(), 'codebuff-agents')
157+
const compiledPath = path.join(tempDir, `${hash}.mjs`)
158+
159+
await fs.promises.mkdir(tempDir, { recursive: true })
160+
await fs.promises.writeFile(compiledPath, jsOutput.text, 'utf8')
161+
162+
return compiledPath
163+
} catch (error) {
164+
if (verbose) {
165+
console.error(
166+
`Error transpiling agent ${fullPath}:`,
167+
error instanceof Error ? error.message : error,
168+
)
169+
}
170+
return null
171+
}
172+
}

sdk/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ export {
6868
export type { CodebuffFileSystem } from '@codebuff/common/types/filesystem'
6969

7070
export { runTerminalCommand } from './tools/run-terminal-command'
71+
export {
72+
promptAiSdk,
73+
promptAiSdkStream,
74+
promptAiSdkStructured,
75+
} from './impl/llm'

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"@codebuff/common/*": ["./common/src/*"],
88
"@codebuff/web/*": ["./web/src/*"],
99
"@codebuff/evals/*": ["./evals/*"],
10+
"@codebuff/sdk": ["./sdk/src/index.ts"],
1011
"@codebuff/sdk/*": ["./sdk/*"],
1112
"@codebuff/agent-runtime/*": ["./packages/agent-runtime/src/*"],
1213
"@codebuff/billing/*": ["./packages/billing/src/*"],

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@codebuff/billing": "workspace:*",
3838
"@codebuff/common": "workspace:*",
3939
"@codebuff/internal": "workspace:*",
40+
"@codebuff/sdk": "workspace:*",
4041
"@emotion/is-prop-valid": "^1.3.1",
4142
"@hookform/resolvers": "^3.9.0",
4243
"@mdx-js/loader": "^3.1.0",

0 commit comments

Comments
 (0)