mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-20 01:04:22 +01:00
feat: file list api
This commit is contained in:
@@ -29,8 +29,25 @@ const FileStatusCommand = cmd({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const FileListCommand = cmd({
|
||||||
|
command: "list <path>",
|
||||||
|
builder: (yargs) =>
|
||||||
|
yargs.positional("path", {
|
||||||
|
type: "string",
|
||||||
|
demandOption: true,
|
||||||
|
description: "File path to list",
|
||||||
|
}),
|
||||||
|
async handler(args) {
|
||||||
|
await bootstrap({ cwd: process.cwd() }, async () => {
|
||||||
|
const files = await File.list(args.path)
|
||||||
|
console.log(JSON.stringify(files, null, 2))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export const FileCommand = cmd({
|
export const FileCommand = cmd({
|
||||||
command: "file",
|
command: "file",
|
||||||
builder: (yargs) => yargs.command(FileReadCommand).command(FileStatusCommand).demandCommand(),
|
builder: (yargs) =>
|
||||||
|
yargs.command(FileReadCommand).command(FileStatusCommand).command(FileListCommand).demandCommand(),
|
||||||
async handler() {},
|
async handler() {},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,6 +24,17 @@ export namespace File {
|
|||||||
|
|
||||||
export type Info = z.infer<typeof Info>
|
export type Info = z.infer<typeof Info>
|
||||||
|
|
||||||
|
export const Node = z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
path: z.string(),
|
||||||
|
type: z.enum(["file", "directory"]),
|
||||||
|
})
|
||||||
|
.openapi({
|
||||||
|
ref: "FileNode",
|
||||||
|
})
|
||||||
|
export type Node = z.infer<typeof Node>
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Edited: Bus.event(
|
Edited: Bus.event(
|
||||||
"file.edited",
|
"file.edited",
|
||||||
@@ -120,4 +131,27 @@ export namespace File {
|
|||||||
}
|
}
|
||||||
return { type: "raw", content }
|
return { type: "raw", content }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function list(dir?: string) {
|
||||||
|
const ignore = [".git", ".DS_Store"]
|
||||||
|
const app = App.info()
|
||||||
|
const resolved = dir ? path.join(app.path.cwd, dir) : app.path.cwd
|
||||||
|
const nodes: Node[] = []
|
||||||
|
for (const entry of await fs.promises.readdir(resolved, { withFileTypes: true })) {
|
||||||
|
if (ignore.includes(entry.name)) continue
|
||||||
|
const fullPath = path.join(resolved, entry.name)
|
||||||
|
const relativePath = path.relative(app.path.cwd, fullPath)
|
||||||
|
nodes.push({
|
||||||
|
name: entry.name,
|
||||||
|
path: relativePath,
|
||||||
|
type: entry.isDirectory() ? "directory" : "file",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nodes.sort((a, b) => {
|
||||||
|
if (a.type !== b.type) {
|
||||||
|
return a.type === "directory" ? -1 : 1
|
||||||
|
}
|
||||||
|
return a.name.localeCompare(b.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -937,6 +937,34 @@ export namespace Server {
|
|||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/file",
|
"/file",
|
||||||
|
describeRoute({
|
||||||
|
description: "List files and directories",
|
||||||
|
operationId: "file.list",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Files and directories",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(File.Node.array()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator(
|
||||||
|
"query",
|
||||||
|
z.object({
|
||||||
|
path: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
async (c) => {
|
||||||
|
const path = c.req.valid("query").path
|
||||||
|
const content = await File.list(path)
|
||||||
|
return c.json(content)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/file/content",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
description: "Read a file",
|
description: "Read a file",
|
||||||
operationId: "file.read",
|
operationId: "file.read",
|
||||||
@@ -965,10 +993,6 @@ export namespace Server {
|
|||||||
async (c) => {
|
async (c) => {
|
||||||
const path = c.req.valid("query").path
|
const path = c.req.valid("query").path
|
||||||
const content = await File.read(path)
|
const content = await File.read(path)
|
||||||
log.info("read file", {
|
|
||||||
path,
|
|
||||||
content: content.content,
|
|
||||||
})
|
|
||||||
return c.json(content)
|
return c.json(content)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ import type {
|
|||||||
FindFilesResponses,
|
FindFilesResponses,
|
||||||
FindSymbolsData,
|
FindSymbolsData,
|
||||||
FindSymbolsResponses,
|
FindSymbolsResponses,
|
||||||
|
FileListData,
|
||||||
|
FileListResponses,
|
||||||
FileReadData,
|
FileReadData,
|
||||||
FileReadResponses,
|
FileReadResponses,
|
||||||
FileStatusData,
|
FileStatusData,
|
||||||
@@ -457,12 +459,22 @@ class Find extends _HeyApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class File extends _HeyApiClient {
|
class File extends _HeyApiClient {
|
||||||
|
/**
|
||||||
|
* List files and directories
|
||||||
|
*/
|
||||||
|
public list<ThrowOnError extends boolean = false>(options: Options<FileListData, ThrowOnError>) {
|
||||||
|
return (options.client ?? this._client).get<FileListResponses, unknown, ThrowOnError>({
|
||||||
|
url: "/file",
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a file
|
* Read a file
|
||||||
*/
|
*/
|
||||||
public read<ThrowOnError extends boolean = false>(options: Options<FileReadData, ThrowOnError>) {
|
public read<ThrowOnError extends boolean = false>(options: Options<FileReadData, ThrowOnError>) {
|
||||||
return (options.client ?? this._client).get<FileReadResponses, unknown, ThrowOnError>({
|
return (options.client ?? this._client).get<FileReadResponses, unknown, ThrowOnError>({
|
||||||
url: "/file",
|
url: "/file/content",
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1138,6 +1138,12 @@ export type Symbol = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FileNode = {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
type: "file" | "directory"
|
||||||
|
}
|
||||||
|
|
||||||
export type File = {
|
export type File = {
|
||||||
path: string
|
path: string
|
||||||
added: number
|
added: number
|
||||||
@@ -1804,7 +1810,7 @@ export type FindSymbolsResponses = {
|
|||||||
|
|
||||||
export type FindSymbolsResponse = FindSymbolsResponses[keyof FindSymbolsResponses]
|
export type FindSymbolsResponse = FindSymbolsResponses[keyof FindSymbolsResponses]
|
||||||
|
|
||||||
export type FileReadData = {
|
export type FileListData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
query: {
|
query: {
|
||||||
@@ -1813,6 +1819,24 @@ export type FileReadData = {
|
|||||||
url: "/file"
|
url: "/file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FileListResponses = {
|
||||||
|
/**
|
||||||
|
* Files and directories
|
||||||
|
*/
|
||||||
|
200: Array<FileNode>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FileListResponse = FileListResponses[keyof FileListResponses]
|
||||||
|
|
||||||
|
export type FileReadData = {
|
||||||
|
body?: never
|
||||||
|
path?: never
|
||||||
|
query: {
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
url: "/file/content"
|
||||||
|
}
|
||||||
|
|
||||||
export type FileReadResponses = {
|
export type FileReadResponses = {
|
||||||
/**
|
/**
|
||||||
* File content
|
* File content
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ resources:
|
|||||||
app:
|
app:
|
||||||
models:
|
models:
|
||||||
app: App
|
app: App
|
||||||
logLevel: LogLevel
|
|
||||||
provider: Provider
|
provider: Provider
|
||||||
model: Model
|
model: Model
|
||||||
agent: Agent
|
agent: Agent
|
||||||
@@ -61,7 +60,6 @@ resources:
|
|||||||
|
|
||||||
find:
|
find:
|
||||||
models:
|
models:
|
||||||
match: Match
|
|
||||||
symbol: Symbol
|
symbol: Symbol
|
||||||
methods:
|
methods:
|
||||||
text: get /find
|
text: get /find
|
||||||
@@ -71,8 +69,11 @@ resources:
|
|||||||
file:
|
file:
|
||||||
models:
|
models:
|
||||||
file: File
|
file: File
|
||||||
|
fileNode: FileNode
|
||||||
|
|
||||||
methods:
|
methods:
|
||||||
read: get /file
|
list: get /file
|
||||||
|
read: get /file/content
|
||||||
status: get /file/status
|
status: get /file/status
|
||||||
|
|
||||||
config:
|
config:
|
||||||
|
|||||||
Reference in New Issue
Block a user