mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
feat: OpenCode integration Phase 5 — API routes, frontend UI
API changes: - GET /api/opencode/status — check if OpenCode CLI is available - POST /api/sessions — support mode: 'opencode' with openCodeConfig - POST /api/quick-start — support mode: 'opencode', guard writeHooksConfig - Fix hardcoded mode: 'claude' in lifecycle logs (use session.mode) Schema changes: - Add 'opencode' to mode enums in CreateSessionSchema, QuickStartSchema - Add OpenCodeConfigSchema (model, autoAllowTools, continueSession, etc.) - Add 'OPENCODE_' to ALLOWED_ENV_PREFIXES - Block OPENCODE_SERVER_PASSWORD in BLOCKED_ENV_KEYS (atomic with prefix addition) Frontend UI: - "Run OC" button in toolbar + "Run OpenCode" on welcome screen - Green "oc" tab badge for OpenCode sessions (matching green brand) - runOpenCode() method — checks availability, quick-starts with autoAllowTools - Feature gating: hide Respawn/Context/Ralph tabs for OpenCode sessions - Session options defaults to Summary tab for OpenCode - CSS: OpenCode button styles, tab badge styles Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-4
@@ -34,12 +34,13 @@ const safePathSchema = z.string().max(1000).refine(isValidWorkingDir, {
|
||||
// ========== Env Var Allowlist ==========
|
||||
|
||||
/** Allowlisted env var key prefixes */
|
||||
const ALLOWED_ENV_PREFIXES = ['CLAUDE_CODE_'];
|
||||
const ALLOWED_ENV_PREFIXES = ['CLAUDE_CODE_', 'OPENCODE_'];
|
||||
|
||||
/** Env var keys that are always blocked (security-sensitive) */
|
||||
const BLOCKED_ENV_KEYS = new Set([
|
||||
'PATH', 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'NODE_OPTIONS',
|
||||
'CLAUDEMAN_MUX_NAME', 'CLAUDEMAN_TMUX',
|
||||
'OPENCODE_SERVER_PASSWORD', // Security-sensitive: server auth password
|
||||
]);
|
||||
|
||||
/** Validate that an env var key is allowed */
|
||||
@@ -54,7 +55,7 @@ const safeEnvOverridesSchema = z.record(z.string(), z.string()).optional().refin
|
||||
if (!val) return true;
|
||||
return Object.keys(val).every(isAllowedEnvKey);
|
||||
},
|
||||
{ message: 'envOverrides contains blocked or disallowed env var keys. Only CLAUDE_CODE_* keys are allowed.' },
|
||||
{ message: 'envOverrides contains blocked or disallowed env var keys. Only CLAUDE_CODE_* and OPENCODE_* keys are allowed.' },
|
||||
);
|
||||
|
||||
// ========== Session Routes ==========
|
||||
@@ -63,11 +64,21 @@ const safeEnvOverridesSchema = z.record(z.string(), z.string()).optional().refin
|
||||
* Schema for POST /api/sessions
|
||||
* Creates a new session with optional working directory, mode, and name.
|
||||
*/
|
||||
/** Schema for OpenCode-specific configuration */
|
||||
const OpenCodeConfigSchema = z.object({
|
||||
model: z.string().max(100).regex(/^[a-zA-Z0-9._\-/]+$/).optional(),
|
||||
autoAllowTools: z.boolean().optional(),
|
||||
continueSession: z.string().max(100).regex(/^[a-zA-Z0-9_-]+$/).optional(),
|
||||
forkSession: z.boolean().optional(),
|
||||
configContent: z.string().max(10000).optional(),
|
||||
}).optional();
|
||||
|
||||
export const CreateSessionSchema = z.object({
|
||||
workingDir: safePathSchema.optional(),
|
||||
mode: z.enum(['claude', 'shell']).optional(),
|
||||
mode: z.enum(['claude', 'shell', 'opencode']).optional(),
|
||||
name: z.string().max(100).optional(),
|
||||
envOverrides: safeEnvOverridesSchema,
|
||||
openCodeConfig: OpenCodeConfigSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -106,7 +117,8 @@ export const CreateCaseSchema = z.object({
|
||||
*/
|
||||
export const QuickStartSchema = z.object({
|
||||
caseName: z.string().regex(/^[a-zA-Z0-9_-]+$/, 'Invalid case name format. Use only letters, numbers, hyphens, underscores.').optional(),
|
||||
mode: z.enum(['claude', 'shell']).optional(),
|
||||
mode: z.enum(['claude', 'shell', 'opencode']).optional(),
|
||||
openCodeConfig: OpenCodeConfigSchema,
|
||||
});
|
||||
|
||||
// ========== Hook Events ==========
|
||||
|
||||
Reference in New Issue
Block a user