Skip to content

Feast manage key bindings in vscode devspace#747

Draft
msivasubramaniaan wants to merge 8 commits into
che-incubator:mainfrom
msivasubramaniaan:feast-manage-key-bindings-in-che
Draft

Feast manage key bindings in vscode devspace#747
msivasubramaniaan wants to merge 8 commits into
che-incubator:mainfrom
msivasubramaniaan:feast-manage-key-bindings-in-che

Conversation

@msivasubramaniaan

@msivasubramaniaan msivasubramaniaan commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

This PR will manage key bindings in devspace

What issues does this PR fix?

https://redhat.atlassian.net/browse/CRW-9166

How to test this PR?

Does this PR contain changes that override default upstream Code-OSS behavior?

  • the PR contains changes in the code folder (you can skip it if your changes are placed in a che extension )
  • the corresponding items were added to the CHANGELOG.md file
  • rules for automatic git rebase were added to the .rebase folder

Summary by CodeRabbit

  • New Features

    • Added a new keybindings sync extension for VS Code.
    • Users can now pull keybindings from the workspace service and push local changes back.
    • Added support for automatic background syncing and initial setup on activation.
  • Bug Fixes

    • Improved handling when keybindings can’t be loaded or saved, with clearer error reporting.
  • Documentation

    • Added user-facing documentation for the new extension and its availability in VS Code.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Click here to review and test in web IDE: Contribute

@msivasubramaniaan msivasubramaniaan marked this pull request as draft July 6, 2026 06:49
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new "che-keybindings" VS Code extension that synchronizes user keybindings with a Che backend. It includes the extension manifest, README, webpack/tsconfig setup, a REST API client (GET/PATCH), local keybindings file read/write helpers, a client-ID utility, a SyncManager orchestrating polling and save-triggered push/pull, and the activate/deactivate entrypoint. Build tooling (gulpfile, npm dirs) and CI license-check workflow are updated to include the new extension.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Extension
  participant SyncManager
  participant KeybindingsFile
  participant CheAPI

  Extension->>SyncManager: initialize()
  SyncManager->>CheAPI: getKeybindings(namespace)
  CheAPI-->>SyncManager: remote keybindings
  SyncManager->>KeybindingsFile: writeUserKeybindings(remoteJson)

  KeybindingsFile->>SyncManager: onDidSaveTextDocument
  SyncManager->>CheAPI: patchKeybindings(namespace, json, clientId)

  loop every 10s
    SyncManager->>CheAPI: getKeybindings(namespace)
    CheAPI-->>SyncManager: remote keybindings
    SyncManager->>KeybindingsFile: writeUserKeybindings(if changed)
  end
Loading

Suggested reviewers: RomanNikitenko


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors)

Check name Status Explanation Resolution
Rebase Rules For Upstream Changes ❌ Error PR changes upstream code/build files, but .rebase/CHANGELOG.md and rebase.sh lack entries/routing for them, and the template checkboxes are unchecked. Add .rebase rules for code/build/gulpfile.extensions.ts and code/build/npm/dirs.ts, add a CHANGELOG entry, route them in rebase.sh, and check the template boxes.
Title check ❌ Error The title is related to the PR, but it is not in imperative mood and the wording is unclear due to the 'Feast' typo. Change it to a clear imperative title such as 'Add keybinding management in VS Code devspace'.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
code/extensions/che-keybindings/extension.webpack.config.js (1)

24-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

bufferutil/utf-8-validate externals likely unnecessary.

These are optional native deps of ws, but this extension's only runtime dependency is node-fetch. If copied from another extension's webpack config, consider removing since they add no value here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/extension.webpack.config.js` around lines 24
- 27, The webpack externals in extension.webpack.config.js include bufferutil
and utf-8-validate, but this extension does not use ws and only depends on
node-fetch at runtime. Remove the unnecessary externals entries from the config
so the extension only declares what it actually needs, and keep the webpack
configuration aligned with the extension’s real dependency set.
code/extensions/che-keybindings/package.json (1)

15-17: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

activationEvents: ["*"] causes eager activation on every VS Code startup.

VS Code's own guidance is to use this activation event in your extension only when no other activation events combination works in your use-case. Given this extension only needs to poll/sync keybindings after startup, onStartupFinished would avoid slowing down initial load.

⚡ Suggested fix
     "activationEvents": [
-        "*"
+        "onStartupFinished"
     ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/package.json` around lines 15 - 17, The
