diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml
new file mode 100644
index 000000000000..990aaeecb7cc
--- /dev/null
+++ b/.github/workflows/build-docker.yaml
@@ -0,0 +1,117 @@
+name: build-docker
+on:
+ workflow_dispatch:
+
+permissions:
+ id-token: write # allows the JWT to be requested from GitHub's OIDC provider
+ contents: read # This is required for actions/checkout
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+env:
+ arch_amd64: amd64
+ arch_arm64: arm64
+ docker_images: |
+ name=rudderstack/profiles-code-server
+ docker_tags: |
+ type=raw,value=latest
+
+jobs:
+ metadata:
+ runs-on: ubuntu-latest
+ outputs:
+ labels: ${{ steps.meta.outputs.labels }}
+ build-date: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
+ version: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
+ revision: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
+ tags: ${{ steps.meta.outputs.tags }}
+ arm64_tags: ${{ steps.arm64_meta.outputs.tags }}
+ arm64_labels: ${{ steps.arm64_meta.outputs.labels }}
+ amd64_tags: ${{ steps.amd64_meta.outputs.tags }}
+ amd64_labels: ${{ steps.amd64_meta.outputs.labels }}
+ steps:
+ - name: docker meta
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.docker_images }}
+ tags: ${{ env.docker_tags }}
+ - name: docker arm64 meta
+ id: arm64_meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.docker_images }}
+ tags: ${{ env.docker_tags }}
+ flavor: |
+ suffix=-${{ env.arch_arm64 }},onlatest=true
+ - name: docker amd64 meta
+ id: amd64_meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.docker_images }}
+ tags: ${{ env.docker_tags }}
+ flavor: |
+ suffix=-${{ env.arch_amd64 }},onlatest=true
+
+ build:
+ needs:
+ - metadata
+ strategy:
+ fail-fast: false
+ matrix:
+ build-config:
+ - os: [self-hosted, Linux, ARM64, ubuntu-22]
+ tags: ${{ needs.metadata.outputs.arm64_tags }}
+ labels: ${{ needs.metadata.outputs.arm64_labels }}
+ platform: linux/arm64
+ - os: ubuntu-latest
+ tags: ${{ needs.metadata.outputs.amd64_tags }}
+ labels: ${{ needs.metadata.outputs.amd64_labels }}
+ platform: linux/amd64
+ runs-on: ${{ matrix.build-config.os }}
+ steps:
+ - name: checkout
+ uses: actions/checkout@v4
+ - name: setup buildx
+ uses: docker/setup-buildx-action@v3
+ - name: docker login
+ uses: docker/login-action@v3.1.0
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: docker build
+ uses: rudderlabs/build-scan-push-action@v1.5.3
+ with:
+ context: .
+ platforms: ${{ matrix.build-config.platform }}
+ push: true
+ tags: ${{ matrix.build-config.tags }}
+ labels: ${{ matrix.build-config.labels }}
+ build-args: |
+ VERSION=${{ github.ref_name }}
+ GITHUB_PAT=${{ secrets.PAT }}
+ ENTERPRISE_TOKEN=${{ secrets.ENTERPRISE_TOKEN }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+
+ manifest:
+ runs-on: ubuntu-latest
+ needs: [build, metadata]
+ steps:
+ - name: setup buildx
+ uses: docker/setup-buildx-action@v3
+ - name: docker login
+ uses: docker/login-action@v3.1.0
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: multi-arch manifest
+ run: |
+ while read -r tag; do
+ echo "$tag"
+ arm_tag=$(echo "${{ needs.metadata.outputs.arm64_tags }}" | grep "$tag")
+ amd_tag=$(echo "${{ needs.metadata.outputs.amd64_tags }}" | grep "$tag")
+ docker buildx imagetools create -t $tag $arm_tag $amd_tag
+ done <<< "${{ needs.metadata.outputs.tags }}"
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 0a243e388740..2ae82c7e8bdc 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -34,7 +34,7 @@ jobs:
- name: Checkout repo
uses: actions/checkout@v4
- name: Check changed files
- uses: dorny/paths-filter@v3
+ uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 #v3
id: filter
with:
filters: |
@@ -103,7 +103,7 @@ jobs:
- uses: actions/checkout@v4
- uses: azure/setup-helm@v4
with:
- token: ${{ secrets.GITHUB_TOKEN }}
+ token: ${{ secrets.PAT }}
- run: helm plugin install https://github.com/instrumenta/helm-kubeval
- run: helm kubeval ci/helm-chart
@@ -173,7 +173,7 @@ jobs:
with:
submodules: true
- run: sudo apt update && sudo apt install -y libkrb5-dev
- - uses: awalsh128/cache-apt-pkgs-action@latest
+ - uses: awalsh128/cache-apt-pkgs-action@4c82c3ccdc1344ee11e9775dbdbdf43aa8a5614e
with:
packages: quilt
version: 1.0
@@ -188,7 +188,7 @@ jobs:
- run: SKIP_SUBMODULE_DEPS=1 npm ci
- run: npm run build
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.PAT }}
# Get Code's git hash. When this changes it means the content is
# different and we need to rebuild.
- name: Get latest lib/vscode rev
@@ -293,7 +293,7 @@ jobs:
key: cache-caddy-2.5.2
- name: Install Caddy
env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_TOKEN: ${{ secrets.PAT }}
if: steps.caddy-cache.outputs.cache-hit != 'true'
run: |
gh release download v2.5.2 --repo caddyserver/caddy --pattern "caddy_2.5.2_linux_amd64.tar.gz"
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index 91e320087175..28973ba8a031 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -49,7 +49,7 @@ jobs:
- run: npm run publish:npm
env:
VERSION: ${{ env.VERSION }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.PAT }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_ENVIRONMENT: "production"
@@ -167,7 +167,7 @@ jobs:
with:
registry: ghcr.io
username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
+ password: ${{ secrets.PAT }}
# Strip out the v (v4.9.1 -> 4.9.1).
- name: Get and set VERSION
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index d5223c0485cb..23a550e27221 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -118,7 +118,7 @@ jobs:
VERSION: ${{ env.VERSION }}
run: npm run package $PKG_ARCH
- - uses: softprops/action-gh-release@v1
+ - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 #v1
with:
draft: true
discussion_category_name: "📣 Announcements"
@@ -130,7 +130,7 @@ jobs:
timeout-minutes: 15
needs: npm-version
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.PAT }}
steps:
- name: Checkout repo
@@ -179,7 +179,7 @@ jobs:
VERSION: ${{ env.VERSION }}
run: npm run package
- - uses: softprops/action-gh-release@v1
+ - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 #v1
with:
draft: true
discussion_category_name: "📣 Announcements"
@@ -191,7 +191,7 @@ jobs:
timeout-minutes: 15
needs: npm-version
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.PAT }}
steps:
- name: Checkout repo
@@ -240,7 +240,7 @@ jobs:
VERSION: ${{ env.VERSION }}
run: npm run package
- - uses: softprops/action-gh-release@v1
+ - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 #v1
with:
draft: true
discussion_category_name: "📣 Announcements"
@@ -257,7 +257,7 @@ jobs:
with:
name: npm-release-package
- - uses: softprops/action-gh-release@v1
+ - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 #v1
with:
draft: true
discussion_category_name: "📣 Announcements"
@@ -269,7 +269,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Download artifacts
- uses: dawidd6/action-download-artifact@v10
+ uses: dawidd6/action-download-artifact@4c1e823582f43b179e2cbb49c3eade4e41f992e2 #v10
id: download
with:
branch: ${{ github.ref }}
diff --git a/.truffleignore b/.truffleignore
new file mode 100644
index 000000000000..35ec3b9d7586
--- /dev/null
+++ b/.truffleignore
@@ -0,0 +1 @@
+/
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000000..66ae56cd530a
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,60 @@
+# Base image
+FROM ubuntu:22.04
+
+# Install Python, pip, git, curl, and wget
+RUN apt-get update && \
+ apt-get install -y python3.10 python3-pip git curl wget sudo && \
+ apt-get clean
+
+# Create a non-root user
+RUN useradd -m -s /bin/bash codeuser
+
+# Create project directory
+RUN mkdir -p /home/codeuser/project
+
+# Create .pb directory and siteconfig.yaml file
+RUN mkdir -p /home/codeuser/.pb && \
+ touch /home/codeuser/.pb/siteconfig.yaml
+
+# Install RudderStack Profiles CLI (assuming pip install)
+RUN pip3 install profiles-rudderstack
+
+# Install code-server (VSCode in the browser)
+RUN curl -fsSL https://code-server.dev/install.sh | sh
+
+# Switch to codeuser for extension installation and MCP setup
+USER codeuser
+WORKDIR /home/codeuser
+
+# Install extension as codeuser
+RUN code-server --install-extension saoudrizwan.claude-dev
+
+# Clone profiles-mcp as codeuser
+RUN git clone https://github.com/rudderlabs/profiles-mcp
+
+# Set up the Python script
+# RUN echo '#!/usr/bin/env python3' > /home/codeuser/profiles-mcp/scripts/update_mcp_config.py
+# RUN echo 'RUDDERSTACK_PAT=xxxx' > /home/codeuser/profiles-mcp/.env
+
+# Run setup as codeuser
+# RUN cd /home/codeuser/profiles-mcp && bash setup.sh
+
+# Create MCP settings directory and file
+# RUN mkdir -p /home/codeuser/.local/share/code-server/User/globalStorage/saoudrizwan.claude-dev/settings/
+# RUN echo '{"mcpServers":{ "Profiles": { "command": "/home/codeuser/profiles-mcp/scripts/start.sh", "args": [] }}}' > /home/codeuser/.local/share/code-server/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
+
+# Set proper ownership and permissions
+USER root
+RUN chown -R codeuser:codeuser /home/codeuser
+RUN chmod 755 /home/codeuser/project
+RUN chmod 644 /home/codeuser/.pb/siteconfig.yaml
+RUN chmod 755 /home/codeuser/.pb
+
+# Switch back to codeuser
+USER codeuser
+WORKDIR /home/codeuser/project
+
+EXPOSE 8080
+
+# Start code-server when container runs, opening the project directory
+CMD ["code-server", "--bind-addr", "0.0.0.0:8080", "/home/codeuser/project"]
\ No newline at end of file
diff --git a/patches/rudderstack-plugin.diff b/patches/rudderstack-plugin.diff
new file mode 100644
index 000000000000..2f51b80045e9
--- /dev/null
+++ b/patches/rudderstack-plugin.diff
@@ -0,0 +1,447 @@
+Index: code-server/lib/vscode/src/vs/workbench/workbench.common.main.ts
+===================================================================
+--- code-server.orig/lib/vscode/src/vs/workbench/workbench.common.main.ts
++++ code-server/lib/vscode/src/vs/workbench/workbench.common.main.ts
+@@ -420,5 +420,8 @@ import './contrib/dropOrPasteInto/browse
+ // Edit Telemetry
+ import './contrib/editTelemetry/browser/editTelemetry.contribution.js';
+
++// Rudderstack
++import './contrib/rudderstack/browser/rudderstack.contribution.js';
++
+
+ //#endregion
+Index: code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/media/rudderstack-icon.svg
+===================================================================
+--- /dev/null
++++ code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/media/rudderstack-icon.svg
+@@ -0,0 +1,4 @@
++
+Index: code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstack.contribution.ts
+===================================================================
+--- /dev/null
++++ code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstack.contribution.ts
+@@ -0,0 +1,39 @@
++/* eslint-disable header/header */
++import { ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.js';
++import { IViewContainersRegistry, IViewsRegistry } from '../../../common/views.js';
++import { Registry } from '../../../../platform/registry/common/platform.js';
++import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
++import * as nls from '../../../../nls.js';
++import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js';
++import { rudderstackFeatureViewIcon } from './rudderstackIcons.js';
++import { RudderstackViewPaneContainer } from './rudderstackViewlet.js';
++import { RudderstackMainView } from './rudderstackMainView.js';
++import { VIEWLET_ID, MAIN_VIEW_ID } from '../common/rudderstack.js';
++
++// Register the view container (activity bar icon)
++const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({
++ id: VIEWLET_ID, // Unique ID
++ title: nls.localize2('rudderstack', "Rudderstack"), // Display name
++ openCommandActionDescriptor: {
++ id: VIEWLET_ID,
++ mnemonicTitle: nls.localize({ key: 'miViewRudderstack', comment: ['&& denotes a mnemonic'] }, "&&Rudderstack"),
++ keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyX }, // Optional shortcut
++ },
++ ctorDescriptor: new SyncDescriptor(RudderstackViewPaneContainer), // Container class
++ icon: rudderstackFeatureViewIcon, // Icon for activity bar
++ alwaysUseContainerInfo: true,
++ order: 1000, // High number to appear last
++}, ViewContainerLocation.Sidebar);
++
++// Register views inside the container
++const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry);
++viewsRegistry.registerViews([{
++ id: MAIN_VIEW_ID,
++ name: nls.localize2('rudderstackMainView', "Rudderstack"),
++ containerIcon: rudderstackFeatureViewIcon,
++ ctorDescriptor: new SyncDescriptor(RudderstackMainView),
++ order: 10,
++ weight: 40,
++ canToggleVisibility: true,
++ canMoveView: true
++}], viewContainer);
+\ No newline at end of file
+Index: code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstackIcons.ts
+===================================================================
+--- /dev/null
++++ code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstackIcons.ts
+@@ -0,0 +1,6 @@
++/* eslint-disable header/header */
++import { URI } from '../../../../base/common/uri.js';
++import { FileAccess } from '../../../../base/common/network.js';
++
++// Main activity bar icon - direct URI to custom SVG
++export const rudderstackFeatureViewIcon = URI.parse(FileAccess.asBrowserUri('vs/workbench/contrib/rudderstack/browser/media/rudderstack-icon.svg').toString());
+Index: code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstackMainView.ts
+===================================================================
+--- /dev/null
++++ code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstackMainView.ts
+@@ -0,0 +1,307 @@
++/* eslint-disable header/header */
++import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
++import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
++import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
++import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
++import { IViewDescriptorService } from '../../../common/views.js';
++import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
++import { IOpenerService } from '../../../../platform/opener/common/opener.js';
++import { IThemeService } from '../../../../platform/theme/common/themeService.js';
++import { IHoverService } from '../../../../platform/hover/browser/hover.js';
++import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
++import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
++import { ITerminalService, ITerminalGroupService, ITerminalEditorService } from '../../../contrib/terminal/browser/terminal.js';
++import { TerminalLocation } from '../../../../platform/terminal/common/terminal.js';
++import { IEditorService } from '../../../services/editor/common/editorService.js';
++import { IFileService } from '../../../../platform/files/common/files.js';
++import { VSBuffer } from '../../../../base/common/buffer.js';
++import { URI } from '../../../../base/common/uri.js';
++import { SaveReason } from '../../../common/editor.js';
++import { ViewPane } from '../../../browser/parts/views/viewPane.js';
++import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js';
++import { MAIN_VIEW_ID } from '../common/rudderstack.js';
++import { Button } from '../../../../base/browser/ui/button/button.js';
++import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js';
++import { $, append } from '../../../../base/browser/dom.js';
++import { Severity } from '../../../../platform/notification/common/notification.js';
++import * as nls from '../../../../nls.js';
++
++export class RudderstackMainView extends ViewPane {
++
++ static readonly ID = MAIN_VIEW_ID;
++
++ private primaryButton: Button | undefined;
++ private runProjectButton: Button | undefined;
++ private compileProjectButton: Button | undefined;
++
++ constructor(
++ options: IViewletViewOptions,
++ @IKeybindingService keybindingService: IKeybindingService,
++ @IContextMenuService contextMenuService: IContextMenuService,
++ @IConfigurationService configurationService: IConfigurationService,
++ @IContextKeyService contextKeyService: IContextKeyService,
++ @IViewDescriptorService viewDescriptorService: IViewDescriptorService,
++ @IInstantiationService instantiationService: IInstantiationService,
++ @IOpenerService openerService: IOpenerService,
++ @IThemeService themeService: IThemeService,
++ @IHoverService hoverService: IHoverService,
++ @IDialogService private readonly dialogService: IDialogService,
++ @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
++ @ITerminalService private readonly terminalService: ITerminalService,
++ @ITerminalGroupService private readonly terminalGroupService: ITerminalGroupService,
++ @ITerminalEditorService private readonly terminalEditorService: ITerminalEditorService,
++ @IEditorService private readonly editorService: IEditorService,
++ @IFileService private readonly fileService: IFileService,
++ ) {
++ super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);
++ }
++
++ protected override renderBody(container: HTMLElement): void {
++ super.renderBody(container);
++
++ // Clear container
++ container.innerHTML = '';
++
++ // Create main content container with proper spacing
++ const contentContainer = append(container, $('.rudderstack-content'));
++ contentContainer.style.padding = '20px';
++ contentContainer.style.display = 'flex';
++ contentContainer.style.flexDirection = 'column';
++ contentContainer.style.gap = '16px';
++
++ // Title
++ const title = append(contentContainer, $('.rudderstack-title'));
++ title.style.fontSize = '14px';
++ title.style.fontWeight = '600';
++ title.style.color = 'var(--vscode-foreground)';
++ title.textContent = nls.localize('rudderstackTitle', 'Rudderstack Profiles');
++
++ // Description
++ const description = append(contentContainer, $('.rudderstack-description'));
++ description.style.fontSize = '12px';
++ description.style.color = 'var(--vscode-descriptionForeground)';
++ description.style.marginBottom = '8px';
++ description.textContent = nls.localize('rudderstackDescription', 'edit your project');
++
++ // Button container
++ const buttonContainer = append(contentContainer, $('.button-container'));
++ buttonContainer.style.display = 'flex';
++ buttonContainer.style.flexDirection = 'column';
++ buttonContainer.style.gap = '8px';
++
++ // Primary action button (like "Run and Debug" button)
++ this.primaryButton = this._register(new Button(buttonContainer, {
++ supportIcons: true,
++ title: nls.localize('saveAndExit', 'Save and Exit'),
++ ...defaultButtonStyles
++ }));
++ this.primaryButton.label = `$(save) ${nls.localize('saveAndExit', 'Save and Exit')}`;
++ this._register(this.primaryButton.onDidClick(async () => {
++ await this.onSaveAndExit();
++ }));
++
++ // Project Actions Section
++ const projectActionsTitle = append(contentContainer, $('.project-actions-title'));
++ projectActionsTitle.style.fontSize = '12px';
++ projectActionsTitle.style.fontWeight = '600';
++ projectActionsTitle.style.color = 'var(--vscode-foreground)';
++ projectActionsTitle.style.marginTop = '16px';
++ projectActionsTitle.style.marginBottom = '8px';
++ projectActionsTitle.textContent = nls.localize('projectActions', 'Project Actions');
++
++ // Project buttons container
++ const projectButtonContainer = append(contentContainer, $('.project-button-container'));
++ projectButtonContainer.style.display = 'flex';
++ projectButtonContainer.style.flexDirection = 'column';
++ projectButtonContainer.style.gap = '8px';
++
++ // Run Project button
++ this.runProjectButton = this._register(new Button(projectButtonContainer, {
++ supportIcons: true,
++ secondary: true,
++ title: nls.localize('runProject', 'Run Project'),
++ ...defaultButtonStyles
++ }));
++ this.runProjectButton.label = `$(play) ${nls.localize('runProject', 'Run Project')}`;
++ this._register(this.runProjectButton.onDidClick(async () => {
++ await this.onRunProject();
++ }));
++
++ // Compile Project button
++ this.compileProjectButton = this._register(new Button(projectButtonContainer, {
++ supportIcons: true,
++ secondary: true,
++ title: nls.localize('compileProject', 'Compile Project'),
++ ...defaultButtonStyles
++ }));
++ this.compileProjectButton.label = `$(tools) ${nls.localize('compileProject', 'Compile Project')}`;
++ this._register(this.compileProjectButton.onDidClick(async () => {
++ await this.onCompileProject();
++ }));
++ }
++
++ private async getStatusFileUri(): Promise {
++ // Try to get the status file path from VS Code configuration
++ const statusFilePath = this.configurationService.getValue('rudderstack.statusFilePath');
++ if (statusFilePath) {
++ // If it's a relative path, resolve it against the workspace
++ if (!statusFilePath.startsWith('/') && !statusFilePath.match(/^[a-zA-Z]:/)) {
++ const workspace = this.workspaceContextService.getWorkspace();
++ if (workspace.folders && workspace.folders.length > 0) {
++ return URI.joinPath(workspace.folders[0].uri, statusFilePath);
++ }
++ }
++ // For absolute paths, try to create URI (may not work in browser context)
++ try {
++ return URI.file(statusFilePath);
++ } catch (error) {
++ console.warn('Failed to create URI from absolute path:', statusFilePath, error);
++ }
++ }
++
++ // Fallback: Use workspace-relative path with proper URI handling
++ const workspace = this.workspaceContextService.getWorkspace();
++ if (workspace.folders && workspace.folders.length > 0) {
++ const statusFileUri = URI.joinPath(workspace.folders[0].uri, 'rudderstack_status.json');
++ console.log('No rudderstack.statusFilePath configured, using workspace-relative location:', statusFileUri.toString());
++ return statusFileUri;
++ }
++
++ console.warn('No workspace found, cannot determine status file location');
++ return undefined;
++ }
++
++ private async onSaveAndExit(): Promise {
++ const result = await this.dialogService.confirm({
++ type: Severity.Warning,
++ message: nls.localize('saveAndExitConfirm', 'Save and Exit'),
++ detail: nls.localize('saveAndExitDetail', 'Are you sure you want to save all files and exit?'),
++ primaryButton: nls.localize('saveAndExit', 'Save and Exit'),
++ cancelButton: nls.localize('cancel', 'Cancel')
++ });
++
++ if (result.confirmed) {
++ try {
++ // Step 1: Save all files in the active folder
++ console.log('Saving all files...');
++ const saveResult = await this.editorService.saveAll({
++ includeUntitled: true,
++ reason: SaveReason.EXPLICIT
++ });
++
++ if (!saveResult.success) {
++ throw new Error('Some files could not be saved');
++ }
++
++ // Step 2: Write status file to configured location
++ const statusFileUri = await this.getStatusFileUri();
++ if (statusFileUri) {
++ console.log(`Writing status file to: ${statusFileUri.toString()}`);
++ const statusData = {
++ timestamp: new Date().toISOString(),
++ action: 'save_and_exit',
++ status: 'completed',
++ savedFiles: saveResult.editors.length
++ };
++
++ const statusFileContent = VSBuffer.fromString(JSON.stringify(statusData, null, 2));
++ await this.fileService.writeFile(statusFileUri, statusFileContent);
++
++ console.log('Status file written successfully');
++ } else {
++ console.warn('No status file path configured, skipping status file creation');
++ }
++
++ // Show success notification
++ this.dialogService.confirm({
++ type: Severity.Info,
++ message: nls.localize('saveAndExitSuccess', 'Save and Exit Complete'),
++ detail: nls.localize('saveAndExitSuccessDetail', 'All files have been saved successfully.'),
++ primaryButton: nls.localize('ok', 'OK')
++ });
++
++ console.log('Save and exit operation completed successfully');
++
++ } catch (error) {
++ console.error('Save and exit operation failed:', error);
++ this.dialogService.confirm({
++ type: Severity.Error,
++ message: nls.localize('saveAndExitError', 'Save and Exit Failed'),
++ detail: nls.localize('saveAndExitErrorDetail', 'Failed to save files or write status: {0}', error instanceof Error ? error.message : 'Unknown error'),
++ primaryButton: nls.localize('ok', 'OK')
++ });
++ }
++ }
++ }
++
++ private async onRunProject(): Promise {
++ try {
++ // Get or create a terminal instance
++ const terminal = await this.terminalService.getActiveOrCreateInstance();
++
++ // Ensure the terminal is visible based on its location
++ if (terminal.target === TerminalLocation.Editor) {
++ const existingEditors = this.editorService.findEditors(terminal.resource);
++ this.terminalEditorService.openEditor(terminal, { viewColumn: existingEditors?.[0]?.groupId });
++ } else {
++ // Show the terminal panel
++ await this.terminalGroupService.showPanel(true);
++ }
++
++ // Make sure the terminal is focused and ready
++ await terminal.focusWhenReady(true);
++
++ // Execute the 'pb run' command
++ terminal.runCommand('pb run', true);
++
++ console.log('Executing: pb run');
++ } catch (error) {
++ console.error('Failed to run project:', error);
++ // Optionally show an error dialog
++ this.dialogService.confirm({
++ type: Severity.Error,
++ message: nls.localize('runProjectError', 'Failed to run project'),
++ detail: nls.localize('runProjectErrorDetail', 'Could not execute "pb run" in terminal. Please check if the terminal is available.'),
++ primaryButton: nls.localize('ok', 'OK')
++ });
++ }
++ }
++
++ private async onCompileProject(): Promise {
++ try {
++ // Get or create a terminal instance
++ const terminal = await this.terminalService.getActiveOrCreateInstance();
++
++ // Ensure the terminal is visible based on its location
++ if (terminal.target === TerminalLocation.Editor) {
++ const existingEditors = this.editorService.findEditors(terminal.resource);
++ this.terminalEditorService.openEditor(terminal, { viewColumn: existingEditors?.[0]?.groupId });
++ } else {
++ // Show the terminal panel
++ await this.terminalGroupService.showPanel(true);
++ }
++
++ // Make sure the terminal is focused and ready
++ await terminal.focusWhenReady(true);
++
++ // Execute the 'pb compile' command
++ terminal.runCommand('pb compile', true);
++
++ console.log('Executing: pb compile');
++ } catch (error) {
++ console.error('Failed to compile project:', error);
++ // Optionally show an error dialog
++ this.dialogService.confirm({
++ type: Severity.Error,
++ message: nls.localize('compileProjectError', 'Failed to compile project'),
++ detail: nls.localize('compileProjectErrorDetail', 'Could not execute "pb compile" in terminal. Please check if the terminal is available.'),
++ primaryButton: nls.localize('ok', 'OK')
++ });
++ }
++ }
++
++ protected override layoutBody(height: number, width: number): void {
++ super.layoutBody(height, width);
++ // Handle any layout changes if needed
++ }
++}
+\ No newline at end of file
+Index: code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstackViewlet.ts
+===================================================================
+--- /dev/null
++++ code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/browser/rudderstackViewlet.ts
+@@ -0,0 +1,37 @@
++/* eslint-disable header/header */
++import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
++import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
++import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
++import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
++import { IStorageService } from '../../../../platform/storage/common/storage.js';
++import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
++import { IThemeService } from '../../../../platform/theme/common/themeService.js';
++import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
++import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js';
++import { IViewDescriptorService } from '../../../common/views.js';
++import { IViewsService } from '../../../services/views/common/viewsService.js';
++import { IExtensionService } from '../../../services/extensions/common/extensions.js';
++import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js';
++import { ILogService } from '../../../../platform/log/common/log.js';
++import { VIEWLET_ID } from '../common/rudderstack.js';
++
++export class RudderstackViewPaneContainer extends ViewPaneContainer {
++
++ constructor(
++ @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
++ @ITelemetryService telemetryService: ITelemetryService,
++ @IWorkspaceContextService contextService: IWorkspaceContextService,
++ @IStorageService storageService: IStorageService,
++ @IConfigurationService configurationService: IConfigurationService,
++ @IInstantiationService instantiationService: IInstantiationService,
++ @IThemeService themeService: IThemeService,
++ @IContextMenuService contextMenuService: IContextMenuService,
++ @IExtensionService extensionService: IExtensionService,
++ @IContextKeyService contextKeyService: IContextKeyService,
++ @IViewDescriptorService viewDescriptorService: IViewDescriptorService,
++ @IViewsService viewsService: IViewsService,
++ @ILogService logService: ILogService,
++ ) {
++ super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService);
++ }
++}
+\ No newline at end of file
+Index: code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/common/rudderstack.ts
+===================================================================
+--- /dev/null
++++ code-server/lib/vscode/src/vs/workbench/contrib/rudderstack/common/rudderstack.ts
+@@ -0,0 +1,7 @@
++/*---------------------------------------------------------------------------------------------
++ * Copyright (c) Microsoft Corporation. All rights reserved.
++ * Licensed under the MIT License. See License.txt in the project root for license information.
++ *--------------------------------------------------------------------------------------------*/
++
++export const VIEWLET_ID = 'workbench.view.rudderstack';
++export const MAIN_VIEW_ID = 'workbench.rudderstack.mainView';
+\ No newline at end of file
diff --git a/patches/series b/patches/series
index ffc15fdd9d75..3b655a24a557 100644
--- a/patches/series
+++ b/patches/series
@@ -22,3 +22,4 @@ clipboard.diff
display-language.diff
trusted-domains.diff
signature-verification.diff
+rudderstack-plugin.diff
diff --git a/src/browser/media/favicon-dark-support.svg b/src/browser/media/favicon-dark-support.svg
deleted file mode 100644
index d64bf32ed96e..000000000000
--- a/src/browser/media/favicon-dark-support.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/browser/media/favicon.ico b/src/browser/media/favicon.ico
index 56078ead6697..9e3647d288ab 100644
Binary files a/src/browser/media/favicon.ico and b/src/browser/media/favicon.ico differ
diff --git a/src/browser/media/favicon.svg b/src/browser/media/favicon.svg
deleted file mode 100644
index 01a01541ec75..000000000000
--- a/src/browser/media/favicon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/browser/media/pwa-icon-192.png b/src/browser/media/pwa-icon-192.png
deleted file mode 100644
index 9f46dd830361..000000000000
Binary files a/src/browser/media/pwa-icon-192.png and /dev/null differ
diff --git a/src/browser/media/pwa-icon-512.png b/src/browser/media/pwa-icon-512.png
deleted file mode 100644
index 9b899e8d690e..000000000000
Binary files a/src/browser/media/pwa-icon-512.png and /dev/null differ
diff --git a/src/browser/media/pwa-icon-maskable-192.png b/src/browser/media/pwa-icon-maskable-192.png
deleted file mode 100644
index 3f28593b3aee..000000000000
Binary files a/src/browser/media/pwa-icon-maskable-192.png and /dev/null differ
diff --git a/src/browser/media/pwa-icon-maskable-512.png b/src/browser/media/pwa-icon-maskable-512.png
deleted file mode 100644
index 7df85f7665ac..000000000000
Binary files a/src/browser/media/pwa-icon-maskable-512.png and /dev/null differ
diff --git a/src/browser/media/templates.png b/src/browser/media/templates.png
deleted file mode 100644
index 1437658ec13a..000000000000
Binary files a/src/browser/media/templates.png and /dev/null differ
diff --git a/src/browser/pages/login.html b/src/browser/pages/login.html
index c7fb2f2ac67e..9796f0579be2 100644
--- a/src/browser/pages/login.html
+++ b/src/browser/pages/login.html
@@ -12,11 +12,9 @@
/>
{{I18N_LOGIN_TITLE}}
-
+
-
-