mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
Splits the nine oversized modules in the daemon/provider/runtime domain into focused per-concern files and drops their max-lines baseline entries. - daemon: `Session` decomposes into an output plane (emulator, pending-output buffer, client fan-out), a producer-pause controller, a shell-ready barrier and a termination controller; `DaemonClient` into socket connect, hello handshake, ndjson readers, pending-request settlement, listener registry and notify settlement; `daemon-health` into pid-file parsing, process identity, stale-kill, TCC attribution and bundle staleness; `shell-ready` into the marker constant and the bash/zsh rcfile generators. - providers: local-pty shell-ready wrapper generation, wrapper root, startup command and bash rcfile split out of local-pty-shell-ready. - runtime: `Coordinator` sheds DAG convergence, decision gates, escalation triage, the runtime contract, the stale-base flag and task dispatch; the files/git/github rpc modules split into per-domain method groups. Behavior-preserving: the extracted units keep their original construction order, guards and timer lifetimes, and every RPC method name is still registered. Test `vi.mock` surfaces were re-partitioned to follow the moved symbols.
220 lines
6.7 KiB
TypeScript
220 lines
6.7 KiB
TypeScript
import { z } from 'zod'
|
|
import { defineMethod, type RpcAnyMethod } from '../core'
|
|
import { FileOpen, WorktreeSelector } from './files-target-schemas'
|
|
|
|
const RUNTIME_FILE_BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/
|
|
|
|
function isValidRuntimeFileBase64(value: unknown): value is string {
|
|
return (
|
|
typeof value === 'string' && value.length % 4 !== 1 && RUNTIME_FILE_BASE64_PATTERN.test(value)
|
|
)
|
|
}
|
|
|
|
type SshMutationParams = {
|
|
expectedExecutionHostId?: string
|
|
expectedSshTargetId?: string
|
|
expectedSshConnectionGeneration?: number
|
|
}
|
|
|
|
function sshMutationArguments(
|
|
params: SshMutationParams
|
|
): [expectedGeneration?: number, expectedTargetId?: string, expectedExecutionHostId?: string] {
|
|
if (
|
|
params.expectedExecutionHostId === undefined &&
|
|
params.expectedSshTargetId === undefined &&
|
|
params.expectedSshConnectionGeneration === undefined
|
|
) {
|
|
return []
|
|
}
|
|
return [
|
|
params.expectedSshConnectionGeneration,
|
|
params.expectedSshTargetId,
|
|
params.expectedExecutionHostId
|
|
]
|
|
}
|
|
|
|
const FileMutationOpen = FileOpen.extend({
|
|
expectedExecutionHostId: z.string().min(1).optional(),
|
|
expectedSshTargetId: z.string().min(1).optional(),
|
|
expectedSshConnectionGeneration: z.number().int().nonnegative().optional()
|
|
})
|
|
|
|
// Why: write content must be a real string. Coercing a missing/non-string value
|
|
// to '' silently truncated the target file to empty instead of erroring. An
|
|
// explicit '' is still accepted (writing an empty file is legitimate).
|
|
const FileWrite = FileMutationOpen.extend({
|
|
content: z
|
|
.unknown()
|
|
.refine((v): v is string => typeof v === 'string', { message: 'Missing file content' })
|
|
})
|
|
|
|
const FileWriteBase64 = FileMutationOpen.extend({
|
|
contentBase64: z
|
|
.unknown()
|
|
.refine((v): v is string => typeof v === 'string', { message: 'Missing file content' })
|
|
// Why: Buffer.from(..., 'base64') accepts malformed input by dropping
|
|
// invalid bytes, which can silently create empty or corrupt uploaded files.
|
|
.refine(isValidRuntimeFileBase64, 'File content must be base64')
|
|
})
|
|
|
|
const FileWriteBase64Chunk = FileWriteBase64.extend({
|
|
append: z.boolean().optional()
|
|
})
|
|
|
|
const FileRename = WorktreeSelector.extend({
|
|
expectedExecutionHostId: z.string().min(1).optional(),
|
|
expectedSshTargetId: z.string().min(1).optional(),
|
|
expectedSshConnectionGeneration: z.number().int().nonnegative().optional(),
|
|
oldRelativePath: z
|
|
.unknown()
|
|
.transform((v) => (typeof v === 'string' ? v : ''))
|
|
.pipe(z.string().min(1, 'Missing source path')),
|
|
newRelativePath: z
|
|
.unknown()
|
|
.transform((v) => (typeof v === 'string' ? v : ''))
|
|
.pipe(z.string().min(1, 'Missing destination path'))
|
|
})
|
|
|
|
const FileCopy = WorktreeSelector.extend({
|
|
expectedExecutionHostId: z.string().min(1).optional(),
|
|
expectedSshTargetId: z.string().min(1).optional(),
|
|
expectedSshConnectionGeneration: z.number().int().nonnegative().optional(),
|
|
sourceRelativePath: z
|
|
.unknown()
|
|
.transform((v) => (typeof v === 'string' ? v : ''))
|
|
.pipe(z.string().min(1, 'Missing source path')),
|
|
destinationRelativePath: z
|
|
.unknown()
|
|
.transform((v) => (typeof v === 'string' ? v : ''))
|
|
.pipe(z.string().min(1, 'Missing destination path'))
|
|
})
|
|
|
|
const FileCommitUpload = WorktreeSelector.extend({
|
|
expectedExecutionHostId: z.string().min(1).optional(),
|
|
expectedSshTargetId: z.string().min(1).optional(),
|
|
expectedSshConnectionGeneration: z.number().int().nonnegative().optional(),
|
|
tempRelativePath: z
|
|
.unknown()
|
|
.transform((v) => (typeof v === 'string' ? v : ''))
|
|
.pipe(z.string().min(1, 'Missing temporary path')),
|
|
finalRelativePath: z
|
|
.unknown()
|
|
.transform((v) => (typeof v === 'string' ? v : ''))
|
|
.pipe(z.string().min(1, 'Missing final path'))
|
|
})
|
|
|
|
const FileDelete = FileMutationOpen.extend({
|
|
recursive: z.boolean().optional()
|
|
})
|
|
|
|
export const FILE_MUTATION_METHODS: RpcAnyMethod[] = [
|
|
defineMethod({
|
|
name: 'files.write',
|
|
params: FileWrite,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.writeFileExplorerFile(
|
|
params.worktree,
|
|
params.relativePath,
|
|
params.content,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.writeBase64',
|
|
params: FileWriteBase64,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.writeFileExplorerFileBase64(
|
|
params.worktree,
|
|
params.relativePath,
|
|
params.contentBase64,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.writeBase64Chunk',
|
|
params: FileWriteBase64Chunk,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.writeFileExplorerFileBase64Chunk(
|
|
params.worktree,
|
|
params.relativePath,
|
|
params.contentBase64,
|
|
params.append === true,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.createFile',
|
|
params: FileMutationOpen,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.createFileExplorerFile(
|
|
params.worktree,
|
|
params.relativePath,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.createDir',
|
|
params: FileMutationOpen,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.createFileExplorerDir(
|
|
params.worktree,
|
|
params.relativePath,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.createDirNoClobber',
|
|
params: FileMutationOpen,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.createFileExplorerDirNoClobber(
|
|
params.worktree,
|
|
params.relativePath,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.commitUpload',
|
|
params: FileCommitUpload,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.commitFileExplorerUpload(
|
|
params.worktree,
|
|
params.tempRelativePath,
|
|
params.finalRelativePath,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.rename',
|
|
params: FileRename,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.renameFileExplorerPath(
|
|
params.worktree,
|
|
params.oldRelativePath,
|
|
params.newRelativePath,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.copy',
|
|
params: FileCopy,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.copyFileExplorerPath(
|
|
params.worktree,
|
|
params.sourceRelativePath,
|
|
params.destinationRelativePath,
|
|
...sshMutationArguments(params)
|
|
)
|
|
}),
|
|
defineMethod({
|
|
name: 'files.delete',
|
|
params: FileDelete,
|
|
handler: async (params, { runtime }) =>
|
|
runtime.deleteFileExplorerPath(
|
|
params.worktree,
|
|
params.relativePath,
|
|
params.recursive,
|
|
...sshMutationArguments(params)
|
|
)
|
|
})
|
|
]
|