che-keybindings extension is using eager activation via activationEvents with
"*", which makes it load on every VS Code startup. Update the package.json
activationEvents for this extension to use a startup-complete trigger such as
onStartupFinished instead, so activation is deferred until after initial
startup. Keep the change localized to the che-keybindings package manifest and
ensure the extension still activates when its polling/sync logic is needed.
code/extensions/che-keybindings/src/utils.ts (1)

14-37: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use a stronger ID generator and handle state.update().

Math.random() gives no collision guarantees for a value used to dedupe sync loops (see SyncManager.pull() comparing remote.meta?.clientId === this.clientId). Also state.update() returns a Thenable that's silently ignored here.

♻️ Proposed fix
+import { randomUUID } from "node:crypto";
+
 export function getOrCreateClientId(
 	state: vscode.Memento,
 	output?: vscode.OutputChannel,
-): string {
+): Promise<string> | string {
 	output?.appendLine("[Utils] Retrieving client ID...");

 	let id = state.get<string>("clientId");

 	if (!id) {
 		output?.appendLine(
 			"[Utils] No existing client ID found. Generating a new one...",
 		);

-		id = Math.random().toString(36).slice(2);
+		id = randomUUID();

-		state.update("clientId", id);
+		return Promise.resolve(state.update("clientId", id)).then(() => {
+			output?.appendLine(`[Utils] Generated new client ID: ${id}`);
+			return id as string;
+		});
-
-		output?.appendLine(`[Utils] Generated new client ID: ${id}`);
 	} else {
 		output?.appendLine(`[Utils] Existing client ID found: ${id}`);
 	}

 	return id;
 }

Note this changes the function signature to async; callers in syncManager.ts:39 would need await.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/src/utils.ts` around lines 14 - 37,
`getOrCreateClientId` should stop using `Math.random()` for a dedupe-sensitive
client identifier and should handle the asynchronous `state.update()` result.
Update the function to use a stronger unique ID generator, make it async so the
`vscode.Memento.update` promise is awaited, and adjust callers such as
`SyncManager.pull()` to await `getOrCreateClientId` before comparing
`remote.meta?.clientId` against `this.clientId`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/extensions/che-keybindings/package.json`:
- Around line 49-58: The contributed commands cheKeybindingsSync.pull and
cheKeybindingsSync.push are declared but not wired up, so add matching
vscode.commands.registerCommand handlers in che-keybindings/src/extension.ts or
che-keybindings/src/syncManager.ts. Use the existing SyncManager flow to
implement the pull and push actions, and make sure the command IDs exactly match
the package.json entries so the Command Palette can invoke them successfully.
- Around line 62-69: Remove the unused cheKeybindingsSync.storageMode
configuration entry from the che-keybindings extension manifest, since it is
never consumed by the extension logic. Update the package.json contribution
settings so the local/global enum and default are no longer exposed to users,
and verify there are no references to cheKeybindingsSync.storageMode elsewhere
in the extension before deleting it.

In `@code/extensions/che-keybindings/src/cheAPi.ts`:
- Around line 25-55: Both keybinding API requests can hang indefinitely, so add
a timeout-driven abort signal to the fetch calls in getKeybindings and
patchKeybindings. Update the request flow in cheAPi.ts to create an
AbortController (or equivalent timeout helper), pass its signal into each fetch,
and ensure the timeout is cleared once the request completes. Keep the existing
logging and return behavior unchanged, but make sure stalled backend calls fail
fast instead of blocking sync or piling up overlapping polls.
- Around line 14-21: The KeybindingsResponse metadata currently carries clientId
only, which filters self-echoes but does not protect against concurrent updates.
Thread resourceVersion through the PATCH flow in the keybindings API and related
handling so updates include and compare the latest version before writing, using
the existing KeybindingsResponse.meta shape and any PATCH handler or client
methods that consume it.

