refactor: share api dto types

This commit is contained in:
Federico Jaramillo Martinez
2026-05-07 23:45:13 +02:00
parent ef63a7a063
commit d7cb7ed655
8 changed files with 165 additions and 266 deletions
+2 -130
View File
@@ -1,134 +1,6 @@
export interface Project {
id: string;
name: string;
path: string;
createdAt: string;
}
import type { CommandOption, CommandResult, FileContentResponse, FileSuggestion, FileTreeEntry, FileTreeResponse, GitDiffResponse, GitFileState, GitStatusFile, GitStatusResponse, MessagePage, Project, SessionInfo, SessionStatus, SlashCommand, Workspace } from "../../shared/apiTypes";
export interface Workspace {
id: string;
projectId: string;
path: string;
label: string;
branch?: string;
isMain: boolean;
isGitWorktree: boolean;
}
export interface SessionInfo {
id: string;
path: string;
cwd: string;
name?: string;
created: string;
modified: string;
messageCount: number;
firstMessage: string;
archived?: boolean;
archivedAt?: string;
}
export interface SessionActivity {
sessionId: string;
phase: "active" | "idle" | "error";
label: string;
detail?: string;
at: string;
}
export interface SessionStatus {
sessionId: string;
model?: { provider?: string; id?: string; name?: string; contextWindow?: number; reasoning?: unknown };
thinkingLevel?: string;
isStreaming: boolean;
isCompacting: boolean;
isBashRunning: boolean;
pendingMessageCount: number;
tokens: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
cost: number;
contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };
}
export interface SlashCommand {
name: string;
description?: string;
source: "extension" | "prompt" | "skill" | "builtin";
}
export interface FileSuggestion {
path: string;
kind: "tracked" | "untracked" | "other";
}
export interface FileTreeEntry {
name: string;
path: string;
type: "file" | "directory" | "symlink";
size?: number;
modifiedAt?: string;
}
export interface FileTreeResponse {
path: string;
entries: FileTreeEntry[];
scannedAt: string;
truncated: boolean;
}
export interface FileContentResponse {
path: string;
language?: string;
encoding: "utf8";
size: number;
modifiedAt: string;
content: string;
truncated: boolean;
binary: boolean;
}
export type GitFileState = "unmodified" | "modified" | "added" | "deleted" | "renamed" | "copied" | "untracked" | "ignored" | "conflicted";
export interface GitStatusFile {
path: string;
oldPath?: string;
index: GitFileState;
workingTree: GitFileState;
}
export interface GitStatusResponse {
isGitRepo: boolean;
hash: string;
branch?: string;
upstream?: string;
ahead?: number;
behind?: number;
files: GitStatusFile[];
}
export interface GitDiffResponse {
path?: string;
staged: boolean;
hash: string;
diff: string;
truncated: boolean;
}
export interface CommandOption {
value: string;
label: string;
description?: string;
}
export interface MessagePage {
messages: unknown[];
start: number;
total: number;
}
export type CommandResult =
| { type: "done"; message?: string; session?: SessionInfo }
| { type: "select"; requestId: string; title: string; options: CommandOption[] }
| { type: "unsupported"; message: string };
export type { CommandOption, CommandResult, FileContentResponse, FileSuggestion, FileTreeEntry, FileTreeResponse, GitDiffResponse, GitFileState, GitStatusFile, GitStatusResponse, MessagePage, Project, SessionActivity, SessionInfo, SessionStatus, SlashCommand, SessionUiEvent, Workspace } from "../../shared/apiTypes";
async function request<T>(url: string, parse: (value: unknown) => T, init?: RequestInit): Promise<T> {
const headers = new Headers(init?.headers);
+3 -12
View File
@@ -1,16 +1,7 @@
import { globalSessionEvents, sessionEvents, type SessionActivity, type SessionStatus } from "./api";
import { globalSessionEvents, sessionEvents } from "./api";
import type { SessionUiEvent } from "../../shared/apiTypes";
export type SessionUiEvent =
| { type: "assistant.delta"; text: string }
| { type: "tool.start"; toolName: string; summary: string; args?: unknown }
| { type: "tool.end"; toolName: string; text: string; isError: boolean; content?: unknown }
| { type: "shell.start"; command: string; excludeFromContext?: boolean }
| { type: "shell.chunk"; chunk: string }
| { type: "shell.end"; output?: string; exitCode?: number | null; cancelled?: boolean; truncated?: boolean; fullOutputPath?: string; isError?: boolean }
| { type: "status.update"; status: SessionStatus }
| { type: "activity.update"; activity: SessionActivity }
| { type: "command.output"; level: "info" | "success" | "error"; message: string }
| { type: "session.error"; message: string };
export type { SessionUiEvent } from "../../shared/apiTypes";
export class SessionSocket {
private socket: WebSocket | undefined;
+1 -27
View File
@@ -1,34 +1,8 @@
import { createHash } from "node:crypto";
import { spawn } from "node:child_process";
import type { GitDiffResponse, GitFileState, GitStatusFile, GitStatusResponse } from "../../shared/apiTypes.js";
import { normalizeRelativePath } from "../workspaces/pathSafety.js";
export type GitFileState = "unmodified" | "modified" | "added" | "deleted" | "renamed" | "copied" | "untracked" | "ignored" | "conflicted";
export interface GitStatusFile {
path: string;
oldPath?: string;
index: GitFileState;
workingTree: GitFileState;
}
export interface GitStatusResponse {
isGitRepo: boolean;
hash: string;
branch?: string;
upstream?: string;
ahead?: number;
behind?: number;
files: GitStatusFile[];
}
export interface GitDiffResponse {
path?: string;
staged: boolean;
hash: string;
diff: string;
truncated: boolean;
}
const MAX_OUTPUT = 2 * 1024 * 1024;
export async function gitStatus(cwd: string): Promise<GitStatusResponse> {
+13 -70
View File
@@ -1,70 +1,13 @@
export interface Project {
id: string;
name: string;
path: string;
createdAt: string;
}
export interface Workspace {
id: string;
projectId: string;
path: string;
label: string;
branch?: string;
isMain: boolean;
isGitWorktree: boolean;
}
export interface ClientSession {
id: string;
path: string;
cwd: string;
name?: string;
created: string;
modified: string;
messageCount: number;
firstMessage: string;
archived?: boolean;
archivedAt?: string;
}
export interface ClientMessagePage {
messages: unknown[];
start: number;
total: number;
}
export interface ClientSessionStatus {
sessionId: string;
model?: { provider?: string; id?: string; name?: string; contextWindow?: number; reasoning?: unknown };
thinkingLevel?: string;
isStreaming: boolean;
isCompacting: boolean;
isBashRunning: boolean;
pendingMessageCount: number;
tokens: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
cost: number;
contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };
}
export interface ClientCommand {
name: string;
description?: string;
source: "extension" | "prompt" | "skill" | "builtin";
}
export interface ClientFileSuggestion {
path: string;
kind: "tracked" | "untracked" | "other";
}
export interface ClientCommandOption {
value: string;
label: string;
description?: string;
}
export type ClientCommandResult =
| { type: "done"; message?: string; session?: ClientSession }
| { type: "select"; requestId: string; title: string; options: ClientCommandOption[] }
| { type: "unsupported"; message: string };
export type {
Project,
Workspace,
SessionInfo as ClientSession,
MessagePage as ClientMessagePage,
SessionStatus as ClientSessionStatus,
SlashCommand as ClientCommand,
FileSuggestion as ClientFileSuggestion,
CommandOption as ClientCommandOption,
CommandResult as ClientCommandResult,
SessionActivity as ClientSessionActivity,
SessionUiEvent,
} from "../shared/apiTypes.js";
+1 -11
View File
@@ -1,17 +1,7 @@
import { readFile, stat } from "node:fs/promises";
import type { FileContentResponse } from "../../shared/apiTypes.js";
import { resolveInsideWorkspace } from "./pathSafety.js";
export interface FileContentResponse {
path: string;
language?: string;
encoding: "utf8";
size: number;
modifiedAt: string;
content: string;
truncated: boolean;
binary: boolean;
}
const MAX_BYTES = 512 * 1024;
export async function readWorkspaceFile(rootPath: string, path: string | undefined): Promise<FileContentResponse> {
+1 -15
View File
@@ -1,22 +1,8 @@
import { lstat, readdir } from "node:fs/promises";
import { join } from "node:path";
import type { FileTreeEntry, FileTreeResponse } from "../../shared/apiTypes.js";
import { resolveInsideWorkspace } from "./pathSafety.js";
export interface FileTreeEntry {
name: string;
path: string;
type: "file" | "directory" | "symlink";
size?: number;
modifiedAt?: string;
}
export interface FileTreeResponse {
path: string;
entries: FileTreeEntry[];
scannedAt: string;
truncated: boolean;
}
const MAX_ENTRIES = 1000;
export async function listWorkspaceTree(rootPath: string, path: string | undefined): Promise<FileTreeResponse> {
+143
View File
@@ -0,0 +1,143 @@
export interface Project {
id: string;
name: string;
path: string;
createdAt: string;
}
export interface Workspace {
id: string;
projectId: string;
path: string;
label: string;
branch?: string;
isMain: boolean;
isGitWorktree: boolean;
}
export interface SessionInfo {
id: string;
path: string;
cwd: string;
name?: string;
created: string;
modified: string;
messageCount: number;
firstMessage: string;
archived?: boolean;
archivedAt?: string;
}
export interface SessionActivity {
sessionId: string;
phase: "active" | "idle" | "error";
label: string;
detail?: string;
at: string;
}
export interface SessionStatus {
sessionId: string;
model?: { provider?: string; id?: string; name?: string; contextWindow?: number; reasoning?: unknown };
thinkingLevel?: string;
isStreaming: boolean;
isCompacting: boolean;
isBashRunning: boolean;
pendingMessageCount: number;
tokens: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
cost: number;
contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };
}
export interface SlashCommand {
name: string;
description?: string;
source: "extension" | "prompt" | "skill" | "builtin";
}
export interface FileSuggestion {
path: string;
kind: "tracked" | "untracked" | "other";
}
export interface FileTreeEntry {
name: string;
path: string;
type: "file" | "directory" | "symlink";
size?: number;
modifiedAt?: string;
}
export interface FileTreeResponse {
path: string;
entries: FileTreeEntry[];
scannedAt: string;
truncated: boolean;
}
export interface FileContentResponse {
path: string;
language?: string;
encoding: "utf8";
size: number;
modifiedAt: string;
content: string;
truncated: boolean;
binary: boolean;
}
export type GitFileState = "unmodified" | "modified" | "added" | "deleted" | "renamed" | "copied" | "untracked" | "ignored" | "conflicted";
export interface GitStatusFile {
path: string;
oldPath?: string;
index: GitFileState;
workingTree: GitFileState;
}
export interface GitStatusResponse {
isGitRepo: boolean;
hash: string;
branch?: string;
upstream?: string;
ahead?: number;
behind?: number;
files: GitStatusFile[];
}
export interface GitDiffResponse {
path?: string;
staged: boolean;
hash: string;
diff: string;
truncated: boolean;
}
export interface CommandOption {
value: string;
label: string;
description?: string;
}
export interface MessagePage {
messages: unknown[];
start: number;
total: number;
}
export type CommandResult =
| { type: "done"; message?: string; session?: SessionInfo }
| { type: "select"; requestId: string; title: string; options: CommandOption[] }
| { type: "unsupported"; message: string };
export type SessionUiEvent =
| { type: "assistant.delta"; text: string }
| { type: "tool.start"; toolName: string; summary: string; args?: unknown }
| { type: "tool.end"; toolName: string; text: string; isError: boolean; content?: unknown }
| { type: "shell.start"; command: string; excludeFromContext?: boolean }
| { type: "shell.chunk"; chunk: string }
| { type: "shell.end"; output?: string; exitCode?: number | null; cancelled?: boolean; truncated?: boolean; fullOutputPath?: string; isError?: boolean }
| { type: "status.update"; status: SessionStatus }
| { type: "activity.update"; activity: SessionActivity }
| { type: "command.output"; level: "info" | "success" | "error"; message: string }
| { type: "session.error"; message: string };