diff --git a/apps/desktop/src-tauri/src/commands/orchestrator.rs b/apps/desktop/src-tauri/src/commands/orchestrator.rs index 347b2f47..327140f4 100644 --- a/apps/desktop/src-tauri/src/commands/orchestrator.rs +++ b/apps/desktop/src-tauri/src/commands/orchestrator.rs @@ -456,6 +456,49 @@ fn truncate_for_report(input: &str) -> String { format!("{}...[truncated]", &trimmed[..MAX_LEN]) } +fn is_sensitive_progress_key(key: &str) -> bool { + let normalized = key.trim().to_ascii_lowercase(); + matches!( + normalized.as_str(), + "token" + | "hosttoken" + | "ownertoken" + | "collaboratortoken" + | "password" + | "opencodepassword" + | "opencodeusername" + | "authorization" + ) || ((key.starts_with("OPENWORK_") || key.starts_with("OPENCODE_") || key.starts_with("DEN_")) + && (key.contains("TOKEN") || key.contains("PASSWORD") || key.contains("USERNAME"))) +} + +fn redact_progress_value(value: serde_json::Value, key: Option<&str>) -> serde_json::Value { + if let Some(key) = key { + if is_sensitive_progress_key(key) { + return serde_json::Value::String("[REDACTED]".to_string()); + } + } + + match value { + serde_json::Value::Object(map) => serde_json::Value::Object( + map.into_iter() + .map(|(entry_key, entry_value)| { + let redacted = redact_progress_value(entry_value, Some(&entry_key)); + (entry_key, redacted) + }) + .collect(), + ), + serde_json::Value::Array(items) => serde_json::Value::Array( + items + .into_iter() + .map(|item| redact_progress_value(item, key)) + .collect(), + ), + serde_json::Value::String(text) => serde_json::Value::String(text), + other => other, + } +} + fn to_command_debug(result: DockerCommandResult) -> SandboxDoctorCommandDebug { SandboxDoctorCommandDebug { status: result.status, @@ -535,6 +578,7 @@ fn emit_sandbox_progress( message: &str, payload: serde_json::Value, ) { + let payload = redact_progress_value(payload, None); let at = now_ms(); let elapsed = payload .get("elapsedMs") @@ -827,11 +871,11 @@ pub fn orchestrator_start_detached( "docker.config", "Inspecting Docker configuration...", json!({ - "candidates": candidates, + "candidateCount": candidates.len(), "resolvedDockerBin": resolved, - "openworkDockerBin": env::var("OPENWORK_DOCKER_BIN").ok(), - "openwrkDockerBin": env::var("OPENWRK_DOCKER_BIN").ok(), - "dockerBin": env::var("DOCKER_BIN").ok(), + "hasOpenworkDockerBinOverride": env::var("OPENWORK_DOCKER_BIN").ok().is_some(), + "hasOpenwrkDockerBinOverride": env::var("OPENWRK_DOCKER_BIN").ok().is_some(), + "hasDockerBinOverride": env::var("DOCKER_BIN").ok().is_some(), }), ); } @@ -855,10 +899,6 @@ pub fn orchestrator_start_detached( "--detach".to_string(), "--openwork-port".to_string(), port.to_string(), - "--openwork-token".to_string(), - token.clone(), - "--openwork-host-token".to_string(), - host_token.clone(), "--run-id".to_string(), sandbox_run_id.clone(), ]; @@ -881,17 +921,21 @@ pub fn orchestrator_start_detached( "Launching sandbox host...", json!({ "command": command_label, - "args": args, - "env": { - "PATH": env::var("PATH").ok(), - "OPENWORK_DOCKER_BIN": env::var("OPENWORK_DOCKER_BIN").ok(), - "OPENWRK_DOCKER_BIN": env::var("OPENWRK_DOCKER_BIN").ok(), - "DOCKER_BIN": env::var("DOCKER_BIN").ok(), - } + "workspacePath": workspace_path, + "openworkUrl": openwork_url, + "argCount": args.len(), + "hasDockerOverrides": env::var("OPENWORK_DOCKER_BIN").ok().is_some() + || env::var("OPENWRK_DOCKER_BIN").ok().is_some() + || env::var("DOCKER_BIN").ok().is_some(), }), ); - if let Err(err) = command.args(str_args).spawn() { + if let Err(err) = command + .args(str_args) + .env("OPENWORK_TOKEN", token.clone()) + .env("OPENWORK_HOST_TOKEN", host_token.clone()) + .spawn() + { emit_sandbox_progress( &app, &sandbox_run_id, diff --git a/apps/desktop/src-tauri/src/openwork_server/spawn.rs b/apps/desktop/src-tauri/src/openwork_server/spawn.rs index bf13bd10..6a14e19d 100644 --- a/apps/desktop/src-tauri/src/openwork_server/spawn.rs +++ b/apps/desktop/src-tauri/src/openwork_server/spawn.rs @@ -116,8 +116,6 @@ pub fn build_openwork_args( host: &str, port: u16, workspace_paths: &[String], - token: &str, - host_token: &str, opencode_base_url: Option<&str>, opencode_directory: Option<&str>, ) -> Vec { @@ -126,10 +124,6 @@ pub fn build_openwork_args( host.to_string(), "--port".to_string(), port.to_string(), - "--token".to_string(), - token.to_string(), - "--host-token".to_string(), - host_token.to_string(), // Always allow all origins since the OpenWork server is designed to accept // remote connections from client devices (phones, laptops) which may use // different origins (localhost dev servers, tauri apps, web browsers). @@ -187,8 +181,6 @@ pub fn spawn_openwork_server( host, port, workspace_paths, - token, - host_token, opencode_base_url, opencode_directory, ); @@ -198,6 +190,10 @@ pub fn spawn_openwork_server( .unwrap_or_else(|| Path::new(".")); let mut command = command.args(args).current_dir(cwd); + command = command + .env("OPENWORK_TOKEN", token) + .env("OPENWORK_HOST_TOKEN", host_token); + if let Some(port) = opencode_router_health_port { command = command.env("OPENCODE_ROUTER_HEALTH_PORT", port.to_string()); } diff --git a/apps/desktop/src-tauri/src/orchestrator/mod.rs b/apps/desktop/src-tauri/src/orchestrator/mod.rs index 51f123b4..cc576466 100644 --- a/apps/desktop/src-tauri/src/orchestrator/mod.rs +++ b/apps/desktop/src-tauri/src/orchestrator/mod.rs @@ -242,20 +242,6 @@ pub fn spawn_orchestrator_daemon( args.push(port.to_string()); } - if let Some(username) = &options.opencode_username { - if !username.trim().is_empty() { - args.push("--opencode-username".to_string()); - args.push(username.to_string()); - } - } - - if let Some(password) = &options.opencode_password { - if !password.trim().is_empty() { - args.push("--opencode-password".to_string()); - args.push(password.to_string()); - } - } - if let Some(cors) = &options.cors { if !cors.trim().is_empty() { args.push("--cors".to_string()); @@ -275,6 +261,18 @@ pub fn spawn_orchestrator_daemon( command = command.env("PATH", path_env); } + if let Some(username) = &options.opencode_username { + if !username.trim().is_empty() { + command = command.env("OPENWORK_OPENCODE_USERNAME", username); + } + } + + if let Some(password) = &options.opencode_password { + if !password.trim().is_empty() { + command = command.env("OPENWORK_OPENCODE_PASSWORD", password); + } + } + for (key, value) in crate::bun_env::bun_env_overrides() { command = command.env(key, value); } diff --git a/apps/orchestrator/README.md b/apps/orchestrator/README.md index 66cb1a6d..5713c927 100644 --- a/apps/orchestrator/README.md +++ b/apps/orchestrator/README.md @@ -69,10 +69,10 @@ pnpm --filter openwork-orchestrator dev -- \ When `OPENWORK_DEV_MODE=1` is set, orchestrator uses an isolated OpenCode dev state for config, auth, data, cache, and state. OpenWork's repo-level `pnpm dev` commands enable this automatically so local development does not reuse your personal OpenCode environment. -The command prints pairing details (OpenWork server URL + token, OpenCode URL + auth) so remote OpenWork clients can connect. +The command prints pairing URLs by default and withholds live credentials from stdout to avoid leaking them into shell history or collected logs. Use `--json` only when you explicitly need the raw pairing secrets in command output. Use `--detach` to keep services running and exit the dashboard. The detach summary includes the -OpenWork URL, tokens, and the `opencode attach` command. +OpenWork URL and a redacted `opencode attach` command, while keeping live credentials out of the detached summary. ## Sandbox mode (Docker / Apple container) diff --git a/apps/orchestrator/src/cli.ts b/apps/orchestrator/src/cli.ts index 2bf919f4..c1e07214 100644 --- a/apps/orchestrator/src/cli.ts +++ b/apps/orchestrator/src/cli.ts @@ -1801,6 +1801,12 @@ function shQuote(value: string): string { return `'${value.replace(/'/g, `'"'"'`)}'`; } +function addEnvPassThroughArgs(args: string[], names: string[]) { + for (const name of names) { + args.push("--env", name); + } +} + function resolveSidecarDir(flags: Map): string { const override = readFlag(flags, "sidecar-dir") ?? process.env.OPENWORK_SIDECAR_DIR; @@ -3784,10 +3790,6 @@ async function startOpenworkServer(options: { options.host, "--port", String(options.port), - "--token", - options.token, - "--host-token", - options.hostToken, "--workspace", options.workspace, "--approval", @@ -3810,12 +3812,6 @@ async function startOpenworkServer(options: { if (options.opencodeDirectory) { args.push("--opencode-directory", options.opencodeDirectory); } - if (options.opencodeUsername) { - args.push("--opencode-username", options.opencodeUsername); - } - if (options.opencodePassword) { - args.push("--opencode-password", options.opencodePassword); - } if (options.logFormat) { args.push("--log-format", options.logFormat); } @@ -4190,28 +4186,25 @@ async function writeSandboxEntrypoint(options: { ? `--cors ${shQuote(options.openwork.corsOrigins.join(","))}` : ""; - const opencodeAuthEnv = [ + const requiredSecretEnv = [ + ': "${OPENWORK_TOKEN:?OPENWORK_TOKEN is required}"', + ': "${OPENWORK_HOST_TOKEN:?OPENWORK_HOST_TOKEN is required}"', options.opencode.username - ? `export OPENCODE_SERVER_USERNAME=${shQuote(options.opencode.username)}` + ? ': "${OPENCODE_SERVER_USERNAME:?OPENCODE_SERVER_USERNAME is required}"' : "", options.opencode.password - ? `export OPENCODE_SERVER_PASSWORD=${shQuote(options.opencode.password)}` + ? ': "${OPENCODE_SERVER_PASSWORD:?OPENCODE_SERVER_PASSWORD is required}"' + : "", + options.openwork.opencodeUsername + ? ': "${OPENWORK_OPENCODE_USERNAME:?OPENWORK_OPENCODE_USERNAME is required}"' + : "", + options.openwork.opencodePassword + ? ': "${OPENWORK_OPENCODE_PASSWORD:?OPENWORK_OPENCODE_PASSWORD is required}"' : "", ] .filter(Boolean) .join("\n"); - const openworkAuthArgs = [ - options.openwork.opencodeUsername - ? `--opencode-username ${shQuote(options.openwork.opencodeUsername)}` - : "", - options.openwork.opencodePassword - ? `--opencode-password ${shQuote(options.openwork.opencodePassword)}` - : "", - ] - .filter(Boolean) - .join(" "); - const opencodeRouterEnv = options.openwork.opencodeRouterEnabled ? `export OPENCODE_ROUTER_HEALTH_PORT=${shQuote(String(SANDBOX_INTERNAL_OPENCODE_ROUTER_HEALTH_PORT))}` : ""; @@ -4249,7 +4242,7 @@ async function writeSandboxEntrypoint(options: { `export OPENWORK_SANDBOX_ENABLED=1`, `export OPENWORK_SANDBOX_BACKEND=${shQuote(options.backend)}`, opencodeRouterEnv, - opencodeAuthEnv, + requiredSecretEnv, 'opencode_pid=""', 'opencodeRouter_pid=""', "cleanup() {", @@ -4264,14 +4257,12 @@ async function writeSandboxEntrypoint(options: { : "", options.openwork.opencodeRouterEnabled ? "opencodeRouter_pid=$!" : "", `exec ${shQuote(openworkBin)} --host 0.0.0.0 --port ${shQuote(String(SANDBOX_INTERNAL_OPENWORK_PORT))}` + - ` --token ${shQuote(options.openwork.token)} --host-token ${shQuote(options.openwork.hostToken)}` + ` --workspace ${shQuote(workspaceDir)}` + ` --approval ${shQuote(options.openwork.approvalMode)}` + ` --approval-timeout ${shQuote(String(options.openwork.approvalTimeoutMs))}` + (options.openwork.readOnly ? " --read-only" : "") + ` --opencode-base-url ${shQuote(`http://127.0.0.1:${SANDBOX_INTERNAL_OPENCODE_PORT}`)}` + ` --opencode-directory ${shQuote(workspaceDir)}` + - ` ${openworkAuthArgs}` + ` --log-format ${shQuote(options.openwork.logFormat)}` + (options.openwork.opencodeRouterEnabled ? ` --opencode-router-health-port ${shQuote(String(SANDBOX_INTERNAL_OPENCODE_ROUTER_HEALTH_PORT))}` @@ -4404,6 +4395,15 @@ async function startDockerSandbox(options: { ); } + addEnvPassThroughArgs(args, [ + "OPENWORK_TOKEN", + "OPENWORK_HOST_TOKEN", + "OPENCODE_SERVER_USERNAME", + "OPENCODE_SERVER_PASSWORD", + "OPENWORK_OPENCODE_USERNAME", + "OPENWORK_OPENCODE_PASSWORD", + ]); + for (const mount of options.extraMounts) { const suffix = mount.readonly ? ":ro" : ""; args.push("-v", `${mount.hostPath}:${mount.containerPath}${suffix}`); @@ -4426,6 +4426,23 @@ async function startDockerSandbox(options: { const child = spawnProcess(options.dockerCommand, args, { stdio: ["ignore", "pipe", "pipe"], + env: { + ...process.env, + OPENWORK_TOKEN: options.openwork.token, + OPENWORK_HOST_TOKEN: options.openwork.hostToken, + ...(options.opencode.username + ? { OPENCODE_SERVER_USERNAME: options.opencode.username } + : {}), + ...(options.opencode.password + ? { OPENCODE_SERVER_PASSWORD: options.opencode.password } + : {}), + ...(options.openwork.opencodeUsername + ? { OPENWORK_OPENCODE_USERNAME: options.openwork.opencodeUsername } + : {}), + ...(options.openwork.opencodePassword + ? { OPENWORK_OPENCODE_PASSWORD: options.openwork.opencodePassword } + : {}), + }, }); prefixStream( child.stdout, @@ -4566,6 +4583,15 @@ async function startAppleContainerSandbox(options: { ); } + addEnvPassThroughArgs(args, [ + "OPENWORK_TOKEN", + "OPENWORK_HOST_TOKEN", + "OPENCODE_SERVER_USERNAME", + "OPENCODE_SERVER_PASSWORD", + "OPENWORK_OPENCODE_USERNAME", + "OPENWORK_OPENCODE_PASSWORD", + ]); + for (const mount of options.extraMounts) { if (mount.readonly) { args.push( @@ -4586,6 +4612,23 @@ async function startAppleContainerSandbox(options: { const child = spawnProcess("container", args, { stdio: ["ignore", "pipe", "pipe"], + env: { + ...process.env, + OPENWORK_TOKEN: options.openwork.token, + OPENWORK_HOST_TOKEN: options.openwork.hostToken, + ...(options.opencode.username + ? { OPENCODE_SERVER_USERNAME: options.opencode.username } + : {}), + ...(options.opencode.password + ? { OPENCODE_SERVER_PASSWORD: options.opencode.password } + : {}), + ...(options.openwork.opencodeUsername + ? { OPENWORK_OPENCODE_USERNAME: options.openwork.opencodeUsername } + : {}), + ...(options.openwork.opencodePassword + ? { OPENWORK_OPENCODE_PASSWORD: options.openwork.opencodePassword } + : {}), + }, }); prefixStream( child.stdout, @@ -5129,6 +5172,108 @@ function mergeResourceAttributes( .join(","); } +const REDACTED_LOG_VALUE = "[REDACTED]"; + +const SENSITIVE_FLAG_NAMES = [ + "--token", + "--host-token", + "--openwork-token", + "--openwork-host-token", + "--opencode-password", + "--opencode-username", +]; + +const SENSITIVE_ATTRIBUTE_KEYS = new Set([ + "token", + "hosttoken", + "ownertoken", + "collaboratortoken", + "controltoken", + "authorization", + "password", + "opencodepassword", + "opencodeusername", + "bottoken", + "apptoken", +]); + +function escapeRegExp(input: string): string { + return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +function isSensitiveAttributeKey(key?: string): boolean { + const trimmed = key?.trim() ?? ""; + if (!trimmed) return false; + const normalized = trimmed.toLowerCase(); + if (SENSITIVE_ATTRIBUTE_KEYS.has(normalized)) return true; + return ( + (trimmed.startsWith("OPENWORK_") || + trimmed.startsWith("OPENCODE_") || + trimmed.startsWith("DEN_")) && + /TOKEN|PASSWORD|USERNAME|AUTHORIZATION/.test(trimmed) + ); +} + +function redactSensitiveString(input: string): string { + let redacted = input; + redacted = redacted.replace(/\b(Bearer)\s+[^\s"']+/gi, "$1 [REDACTED]"); + redacted = redacted.replace(/\b(Basic)\s+[A-Za-z0-9+/=]+/g, "$1 [REDACTED]"); + redacted = redacted.replace( + /((?:OPENWORK|OPENCODE|DEN)_[A-Z0-9_]*(?:TOKEN|PASSWORD|USERNAME|AUTHORIZATION)[A-Z0-9_]*=)([^\s]+)/g, + `$1${REDACTED_LOG_VALUE}`, + ); + redacted = redacted.replace( + /("?(?:token|hostToken|ownerToken|collaboratorToken|controlToken|password|authorization|opencodePassword|opencodeUsername|botToken|appToken)"?\s*[:=]\s*")([^"]*)(")/g, + `$1${REDACTED_LOG_VALUE}$3`, + ); + redacted = redacted.replace( + /("?(?:token|hostToken|ownerToken|collaboratorToken|controlToken|password|authorization|opencodePassword|opencodeUsername|botToken|appToken)"?\s*[:=]\s*)([^,\s}]+)/g, + `$1${REDACTED_LOG_VALUE}`, + ); + for (const flag of SENSITIVE_FLAG_NAMES) { + redacted = redacted.replace( + new RegExp(`(${escapeRegExp(flag)}\\s+)([^\\s]+)`, "g"), + `$1${REDACTED_LOG_VALUE}`, + ); + } + return redacted; +} + +function redactLogValue(value: unknown, key?: string): unknown { + if (isSensitiveAttributeKey(key)) { + return REDACTED_LOG_VALUE; + } + if (value === null || value === undefined) { + return value; + } + if (typeof value === "string") { + return redactSensitiveString(value); + } + if ( + typeof value === "number" || + typeof value === "boolean" || + typeof value === "bigint" + ) { + return value; + } + if (Array.isArray(value)) { + return value.map((item) => redactLogValue(item, key)); + } + if (value instanceof Error) { + return `${value.name}: ${redactSensitiveString(value.message)}`; + } + if (typeof value === "object") { + const entries = Object.entries(value as Record).map( + ([entryKey, entryValue]) => [ + entryKey, + redactLogValue(entryValue, entryKey), + ], + ); + return Object.fromEntries(entries); + } + return redactSensitiveString(String(value)); +} + function createLogger(options: { format: LogFormat; runId: string; @@ -5180,12 +5325,14 @@ function createLogger(options: { ...(component ? { "service.component": component } : {}), ...(attributes ?? {}), }; + const redactedMessage = redactSensitiveString(message); + const redactedAttributes = redactLogValue(mergedAttributes) as LogAttributes; options.onLog?.({ time: Date.now(), level, - message, + message: redactedMessage, component, - attributes: mergedAttributes, + attributes: redactedAttributes, }); if (output === "silent") return; if (options.format === "json") { @@ -5193,8 +5340,8 @@ function createLogger(options: { timeUnixNano: toUnixNano(), severityText: level.toUpperCase(), severityNumber: LOG_LEVEL_NUMBERS[level], - body: message, - attributes: mergedAttributes, + body: redactedMessage, + attributes: redactedAttributes, resource, }; process.stdout.write(`${JSON.stringify(record)}\n`); @@ -5210,7 +5357,7 @@ function createLogger(options: { ? colorize(levelTag, levelColors[level] ?? ANSI.gray, colorEnabled) : ""; const tag = [coloredLabel, coloredLevel].filter(Boolean).join(" "); - const line = tag ? `${tag} ${message}` : message; + const line = tag ? `${tag} ${redactedMessage}` : redactedMessage; process.stdout.write(`${line}\n`); }; @@ -5421,8 +5568,6 @@ async function spawnRouterDaemon( "--opencode-hot-reload-cooldown-ms", String(opencodeHotReloadCooldownMs), ); - commandArgs.push("--opencode-username", opencodeCredentials.username); - commandArgs.push("--opencode-password", opencodeCredentials.password); if (corsValue) commandArgs.push("--cors", corsValue); if (allowExternal) commandArgs.push("--allow-external"); if (sidecarSource) commandArgs.push("--sidecar-source", sidecarSource); @@ -5436,6 +5581,8 @@ async function spawnRouterDaemon( stdio: "ignore", env: { ...process.env, + OPENWORK_OPENCODE_USERNAME: opencodeUsername, + OPENWORK_OPENCODE_PASSWORD: opencodePassword, }, }); child.unref(); @@ -7418,12 +7565,11 @@ async function runStart(args: ParsedArgs) { ] : []), `OpenWork URL: ${openworkConnectUrl}`, - `OpenWork Collaborator Token: ${openworkToken}`, - ...(openworkOwnerToken - ? [`OpenWork Owner Token: ${openworkOwnerToken}`] - : []), + "Credentials withheld from detached stdout.", + ...(openworkOwnerToken ? ["OpenWork owner token issued."] : []), `OpenCode URL: ${opencodeConnectUrl}`, - `Attach: ${attachCommand}`, + `Attach: ${redactSensitiveString(attachCommand)}`, + "Use `--json` only when you explicitly need the raw tokens or passwords in command output.", ].join("\n"); process.stdout.write(`${summary}\n`); process.exit(0); @@ -8352,26 +8498,25 @@ async function runStart(args: ParsedArgs) { console.log(`OpenCode: ${payload.opencode.baseUrl}`); console.log(`OpenCode connect URL: ${payload.opencode.connectUrl}`); if (payload.opencode.username && payload.opencode.password) { - console.log( - `OpenCode auth: ${payload.opencode.username} / ${payload.opencode.password}`, - ); + console.log("OpenCode auth: managed credentials configured (withheld from stdout)"); } console.log(`OpenWork server: ${payload.openwork.baseUrl}`); console.log(`OpenWork connect URL: ${payload.openwork.connectUrl}`); - console.log( - `OpenWork Collaborator Token: ${payload.openwork.collaboratorToken}`, - ); + console.log("OpenWork collaborator token: issued (withheld from stdout)"); console.log(" Routine remote access for shared workers."); if (payload.openwork.ownerToken) { - console.log(`OpenWork Owner Token: ${payload.openwork.ownerToken}`); + console.log("OpenWork owner token: issued (withheld from stdout)"); console.log( " Use this when the remote client must answer permission prompts.", ); } - console.log(`OpenWork Host Admin Token: ${payload.openwork.hostToken}`); + console.log("OpenWork host admin token: issued (withheld from stdout)"); console.log( " Internal host/admin token for approvals CLI and host-only APIs.", ); + console.log( + "Use `--json` only when you explicitly need raw credentials in command output.", + ); } if (detachRequested) { diff --git a/scripts/dev-headless-web.ts b/scripts/dev-headless-web.ts index a7543e82..026a483b 100644 --- a/scripts/dev-headless-web.ts +++ b/scripts/dev-headless-web.ts @@ -257,8 +257,8 @@ logLine(`[dev:headless-web] Web URL: ${webUrl}`); logLine( `[dev:headless-web] OpenCodeRouter: ${opencodeRouterEnabled ? "on" : "off"} (set OPENWORK_DEV_OPENCODE_ROUTER=0 to disable)`, ); -logLine(`[dev:headless-web] OPENWORK_TOKEN: ${openworkToken}`); -logLine(`[dev:headless-web] OPENWORK_HOST_TOKEN: ${openworkHostToken}`); +logLine("[dev:headless-web] OPENWORK_TOKEN: [REDACTED]"); +logLine("[dev:headless-web] OPENWORK_HOST_TOKEN: [REDACTED]"); logLine( `[dev:headless-web] Web logs: ${path.relative(cwd, path.join(tmpDir, "dev-web.log"))}`, ); @@ -302,10 +302,6 @@ const headlessProcess = spawnLogged( ...(remoteAccessEnabled ? ["--remote-access"] : []), "--openwork-port", String(openworkPort), - "--openwork-token", - openworkToken, - "--openwork-host-token", - openworkHostToken, ], path.join(tmpDir, "dev-headless.log"), headlessEnv,