In `@code/extensions/che-keybindings/src/keybindings.ts`:
- Around line 19-41: The readUserKeybindings() fallback to "[]" is masking real
read failures and can cause SyncManager.pull() to overwrite valid local
keybindings; change readUserKeybindings() in che-keybindings/src/keybindings.ts
to fail loudly by propagating the exception instead of returning an empty array
string, while keeping the existing logging in the catch path so
SyncManager.pull() can hit its outer try/catch and abort the sync cycle safely.

In `@code/extensions/che-keybindings/src/syncManager.ts`:
- Around line 46-70: The polling loop in initialize() can start a new pull()
before the previous one finishes, which can interleave with onSave() and corrupt
shared sync state like suppressPush and lastSynced. Add an overlap guard in
SyncManager so only one pull runs at a time, and have the timer skip or queue if
a pull is already in progress. Use the existing pull() and onSave() paths as the
coordination points, and keep the guard around the setInterval callback and any
direct pull invocations.

---

Nitpick comments:
In `@code/extensions/che-keybindings/extension.webpack.config.js`:
- Around line 24-27: The webpack externals in extension.webpack.config.js
include bufferutil and utf-8-validate, but this extension does not use ws and
only depends on node-fetch at runtime. Remove the unnecessary externals entries
from the config so the extension only declares what it actually needs, and keep
the webpack configuration aligned with the extension’s real dependency set.

In `@code/extensions/che-keybindings/package.json`:
- Around line 15-17: The che-keybindings extension is using eager activation via
activationEvents with "*", which makes it load on every VS Code startup. Update
the package.json activationEvents for this extension to use a startup-complete
trigger such as onStartupFinished instead, so activation is deferred until after
initial startup. Keep the change localized to the che-keybindings package
manifest and ensure the extension still activates when its polling/sync logic is
needed.

In `@code/extensions/che-keybindings/src/utils.ts`:
- Around line 14-37: `getOrCreateClientId` should stop using `Math.random()` for
a dedupe-sensitive client identifier and should handle the asynchronous
`state.update()` result. Update the function to use a stronger unique ID
generator, make it async so the `vscode.Memento.update` promise is awaited, and
adjust callers such as `SyncManager.pull()` to await `getOrCreateClientId`
before comparing `remote.meta?.clientId` against `this.clientId`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 07fc250d-4501-4915-ab29-4021fca3453f

📥 Commits

Reviewing files that changed from the base of the PR and between 08c0f29 and d04ab02.

