feat: add persistent desktop assistant workspace

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
lofyer
2026-07-31 22:33:03 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent 698a15ad14
commit 6ef1795b81
101 changed files with 31866 additions and 1176 deletions
+212
View File
@@ -0,0 +1,212 @@
import { z } from 'zod'
const controlCharacterFreeString = (maximumLength: number) =>
z
.string()
.trim()
.min(1)
.max(maximumLength)
.refine(
(value) =>
[...value].every((character) => {
const code = character.charCodeAt(0)
return code > 31 && code !== 127
}),
'值包含控制字符'
)
export const runtimeTargetSchema = z.enum([
'model',
'opencode',
'continue'
])
export type RuntimeTarget = z.infer<typeof runtimeTargetSchema>
export const capabilityAssignmentsSchema = z
.array(runtimeTargetSchema)
.max(3)
.refine(
(assignments) => new Set(assignments).size === assignments.length,
'Runtime 分配不能重复'
)
export type CapabilityAssignments = z.infer<
typeof capabilityAssignmentsSchema
>
export const secretActionSchema = z.discriminatedUnion('action', [
z.object({ action: z.literal('keep') }).strict(),
z
.object({
action: z.literal('replace'),
value: controlCharacterFreeString(8_192)
})
.strict(),
z.object({ action: z.literal('clear') }).strict()
])
export type SecretAction = z.infer<typeof secretActionSchema>
export const skillIdSchema = z
.string()
.min(1)
.max(128)
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
export const skillToggleInputSchema = z
.object({
skillId: skillIdSchema,
enabled: z.boolean()
})
.strict()
export const skillAssignmentsInputSchema = z
.object({
skillId: skillIdSchema,
assignments: capabilityAssignmentsSchema
})
.strict()
export const skillSummarySchema = z
.object({
id: skillIdSchema,
name: z.string().min(1).max(80),
description: z.string().min(1).max(500),
version: z.string().max(32).optional(),
tags: z.array(z.string().min(1).max(32)).max(12),
source: z.enum(['builtin', 'imported']),
digest: z.string().regex(/^[a-f0-9]{64}$/),
enabled: z.boolean(),
assignments: capabilityAssignmentsSchema
})
.strict()
export type SkillSummary = z.infer<typeof skillSummarySchema>
export const mcpTransportSchema = z.enum(['stdio', 'http', 'sse'])
export type McpTransport = z.infer<typeof mcpTransportSchema>
export const mcpServerIdSchema = z.string().uuid()
const mcpServerNameSchema = controlCharacterFreeString(80)
const mcpServerDescriptionSchema = z.string().trim().max(500)
const mcpCommandSchema = controlCharacterFreeString(4_096)
const mcpArgumentSchema = controlCharacterFreeString(4_096)
const mcpRemoteUrlSchema = z
.string()
.url()
.max(2_048)
.superRefine((value, context) => {
const url = new URL(value)
if (
!['http:', 'https:'].includes(url.protocol) ||
url.username ||
url.password ||
url.hash
) {
context.addIssue({
code: 'custom',
message: 'MCP URL 必须是无凭据和片段的 HTTP(S) 地址'
})
}
})
const mcpCommonInputShape = {
name: mcpServerNameSchema,
description: mcpServerDescriptionSchema,
enabled: z.boolean(),
assignments: capabilityAssignmentsSchema,
secret: secretActionSchema
}
export const mcpServerInputSchema = z.discriminatedUnion('transport', [
z
.object({
...mcpCommonInputShape,
transport: z.literal('stdio'),
command: mcpCommandSchema,
args: z.array(mcpArgumentSchema).max(64)
})
.strict(),
z
.object({
...mcpCommonInputShape,
transport: z.literal('http'),
url: mcpRemoteUrlSchema
})
.strict(),
z
.object({
...mcpCommonInputShape,
transport: z.literal('sse'),
url: mcpRemoteUrlSchema
})
.strict()
])
export type McpServerInput = z.infer<typeof mcpServerInputSchema>
export const mcpServerSummarySchema = z.discriminatedUnion('transport', [
z
.object({
id: mcpServerIdSchema,
name: mcpServerNameSchema,
description: mcpServerDescriptionSchema,
enabled: z.boolean(),
assignments: capabilityAssignmentsSchema,
secretConfigured: z.boolean(),
transport: z.literal('stdio'),
command: mcpCommandSchema,
args: z.array(mcpArgumentSchema).max(64)
})
.strict(),
z
.object({
id: mcpServerIdSchema,
name: mcpServerNameSchema,
description: mcpServerDescriptionSchema,
enabled: z.boolean(),
assignments: capabilityAssignmentsSchema,
secretConfigured: z.boolean(),
transport: z.literal('http'),
url: mcpRemoteUrlSchema
})
.strict(),
z
.object({
id: mcpServerIdSchema,
name: mcpServerNameSchema,
description: mcpServerDescriptionSchema,
enabled: z.boolean(),
assignments: capabilityAssignmentsSchema,
secretConfigured: z.boolean(),
transport: z.literal('sse'),
url: mcpRemoteUrlSchema
})
.strict()
])
export type McpServerSummary = z.infer<typeof mcpServerSummarySchema>
export const capabilitySnapshotSchema = z
.object({
skills: z.array(skillSummarySchema).max(256),
mcpServers: z.array(mcpServerSummarySchema).max(64)
})
.strict()
export type CapabilitySnapshot = z.infer<typeof capabilitySnapshotSchema>
export const mcpServerTestResultSchema = z
.object({
serverName: z.string().min(1).max(120).optional(),
serverVersion: z.string().min(1).max(64).optional(),
toolCount: z.number().int().min(0).max(10_000),
tools: z
.array(
z
.object({
name: z.string().min(1).max(128),
description: z.string().max(500).optional()
})
.strict()
)
.max(100)
})
.strict()
export type McpServerTestResult = z.infer<
typeof mcpServerTestResultSchema
>