mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-11 02:11:37 +00:00
* fix(web): expose Codex Fast and Plan on Create Session Wire serviceTier and collaborationMode through spawn so Create can set the same Codex options chat Settings already supports (#1015). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): forward collaborationMode through machine spawn RPC Create Session Plan was accepted by the hub but dropped in apiMachine before buildCliArgs; also preserve collaborationMode on resume spawn. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): correct stopSession mock type in spawn RPC test Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): keep Fast mode across Create draft restore while models load Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): preserve pending Fast selection * fix: apply Fast and Plan to imported Codex sessions * test: narrow imported Codex session id * fix: forward explicit Standard service tier * fix: integrate create-session controls with current main * test: close Codex RPC suite * fix: preserve existing session spawn field * fix(web): integrate Codex controls with current New Session form * fix(web): reconcile draft types and submit state * fix(hub): integrate spawn arguments with current resume flow * test(cli): isolate spawn RPC suite --------- Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
5.8 KiB
TypeScript
136 lines
5.8 KiB
TypeScript
import chalk from 'chalk'
|
|
import { authAndSetupMachineIfNeeded } from '@/ui/auth'
|
|
import { initializeToken } from '@/ui/tokenInit'
|
|
import { maybeAutoStartServer } from '@/utils/autoStartServer'
|
|
import type { CommandDefinition } from './types'
|
|
import { CODEX_PERMISSION_MODES } from '@hapi/protocol/modes'
|
|
import type { CodexPermissionMode } from '@hapi/protocol/types'
|
|
import { CodexCollaborationModeSchema } from '@hapi/protocol/schemas'
|
|
import type { ReasoningEffort } from '@/codex/appServerTypes'
|
|
import { assertCodexLocalSupported } from '@/codex/utils/codexVersion'
|
|
import { parseReasoningEffortValue } from '@/codex/utils/reasoningEffort'
|
|
|
|
// Mirror the web /service-tier endpoint's enum so the internal resume spawn
|
|
// path can never seed/persist an unsupported tier string.
|
|
function parseServiceTier(value: string): 'fast' | 'standard' {
|
|
const normalized = value.trim().toLowerCase()
|
|
if (normalized === 'fast' || normalized === 'standard') {
|
|
return normalized
|
|
}
|
|
throw new Error('Invalid --service-tier value')
|
|
}
|
|
|
|
function parseCollaborationMode(value: string): 'default' | 'plan' {
|
|
const parsed = CodexCollaborationModeSchema.safeParse(value.trim().toLowerCase())
|
|
if (!parsed.success) {
|
|
throw new Error('Invalid --collaboration-mode value')
|
|
}
|
|
return parsed.data
|
|
}
|
|
|
|
export const codexCommand: CommandDefinition = {
|
|
name: 'codex',
|
|
requiresRuntimeAssets: true,
|
|
run: async ({ commandArgs }) => {
|
|
try {
|
|
const { runCodex } = await import('@/codex/runCodex')
|
|
|
|
const options: {
|
|
startedBy?: 'runner' | 'terminal'
|
|
codexArgs?: string[]
|
|
permissionMode?: CodexPermissionMode
|
|
resumeSessionId?: string
|
|
existingSessionId?: string
|
|
model?: string
|
|
modelReasoningEffort?: ReasoningEffort
|
|
serviceTier?: string
|
|
collaborationMode?: 'default' | 'plan'
|
|
} = {}
|
|
const unknownArgs: string[] = []
|
|
let hasExplicitPermissionMode = false
|
|
|
|
for (let i = 0; i < commandArgs.length; i++) {
|
|
const arg = commandArgs[i]
|
|
if (i === 0 && arg === 'resume') {
|
|
const candidate = commandArgs[i + 1]
|
|
if (!candidate || candidate.startsWith('-')) {
|
|
unknownArgs.push(arg)
|
|
continue
|
|
}
|
|
options.resumeSessionId = candidate
|
|
i += 1
|
|
continue
|
|
}
|
|
if (arg === '--started-by') {
|
|
options.startedBy = commandArgs[++i] as 'runner' | 'terminal'
|
|
} else if (arg === '--existing-session-id') {
|
|
const sessionId = commandArgs[++i]
|
|
if (!sessionId) {
|
|
throw new Error('Missing --existing-session-id value')
|
|
}
|
|
options.existingSessionId = sessionId
|
|
} else if (arg === '--permission-mode') {
|
|
const mode = commandArgs[++i]
|
|
if (!mode || !(CODEX_PERMISSION_MODES as readonly string[]).includes(mode)) {
|
|
throw new Error(`Invalid --permission-mode value: ${mode ?? '(missing)'}`)
|
|
}
|
|
options.permissionMode = mode as CodexPermissionMode
|
|
hasExplicitPermissionMode = true
|
|
} else if ((arg === '--yolo' || arg === '--dangerously-bypass-approvals-and-sandbox') && !hasExplicitPermissionMode) {
|
|
options.permissionMode = 'yolo'
|
|
unknownArgs.push(arg)
|
|
} else if (arg === '--model') {
|
|
const model = commandArgs[++i]
|
|
if (!model) {
|
|
throw new Error('Missing --model value')
|
|
}
|
|
options.model = model
|
|
unknownArgs.push('--model', model)
|
|
} else if (arg === '--model-reasoning-effort') {
|
|
const effort = commandArgs[++i]
|
|
if (!effort) {
|
|
throw new Error('Missing --model-reasoning-effort value')
|
|
}
|
|
options.modelReasoningEffort = parseReasoningEffortValue(effort)
|
|
} else if (arg === '--service-tier') {
|
|
const tier = commandArgs[++i]
|
|
if (!tier) {
|
|
throw new Error('Missing --service-tier value')
|
|
}
|
|
options.serviceTier = parseServiceTier(tier)
|
|
} else if (arg === '--collaboration-mode') {
|
|
const mode = commandArgs[++i]
|
|
if (!mode) {
|
|
throw new Error('Missing --collaboration-mode value')
|
|
}
|
|
options.collaborationMode = parseCollaborationMode(mode)
|
|
} else {
|
|
unknownArgs.push(arg)
|
|
}
|
|
}
|
|
if (unknownArgs.length > 0) {
|
|
options.codexArgs = unknownArgs
|
|
}
|
|
|
|
if (options.startedBy !== 'runner') {
|
|
assertCodexLocalSupported()
|
|
}
|
|
|
|
await initializeToken()
|
|
if (options.startedBy === 'runner') {
|
|
await maybeAutoStartServer()
|
|
} else {
|
|
void maybeAutoStartServer({ waitForReady: false, quiet: true })
|
|
}
|
|
await authAndSetupMachineIfNeeded()
|
|
await runCodex(options)
|
|
} catch (error) {
|
|
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error')
|
|
if (process.env.DEBUG) {
|
|
console.error(error)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
}
|
|
}
|