Skip to content

Commit e23dd53

Browse files
authored
Merge pull request #29 from appwrite/feat/list-files-in-dir-and-execute-command
2 parents 1d97e29 + ffe7de1 commit e23dd53

File tree

6 files changed

+372
-19
lines changed

6 files changed

+372
-19
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@appwrite.io/synapse",
3-
"version": "0.6.1",
3+
"version": "0.6.2",
44
"description": "Operating system gateway for remote serverless environments",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/services/filesystem.ts

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ import mime from "mime-types";
88
import * as path from "path";
99
import { Synapse } from "../synapse";
1010

11-
const IGNORE_PATTERNS = ["node_modules", "dist", "build", "coverage", "logs"];
11+
const IGNORE_PATTERNS = [
12+
"node_modules",
13+
"dist",
14+
"build",
15+
"coverage",
16+
"logs",
17+
".git",
18+
];
1219

1320
export type FileItem = {
1421
name: string;
@@ -61,6 +68,17 @@ export type ZipResult = {
6168
};
6269
};
6370

71+
export type FileListItem<WithContent extends boolean = false> =
72+
WithContent extends true
73+
? { path: string; content: string }
74+
: { path: string };
75+
76+
export type FileListResult<WithContent extends boolean = false> = {
77+
success: boolean;
78+
data?: FileListItem<WithContent>[];
79+
error?: string;
80+
};
81+
6482
export class Filesystem {
6583
private synapse: Synapse;
6684
private workDir: string;
@@ -799,4 +817,108 @@ export class Filesystem {
799817
};
800818
}
801819
}
820+
821+
/**
822+
* Lists all files in a directory with optional content and recursive traversal
823+
* @param dirPath - The directory path to list files from
824+
* @param withContent - Whether to include file content in the results
825+
* @param recursive - Whether to recursively traverse subdirectories
826+
* @returns Promise<FileListResult> containing array of file objects
827+
*/
828+
async listFilesInDir({
829+
dirPath,
830+
withContent,
831+
recursive,
832+
additionalIgnorePatterns,
833+
}: {
834+
dirPath: string;
835+
withContent?: boolean;
836+
recursive?: boolean;
837+
additionalIgnorePatterns?: string[];
838+
}): Promise<FileListResult<boolean>> {
839+
if (!dirPath) {
840+
return { success: false, error: "path is required" };
841+
}
842+
843+
try {
844+
const fullPath = this.resolvePath(dirPath);
845+
const workDir = path.resolve(this.workDir);
846+
const results: Array<{ path: string; content?: string }> = [];
847+
848+
// Read and parse .gitignore
849+
let ig: ReturnType<typeof ignore> | null = null;
850+
try {
851+
const gitignorePath = this.resolvePath(".gitignore");
852+
const gitignoreContent = fsSync.existsSync(gitignorePath)
853+
? fsSync.readFileSync(gitignorePath, "utf-8")
854+
: "";
855+
ig = ignore().add(gitignoreContent);
856+
} catch {
857+
ig = null;
858+
}
859+
860+
const walkDirectory = async (dir: string) => {
861+
let entries: fsSync.Dirent[];
862+
try {
863+
entries = await fs.readdir(dir, { withFileTypes: true });
864+
} catch {
865+
return;
866+
}
867+
868+
for (const entry of entries) {
869+
const absPath = path.join(dir, entry.name);
870+
const relPath = path.relative(workDir, absPath).replace(/\\/g, "/");
871+
872+
// Skip if ignored by .gitignore or IGNORE_PATTERNS
873+
if (ig && ig.ignores(relPath)) {
874+
continue;
875+
}
876+
if (
877+
[...IGNORE_PATTERNS, ...(additionalIgnorePatterns || [])].some(
878+
(pattern) => relPath.includes(pattern),
879+
)
880+
) {
881+
continue;
882+
}
883+
884+
if (entry.isDirectory()) {
885+
if (recursive) {
886+
await walkDirectory(absPath);
887+
}
888+
} else if (entry.isFile()) {
889+
const fileObj: { path: string; content?: string } = {
890+
path: relPath,
891+
};
892+
893+
if (withContent) {
894+
try {
895+
const content = await fs.readFile(absPath, "utf-8");
896+
fileObj.content = content;
897+
} catch {
898+
// If we can't read the file, skip it or include it without content
899+
continue;
900+
}
901+
}
902+
903+
results.push(fileObj);
904+
}
905+
}
906+
};
907+
908+
await walkDirectory(fullPath);
909+
910+
return {
911+
success: true,
912+
data: results as FileListItem<boolean>[],
913+
};
914+
} catch (error) {
915+
this.log(
916+
`Error listing files in directory ${dirPath}: ${error instanceof Error ? error.message : String(error)}`,
917+
);
918+
return {
919+
success: false,
920+
error: error instanceof Error ? error.message : String(error),
921+
};
922+
}
923+
}
802924
}

src/services/terminal.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import fs from "fs";
22
import * as pty from "node-pty";
33
import * as os from "os";
44
import { Synapse } from "../synapse";
5+
import { exec } from "child_process";
6+
import { promisify } from "util";
57

68
export type TerminalOptions = {
79
shell: string;
@@ -137,6 +139,39 @@ export class Terminal {
137139
}
138140
}
139141

142+
async executeCommand(
143+
command: string,
144+
cwd: string,
145+
timeout: number = 5000,
146+
): Promise<{ output: string; exitCode: number }> {
147+
if (!command) {
148+
throw new Error("Command is required");
149+
}
150+
151+
if (!cwd) {
152+
throw new Error("cwd is required");
153+
}
154+
155+
try {
156+
const { stdout, stderr } = await promisify(exec)(command, {
157+
cwd,
158+
encoding: "utf-8",
159+
timeout,
160+
});
161+
162+
return {
163+
output: stdout || stderr || "",
164+
exitCode: 0, // Success case - error case is handled in catch block
165+
};
166+
} catch (error: any) {
167+
console.error("Failed to execute command:", error);
168+
return {
169+
output: `Error: ${error instanceof Error ? error.message : String(error)}`,
170+
exitCode: 1,
171+
};
172+
}
173+
}
174+
140175
/**
141176
* Sets the callback for when data is received from the terminal
142177
* @param callback - The callback to set

0 commit comments

Comments
 (0)