⛔ Files ignored due to path filters (2)
  • code/extensions/che-keybindings/images/eclipse-che-logo.png is excluded by !**/*.png
  • code/extensions/che-keybindings/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • .github/workflows/pull-request-check-licenses.yml
  • code/build/gulpfile.extensions.ts
  • code/build/npm/dirs.ts
  • code/extensions/che-keybindings/.vscodeignore
  • code/extensions/che-keybindings/README.md
  • code/extensions/che-keybindings/extension.webpack.config.js
  • code/extensions/che-keybindings/package.json
  • code/extensions/che-keybindings/package.nls.json
  • code/extensions/che-keybindings/src/cheAPi.ts
  • code/extensions/che-keybindings/src/extension.ts
  • code/extensions/che-keybindings/src/keybindings.ts
  • code/extensions/che-keybindings/src/syncManager.ts
  • code/extensions/che-keybindings/src/utils.ts
  • code/extensions/che-keybindings/tsconfig.json

Comment on lines +49 to +58
"commands": [
{
"command": "cheKeybindingsSync.pull",
"title": "Keybindings: Pull from Che"
},
{
"command": "cheKeybindingsSync.push",
"title": "Keybindings: Push to Che"
}
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "registerCommand" code/extensions/che-keybindings/src

Repository: che-incubator/che-code

Length of output: 160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package.json =="
sed -n '1,220p' code/extensions/che-keybindings/package.json

echo
echo "== source files =="
fd -a . code/extensions/che-keybindings/src

echo
echo "== search for command ids and registrations =="
rg -n "cheKeybindingsSync\.(pull|push)|registerCommand|commands\s*:" code/extensions/che-keybindings

echo
echo "== likely activation and sync manager files =="
for f in \
  code/extensions/che-keybindings/src/extension.ts \
  code/extensions/che-keybindings/src/syncManager.ts \
  code/extensions/che-keybindings/src/sync-manager.ts
do
  [ -f "$f" ] && { echo "--- $f ---"; cat -n "$f"; }
done

Repository: che-incubator/che-code

Length of output: 10643


Register the contributed keybinding sync commands
cheKeybindingsSync.pull and cheKeybindingsSync.push are declared in code/extensions/che-keybindings/package.json, but there’s no corresponding vscode.commands.registerCommand in code/extensions/che-keybindings/src/extension.ts or code/extensions/che-keybindings/src/syncManager.ts. Invoking them from the Command Palette will fail unless handlers are added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/package.json` around lines 49 - 58, The
contributed commands cheKeybindingsSync.pull and cheKeybindingsSync.push are
declared but not wired up, so add matching vscode.commands.registerCommand
handlers in che-keybindings/src/extension.ts or
che-keybindings/src/syncManager.ts. Use the existing SyncManager flow to
implement the pull and push actions, and make sure the command IDs exactly match
the package.json entries so the Command Palette can invoke them successfully.

Comment on lines +62 to +69
"cheKeybindingsSync.storageMode": {
"type": "string",
"enum": [
"local",
"global"
],
"default": "global"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "storageMode" code/extensions/che-keybindings/src

Repository: che-incubator/che-code

Length of output: 160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files code/extensions/che-keybindings | sed -n '1,200p'

printf '\n== package.json excerpt ==\n'
sed -n '1,220p' code/extensions/che-keybindings/package.json

printf '\n== src search for related config keys ==\n'
rg -n "cheKeybindingsSync|namespace|storageMode|keybindings\.json|vscode-userdata" code/extensions/che-keybindings -S

printf '\n== source outlines ==\n'
for f in $(git ls-files 'code/extensions/che-keybindings/src/*' 2>/dev/null); do
  echo "--- $f"
  wc -l "$f"
done

Repository: che-incubator/che-code

Length of output: 5797


Remove the unused cheKeybindingsSync.storageMode setting. The extension never reads this config, so the local/global option has no effect and will confuse users.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/package.json` around lines 62 - 69, Remove
the unused cheKeybindingsSync.storageMode configuration entry from the
che-keybindings extension manifest, since it is never consumed by the extension
logic. Update the package.json contribution settings so the local/global enum
and default are no longer exposed to users, and verify there are no references
to cheKeybindingsSync.storageMode elsewhere in the extension before deleting it.

Comment on lines +14 to +21
export interface KeybindingsResponse {
keybindingsJson: string;

meta?: {
clientId?: string;
resourceVersion?: string;
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and inspect the surrounding code paths.
git ls-files code/extensions/che-keybindings/src/cheAPi.ts
wc -l code/extensions/che-keybindings/src/cheAPi.ts
cat -n code/extensions/che-keybindings/src/cheAPi.ts | sed -n '1,220p'

# Find all uses of resourceVersion / clientId in this extension.
rg -n "resourceVersion|clientId|patchKeybindings|getKeybindings" code/extensions/che-keybindings -S

Repository: che-incubator/che-code

Length of output: 4834


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the caller to understand whether conflict detection is handled elsewhere.
cat -n code/extensions/che-keybindings/src/syncManager.ts | sed -n '1,220p'

# Search for any backend or protocol references to resourceVersion / optimistic concurrency.
rg -n "resourceVersion|If-Match|etag|conflict|409|clientId" -S .

Repository: che-incubator/che-code

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the sync flow and any local handling of server metadata.
cat -n code/extensions/che-keybindings/src/syncManager.ts | sed -n '1,220p'
cat -n code/extensions/che-keybindings/src/utils.ts | sed -n '1,160p'

# Search only within the che-keybindings extension for protocol fields and conflict handling.
rg -n "resourceVersion|If-Match|etag|conflict|409|meta\\." code/extensions/che-keybindings/src -S

Repository: che-incubator/che-code

Length of output: 7234


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the backend API and any schema that mentions keybindingsJson/resourceVersion.
rg -n "keybindingsJson|resourceVersion|clientId" code -S

Repository: che-incubator/che-code

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read only the relevant sync manager logic and any local API/schema references.
sed -n '1,220p' code/extensions/che-keybindings/src/syncManager.ts | cat -n
sed -n '1,140p' code/extensions/che-keybindings/src/utils.ts | cat -n

# Search narrowly for backend or protocol handling of keybinding metadata.
rg -n "/keybindings|resourceVersion|clientId|If-Match|409" code/extensions/che-keybindings code/src -S --glob '!**/node_modules/**'

Repository: che-incubator/che-code

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the backend implementation or docs for the keybindings API only.
rg -n "/namespace/.*/keybindings|keybindingsJson|resourceVersion" code/extensions code/src -S --glob '!**/node_modules/**' --max-count 200

