Files
vibe-kanban/shared/types.ts
Louis Knight-Webb e8ff40d5a4 Remote host + username opening for VSCode based IDEs (#1134)
* feat: add remote VSCode SSH support for remote server deployments

Add support for opening VSCode via SSH when Vibe Kanban is running on a remote server.
This allows users accessing the web UI through a tunnel to open projects/tasks in their
local VSCode which connects to the remote server via SSH.

Backend changes:
- Add remote_ssh_host and remote_ssh_user fields to EditorConfig (v3)
- Create config v8 with migration from v7
- Modify EditorConfig.open_file() to return URL when remote mode is enabled
- Update API routes to return OpenEditorResponse with optional URL
- Generate vscode:// URL scheme for remote SSH connections

Frontend changes:
- Update API client to handle OpenEditorResponse type
- Modify hooks to open URLs in new tabs when returned
- Add UI fields in settings for remote SSH configuration

When remote_ssh_host is configured, clicking "Open in VSCode" generates a URL like:
vscode://vscode-remote/ssh-remote+user@host/path/to/project

This works for both project-level and task-level editor opening.

* feat: extend remote SSH support to Cursor and Windsurf editors

Extend the remote SSH feature to support Cursor and Windsurf editors,
which are VSCode forks that use the same remote SSH protocol.

Changes:
- Update EditorConfig.open_file() to generate cursor:// and windsurf:// URLs
- Show SSH configuration fields in settings for all three VSCode-based editors
- Use same vscode-remote SSH URL pattern for all three editors

When remote_ssh_host is configured, supported editors generate URLs like:
- vscode://vscode-remote/ssh-remote+user@host/path
- cursor://vscode-remote/ssh-remote+user@host/path
- windsurf://vscode-remote/ssh-remote+user@host/path

* fix: address clippy linting issues

- Fix uninlined format args in v3.rs
- Add allow attributes for re-exported types in v5-v7

* docs: add remote SSH configuration documentation

Add comprehensive documentation for the remote SSH editor feature:

- Expand Editor Integration section in global-settings.mdx
  - Document when to use remote SSH (tunnels, systemctl services, remote deployments)
  - Explain configuration fields (remote_ssh_host, remote_ssh_user)
  - Detail how the feature works with protocol URLs
  - List prerequisites for SSH access and VSCode Remote-SSH extension
  - Support for VSCode, Cursor, and Windsurf editors

- Add Remote Deployment section to README.md
  - Quick guide for setting up remote SSH access
  - Link to detailed documentation
  - Include in feature list

This documentation helps users understand and configure the remote SSH
feature when running Vibe Kanban on remote servers accessed via browser.

* remove package-lock in favour of pnpm lock

* rollback config version increment

* re-impl remote URL

* Update i18n for general settings (vibe-kanban 4a1a3ae1)

frontend/src/pages/settings/GeneralSettings.tsx

Find any strings here that haven't been i18n'd and i18n them

* add line number/col to the end of vscode-remote file paths

* handle response url when opening file in ide from DiffCard

* update remote-ssh guidance in readme

* add image to global settings docs

---------

Co-authored-by: Stephan Fitzpatrick <stephan@knowsuchagency.com>
Co-authored-by: Britannio Jarrett <britanniojarrett@gmail.com>
2025-10-31 12:39:58 +00:00

329 lines
19 KiB
TypeScript

// This file was generated by `crates/core/src/bin/generate_types.rs`.
// Do not edit this file manually.
// If you are an AI, and you absolutely have to edit this file, please confirm with the user first.
export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, last_modified: bigint | null, };
export type DirectoryListResponse = { entries: Array<DirectoryEntry>, current_path: string, };
export type Project = { id: string, name: string, git_repo_path: string, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, copy_files: string | null, created_at: Date, updated_at: Date, };
export type CreateProject = { name: string, git_repo_path: string, use_existing_repo: boolean, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, copy_files: string | null, };
export type UpdateProject = { name: string | null, git_repo_path: string | null, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, copy_files: string | null, };
export type SearchResult = { path: string, is_file: boolean, match_type: SearchMatchType, };
export type SearchMatchType = "FileName" | "DirectoryName" | "FullPath";
export type ExecutorAction = { typ: ExecutorActionType, next_action: ExecutorAction | null, };
export type McpConfig = { servers: { [key in string]?: JsonValue }, servers_path: Array<string>, template: JsonValue, preconfigured: JsonValue, is_toml_config: boolean, };
export type ExecutorActionType = { "type": "CodingAgentInitialRequest" } & CodingAgentInitialRequest | { "type": "CodingAgentFollowUpRequest" } & CodingAgentFollowUpRequest | { "type": "ScriptRequest" } & ScriptRequest;
export type ScriptContext = "SetupScript" | "CleanupScript" | "DevServer";
export type ScriptRequest = { script: string, language: ScriptRequestLanguage, context: ScriptContext, };
export type ScriptRequestLanguage = "Bash";
export enum BaseCodingAgent { CLAUDE_CODE = "CLAUDE_CODE", AMP = "AMP", GEMINI = "GEMINI", CODEX = "CODEX", OPENCODE = "OPENCODE", CURSOR_AGENT = "CURSOR_AGENT", QWEN_CODE = "QWEN_CODE", COPILOT = "COPILOT" }
export type CodingAgent = { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot };
export type Tag = { id: string, tag_name: string, content: string, created_at: string, updated_at: string, };
export type CreateTag = { tag_name: string, content: string, };
export type UpdateTag = { tag_name: string | null, content: string | null, };
export type TagSearchParams = { search: string | null, };
export type TaskStatus = "todo" | "inprogress" | "inreview" | "done" | "cancelled";
export type Task = { id: string, project_id: string, title: string, description: string | null, status: TaskStatus, parent_task_attempt: string | null, created_at: string, updated_at: string, };
export type TaskWithAttemptStatus = { has_in_progress_attempt: boolean, has_merged_attempt: boolean, last_attempt_failed: boolean, executor: string, id: string, project_id: string, title: string, description: string | null, status: TaskStatus, parent_task_attempt: string | null, created_at: string, updated_at: string, };
export type TaskRelationships = { parent_task: Task | null, current_attempt: TaskAttempt, children: Array<Task>, };
export type CreateTask = { project_id: string, title: string, description: string | null, parent_task_attempt: string | null, image_ids: Array<string> | null, };
export type UpdateTask = { title: string | null, description: string | null, status: TaskStatus | null, parent_task_attempt: string | null, image_ids: Array<string> | null, };
export type Image = { id: string, file_path: string, original_name: string, mime_type: string | null, size_bytes: bigint, hash: string, created_at: string, updated_at: string, };
export type CreateImage = { file_path: string, original_name: string, mime_type: string | null, size_bytes: bigint, hash: string, };
export type ApiResponse<T, E = T> = { success: boolean, data: T | null, error_data: E | null, message: string | null, };
export type UserSystemInfo = { config: Config, analytics_user_id: string, environment: Environment,
/**
* Capabilities supported per executor (e.g., { "CLAUDE_CODE": ["SESSION_FORK"] })
*/
capabilities: { [key in string]?: Array<BaseAgentCapability> }, executors: { [key in BaseCodingAgent]?: ExecutorConfig }, };
export type Environment = { os_type: string, os_version: string, os_architecture: string, bitness: string, };
export type McpServerQuery = { executor: BaseCodingAgent, };
export type UpdateMcpServersBody = { servers: { [key in string]?: JsonValue }, };
export type GetMcpServerResponse = { mcp_config: McpConfig, config_path: string, };
export type CreateFollowUpAttempt = { prompt: string, variant: string | null, image_ids: Array<string> | null, retry_process_id: string | null, force_when_dirty: boolean | null, perform_git_reset: boolean | null, };
export type DraftResponse = { task_attempt_id: string, draft_type: DraftType, retry_process_id: string | null, prompt: string, queued: boolean, variant: string | null, image_ids: Array<string> | null, version: bigint, };
export type UpdateFollowUpDraftRequest = { prompt: string | null, variant: string | null | null, image_ids: Array<string> | null, version: bigint | null, };
export type UpdateRetryFollowUpDraftRequest = { retry_process_id: string, prompt: string | null, variant: string | null | null, image_ids: Array<string> | null, version: bigint | null, };
export type ChangeTargetBranchRequest = { new_target_branch: string, };
export type ChangeTargetBranchResponse = { new_target_branch: string, status: [number, number], };
export type CreateAndStartTaskRequest = { task: CreateTask, executor_profile_id: ExecutorProfileId, base_branch: string, };
export type CreateGitHubPrRequest = { title: string, body: string | null, target_branch: string | null, };
export type ImageResponse = { id: string, file_path: string, original_name: string, mime_type: string | null, size_bytes: bigint, hash: string, created_at: string, updated_at: string, };
export enum GitHubServiceError { TOKEN_INVALID = "TOKEN_INVALID", INSUFFICIENT_PERMISSIONS = "INSUFFICIENT_PERMISSIONS", REPO_NOT_FOUND_OR_NO_ACCESS = "REPO_NOT_FOUND_OR_NO_ACCESS" }
export type Config = { config_version: string, theme: ThemeMode, executor_profile: ExecutorProfileId, disclaimer_acknowledged: boolean, onboarding_acknowledged: boolean, github_login_acknowledged: boolean, telemetry_acknowledged: boolean, notifications: NotificationConfig, editor: EditorConfig, github: GitHubConfig, analytics_enabled: boolean | null, workspace_dir: string | null, last_app_version: string | null, show_release_notes: boolean, language: UiLanguage, git_branch_prefix: string, showcases: ShowcaseState, };
export type NotificationConfig = { sound_enabled: boolean, push_enabled: boolean, sound_file: SoundFile, };
export enum ThemeMode { LIGHT = "LIGHT", DARK = "DARK", SYSTEM = "SYSTEM" }
export type EditorConfig = { editor_type: EditorType, custom_command: string | null, remote_ssh_host: string | null, remote_ssh_user: string | null, };
export enum EditorType { VS_CODE = "VS_CODE", CURSOR = "CURSOR", WINDSURF = "WINDSURF", INTELLI_J = "INTELLI_J", ZED = "ZED", XCODE = "XCODE", CUSTOM = "CUSTOM" }
export type GitHubConfig = { pat: string | null, oauth_token: string | null, username: string | null, primary_email: string | null, default_pr_base: string | null, };
export enum SoundFile { ABSTRACT_SOUND1 = "ABSTRACT_SOUND1", ABSTRACT_SOUND2 = "ABSTRACT_SOUND2", ABSTRACT_SOUND3 = "ABSTRACT_SOUND3", ABSTRACT_SOUND4 = "ABSTRACT_SOUND4", COW_MOOING = "COW_MOOING", PHONE_VIBRATION = "PHONE_VIBRATION", ROOSTER = "ROOSTER" }
export type UiLanguage = "BROWSER" | "EN" | "JA" | "ES" | "KO";
export type ShowcaseState = { seen_features: Array<string>, };
export type DeviceFlowStartResponse = { user_code: string, verification_uri: string, expires_in: number, interval: number, };
export enum DevicePollStatus { SLOW_DOWN = "SLOW_DOWN", AUTHORIZATION_PENDING = "AUTHORIZATION_PENDING", SUCCESS = "SUCCESS" }
export enum CheckTokenResponse { VALID = "VALID", INVALID = "INVALID" }
export type GitBranch = { name: string, is_current: boolean, is_remote: boolean, last_commit_date: Date, };
export type Diff = { change: DiffChangeKind, oldPath: string | null, newPath: string | null, oldContent: string | null, newContent: string | null,
/**
* True when file contents are intentionally omitted (e.g., too large)
*/
contentOmitted: boolean,
/**
* Optional precomputed stats for omitted content
*/
additions: number | null, deletions: number | null, };
export type DiffChangeKind = "added" | "deleted" | "modified" | "renamed" | "copied" | "permissionChange";
export type RepositoryInfo = { id: bigint, name: string, full_name: string, owner: string, description: string | null, clone_url: string, ssh_url: string, default_branch: string, private: boolean, };
export type CommandBuilder = {
/**
* Base executable command (e.g., "npx -y @anthropic-ai/claude-code@latest")
*/
base: string,
/**
* Optional parameters to append to the base command
*/
params: Array<string> | null, };
export type ExecutorProfileId = {
/**
* The executor type (e.g., "CLAUDE_CODE", "AMP")
*/
executor: BaseCodingAgent,
/**
* Optional variant name (e.g., "PLAN", "ROUTER")
*/
variant: string | null, };
export type ExecutorConfig = { [key in string]?: { "CLAUDE_CODE": ClaudeCode } | { "AMP": Amp } | { "GEMINI": Gemini } | { "CODEX": Codex } | { "OPENCODE": Opencode } | { "CURSOR_AGENT": CursorAgent } | { "QWEN_CODE": QwenCode } | { "COPILOT": Copilot } };
export type BaseAgentCapability = "SESSION_FORK";
export type ClaudeCode = { append_prompt: AppendPrompt, claude_code_router?: boolean | null, plan?: boolean | null, approvals?: boolean | null, model?: string | null, dangerously_skip_permissions?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Gemini = { append_prompt: AppendPrompt, model: GeminiModel, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type GeminiModel = "default" | "flash";
export type Amp = { append_prompt: AppendPrompt, dangerously_allow_all?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Codex = { append_prompt: AppendPrompt, sandbox?: SandboxMode | null, ask_for_approval?: AskForApproval | null, oss?: boolean | null, model?: string | null, model_reasoning_effort?: ReasoningEffort | null, model_reasoning_summary?: ReasoningSummary | null, model_reasoning_summary_format?: ReasoningSummaryFormat | null, profile?: string | null, base_instructions?: string | null, include_plan_tool?: boolean | null, include_apply_patch_tool?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type SandboxMode = "auto" | "read-only" | "workspace-write" | "danger-full-access";
export type AskForApproval = "unless-trusted" | "on-failure" | "on-request" | "never";
export type ReasoningEffort = "low" | "medium" | "high";
export type ReasoningSummary = "auto" | "concise" | "detailed" | "none";
export type ReasoningSummaryFormat = "none" | "experimental";
export type CursorAgent = { append_prompt: AppendPrompt, force?: boolean | null, model?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Copilot = { append_prompt: AppendPrompt, model?: string | null, allow_all_tools?: boolean | null, allow_tool?: string | null, deny_tool?: string | null, add_dir?: Array<string> | null, disable_mcp_server?: Array<string> | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Opencode = { append_prompt: AppendPrompt, model?: string | null, agent?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type QwenCode = { append_prompt: AppendPrompt, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type AppendPrompt = string | null;
export type CodingAgentInitialRequest = { prompt: string,
/**
* Executor profile specification
*/
executor_profile_id: ExecutorProfileId, };
export type CodingAgentFollowUpRequest = { prompt: string, session_id: string,
/**
* Executor profile specification
*/
executor_profile_id: ExecutorProfileId, };
export type CreateTaskAttemptBody = { task_id: string,
/**
* Executor profile specification
*/
executor_profile_id: ExecutorProfileId, base_branch: string, };
export type RebaseTaskAttemptRequest = { old_base_branch: string | null, new_base_branch: string | null, };
export type GitOperationError = { "type": "merge_conflicts", message: string, op: ConflictOp, } | { "type": "rebase_in_progress" };
export type ReplaceProcessRequest = {
/**
* Process to replace (delete this and later ones)
*/
process_id: string,
/**
* New prompt to use for the replacement follow-up
*/
prompt: string,
/**
* Optional variant override
*/
variant: string | null,
/**
* If true, allow resetting Git even when uncommitted changes exist
*/
force_when_dirty: boolean | null,
/**
* If false, skip performing the Git reset step (history drop still applies)
*/
perform_git_reset: boolean | null, };
export type CommitInfo = { sha: string, subject: string, };
export type BranchStatus = { commits_behind: number | null, commits_ahead: number | null, has_uncommitted_changes: boolean | null, head_oid: string | null, uncommitted_count: number | null, untracked_count: number | null, target_branch_name: string, remote_commits_behind: number | null, remote_commits_ahead: number | null, merges: Array<Merge>,
/**
* True if a `git rebase` is currently in progress in this worktree
*/
is_rebase_in_progress: boolean,
/**
* Current conflict operation if any
*/
conflict_op: ConflictOp | null,
/**
* List of files currently in conflicted (unmerged) state
*/
conflicted_files: Array<string>, };
export type ConflictOp = "rebase" | "merge" | "cherry_pick" | "revert";
export type TaskAttempt = { id: string, task_id: string, container_ref: string | null, branch: string, target_branch: string, executor: string, worktree_deleted: boolean, setup_completed_at: string | null, created_at: string, updated_at: string, };
export type ExecutionProcess = { id: string, task_attempt_id: string, run_reason: ExecutionProcessRunReason, executor_action: ExecutorAction,
/**
* Git HEAD commit OID captured before the process starts
*/
before_head_commit: string | null,
/**
* Git HEAD commit OID captured after the process ends
*/
after_head_commit: string | null, status: ExecutionProcessStatus, exit_code: bigint | null,
/**
* dropped: true if this process is excluded from the current
* history view (due to restore/trimming). Hidden from logs/timeline;
* still listed in the Processes tab.
*/
dropped: boolean, started_at: string, completed_at: string | null, created_at: string, updated_at: string, };
export enum ExecutionProcessStatus { running = "running", completed = "completed", failed = "failed", killed = "killed" }
export type ExecutionProcessRunReason = "setupscript" | "cleanupscript" | "codingagent" | "devserver";
export type Merge = { "type": "direct" } & DirectMerge | { "type": "pr" } & PrMerge;
export type DirectMerge = { id: string, task_attempt_id: string, merge_commit: string, target_branch_name: string, created_at: string, };
export type PrMerge = { id: string, task_attempt_id: string, created_at: string, target_branch_name: string, pr_info: PullRequestInfo, };
export type MergeStatus = "open" | "merged" | "closed" | "unknown";
export type PullRequestInfo = { number: bigint, url: string, status: MergeStatus, merged_at: string | null, merge_commit_sha: string | null, };
export type Draft = { id: string, task_attempt_id: string, draft_type: DraftType, retry_process_id: string | null, prompt: string, queued: boolean, sending: boolean, variant: string | null, image_ids: Array<string> | null, created_at: string, updated_at: string, version: bigint, };
export type DraftType = "follow_up" | "retry";
export type CommandExitStatus = { "type": "exit_code", code: number, } | { "type": "success", success: boolean, };
export type CommandRunResult = { exit_status: CommandExitStatus | null, output: string | null, };
export type NormalizedEntry = { timestamp: string | null, entry_type: NormalizedEntryType, content: string, };
export type NormalizedEntryType = { "type": "user_message" } | { "type": "user_feedback", denied_tool: string, } | { "type": "assistant_message" } | { "type": "tool_use", tool_name: string, action_type: ActionType, status: ToolStatus, } | { "type": "system_message" } | { "type": "error_message" } | { "type": "thinking" } | { "type": "loading" } | { "type": "next_action", failed: boolean, execution_processes: number, };
export type FileChange = { "action": "write", content: string, } | { "action": "delete" } | { "action": "rename", new_path: string, } | { "action": "edit",
/**
* Unified diff containing file header and hunks.
*/
unified_diff: string,
/**
* Whether line number in the hunks are reliable.
*/
has_line_numbers: boolean, };
export type ActionType = { "action": "file_read", path: string, } | { "action": "file_edit", path: string, changes: Array<FileChange>, } | { "action": "command_run", command: string, result: CommandRunResult | null, } | { "action": "search", query: string, } | { "action": "web_fetch", url: string, } | { "action": "tool", tool_name: string, arguments: JsonValue | null, result: ToolResult | null, } | { "action": "task_create", description: string, } | { "action": "plan_presentation", plan: string, } | { "action": "todo_management", todos: Array<TodoItem>, operation: string, } | { "action": "other", description: string, };
export type TodoItem = { content: string, status: string, priority: string | null, };
export type ToolResult = { type: ToolResultValueType,
/**
* For Markdown, this will be a JSON string; for JSON, a structured value
*/
value: JsonValue, };
export type ToolResultValueType = { "type": "markdown" } | { "type": "json" };
export type ToolStatus = { "status": "created" } | { "status": "success" } | { "status": "failed" } | { "status": "denied", reason: string | null, } | { "status": "pending_approval", approval_id: string, requested_at: string, timeout_at: string, } | { "status": "timed_out" };
export type PatchType = { "type": "NORMALIZED_ENTRY", "content": NormalizedEntry } | { "type": "STDOUT", "content": string } | { "type": "STDERR", "content": string } | { "type": "DIFF", "content": Diff };
export type ApprovalStatus = { "status": "pending" } | { "status": "approved" } | { "status": "denied", reason?: string, } | { "status": "timed_out" };
export type CreateApprovalRequest = { tool_name: string, tool_input: JsonValue, tool_call_id: string, };
export type ApprovalResponse = { execution_process_id: string, status: ApprovalStatus, };
export type JsonValue = number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null;