feat: expand secure runtime and workspace UX

This commit is contained in:
lofyer
2026-08-04 00:57:08 +08:00
parent 09e9fbf5e2
commit 2910d315f7
56 changed files with 6164 additions and 882 deletions
+27
View File
@@ -107,10 +107,37 @@ export type WorkspaceChanges = {
available: boolean
status: string
patch: string
files: WorkspaceChangedFile[]
truncated: boolean
error?: string
}
export type WorkspaceChangedFile = {
path: string
status: string
previousPath?: string
}
export type WorkspaceDirectoryEntry = {
name: string
path: string
type: 'file' | 'directory'
}
export type WorkspaceDirectoryListing = {
path: string
entries: WorkspaceDirectoryEntry[]
truncated: boolean
}
export type WorkspaceFilePreview = {
path: string
name: string
content: string
mimeType: 'text/markdown' | 'text/plain' | 'application/json'
size: number
}
export type AssistantTaskStatus =
| 'queued'
| 'running'
+55 -2
View File
@@ -6,6 +6,7 @@ import type {
McpServerTestResult
} from './capability-contracts'
import {
assistantIdSchema,
workModeSchema,
type AssistantProject,
type AssistantArtifact,
@@ -19,6 +20,8 @@ import {
type TokenUsageSummary,
type ConversationSnapshot,
type WorkspaceChanges,
type WorkspaceDirectoryListing,
type WorkspaceFilePreview,
type ProjectCreateInput,
type MemoryCreateInput,
type ScheduleCreateInput,
@@ -27,6 +30,37 @@ import {
type ExpertCreateInput
} from './assistant-contracts'
export const workspaceRelativePathSchema = z
.string()
.min(1)
.max(1_024)
.refine((value) => {
if (
value.includes('\0') ||
/^[\\/]/u.test(value) ||
/^[a-zA-Z]:[\\/]/u.test(value)
) {
return false
}
return value
.split(/[\\/]/u)
.every((segment) => segment.length > 0 && segment !== '.' && segment !== '..')
}, '路径必须是工作区内的相对路径')
export const workspaceDirectoryRequestSchema = z
.object({
projectId: assistantIdSchema,
path: z.union([workspaceRelativePathSchema, z.literal('')])
})
.strict()
export const workspaceFileRequestSchema = z
.object({
projectId: assistantIdSchema,
path: workspaceRelativePathSchema
})
.strict()
export const agentRequestSchema = z
.object({
requestId: z.string().uuid(),
@@ -85,6 +119,7 @@ export const continueModeSchema = z.enum(['chat', 'agent'])
export const runtimeSandboxModeSchema = z.enum(['off', 'auto', 'strict'])
export const modelProtocolSchema = z.enum([
'anthropic-messages',
'openai-responses',
'openai-chat-completions',
'openai-images-generations'
])
@@ -345,11 +380,16 @@ export const runtimeSettingsInputSchema = z
(profile) => profile.id === continueSource.profileId
)
: undefined
if (continueProfile?.protocol === 'openai-images-generations') {
if (
continueProfile &&
continueProfile.protocol !== 'anthropic-messages' &&
continueProfile.protocol !== 'openai-chat-completions'
) {
context.addIssue({
code: 'custom',
path: ['continueModelSource'],
message: 'Continue 不支持图像生成模型连接'
message:
'Continue 独立模型连接仅支持 Anthropic Messages 或 OpenAI 兼容 Chat Completions'
})
}
}
@@ -690,6 +730,11 @@ export type DesktopApi = {
getInfo: () => Promise<AppInfo>
show: () => Promise<void>
hide: () => Promise<void>
minimize: () => Promise<void>
toggleMaximize: () => Promise<void>
close: () => Promise<void>
isMaximized: () => Promise<boolean>
onMaximizedChanged: (listener: (maximized: boolean) => void) => () => void
clearLocalData: () => Promise<void>
onNewConversation: (listener: () => void) => () => void
onOpenSettings: (listener: () => void) => () => void
@@ -729,6 +774,14 @@ export type DesktopApi = {
}
workspace: {
getChanges: (projectId: string) => Promise<WorkspaceChanges>
listDirectory: (
projectId: string,
path: string
) => Promise<WorkspaceDirectoryListing>
readFile: (
projectId: string,
path: string
) => Promise<WorkspaceFilePreview>
}
tasks: {
list: () => Promise<AssistantTask[]>
+7
View File
@@ -2,6 +2,11 @@ export const ipcChannels = {
appInfo: 'app:get-info',
appShow: 'app:show',
appHide: 'app:hide',
windowMinimize: 'window:minimize',
windowToggleMaximize: 'window:toggle-maximize',
windowClose: 'window:close',
windowIsMaximized: 'window:is-maximized',
windowMaximizedChanged: 'window:maximized-changed',
appClearLocalData: 'app:clear-local-data',
conversationNew: 'conversation:new',
settingsOpen: 'settings:open',
@@ -23,6 +28,8 @@ export const ipcChannels = {
conversationsList: 'conversations:list',
conversationsReplace: 'conversations:replace',
workspaceChangesGet: 'workspace:changes:get',
workspaceDirectoryList: 'workspace:directory:list',
workspaceFileRead: 'workspace:file:read',
tasksList: 'tasks:list',
tasksSetStatus: 'tasks:set-status',
tokenUsageSummary: 'usage:token-summary',
+8
View File
@@ -16,6 +16,7 @@ describe('modelProfilePresets', () => {
'hunyuan-deployment',
'huawei-deployment',
'ollama',
'openai',
'openai-compatible',
'anthropic-compatible'
])
@@ -37,6 +38,13 @@ describe('modelProfilePresets', () => {
protocol: 'openai-images-generations',
authentication: 'api-key'
})
expect(
modelProfilePresets.find((preset) => preset.id === 'openai')
).toMatchObject({
baseUrl: 'https://api.openai.com/v1',
protocol: 'openai-responses',
authentication: 'api-key'
})
})
it('does not invent universal Hunyuan or Huawei endpoints', () => {
+2 -2
View File
@@ -119,10 +119,10 @@ export const modelProfilePresets = [
{
id: 'openai',
name: 'OpenAI',
description: 'OpenAI Chat Completions 接口',
description: 'OpenAI Responses API',
baseUrl: 'https://api.openai.com/v1',
modelName: 'gpt-4.1',
protocol: 'openai-chat-completions',
protocol: 'openai-responses',
authentication: 'api-key'
},
{