Repository: che-incubator/che-code

Length of output: 2036


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrow to the che-keybindings extension files to see whether resourceVersion is propagated anywhere else.
rg -n "resourceVersion|patchKeybindings|getKeybindings|keybindingsJson|clientId" code/extensions/che-keybindings/src -S --max-count 80

Repository: che-incubator/che-code

Length of output: 2847


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect any docs or tests that describe the keybindings sync payload/semantics.
fd -a -t f -g '*keybindings*' code/extensions/che-keybindings code | sed -n '1,200p'
rg -n "che-keybindings|keybindings sync|resourceVersion|clientId" code/extensions/che-keybindings -S --glob '!**/node_modules/**'

Repository: che-incubator/che-code

Length of output: 4071


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the built-in user data sync model for how resourceVersion is treated.
rg -n "resourceVersion" code/src/vs/platform/userDataSync code/src/vs/workbench/services/userDataProfile -S --max-count 80

Repository: che-incubator/che-code

Length of output: 160


Thread resourceVersion through PATCH to prevent stale overwrites. clientId only suppresses self-echoes on pull; it does not detect concurrent edits. A second client can still overwrite newer keybindings without any conflict signal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/src/cheAPi.ts` around lines 14 - 21, The
KeybindingsResponse metadata currently carries clientId only, which filters
self-echoes but does not protect against concurrent updates. Thread
resourceVersion through the PATCH flow in the keybindings API and related
handling so updates include and compare the latest version before writing, using
the existing KeybindingsResponse.meta shape and any PATCH handler or client
methods that consume it.

Comment on lines +25 to +55
export async function getKeybindings(
namespace: string,
output?: vscode.OutputChannel,
): Promise<KeybindingsResponse | null> {
output?.appendLine(`[API] GET ${API}/namespace/${namespace}/keybindings`);

const res = await fetch(`${API}/namespace/${namespace}/keybindings`, {
credentials: "include",
});

output?.appendLine(`[API] GET Status: ${res.status}`);

if (!res.ok) {
output?.appendLine(`[API] GET Failed: ${await res.text()}`);
return null;
}

const data = (await res.json()) as KeybindingsResponse;

output?.appendLine(
`[API] GET Success - Received ${data.keybindingsJson.length} bytes`,
);

if (data.meta) {
output?.appendLine(
`[API] Meta => clientId=${data.meta.clientId ?? "N/A"}, resourceVersion=${data.meta.resourceVersion ?? "N/A"}`,
);
}

return data;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files:\n'
git ls-files | rg '(^|/)code/extensions/che-keybindings/src/(cheAPi|syncManager|.*types.*|.*api.*)\.(ts|tsx|js|jsx)$|(^|/)code/extensions/che-keybindings/src/'

printf '\nOutline cheAPi.ts:\n'
ast-grep outline code/extensions/che-keybindings/src/cheAPi.ts --view expanded || true

printf '\nOutline syncManager.ts:\n'
ast-grep outline code/extensions/che-keybindings/src/syncManager.ts --view expanded || true

printf '\nRelevant type/search hits:\n'
rg -n "resourceVersion|AbortSignal\.timeout|fetch\(" code/extensions/che-keybindings/src -S

Repository: che-incubator/che-code

Length of output: 1629


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- cheAPi.ts (1-140) ---'
cat -n code/extensions/che-keybindings/src/cheAPi.ts | sed -n '1,140p'

echo
echo '--- syncManager.ts (1-220) ---'
cat -n code/extensions/che-keybindings/src/syncManager.ts | sed -n '1,220p'

Repository: che-incubator/che-code

Length of output: 8701


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Search for resourceVersion and keybindings API server:\n'
rg -n "resourceVersion|namespace/.*/keybindings|keybindingsJson|clientId" . -S

printf '\nPotential server-side files:\n'
git ls-files | rg '(^|/)(server|backend|api|routes|handlers|controllers|services|src)/.*\.(ts|js|tsx|jsx)$' | head -n 200

Repository: che-incubator/che-code

Length of output: 50378


Add a timeout to both keybinding API calls. getKeybindings() and patchKeybindings() can hang forever on a stalled backend, which blocks sync work and can stack up overlapping poll requests. Use an abort signal/timeout on both fetch() calls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/src/cheAPi.ts` around lines 25 - 55, Both
keybinding API requests can hang indefinitely, so add a timeout-driven abort
signal to the fetch calls in getKeybindings and patchKeybindings. Update the
request flow in cheAPi.ts to create an AbortController (or equivalent timeout
helper), pass its signal into each fetch, and ensure the timeout is cleared once
the request completes. Keep the existing logging and return behavior unchanged,
but make sure stalled backend calls fail fast instead of blocking sync or piling
up overlapping polls.

