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({
|
||||
command: "file",
|
||||
builder: (yargs) => yargs.command(FileReadCommand).command(FileStatusCommand).demandCommand(),
|
||||
builder: (yargs) =>
|
||||
yargs.command(FileReadCommand).command(FileStatusCommand).command(FileListCommand).demandCommand(),
|
||||
async handler() {},
|
||||
})
|
||||
|
||||
@@ -24,6 +24,17 @@ export namespace File {
|
||||
|
||||
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 = {
|
||||
Edited: Bus.event(
|
||||
"file.edited",
|
||||
@@ -120,4 +131,27 @@ export namespace File {
|
||||
}
|
||||
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(
|
||||
"/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({
|
||||
description: "Read a file",
|
||||
operationId: "file.read",
|
||||
@@ -965,10 +993,6 @@ export namespace Server {
|
||||
async (c) => {
|
||||
const path = c.req.valid("query").path
|
||||
const content = await File.read(path)
|
||||
log.info("read file", {
|
||||
path,
|
||||
content: content.content,
|
||||
})
|
||||
return c.json(content)
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user