feat: complete WorkBuddy parity and Windows release workflow
This commit is contained in:
1 parent
143516b3a8
commit
d5512df549
434 files changed
+54977
-1854
No files matched your search
+38
-5
@@ -16,7 +16,8 @@ import { EventEmitter } from 'node:events'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { mkdir } from 'node:fs/promises'
|
||||
import { homedir } from 'node:os'
|
||||
import { dirname, isAbsolute, join } from 'node:path'
|
||||
import { dirname, isAbsolute, join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
/** OMP 单帧上限(含换行) */
|
||||
const MAX_FRAME_BYTES = 1024 * 1024
|
||||
@@ -89,10 +90,37 @@ interface Pending {
|
||||
timer: NodeJS.Timeout
|
||||
}
|
||||
|
||||
/** 在常见位置探测 OMP 可执行文件 */
|
||||
/**
|
||||
* 随包内置的 OMP:打包后位于 resources/omp,开发态位于项目 resources/omp。
|
||||
* 版本与摘要由 resources/omp/runtime.lock.json 锁定(npm run omp:verify 校验)。
|
||||
*/
|
||||
function detectBundledOmpPath(): string | null {
|
||||
let moduleBundled: string | null = null
|
||||
if (typeof __dirname === 'string' && __dirname) {
|
||||
moduleBundled = resolve(__dirname, '..', '..', 'resources', 'omp', 'omp.exe')
|
||||
} else if (typeof import.meta.url === 'string') {
|
||||
try {
|
||||
moduleBundled = fileURLToPath(new URL('../../resources/omp/omp.exe', import.meta.url))
|
||||
} catch {
|
||||
// CJS test bundles may expose an empty import.meta object.
|
||||
}
|
||||
}
|
||||
const candidates = [
|
||||
process.resourcesPath ? join(process.resourcesPath, 'omp', 'omp.exe') : null,
|
||||
join(process.cwd(), 'resources', 'omp', 'omp.exe'),
|
||||
moduleBundled
|
||||
]
|
||||
for (const candidate of candidates) {
|
||||
if (candidate && existsSync(candidate)) return candidate
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/** 探测 OMP 可执行文件:显式覆盖 > 随包内置 > 本机 Ygent 安装 */
|
||||
export function detectOmpPath(): string | null {
|
||||
const candidates = [
|
||||
process.env['YCODE_OMP_PATH'],
|
||||
detectBundledOmpPath(),
|
||||
process.env['LOCALAPPDATA'] ? `${process.env['LOCALAPPDATA']}\\Ygent\\omp.exe` : null,
|
||||
'C:\\Program Files\\Ygent\\omp.exe'
|
||||
].filter((p): p is string => !!p)
|
||||
@@ -170,6 +198,7 @@ export function getOmpInfo(profile = 'ycode'): OmpInfo {
|
||||
}
|
||||
|
||||
export class OmpKernel extends EventEmitter {
|
||||
get isStarted(): boolean { return this.readyDone && !this.disposed }
|
||||
private child: ChildProcessWithoutNullStreams | null = null
|
||||
private pending = new Map<string, Pending>()
|
||||
private seq = 0
|
||||
@@ -227,6 +256,8 @@ export class OmpKernel extends EventEmitter {
|
||||
}
|
||||
|
||||
const args = ['--mode', 'rpc', '--profile', this.profile, `--model=${model}`, '--cwd', cwd, '--no-tools']
|
||||
if (this.environment?.YCODE_MODEL_CONFIG) args.push('--config', this.environment.YCODE_MODEL_CONFIG)
|
||||
if (this.environment?.YCODE_CONTEXT_OBSERVER_EXTENSION) args.push('--extension',this.environment.YCODE_CONTEXT_OBSERVER_EXTENSION)
|
||||
if (this.sessionPath) {
|
||||
args.push(`--session-dir=${dirname(this.sessionPath)}`, `--resume=${this.sessionPath}`)
|
||||
} else {
|
||||
@@ -378,7 +409,7 @@ export class OmpKernel extends EventEmitter {
|
||||
}
|
||||
|
||||
/** Resolve an OMP host_tool_call. The host request id, not toolCallId, correlates the reply. */
|
||||
async respondToHostTool(requestId: string, content: string, isError = false): Promise<void> {
|
||||
async respondToHostTool(requestId: string, content: string, isError = false, images?: OmpPromptImage[]): Promise<void> {
|
||||
await this.start()
|
||||
if (!requestId.trim()) throw new Error('OMP host tool response requires a request id')
|
||||
const child = this.child
|
||||
@@ -386,10 +417,12 @@ export class OmpKernel extends EventEmitter {
|
||||
const payload = {
|
||||
type: 'host_tool_result',
|
||||
id: requestId,
|
||||
result: { content: [{ type: 'text', text: content }] },
|
||||
result: { content: [{ type: 'text', text: content }, ...(!isError ? images ?? [] : [])] },
|
||||
...(isError ? { isError: true } : {})
|
||||
}
|
||||
child.stdin.write(`${JSON.stringify(payload)}\n`)
|
||||
const frame = `${JSON.stringify(payload)}\n`
|
||||
if (Buffer.byteLength(frame) > MAX_FRAME_BYTES) throw new Error('OMP 工具结果超过单帧大小限制')
|
||||
child.stdin.write(frame)
|
||||
}
|
||||
|
||||
dispose(): Promise<void> {
|
||||
|
||||
Reference in new issue
Block a user