Comment on lines +19 to +41
export async function readUserKeybindings(
output?: vscode.OutputChannel,
): Promise<string> {
output?.appendLine("[Keybindings] Reading user keybindings...");

try {
const doc = await vscode.workspace.openTextDocument(USER_KEYBINDINGS_URI);

output?.appendLine(
`[Keybindings] Opened: ${USER_KEYBINDINGS_URI.toString()}`,
);

const text = doc.getText();

output?.appendLine(`[Keybindings] Read successful (${text.length} bytes).`);

return text;
} catch (err) {
output?.appendLine(`[Keybindings] Failed to read keybindings: ${err}`);

return "[]";
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Swallowing read failures as "[]" risks overwriting real local keybindings.

pull() (syncManager.ts lines 141-157) uses this return value to decide whether to overwrite the local file with remote data. If the actual read fails transiently while the local file has real content, this fallback makes pull() think local is empty/different and unconditionally overwrites it with remote content, silently discarding un-synced local changes.

🛡️ Proposed fix
 	try {
 		const doc = await vscode.workspace.openTextDocument(USER_KEYBINDINGS_URI);
 		...
 		return text;
 	} catch (err) {
 		output?.appendLine(`[Keybindings] Failed to read keybindings: ${err}`);
-
-		return "[]";
+		throw err;
 	}

Then in SyncManager.pull(), the existing outer try/catch (syncManager.ts lines 122-164) will log "Pull failed" and skip the cycle instead of proceeding with a masked empty state.

📝 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.

Suggested change
export async function readUserKeybindings(
output?: vscode.OutputChannel,
): Promise<string> {
output?.appendLine("[Keybindings] Reading user keybindings...");
try {
const doc = await vscode.workspace.openTextDocument(USER_KEYBINDINGS_URI);
output?.appendLine(
`[Keybindings] Opened: ${USER_KEYBINDINGS_URI.toString()}`,
);
const text = doc.getText();
output?.appendLine(`[Keybindings] Read successful (${text.length} bytes).`);
return text;
} catch (err) {
output?.appendLine(`[Keybindings] Failed to read keybindings: ${err}`);
return "[]";
}
}
export async function readUserKeybindings(
output?: vscode.OutputChannel,
): Promise<string> {
output?.appendLine("[Keybindings] Reading user keybindings...");
try {
const doc = await vscode.workspace.openTextDocument(USER_KEYBINDINGS_URI);
output?.appendLine(
`[Keybindings] Opened: ${USER_KEYBINDINGS_URI.toString()}`,
);
const text = doc.getText();
output?.appendLine(`[Keybindings] Read successful (${text.length} bytes).`);
return text;
} catch (err) {
output?.appendLine(`[Keybindings] Failed to read keybindings: ${err}`);
throw err;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/src/keybindings.ts` around lines 19 - 41, The
readUserKeybindings() fallback to "[]" is masking real read failures and can
cause SyncManager.pull() to overwrite valid local keybindings; change
readUserKeybindings() in che-keybindings/src/keybindings.ts to fail loudly by
propagating the exception instead of returning an empty array string, while
keeping the existing logging in the catch path so SyncManager.pull() can hit its
outer try/catch and abort the sync cycle safely.

Comment on lines +46 to +70
async initialize() {
this.output.appendLine("Initializing SyncManager...");

try {
await this.pull();
this.output.appendLine("Initial pull completed.");
} catch (err) {
this.output.appendLine(`Initial pull failed: ${err}`);
}

this.context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument(this.onSave, this),
);

this.output.appendLine("Registered onDidSaveTextDocument listener.");

this.timer = setInterval(() => {
this.output.appendLine("Polling backend for remote keybindings...");
this.pull().catch((err) =>
this.output.appendLine(`Polling failed: ${err}`),
);
}, 10000);

this.output.appendLine("Polling started (every 10 seconds).");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Overlapping polls can race on shared sync state.

setInterval fires every 10s without waiting for the previous pull() (lines 119-165) to finish. Since pull() and onSave() share mutable state (suppressPush, lastSynced), concurrent invocations can interleave: one pull's suppressPush = true / write / save sequence can be corrupted by another concurrent pull, risking spurious pushes back to the backend or lastSynced being set from stale data.

🔒 Proposed fix (guard against overlap)
+	private pulling = false;
+
 	async initialize() {
 		...
-		this.timer = setInterval(() => {
+		const scheduleNext = () => {
+			this.timer = setTimeout(() => {
+				this.output.appendLine("Polling backend for remote keybindings...");
+				this.pull()
+					.catch((err) => this.output.appendLine(`Polling failed: ${err}`))
+					.finally(scheduleNext);
+			}, 10000);
+		};
+		scheduleNext();
-			this.output.appendLine("Polling backend for remote keybindings...");
-			this.pull().catch((err) =>
-				this.output.appendLine(`Polling failed: ${err}`),
-			);
-		}, 10000);
 	}

 	dispose() {
 		...
 		if (this.timer) {
-			clearInterval(this.timer);
+			clearTimeout(this.timer);
 		}
 	}

 	async pull() {
+		if (this.pulling) {
+			this.output.appendLine("Pull already in progress. Skipping.");
+			return;
+		}
+		this.pulling = true;
 		try {
 			...
+		} finally {
+			this.pulling = false;
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/extensions/che-keybindings/src/syncManager.ts` around lines 46 - 70, The
polling loop in initialize() can start a new pull() before the previous one
finishes, which can interleave with onSave() and corrupt shared sync state like
suppressPush and lastSynced. Add an overlap guard in SyncManager so only one
pull runs at a time, and have the timer skip or queue if a pull is already in
progress. Use the existing pull() and onSave() paths as the coordination points,
and keep the guard around the setInterval callback and any direct pull
invocations.

@tolusha

tolusha commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Hi! I'm che-ai-assistant — I help with your pull requests.

Available commands:

  • /che-ai-assistant generate-che-doc — Generate a documentation PR based on this PR's changes
  • /che-ai-assistant ok-pr-review — Run a comprehensive PR review (summary, code review, deep review, impact analysis)
  • /che-ai-assistant check-pr-test-failures — Analyze failing CI checks, identify root causes, and suggest fixes
  • /che-ai-assistant help — Show this help message

@msivasubramaniaan msivasubramaniaan changed the title Feast manage key bindings in che Feast manage key bindings in vscode devspace Jul 6, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants