Fix handling of symlink when displayed in the File manager (#214 )
It identifies the type of target of symlink which is either file or directory and then accordingly moves ahead.
Summary by CodeRabbit
New Features
SSH File Manager now resolves symlinks: clicking a link opens its target (navigates to directories or opens files).
Symlinks are visually distinguished with a dedicated icon and are labeled “Link” in rename dialogs.
Added clear error toasts when resolving symlinks or reconnecting SSH sessions fails, with automatic reconnect attempts where possible.
Fix handling of symlink when displayed in the File manager (#214 )
It identifies the type of target of symlink which is either file or directory and then accordingly moves ahead.
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* New Features
* SSH File Manager now resolves symlinks: clicking a link opens its target (navigates to directories or opens files).
* Symlinks are visually distinguished with a dedicated icon and are labeled “Link” in rename dialogs.
* Added clear error toasts when resolving symlinks or reconnecting SSH sessions fails, with automatic reconnect attempts where possible.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Adds a new backend GET endpoint to identify symlinks over SSH, a UI API wrapper to call it, and UI logic to handle symlink clicks in the File Manager sidebar, resolving targets and navigating/opening accordingly. The backend route appears duplicated in the file.
Adds GET endpoint at /ssh/file_manager/ssh/identifySymlink that validates sessionId and path, executes stat and readlink via the active SSH session, and returns { path, target, type }. Handles errors and updates session activity. Note: route added twice (duplicate definition).
Introduces handleSymlinkClick(item) for SSH symlink resolution. Differentiates items of type "link", uses a FileSymlink icon, resolves via API, navigates to directories or opens files, and expands toasts/labels to include "Link". Manages SSH session status and reconnection.
API client wrapper src/ui/main-axios.ts
Adds identifySSHSymlink(sessionId, path) that calls /ssh/identifySymlink via fileManagerApi and returns `{ path, target, type: "directory"
Sequence Diagram(s)
sequenceDiagram
autonumber
participant U as User
participant FM as FileManagerLeftSidebar (UI)
participant API as main-axios identifySSHSymlink()
participant BE as Backend route /ssh/.../identifySymlink
participant SSH as Active SSH Session
U->>FM: Click symlink item
FM->>FM: Ensure SSH session (status/reconnect)
alt Session OK
FM->>API: identifySSHSymlink(sessionId, symlinkPath)
API->>BE: GET /ssh/identifySymlink?sessionId&path
BE->>SSH: Exec: stat/readlink on path
alt Exec success
SSH-->>BE: fileType, target
BE-->>API: { path, target, type }
API-->>FM: { path, target, type }
alt type == directory
FM->>FM: Navigate to target directory
else type == file
FM->>FM: Open target file
end
else Exec error / non-zero exit
BE-->>API: 500 error
API-->>FM: Error
FM->>U: Toast: failed to resolve symlink
end
else Session not OK
FM->>FM: Attempt reconnect
alt Reconnect fails
FM->>U: Toast: failed to connect/reconnect SSH
end
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
Check name
Status
Explanation
Resolution
Docstring Coverage
⚠️ Warning
Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.
You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name
Status
Explanation
Description Check
✅ Passed
Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check
✅ Passed
The current title "Handle Symlink in the File manager." is concise and directly reflects the PR objective of fixing symlink handling in the File Manager; it matches the changeset which adds symlink resolution in backend, UI, and API layers and avoids extraneous details. It clearly communicates the primary change so a teammate scanning history will understand the intent.
Tip
👮 Agentic pre-merge checks are now available in preview!
Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
- Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
- Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.
reviews:pre_merge_checks:custom_checks:- name:"Undocumented Breaking Changes"mode:"warning"instructions:| Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).
Please share your feedback with us on this Discord post.
✨ Finishing touches
📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again)
🧪 Generate unit tests
Create PR with unit tests
Post copyable unit tests in a comment
Comment @coderabbitai help to get the list of available commands and usage tips.
<!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->
## Walkthrough
Adds a new backend GET endpoint to identify symlinks over SSH, a UI API wrapper to call it, and UI logic to handle symlink clicks in the File Manager sidebar, resolving targets and navigating/opening accordingly. The backend route appears duplicated in the file.
## Changes
| Cohort / File(s) | Summary |
| --- | --- |
| **Backend: SSH symlink identification endpoint**<br>`src/backend/ssh/file-manager.ts` | Adds GET endpoint at `/ssh/file_manager/ssh/identifySymlink` that validates `sessionId` and `path`, executes `stat` and `readlink` via the active SSH session, and returns `{ path, target, type }`. Handles errors and updates session activity. Note: route added twice (duplicate definition). |
| **UI: File Manager symlink handling**<br>`src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx` | Introduces `handleSymlinkClick(item)` for SSH symlink resolution. Differentiates items of type "link", uses a FileSymlink icon, resolves via API, navigates to directories or opens files, and expands toasts/labels to include "Link". Manages SSH session status and reconnection. |
| **API client wrapper**<br>`src/ui/main-axios.ts` | Adds `identifySSHSymlink(sessionId, path)` that calls `/ssh/identifySymlink` via `fileManagerApi` and returns `{ path, target, type: "directory" | "file" }`, with error handling through `handleApiError`. |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
autonumber
participant U as User
participant FM as FileManagerLeftSidebar (UI)
participant API as main-axios identifySSHSymlink()
participant BE as Backend route /ssh/.../identifySymlink
participant SSH as Active SSH Session
U->>FM: Click symlink item
FM->>FM: Ensure SSH session (status/reconnect)
alt Session OK
FM->>API: identifySSHSymlink(sessionId, symlinkPath)
API->>BE: GET /ssh/identifySymlink?sessionId&path
BE->>SSH: Exec: stat/readlink on path
alt Exec success
SSH-->>BE: fileType, target
BE-->>API: { path, target, type }
API-->>FM: { path, target, type }
alt type == directory
FM->>FM: Navigate to target directory
else type == file
FM->>FM: Open target file
end
else Exec error / non-zero exit
BE-->>API: 500 error
API-->>FM: Error
FM->>U: Toast: failed to resolve symlink
end
else Session not OK
FM->>FM: Attempt reconnect
alt Reconnect fails
FM->>U: Toast: failed to connect/reconnect SSH
end
end
```
## Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
<!-- pre_merge_checks_walkthrough_start -->
## Pre-merge checks (2 passed, 1 warning)
<details>
<summary>❌ Failed checks (1 warning)</summary>
| Check name | Status | Explanation | Resolution |
| :----------------: | :--------- | :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- |
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run `@coderabbitai generate docstrings` to improve docstring coverage. |
</details>
<details>
<summary>✅ Passed checks (2 passed)</summary>
| Check name | Status | Explanation |
| :---------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title Check | ✅ Passed | The current title "Handle Symlink in the File manager." is concise and directly reflects the PR objective of fixing symlink handling in the File Manager; it matches the changeset which adds symlink resolution in backend, UI, and API layers and avoids extraneous details. It clearly communicates the primary change so a teammate scanning history will understand the intent. |
</details>
<!-- pre_merge_checks_walkthrough_end -->
<!-- walkthrough_end -->
<!-- announcements_start -->
> [!TIP]
> <details>
> <summary>👮 Agentic pre-merge checks are now available in preview!</summary>
>
> Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
>
> - Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
> - Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.
>
> Please see the [documentation](https://docs.coderabbit.ai/pr-reviews/pre-merge-checks) for more information.
>
> Example:
>
> ```yaml
> reviews:
> pre_merge_checks:
> custom_checks:
> - name: "Undocumented Breaking Changes"
> mode: "warning"
> instructions: |
> Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).
> ```
>
> Please share your feedback with us on this [Discord post](https://discord.com/channels/1134356397673414807/1414771631775158383).
>
> </details>
<!-- announcements_end -->
<!-- internal state start -->
<!-- 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 -->
<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->
<details>
<summary>✨ Finishing touches</summary>
- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again)
<details>
<summary>🧪 Generate unit tests</summary>
- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Post copyable unit tests in a comment
</details>
</details>
<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->
---
<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>
<!-- tips_end -->
coderabbitai[bot]
(Migrated from github.com)
reviewed 2025-09-13 06:30:37 +10:00
coderabbitai[bot]
(Migrated from github.com)
left a comment
Copy Link
Copy Source
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
Currently only files show the pin button. If users often pin directories or useful links, expose the same control for item.type === "directory" or "link".
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between d4dc310c93 and d5471d68a3.
If stderr contains “No such file or directory”, respond with 404 to let the UI show a clearer toast for broken symlinks instead of a generic 500.
Apply:
- if (code !== 0) {
+ if (code !== 0) {
fileLogger.error(
`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
);
- return res.status(500).json({ error: `Command failed: ${errorData}` });
+ const notFound = /No such file or directory/i.test(errorData);
+ return res.status(notFound ? 404 : 500).json({
+ error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`,
+ });
}
📝 Committable suggestion
‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
stream.on("close", (code) => {
if (code !== 0) {
fileLogger.error(
`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
);
const notFound = /No such file or directory/i.test(errorData);
return res.status(notFound ? 404 : 500).json({
error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`,
});
}
🤖 Prompt for AI Agents
In src/backend/ssh/file-manager.ts around lines 371-377, the handler currently
maps any non-zero SSH command exit to a 500; change it to detect broken symlink
errors by inspecting errorData (stderr) for "No such file or directory"
(case-insensitive) and return res.status(404). Keep the fileLogger.error call
but include the detected error text and indicate it's a missing target;
otherwise preserve the existing 500 behavior and message for other errors.
_🛠️ Refactor suggestion_
**Return a specific status for broken links.**
If stderr contains “No such file or directory”, respond with 404 to let the UI show a clearer toast for broken symlinks instead of a generic 500.
Apply:
```diff
- if (code !== 0) {
+ if (code !== 0) {
fileLogger.error(
`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
);
- return res.status(500).json({ error: `Command failed: ${errorData}` });
+ const notFound = /No such file or directory/i.test(errorData);
+ return res.status(notFound ? 404 : 500).json({
+ error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`,
+ });
}
```
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
stream.on("close", (code) => {
if (code !== 0) {
fileLogger.error(
`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
);
const notFound = /No such file or directory/i.test(errorData);
return res.status(notFound ? 404 : 500).json({
error: notFound ? "Target not found or broken symlink" : `Command failed: ${errorData}`,
});
}
```
</details>
<!-- suggestion_end -->
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
In src/backend/ssh/file-manager.ts around lines 371-377, the handler currently
maps any non-zero SSH command exit to a 500; change it to detect broken symlink
errors by inspecting errorData (stderr) for "No such file or directory"
(case-insensitive) and return res.status(404). Keep the fileLogger.error call
but include the detected error text and indicate it's a missing target;
otherwise preserve the existing 500 behavior and message for other errors.
```
</details>
<!-- fingerprinting:phantom:medusa:chinchilla -->
<!-- This is an auto-generated comment by CodeRabbit -->
Guard against missing/invalid path and harden output parsing.
decodeURIComponent is called before validating presence; undefined becomes the string "undefined" and the command runs against that path. Also, parsing assumes exactly two non-empty lines. Tighten both.
‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
In src/backend/ssh/file-manager.ts around lines 332-396, decodeURIComponent is
called before validating the raw query path and the output parsing assumes
exactly two non-empty lines; fix by first reading and validating req.query.path
(return 400 if missing/empty), then safely decode it inside a try/catch (return
400 if decoding fails), continue to escape the decoded path for the shell, and
after command completes robustly parse output by trimming and splitting into
lines, treating missing lines gracefully (e.g. set target = lines[1] || "" and
fileType = lines[0] || "" and if fileType is empty return 500 or an informative
error), and ensure responses are only sent once (check res.headersSent) when
handling stream errors or command failures.
_⚠️ Potential issue_
**Guard against missing/invalid path and harden output parsing.**
decodeURIComponent is called before validating presence; undefined becomes the string "undefined" and the command runs against that path. Also, parsing assumes exactly two non-empty lines. Tighten both.
Apply:
```diff
-app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => {
- const sessionId = req.query.sessionId as string;
- const sshConn = sshSessions[sessionId];
- const linkPath = decodeURIComponent(req.query.path as string);
+app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => {
+ const sessionId = req.query.sessionId as string;
+ const sshConn = sshSessions[sessionId];
+ const rawPath = req.query.path;
+ if (typeof rawPath !== "string" || rawPath.trim() === "") {
+ return res.status(400).json({ error: "Link path is required" });
+ }
+ const linkPath = decodeURIComponent(rawPath);
@@
- sshConn.client.exec(command, (err, stream) => {
+ sshConn.client.exec(command, (err, stream) => {
@@
- stream.on("close", (code) => {
+ stream.on("close", (code) => {
if (code !== 0) {
@@
- const [fileType, target] = data.trim().split("\n");
-
+ const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
+ if (lines.length < 2) {
+ fileLogger.error(`SSH identifySymlink command returned unexpected output: ${JSON.stringify(lines)}`);
+ return res.status(500).json({ error: "Unexpected command output while resolving link" });
+ }
+ const [fileType, target] = lines;
+
res.json({
path: linkPath,
target: target,
type: fileType.toLowerCase().includes("directory") ? "directory" : "file"
});
```
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
app.get("/ssh/file_manager/ssh/identifySymlink", (req, res) => {
const sessionId = req.query.sessionId as string;
const sshConn = sshSessions[sessionId];
const rawPath = req.query.path;
if (typeof rawPath !== "string" || rawPath.trim() === "") {
return res.status(400).json({ error: "Link path is required" });
}
const linkPath = decodeURIComponent(rawPath);
if (!sessionId) {
return res.status(400).json({ error: "Session ID is required" });
}
if (!sshConn?.isConnected) {
return res.status(400).json({ error: "SSH connection not established" });
}
if (!linkPath) {
return res.status(400).json({ error: "Link path is required" });
}
sshConn.lastActive = Date.now();
const escapedPath = linkPath.replace(/'/g, "'\"'\"'");
const command = `stat -L -c "%F" '${escapedPath}' && readlink -f '${escapedPath}'`;
sshConn.client.exec(command, (err, stream) => {
if (err) {
fileLogger.error("SSH identifySymlink error:", err);
return res.status(500).json({ error: err.message });
}
let data = "";
let errorData = "";
stream.on("data", (chunk: Buffer) => {
data += chunk.toString();
});
stream.stderr.on("data", (chunk: Buffer) => {
errorData += chunk.toString();
});
stream.on("close", (code) => {
if (code !== 0) {
fileLogger.error(
`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,
);
return res.status(500).json({ error: `Command failed: ${errorData}` });
}
const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
if (lines.length < 2) {
fileLogger.error(`SSH identifySymlink command returned unexpected output: ${JSON.stringify(lines)}`);
return res.status(500).json({ error: "Unexpected command output while resolving link" });
}
const [fileType, target] = lines;
res.json({
path: linkPath,
target: target,
type: fileType.toLowerCase().includes("directory") ? "directory" : "file"
});
});
stream.on("error", (streamErr) => {
fileLogger.error("SSH identifySymlink stream error:", streamErr);
if (!res.headersSent) {
res.status(500).json({ error: `Stream error: ${streamErr.message}` });
}
});
});
});
```
</details>
<!-- suggestion_end -->
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
In src/backend/ssh/file-manager.ts around lines 332-396, decodeURIComponent is
called before validating the raw query path and the output parsing assumes
exactly two non-empty lines; fix by first reading and validating req.query.path
(return 400 if missing/empty), then safely decode it inside a try/catch (return
400 if decoding fails), continue to escape the decoded path for the shell, and
after command completes robustly parse output by trimming and splitting into
lines, treating missing lines gracefully (e.g. set target = lines[1] || "" and
fileType = lines[0] || "" and if fileType is empty return 500 or an informative
error), and ensure responses are only sent once (check res.headersSent) when
handling stream errors or command failures.
```
</details>
<!-- fingerprinting:phantom:medusa:chinchilla -->
<!-- This is an auto-generated comment by CodeRabbit -->
I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).
I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).
I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).
Surely thank you. I was working in the dev branch but then saw you merged it so raised against main.
> I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).
Surely thank you. I was working in the dev branch but then saw you merged it so raised against main.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fix handling of symlink when displayed in the File manager (#214 )
It identifies the type of target of symlink which is either file or directory and then accordingly moves ahead.
Summary by CodeRabbit
Walkthrough
Adds a new backend GET endpoint to identify symlinks over SSH, a UI API wrapper to call it, and UI logic to handle symlink clicks in the File Manager sidebar, resolving targets and navigating/opening accordingly. The backend route appears duplicated in the file.
Changes
src/backend/ssh/file-manager.ts/ssh/file_manager/ssh/identifySymlinkthat validatessessionIdandpath, executesstatandreadlinkvia the active SSH session, and returns{ path, target, type }. Handles errors and updates session activity. Note: route added twice (duplicate definition).src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsxhandleSymlinkClick(item)for SSH symlink resolution. Differentiates items of type "link", uses a FileSymlink icon, resolves via API, navigates to directories or opens files, and expands toasts/labels to include "Link". Manages SSH session status and reconnection.src/ui/main-axios.tsidentifySSHSymlink(sessionId, path)that calls/ssh/identifySymlinkviafileManagerApiand returns `{ path, target, type: "directory"Sequence Diagram(s)
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks (2 passed, 1 warning)
❌ Failed checks (1 warning)
@coderabbitai generate docstringsto improve docstring coverage.✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment
@coderabbitai helpto get the list of available commands and usage tips.Actionable comments posted: 2
🧹 Nitpick comments (2)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between
d4dc310c93andd5471d68a3.📒 Files selected for processing (3)
src/backend/ssh/file-manager.ts(1 hunks)src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx(8 hunks)src/ui/main-axios.ts(1 hunks)🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
Files:
src/ui/main-axios.tssrc/backend/ssh/file-manager.tssrc/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx**/main-axios.ts
⚙️ CodeRabbit configuration file
Files:
src/ui/main-axios.ts**/backend/**/*.{ts,js}
⚙️ CodeRabbit configuration file
Files:
src/backend/ssh/file-manager.ts🧬 Code graph analysis (1)
src/ui/Desktop/Apps/File Manager/FileManagerLeftSidebar.tsx (1)
🔇 Additional comments (6)
@@ -332,0 +374,4 @@`SSH identifySymlink command failed with code ${code}: ${errorData.replace(/\n/g, " ").trim()}`,);return res.status(500).json({ error: `Command failed: ${errorData}` });}🛠️ Refactor suggestion
Return a specific status for broken links.
If stderr contains “No such file or directory”, respond with 404 to let the UI show a clearer toast for broken symlinks instead of a generic 500.
Apply:
📝 Committable suggestion
🤖 Prompt for AI Agents
@@ -332,0 +393,4 @@});});});⚠️ Potential issue
Guard against missing/invalid path and harden output parsing.
decodeURIComponent is called before validating presence; undefined becomes the string "undefined" and the command runs against that path. Also, parsing assumes exactly two non-empty lines. Tighten both.
Apply:
📝 Committable suggestion
🤖 Prompt for AI Agents
I'll check out this PR soon. You caught me at a bad time since I just released a new version about 10 minutes before your PR. In the meantime, you can check out some of the possible issues that coderabbit mentioned (they may be insignificant or wrong, I haven't reviewed them).
Surely thank you. I was working in the dev branch but then saw you merged it so raised against main.