diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ee3f41..676f688 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,18 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@stable + - name: Install Linux Tauri dependencies + if: matrix.platform == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf \ + libssl-dev + - name: Rust cache uses: swatinem/rust-cache@v2 with: diff --git a/README.md b/README.md index ad11a06..c83b9b1 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,10 @@ Friday/ Prerequisites: Node.js and Rust (for Tauri). See [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/). +For **Local CLI** mode, install the Cursor Agent CLI and ensure `cursor-agent` is on your `PATH` (or set the path in Settings → Advanced: Cursor CLI). Onboarding probes for it automatically. + +For **Cursor API / Cloud** mode, add a Cursor dashboard API key (`crsr_…`) in onboarding or Settings. + ```bash npm install diff --git a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_event_mapper.rs b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_event_mapper.rs index 3f457a8..cf60fbc 100644 --- a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_event_mapper.rs +++ b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_event_mapper.rs @@ -1,6 +1,8 @@ use serde_json::Value; -use crate::adapters::cursor_cli_local::cursor_parser::{CursorIntermediateEvent, CursorIntermediateKind}; +use crate::adapters::cursor_cli_local::cursor_parser::{ + extract_tool_name, CursorIntermediateEvent, CursorIntermediateKind, +}; use crate::core::event::{ AgentEvent, FileAction, FridaySessionStatus, MessageRole, RiskLevel, now_iso, }; @@ -54,14 +56,9 @@ impl CursorEventMapper { vec![] } } - CursorIntermediateKind::ToolUse => { - let tool_name = event - .payload - .get("tool") - .or_else(|| event.payload.get("name")) - .and_then(|v| v.as_str()) - .unwrap_or("tool") - .to_string(); + CursorIntermediateKind::ToolUse | CursorIntermediateKind::ToolCompleted => { + let tool_name = extract_tool_name(&event.payload) + .unwrap_or_else(|| "tool".into()); let title = event .payload .get("title") @@ -69,7 +66,7 @@ impl CursorEventMapper { .unwrap_or(&tool_name) .to_string(); let status = crate::core::state_machine::status_from_tool_name(&tool_name); - vec![ + let mut events = vec![ AgentEvent::ToolCall { session_id: session_id.to_string(), tool_name: tool_name.clone(), @@ -83,20 +80,62 @@ impl CursorEventMapper { message: Some(format!("Using {tool_name}")), timestamp: now_iso(), }, - ] + ]; + if matches!(event.kind, CursorIntermediateKind::ToolCompleted) { + if let Some(path) = extract_path(&event.payload) { + let action = if tool_name.to_lowercase().contains("delete") { + FileAction::Deleted + } else if tool_name.to_lowercase().contains("write") { + FileAction::Created + } else { + FileAction::Edited + }; + events.push(AgentEvent::FileChanged { + session_id: session_id.to_string(), + path, + action, + timestamp: now_iso(), + }); + } + } + events } CursorIntermediateKind::FileRead => { - let msg = extract_text_or_raw(&event); - vec![AgentEvent::AgentStatus { + let path = extract_path(&event.payload); + let msg = path + .clone() + .unwrap_or_else(|| extract_text_or_raw(&event)); + let mut events = vec![AgentEvent::AgentStatus { session_id: session_id.to_string(), status: FridaySessionStatus::Reading, message: Some(msg), timestamp: now_iso(), - }] + }]; + if let Some(path) = path { + events.insert( + 0, + AgentEvent::ToolCall { + session_id: session_id.to_string(), + tool_name: "read".into(), + title: format!("Read {path}"), + args: Some(event.payload.clone()), + timestamp: now_iso(), + }, + ); + } + events } CursorIntermediateKind::FileEdit => { let path = extract_path(&event.payload).unwrap_or_else(|| "unknown".into()); + let tool_name = extract_tool_name(&event.payload).unwrap_or_else(|| "edit".into()); vec![ + AgentEvent::ToolCall { + session_id: session_id.to_string(), + tool_name, + title: format!("Edit {path}"), + args: Some(event.payload.clone()), + timestamp: now_iso(), + }, AgentEvent::FileChanged { session_id: session_id.to_string(), path, @@ -112,15 +151,40 @@ impl CursorEventMapper { ] } CursorIntermediateKind::CommandRun => { - let command = extract_text_or_raw(&event); + let command = extract_command(&event); let risk = classify_command_risk(&command); - vec![AgentEvent::CommandStarted { - session_id: session_id.to_string(), - command: command.clone(), - cwd: None, - risk, - timestamp: now_iso(), - }] + vec![ + AgentEvent::CommandStarted { + session_id: session_id.to_string(), + command: command.clone(), + cwd: extract_cwd(&event.payload), + risk, + timestamp: now_iso(), + }, + AgentEvent::AgentStatus { + session_id: session_id.to_string(), + status: FridaySessionStatus::RunningCommand, + message: Some(command), + timestamp: now_iso(), + }, + ] + } + CursorIntermediateKind::CommandCompleted => { + let command = extract_command(&event); + vec![ + AgentEvent::CommandCompleted { + session_id: session_id.to_string(), + command, + exit_code: extract_exit_code(&event.payload), + timestamp: now_iso(), + }, + AgentEvent::AgentStatus { + session_id: session_id.to_string(), + status: FridaySessionStatus::Thinking, + message: Some("Command finished".into()), + timestamp: now_iso(), + }, + ] } CursorIntermediateKind::Approval => vec![AgentEvent::ApprovalRequired { session_id: session_id.to_string(), @@ -150,19 +214,27 @@ impl CursorEventMapper { }, ] } - CursorIntermediateKind::Error => vec![ - AgentEvent::SessionError { - session_id: session_id.to_string(), - error: extract_text_or_raw(&event), - timestamp: now_iso(), - }, - AgentEvent::AgentStatus { - session_id: session_id.to_string(), - status: FridaySessionStatus::Error, - message: None, - timestamp: now_iso(), - }, - ], + CursorIntermediateKind::Error => { + let error = event + .payload + .get("result") + .and_then(|v| v.as_str()) + .map(String::from) + .unwrap_or_else(|| extract_text_or_raw(&event)); + vec![ + AgentEvent::SessionError { + session_id: session_id.to_string(), + error, + timestamp: now_iso(), + }, + AgentEvent::AgentStatus { + session_id: session_id.to_string(), + status: FridaySessionStatus::Error, + message: None, + timestamp: now_iso(), + }, + ] + } } } } @@ -200,6 +272,49 @@ fn extract_path(payload: &Value) -> Option { .and_then(|v| v.as_str()) .map(String::from) }) + .or_else(|| { + payload + .get("tool_call") + .and_then(|tc| tc.as_object()) + .and_then(|obj| obj.values().next()) + .and_then(|tool| { + tool.pointer("/args/path") + .or_else(|| tool.pointer("/result/success/path")) + }) + .and_then(|v| v.as_str()) + .map(String::from) + }) +} + +fn extract_command(event: &CursorIntermediateEvent) -> String { + if let Some(cmd) = event + .payload + .pointer("/tool_call/shellToolCall/args/command") + .or_else(|| event.payload.pointer("/tool_call/bashToolCall/args/command")) + .or_else(|| event.payload.pointer("/args/command")) + .and_then(|v| v.as_str()) + { + return cmd.to_string(); + } + extract_text_or_raw(event) +} + +fn extract_cwd(payload: &Value) -> Option { + payload + .pointer("/tool_call/shellToolCall/args/working_directory") + .or_else(|| payload.pointer("/tool_call/shellToolCall/args/cwd")) + .or_else(|| payload.pointer("/args/cwd")) + .and_then(|v| v.as_str()) + .map(String::from) +} + +fn extract_exit_code(payload: &Value) -> Option { + payload + .pointer("/tool_call/shellToolCall/result/success/exitCode") + .or_else(|| payload.pointer("/tool_call/shellToolCall/result/exitCode")) + .or_else(|| payload.get("exit_code")) + .and_then(|v| v.as_i64()) + .map(|n| n as i32) } fn extract_text_or_raw(event: &CursorIntermediateEvent) -> String { diff --git a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_parser.rs b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_parser.rs index f2f4987..bdfa402 100644 --- a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_parser.rs +++ b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/cursor_parser.rs @@ -4,9 +4,11 @@ use serde_json::Value; pub enum CursorIntermediateKind { AssistantText, ToolUse, + ToolCompleted, FileRead, FileEdit, CommandRun, + CommandCompleted, Approval, SessionStart, SessionEnd, @@ -52,21 +54,35 @@ impl CursorParser { .unwrap_or("unknown"); let kind = match event_type { - "start" => CursorIntermediateKind::SessionStart, - "assistant" => { - if json.get("timestamp_ms").is_some() { - CursorIntermediateKind::AssistantText + "system" => { + let subtype = json.get("subtype").and_then(|v| v.as_str()).unwrap_or(""); + if subtype == "init" || subtype.is_empty() { + CursorIntermediateKind::SessionStart } else { CursorIntermediateKind::Unknown } } - "result" => CursorIntermediateKind::SessionEnd, - "tool_use" | "tool_call" | "tool" => CursorIntermediateKind::ToolUse, + "user" => CursorIntermediateKind::Unknown, + "assistant" => classify_assistant(&json), + "result" => { + let is_error = json + .get("is_error") + .and_then(|v| v.as_bool()) + .unwrap_or(false) + || json.get("subtype").and_then(|v| v.as_str()) == Some("error"); + if is_error { + CursorIntermediateKind::Error + } else { + CursorIntermediateKind::SessionEnd + } + } + "tool_call" | "tool_use" | "tool" => classify_tool_call(&json), "error" => CursorIntermediateKind::Error, "content" => CursorIntermediateKind::AssistantText, + "permission" | "approval" | "ask" => CursorIntermediateKind::Approval, _ => { - if json.get("tool").is_some() { - CursorIntermediateKind::ToolUse + if json.get("tool").is_some() || json.get("tool_call").is_some() { + classify_tool_call(&json) } else { classify_plain_text(raw) } @@ -81,6 +97,104 @@ impl CursorParser { } } +/// With `--stream-partial-output`, only deltas with `timestamp_ms` and without +/// `model_call_id` carry new text. Without streaming, complete assistant +/// messages have neither field and should be kept. +fn classify_assistant(json: &Value) -> CursorIntermediateKind { + let has_ts = json.get("timestamp_ms").is_some(); + let has_model_call = json.get("model_call_id").is_some(); + + if has_ts && has_model_call { + // Buffered duplicate flush before a tool call. + return CursorIntermediateKind::Unknown; + } + if !has_ts && has_model_call { + return CursorIntermediateKind::Unknown; + } + // Streaming delta (timestamp only) OR complete non-streamed message (neither). + // Final flush has neither timestamp nor model_call_id but is a duplicate when + // streaming was used — we still accept it when there is message content and + // no prior streaming context is available; duplicates are harmless for chat. + if !has_ts { + // Final flush duplicate under --stream-partial-output: skip if empty-ish + // and looks like a flush (no delta field). Keep complete messages. + if json.get("delta").is_none() + && json.get("message").is_some() + && json.get("session_id").is_some() + { + // Could be either a complete message (no streaming) or final flush. + // Prefer keeping it — UI can dedupe if needed; dropping loses text. + return CursorIntermediateKind::AssistantText; + } + } + CursorIntermediateKind::AssistantText +} + +fn classify_tool_call(json: &Value) -> CursorIntermediateKind { + let subtype = json + .get("subtype") + .and_then(|v| v.as_str()) + .unwrap_or("started"); + + let tool_name = extract_tool_name(json).unwrap_or_default(); + let lower = tool_name.to_lowercase(); + + let is_shell = lower.contains("shell") + || lower.contains("bash") + || lower.contains("terminal") + || lower.contains("command"); + let is_read = lower.contains("read"); + let is_edit = lower.contains("edit") + || lower.contains("write") + || lower.contains("delete"); + + match subtype { + "completed" => { + if is_shell { + CursorIntermediateKind::CommandCompleted + } else { + CursorIntermediateKind::ToolCompleted + } + } + _ => { + if is_shell { + CursorIntermediateKind::CommandRun + } else if is_read { + CursorIntermediateKind::FileRead + } else if is_edit { + CursorIntermediateKind::FileEdit + } else { + CursorIntermediateKind::ToolUse + } + } + } +} + +pub fn extract_tool_name(payload: &Value) -> Option { + if let Some(name) = payload + .get("tool") + .or_else(|| payload.get("name")) + .and_then(|v| v.as_str()) + { + return Some(name.to_string()); + } + + if let Some(obj) = payload.get("tool_call").and_then(|v| v.as_object()) { + if let Some(key) = obj.keys().next() { + return Some(key.clone()); + } + } + + if let Some(name) = payload + .pointer("/tool_call/function/name") + .and_then(|v| v.as_str()) + { + return Some(name.to_string()); + } + + None +} + fn classify_plain_text(text: &str) -> CursorIntermediateKind { let lower = text.to_lowercase(); if lower.contains("reading ") || lower.starts_with("read ") { @@ -89,9 +203,58 @@ fn classify_plain_text(text: &str) -> CursorIntermediateKind { CursorIntermediateKind::FileEdit } else if lower.starts_with('$') || lower.contains("running command") { CursorIntermediateKind::CommandRun + } else if lower.contains("approval") || lower.contains("permission") { + CursorIntermediateKind::Approval } else if lower.contains("error") { CursorIntermediateKind::Error } else { CursorIntermediateKind::AssistantText } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_system_init() { + let line = r#"{"type":"system","subtype":"init","session_id":"abc"}"#; + let ev = CursorParser::parse_line(line); + assert!(matches!(ev.kind, CursorIntermediateKind::SessionStart)); + } + + #[test] + fn keeps_complete_assistant_without_timestamp() { + let line = r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"hi"}]},"session_id":"abc"}"#; + let ev = CursorParser::parse_line(line); + assert!(matches!(ev.kind, CursorIntermediateKind::AssistantText)); + } + + #[test] + fn skips_buffered_assistant_flush() { + let line = r#"{"type":"assistant","timestamp_ms":1,"model_call_id":"x","message":{"content":[{"type":"text","text":"dup"}]}}"#; + let ev = CursorParser::parse_line(line); + assert!(matches!(ev.kind, CursorIntermediateKind::Unknown)); + } + + #[test] + fn classifies_shell_tool_started() { + let line = r#"{"type":"tool_call","subtype":"started","call_id":"1","tool_call":{"shellToolCall":{"args":{"command":"ls"}}}}"#; + let ev = CursorParser::parse_line(line); + assert!(matches!(ev.kind, CursorIntermediateKind::CommandRun)); + } + + #[test] + fn classifies_shell_tool_completed() { + let line = r#"{"type":"tool_call","subtype":"completed","call_id":"1","tool_call":{"shellToolCall":{"args":{"command":"ls"},"result":{}}}}"#; + let ev = CursorParser::parse_line(line); + assert!(matches!(ev.kind, CursorIntermediateKind::CommandCompleted)); + } + + #[test] + fn classifies_result_success() { + let line = r#"{"type":"result","subtype":"success","is_error":false,"result":"done"}"#; + let ev = CursorParser::parse_line(line); + assert!(matches!(ev.kind, CursorIntermediateKind::SessionEnd)); + } +} diff --git a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/launcher.rs b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/launcher.rs index 19a5302..0d0b9bf 100644 --- a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/launcher.rs +++ b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/launcher.rs @@ -11,6 +11,15 @@ pub struct BuiltCommand { pub env: Vec<(String, String)>, } +#[derive(Debug, Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CursorCliProbe { + pub found: bool, + pub path: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + pub fn resolve_executable(settings: &CursorSettings) -> String { settings .executable_path @@ -20,17 +29,38 @@ pub fn resolve_executable(settings: &CursorSettings) -> String { pub fn validate_executable(path: &str) -> AppResult<()> { let p = Path::new(path); - if p.exists() { + if p.is_file() { return Ok(()); } - if which_in_path(path) { + if which_in_path(path).is_some() { return Ok(()); } Err(AppError::Process(format!( - "cursor executable not found: {path}" + "cursor-agent not found: {path}. Install Cursor CLI and ensure it is on PATH, or set the executable path in Settings." ))) } +pub fn probe_executable(settings: &CursorSettings) -> CursorCliProbe { + let path = resolve_executable(settings); + match validate_executable(&path) { + Ok(()) => { + let resolved = which_in_path(&path) + .map(|p| p.display().to_string()) + .unwrap_or_else(|| path.clone()); + CursorCliProbe { + found: true, + path: resolved, + error: None, + } + } + Err(e) => CursorCliProbe { + found: false, + path, + error: Some(e.to_string()), + }, + } +} + pub fn build_command( prompt: &str, cwd: &str, @@ -39,28 +69,31 @@ pub fn build_command( let executable = resolve_executable(settings); validate_executable(&executable)?; + let templates = &settings.arg_templates.headless_stream; + let had_prompt_placeholder = templates.iter().any(|t| t.contains("{prompt}")); + let mut args = Vec::new(); - for template in &settings.arg_templates.headless_stream { + for template in templates { let rendered = template .replace("{prompt}", prompt) .replace("{cwd}", cwd) - .replace( - "{outputFormat}", - &settings.default_output_format, - ); + .replace("{outputFormat}", &settings.default_output_format); if !rendered.is_empty() { args.push(rendered); } } if args.is_empty() { - args.push("-p".to_string()); + args.push("--print".to_string()); args.push("--output-format".to_string()); args.push(settings.default_output_format.clone()); if settings.default_output_format == "stream-json" { args.push("--stream-partial-output".to_string()); } args.push(prompt.to_string()); + } else if !had_prompt_placeholder && !args.iter().any(|a| a == prompt) { + // Templates without `{prompt}` previously dropped the task entirely. + args.push(prompt.to_string()); } Ok(BuiltCommand { @@ -79,9 +112,75 @@ fn default_cursor_executable() -> String { } } -fn which_in_path(name: &str) -> bool { - let Some(path_var) = std::env::var_os("PATH") else { - return false; - }; - std::env::split_paths(&path_var).any(|dir| dir.join(name).exists()) +fn which_in_path(name: &str) -> Option { + let path = Path::new(name); + if path.is_file() { + return Some(path.to_path_buf()); + } + let path_var = std::env::var_os("PATH")?; + for dir in std::env::split_paths(&path_var) { + let candidate = dir.join(name); + if candidate.is_file() { + return Some(candidate); + } + #[cfg(target_os = "windows")] + { + let with_cmd = dir.join(format!("{name}.cmd")); + if with_cmd.is_file() { + return Some(with_cmd); + } + let with_exe = dir.join(format!("{name}.exe")); + if with_exe.is_file() { + return Some(with_exe); + } + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::settings_repo::{CursorArgTemplates, CursorSettings}; + + fn settings_with_templates(templates: Vec<&str>) -> CursorSettings { + CursorSettings { + executable_path: Some("/bin/true".into()), + api_key_configured: false, + default_mode: "headless".into(), + default_output_format: "stream-json".into(), + use_pty: true, + arg_templates: CursorArgTemplates { + headless_stream: templates.into_iter().map(String::from).collect(), + }, + terminal_cols: 120, + terminal_rows: 30, + } + } + + #[test] + fn appends_prompt_when_templates_omit_placeholder() { + let settings = settings_with_templates(vec![ + "--print", + "--output-format", + "stream-json", + ]); + let built = build_command("fix the bug", "/tmp", &settings).unwrap(); + assert_eq!(built.args.last().map(String::as_str), Some("fix the bug")); + } + + #[test] + fn substitutes_prompt_placeholder() { + let settings = settings_with_templates(vec![ + "--print", + "--output-format", + "{outputFormat}", + "--stream-partial-output", + "{prompt}", + ]); + let built = build_command("hello", "/repo", &settings).unwrap(); + assert!(built.args.contains(&"stream-json".to_string())); + assert!(built.args.contains(&"hello".to_string())); + assert!(!built.args.iter().any(|a| a.contains("{prompt}"))); + } } diff --git a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/mod.rs b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/mod.rs index 7390347..739d3b8 100644 --- a/apps/desktop/src-tauri/src/adapters/cursor_cli_local/mod.rs +++ b/apps/desktop/src-tauri/src/adapters/cursor_cli_local/mod.rs @@ -53,7 +53,7 @@ impl AgentAdapter for CursorCliLocalAdapter { let handler = ctx.event_handler.clone(); let (pid, pty_id) = if ctx.cursor_settings.use_pty { - let (pty_id, mut rx) = ctx.pty_manager.create_pty( + let (pty_id, child_pid, mut rx) = ctx.pty_manager.create_pty( &session_id, ctx.cursor_settings.terminal_cols, ctx.cursor_settings.terminal_rows, @@ -72,7 +72,27 @@ impl AgentAdapter for CursorCliLocalAdapter { flush_parser_buffer(&sid, &mut line_buf, &h); }); - (0u32, Some(pty_id)) + // Watch PTY child exit so sessions don't stay "running" forever. + let pty_mgr = ctx.pty_manager.clone(); + let pty_id_watch = pty_id.clone(); + let sid = session_id.clone(); + let h = handler.clone(); + tauri::async_runtime::spawn_blocking(move || { + let success = pty_mgr.wait_for_exit(&pty_id_watch); + let status = if success { + FridaySessionStatus::Done + } else { + FridaySessionStatus::Stopped + }; + h(AgentEvent::AgentStatus { + session_id: sid, + status, + message: Some("Process exited".into()), + timestamp: now_iso(), + }); + }); + + (child_pid.unwrap_or(0), Some(pty_id)) } else { let (pid, mut rx) = ctx .supervisor @@ -93,8 +113,10 @@ impl AgentAdapter for CursorCliLocalAdapter { while let Some((line, is_stderr)) = rx.recv().await { if is_stderr { emit_stderr_line(&sid, &line, &h); + } else { + // Only feed stdout into the NDJSON parser. + feed_parser_lines(&sid, &mut line_buf, line.as_bytes(), &h); } - feed_parser_lines(&sid, &mut line_buf, line.as_bytes(), &h); } flush_parser_buffer(&sid, &mut line_buf, &h); }); @@ -148,6 +170,7 @@ impl AgentAdapter for CursorCliLocalAdapter { async fn stop_session(&self, session_id: &str, ctx: &AdapterContext) -> AppResult<()> { if ctx.cursor_settings.use_pty { + // Soft stop: Ctrl+C → grace → kill (handled inside PtyManager::close). ctx.pty_manager.close_by_session(session_id, false)?; } else { ctx.supervisor.stop_session(session_id, 3).await?; diff --git a/apps/desktop/src-tauri/src/commands/settings_commands.rs b/apps/desktop/src-tauri/src/commands/settings_commands.rs index bfb9a4b..6568830 100644 --- a/apps/desktop/src-tauri/src/commands/settings_commands.rs +++ b/apps/desktop/src-tauri/src/commands/settings_commands.rs @@ -7,6 +7,16 @@ use crate::storage::local_data::{local_data_dir_display, schedule_wipe_on_restar use crate::storage::settings_repo::FridaySettings; use crate::storage::SettingsRepo; +#[tauri::command] +pub fn probe_cursor_cli( + core: State<'_, Arc>, +) -> Result { + let settings = core.settings_snapshot(); + Ok(crate::adapters::cursor_cli_local::launcher::probe_executable( + &settings.cursor, + )) +} + #[tauri::command] pub fn get_settings( core: State<'_, Arc>, diff --git a/apps/desktop/src-tauri/src/core/agent_core.rs b/apps/desktop/src-tauri/src/core/agent_core.rs index fa63330..f18c631 100644 --- a/apps/desktop/src-tauri/src/core/agent_core.rs +++ b/apps/desktop/src-tauri/src/core/agent_core.rs @@ -75,6 +75,17 @@ impl AgentCore { let sessions = SessionsRepo::new(&self.db).list()?; self.session_manager.lock().await.load_from_db(sessions); + // Persist stale local sessions marked stopped on load. + { + let mgr = self.session_manager.lock().await; + let repo = SessionsRepo::new(&self.db); + for session in mgr.list() { + if session.status == FridaySessionStatus::Stopped { + let _ = repo.update_status(&session.id, FridaySessionStatus::Stopped, None); + } + } + } + self.resume_cloud_sessions(app).await?; Ok(()) diff --git a/apps/desktop/src-tauri/src/core/event_dispatch.rs b/apps/desktop/src-tauri/src/core/event_dispatch.rs index 38733a8..29b4814 100644 --- a/apps/desktop/src-tauri/src/core/event_dispatch.rs +++ b/apps/desktop/src-tauri/src/core/event_dispatch.rs @@ -4,7 +4,7 @@ use tauri::{AppHandle, Manager}; use crate::bridge::BridgeBroadcast; use crate::core::event_pipeline::handle_event; -use crate::core::event::{AgentEvent, FridaySessionStatus, now_iso}; +use crate::core::event::{AgentEvent, FridaySessionStatus}; use crate::core::AgentCore; use crate::errors::AppResult; use crate::storage::{Database, SessionsRepo}; @@ -17,41 +17,35 @@ pub async fn dispatch_event( ) -> AppResult<()> { let session_id = event.session_id().to_string(); + // Sync in-memory SessionManager so owned-CLI slot frees on Done/Error/Stopped. + sync_session_manager(core, &event).await; + if let AgentEvent::CommandStarted { ref command, .. } = event { let settings = core.settings_snapshot(); if crate::security::CommandPolicy::requires_approval(command, &settings.security) { let risk = crate::security::risk_classifier::classify_command_risk(command); - let (_approval_id, approval_event, rx) = core + let (_approval_id, approval_event, _rx) = core .approval_manager .lock() .await .request_approval(&session_id, command, risk) .await; + // Non-blocking: Cursor CLI keeps running; Reject stops the session. + // Blocking the event worker previously stalled the UI while the agent continued. handle_event(app.clone(), db.clone(), approval_event, None).await?; if !session_id.is_empty() { - core.session_manager + let _ = core + .session_manager .lock() .await - .update_status(&session_id, FridaySessionStatus::WaitingPermission)?; - SessionsRepo::new(&db).update_status( + .update_status(&session_id, FridaySessionStatus::WaitingPermission); + let _ = SessionsRepo::new(&db).update_status( &session_id, FridaySessionStatus::WaitingPermission, None, - )?; - } - - let approved = rx.await.unwrap_or(false); - if !approved { - let reject_event = AgentEvent::AgentStatus { - session_id: session_id.clone(), - status: FridaySessionStatus::Stopped, - message: Some("Command rejected by user".into()), - timestamp: now_iso(), - }; - handle_event(app, db, reject_event, None).await?; - return Ok(()); + ); } } } @@ -65,3 +59,58 @@ pub async fn dispatch_event( let bridge = app.try_state::().map(|b| b.inner().clone()); handle_event(app, db, event, bridge).await } + +async fn sync_session_manager(core: &AgentCore, event: &AgentEvent) { + let session_id = event.session_id(); + if session_id.is_empty() { + return; + } + + match event { + AgentEvent::AgentStatus { status, .. } => { + let mut mgr = core.session_manager.lock().await; + if let Ok(existing) = mgr.get(session_id) { + // Don't resurrect or downgrade terminal sessions from late process-exit events. + if matches!( + existing.status, + FridaySessionStatus::Done + | FridaySessionStatus::Error + | FridaySessionStatus::Stopped + ) { + return; + } + let _ = mgr.update_status(session_id, *status); + } + } + AgentEvent::SessionCompleted { summary, .. } => { + let mut mgr = core.session_manager.lock().await; + if mgr.get(session_id).is_ok() { + if let Some(summary) = summary { + let _ = mgr.set_summary(session_id, summary.clone()); + } + let _ = mgr.update_status(session_id, FridaySessionStatus::Done); + } + } + AgentEvent::SessionError { .. } => { + let mut mgr = core.session_manager.lock().await; + if mgr.get(session_id).is_ok() { + let _ = mgr.update_status(session_id, FridaySessionStatus::Error); + } + } + AgentEvent::SessionStarted { .. } => { + let mut mgr = core.session_manager.lock().await; + if let Ok(existing) = mgr.get(session_id) { + if matches!( + existing.status, + FridaySessionStatus::Done + | FridaySessionStatus::Error + | FridaySessionStatus::Stopped + ) { + return; + } + let _ = mgr.update_status(session_id, FridaySessionStatus::Thinking); + } + } + _ => {} + } +} diff --git a/apps/desktop/src-tauri/src/core/session_manager.rs b/apps/desktop/src-tauri/src/core/session_manager.rs index b8efc9e..2825564 100644 --- a/apps/desktop/src-tauri/src/core/session_manager.rs +++ b/apps/desktop/src-tauri/src/core/session_manager.rs @@ -111,7 +111,17 @@ impl SessionManager { } pub fn load_from_db(&mut self, sessions: Vec) { - for session in sessions { + for mut session in sessions { + // Local processes do not survive app restart. Mark stale running + // CLI sessions stopped so the owned-CLI slot is free. Cloud sessions + // may still be live and are resumed separately. + if session.session_type != AgentSessionType::CursorCloud + && is_running_status(session.status) + { + session.status = FridaySessionStatus::Stopped; + session.completed_at = Some(now_iso()); + session.updated_at = now_iso(); + } self.sessions.insert(session.id.clone(), session); } } diff --git a/apps/desktop/src-tauri/src/discovery/external_loop.rs b/apps/desktop/src-tauri/src/discovery/external_loop.rs index 4e7de9f..65c9051 100644 --- a/apps/desktop/src-tauri/src/discovery/external_loop.rs +++ b/apps/desktop/src-tauri/src/discovery/external_loop.rs @@ -68,6 +68,10 @@ async fn discovery_loop(core: &AgentCore) { if external_pids.contains(&proc.pid) { continue; } + // Skip Friday-owned PTY children even if SessionProcess.pid was missing historically. + if core.pty_manager.owns_pid(proc.pid) { + continue; + } let session_id = Uuid::new_v4().to_string(); diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index af332f2..3824ef6 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -124,6 +124,7 @@ pub fn run() { commands::project_commands::get_project, commands::settings_commands::get_settings, commands::settings_commands::save_settings, + commands::settings_commands::probe_cursor_cli, commands::settings_commands::save_cursor_api_key, commands::settings_commands::verify_cursor_api_key, commands::settings_commands::clear_cursor_api_key, diff --git a/apps/desktop/src-tauri/src/pty/manager.rs b/apps/desktop/src-tauri/src/pty/manager.rs index 6a0562c..a7b5764 100644 --- a/apps/desktop/src-tauri/src/pty/manager.rs +++ b/apps/desktop/src-tauri/src/pty/manager.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::io::{Read, Write}; use std::path::Path; use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; use portable_pty::{CommandBuilder, NativePtySystem, PtySize, PtySystem}; use tokio::sync::mpsc; @@ -11,8 +12,10 @@ use crate::errors::{AppError, AppResult}; struct PtyEntry { master: Arc>>, + writer: Arc>>, child: Arc>>, session_id: String, + child_pid: Option, } pub struct PtyManager { @@ -40,7 +43,7 @@ impl PtyManager { cwd: &Path, executable: &str, args: &[String], - ) -> AppResult<(String, mpsc::UnboundedReceiver>)> { + ) -> AppResult<(String, Option, mpsc::UnboundedReceiver>)> { let pty_system = NativePtySystem::default(); let pair = pty_system .openpty(PtySize { @@ -62,7 +65,12 @@ impl PtyManager { .spawn_command(cmd) .map_err(|e| AppError::Process(format!("pty spawn failed: {e}")))?; + let child_pid = child.process_id(); let master = pair.master; + let writer = master + .take_writer() + .map_err(|e| AppError::Process(format!("pty writer failed: {e}")))?; + let pty_id = Uuid::new_v4().to_string(); let (tx, rx) = mpsc::unbounded_channel(); @@ -88,8 +96,10 @@ impl PtyManager { let entry = PtyEntry { master: Arc::new(Mutex::new(master)), + writer: Arc::new(Mutex::new(writer)), child: Arc::new(Mutex::new(child)), session_id: session_id.to_string(), + child_pid, }; self.ptys @@ -97,7 +107,7 @@ impl PtyManager { .map_err(|e| AppError::Other(e.to_string()))? .insert(pty_id.clone(), entry); - Ok((pty_id, rx)) + Ok((pty_id, child_pid, rx)) } pub fn write(&self, pty_id: &str, data: &[u8]) -> AppResult<()> { @@ -109,17 +119,19 @@ impl PtyManager { .get(pty_id) .ok_or_else(|| AppError::Other(format!("pty not found: {pty_id}")))?; let mut writer = entry - .master + .writer .lock() - .map_err(|e| AppError::Other(e.to_string()))? - .take_writer() - .map_err(|e| AppError::Process(format!("pty writer failed: {e}")))?; - writer - .write_all(data) - .map_err(|e| AppError::Io(e))?; + .map_err(|e| AppError::Other(e.to_string()))?; + writer.write_all(data).map_err(AppError::Io)?; + writer.flush().map_err(AppError::Io)?; Ok(()) } + pub fn interrupt(&self, pty_id: &str) -> AppResult<()> { + // Ctrl+C + self.write(pty_id, &[0x03]) + } + pub fn resize(&self, pty_id: &str, cols: u16, rows: u16) -> AppResult<()> { let ptys = self .ptys @@ -142,33 +154,79 @@ impl PtyManager { Ok(()) } - pub fn close(&self, pty_id: &str, force: bool) -> AppResult<()> { - let mut ptys = self - .ptys + pub fn child_pid(&self, pty_id: &str) -> Option { + self.ptys .lock() - .map_err(|e| AppError::Other(e.to_string()))?; - if let Some(entry) = ptys.remove(pty_id) { + .ok() + .and_then(|ptys| ptys.get(pty_id).and_then(|e| e.child_pid)) + } + + pub fn child_pid_for_session(&self, session_id: &str) -> Option { + self.ptys.lock().ok().and_then(|ptys| { + ptys.values() + .find(|e| e.session_id == session_id) + .and_then(|e| e.child_pid) + }) + } + + /// Soft stop: Ctrl+C, wait up to `grace`, then kill if still alive. + pub fn close(&self, pty_id: &str, force: bool) -> AppResult<()> { + let entry = { + let mut ptys = self + .ptys + .lock() + .map_err(|e| AppError::Other(e.to_string()))?; + ptys.remove(pty_id) + }; + + let Some(entry) = entry else { + return Ok(()); + }; + + if force { if let Ok(mut child) = entry.child.lock() { - if force { + let _ = child.kill(); + let _ = child.wait(); + } + return Ok(()); + } + + // Interrupt first (SIGINT via Ctrl+C). + if let Ok(mut writer) = entry.writer.lock() { + let _ = writer.write_all(&[0x03]); + let _ = writer.flush(); + } + + let grace = Duration::from_secs(3); + let deadline = Instant::now() + grace; + loop { + let exited = entry + .child + .lock() + .ok() + .and_then(|mut child| child.try_wait().ok().flatten()); + if exited.is_some() { + break; + } + if Instant::now() >= deadline { + if let Ok(mut child) = entry.child.lock() { let _ = child.kill(); - } else { let _ = child.wait(); } + break; } + std::thread::sleep(Duration::from_millis(50)); } + Ok(()) } pub fn pty_id_for_session(&self, session_id: &str) -> Option { - self.ptys - .lock() - .ok() - .and_then(|ptys| { - ptys - .iter() - .find(|(_, e)| e.session_id == session_id) - .map(|(id, _)| id.clone()) - }) + self.ptys.lock().ok().and_then(|ptys| { + ptys.iter() + .find(|(_, e)| e.session_id == session_id) + .map(|(id, _)| id.clone()) + }) } pub fn close_by_session(&self, session_id: &str, force: bool) -> AppResult<()> { @@ -185,4 +243,40 @@ impl PtyManager { } Ok(()) } + + /// Poll until the PTY child exits (or the entry is removed). Returns exit success. + pub fn wait_for_exit(&self, pty_id: &str) -> bool { + loop { + let status = { + let ptys = match self.ptys.lock() { + Ok(p) => p, + Err(_) => return false, + }; + let Some(entry) = ptys.get(pty_id) else { + return false; + }; + let result = match entry.child.lock() { + Ok(mut child) => child.try_wait().ok().flatten(), + Err(_) => return false, + }; + result + }; + + if let Some(status) = status { + let success = status.success(); + let _ = self.close(pty_id, true); + return success; + } + + std::thread::sleep(Duration::from_millis(100)); + } + } + + pub fn owns_pid(&self, pid: u32) -> bool { + self.ptys + .lock() + .ok() + .map(|ptys| ptys.values().any(|e| e.child_pid == Some(pid))) + .unwrap_or(false) + } } diff --git a/apps/desktop/src-tauri/src/security/approval_manager.rs b/apps/desktop/src-tauri/src/security/approval_manager.rs index 2469439..ce2b9e5 100644 --- a/apps/desktop/src-tauri/src/security/approval_manager.rs +++ b/apps/desktop/src-tauri/src/security/approval_manager.rs @@ -55,7 +55,7 @@ impl ApprovalManager { let event = AgentEvent::ApprovalRequired { session_id: session_id.to_string(), approval_id: approval_id.clone(), - title: "Command approval required — Friday paused until you approve or reject.".into(), + title: "High-risk command observed — Approve to acknowledge, or Reject to stop the session.".into(), command: Some(command.to_string()), risk, timestamp: now_iso(), diff --git a/apps/desktop/src-tauri/src/storage/settings_repo.rs b/apps/desktop/src-tauri/src/storage/settings_repo.rs index 984fde6..259ea69 100644 --- a/apps/desktop/src-tauri/src/storage/settings_repo.rs +++ b/apps/desktop/src-tauri/src/storage/settings_repo.rs @@ -6,13 +6,31 @@ use crate::security::secret_store::{SecretStore, CURSOR_API_KEY_ACCOUNT}; use crate::storage::secret_sqlite; use crate::storage::sqlite::Database; -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CursorArgTemplates { - #[serde(default)] + #[serde(default = "default_headless_stream")] pub headless_stream: Vec, } +impl Default for CursorArgTemplates { + fn default() -> Self { + Self { + headless_stream: default_headless_stream(), + } + } +} + +fn default_headless_stream() -> Vec { + vec![ + "--print".into(), + "--output-format".into(), + "{outputFormat}".into(), + "--stream-partial-output".into(), + "{prompt}".into(), + ] +} + fn deserialize_arg_templates<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -220,11 +238,23 @@ pub struct ShortcutSettings { impl Default for ShortcutSettings { fn default() -> Self { - Self { - quick_bubble: "CommandOrControl+Space".into(), - open_panel: "CommandOrControl+Shift+F".into(), - voice_input: "CommandOrControl+Shift+V".into(), - stop_session: "CommandOrControl+Period".into(), + #[cfg(target_os = "windows")] + { + Self { + quick_bubble: "Alt+Shift+Space".into(), + open_panel: "CommandOrControl+Shift+F".into(), + voice_input: "CommandOrControl+Shift+V".into(), + stop_session: "CommandOrControl+Period".into(), + } + } + #[cfg(not(target_os = "windows"))] + { + Self { + quick_bubble: "CommandOrControl+Space".into(), + open_panel: "CommandOrControl+Shift+F".into(), + voice_input: "CommandOrControl+Shift+V".into(), + stop_session: "CommandOrControl+Period".into(), + } } } } diff --git a/apps/desktop/src-tauri/src/system/shortcuts.rs b/apps/desktop/src-tauri/src/system/shortcuts.rs index a773d51..182d875 100644 --- a/apps/desktop/src-tauri/src/system/shortcuts.rs +++ b/apps/desktop/src-tauri/src/system/shortcuts.rs @@ -68,10 +68,22 @@ fn bind(app: &tauri::AppHandle, shortcut: Shortcut, action: &'static str) { let core = core.inner().clone(); let app = app.clone(); tauri::async_runtime::spawn(async move { - if let Ok(sessions) = core.list_active_sessions().await { - if let Some(s) = sessions.first() { - let _ = core.close_session_safely(app, &s.id).await; - } + let target = { + let mgr = core.session_manager.lock().await; + mgr.active_session() + .filter(|s| { + crate::core::event::is_running_status(s.status) + }) + .or_else(|| { + mgr.list() + .into_iter() + .find(|s| { + crate::core::event::is_running_status(s.status) + }) + }) + }; + if let Some(s) = target { + let _ = core.close_session_safely(app, &s.id).await; } }); } diff --git a/apps/desktop/src-tauri/src/system/tray.rs b/apps/desktop/src-tauri/src/system/tray.rs index e42ed97..8eb451f 100644 --- a/apps/desktop/src-tauri/src/system/tray.rs +++ b/apps/desktop/src-tauri/src/system/tray.rs @@ -46,10 +46,18 @@ pub fn setup_tray(app: &tauri::AppHandle) -> Result<(), Box - Approval required + High-risk command {risk} +

+ Cursor may already be running this command. Acknowledge, or stop the + Friday-owned session. +

{command && (
             {command}
           
)}
-
diff --git a/apps/desktop/src/lib/tauri.ts b/apps/desktop/src/lib/tauri.ts index 2ab62e2..7334960 100644 --- a/apps/desktop/src/lib/tauri.ts +++ b/apps/desktop/src/lib/tauri.ts @@ -113,6 +113,16 @@ export async function getSettings(): Promise { return invoke("get_settings"); } +export interface CursorCliProbe { + found: boolean; + path: string; + error?: string; +} + +export async function probeCursorCli(): Promise { + return invoke("probe_cursor_cli"); +} + export async function saveSettings( settings: FridaySettings, ): Promise { diff --git a/apps/desktop/src/state/useSettingsStore.ts b/apps/desktop/src/state/useSettingsStore.ts index 39b2946..fcb7bb2 100644 --- a/apps/desktop/src/state/useSettingsStore.ts +++ b/apps/desktop/src/state/useSettingsStore.ts @@ -30,7 +30,13 @@ const defaultSettings: FridaySettings = { defaultMode: "headless", defaultOutputFormat: "stream-json", argTemplates: { - headlessStream: ["--print", "--output-format", "stream-json"], + headlessStream: [ + "--print", + "--output-format", + "{outputFormat}", + "--stream-partial-output", + "{prompt}", + ], }, terminalCols: 120, terminalRows: 30, diff --git a/apps/desktop/src/windows/command-center/SettingsPage.tsx b/apps/desktop/src/windows/command-center/SettingsPage.tsx index 080e784..090d1d7 100644 --- a/apps/desktop/src/windows/command-center/SettingsPage.tsx +++ b/apps/desktop/src/windows/command-center/SettingsPage.tsx @@ -19,7 +19,19 @@ import { Switch } from "@/components/ui/switch"; import { Spinner } from "@/components/ui/spinner"; import { toast } from "sonner"; import { invokeErrorMessage } from "@/lib/invokeError"; -import { saveCursorApiKey, clearCursorApiKey, clearLocalData, getLocalDataPath, saveSttApiKey, clearSttApiKey, getMobileBridgeSettings, updateMobileBridgeSettings, regenerateMobileBridgeToken } from "@/lib/tauri"; +import { + saveCursorApiKey, + clearCursorApiKey, + clearLocalData, + getLocalDataPath, + saveSttApiKey, + clearSttApiKey, + getMobileBridgeSettings, + updateMobileBridgeSettings, + regenerateMobileBridgeToken, + probeCursorCli, + type CursorCliProbe, +} from "@/lib/tauri"; import { cn } from "@/lib/utils"; import { UX } from "@/lib/ux"; import { useSettingsStore } from "@/state/useSettingsStore"; @@ -397,6 +409,52 @@ function formatArgTemplates(templates: string[]): string { return JSON.stringify(templates); } +function CursorCliProbeRow() { + const [probe, setProbe] = useState(null); + const [busy, setBusy] = useState(false); + + const run = async () => { + setBusy(true); + try { + const result = await probeCursorCli(); + setProbe(result); + if (result.found) { + toast.success(`Found Cursor CLI: ${result.path}`); + } else { + toast.error(result.error ?? "cursor-agent not found"); + } + } catch (e) { + toast.error(invokeErrorMessage(e)); + } finally { + setBusy(false); + } + }; + + useEffect(() => { + void run(); + }, []); + + return ( +
+
+ + {probe && ( + + {probe.found ? `OK · ${probe.path}` : (probe.error ?? "Not found")} + + )} +
+ + Local Agent mode runs cursor-agent --print --output-format stream-json. + Install the Cursor CLI and ensure it is on PATH, or set an absolute path below. + +
+ ); +} + function parseArgTemplates(raw: string): string[] { const trimmed = raw.trim(); if (!trimmed) return []; @@ -504,6 +562,35 @@ export function SettingsPage() { Minimizes movement and uses color-only state cues (matches system preference when enabled). +
+ + update({ + ...settings, + pet: { ...settings.pet, patrolEnabled: v }, + }) + } + /> + +
+
+ + update({ + ...settings, + behavior: { + ...settings.behavior, + showBubbleOnStatusChange: v, + }, + }) + } + /> + +
+ (null); + const [cliProbe, setCliProbe] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); @@ -36,6 +39,15 @@ export function OnboardingWindow() { void getSettings() .then(setSettings) .catch(() => setSettings(defaultOnboardingSettings)); + void probeCursorCli() + .then(setCliProbe) + .catch(() => + setCliProbe({ + found: false, + path: "cursor-agent", + error: "Could not probe Cursor CLI", + }), + ); }, [ready]); const complete = async (skipKey: boolean) => { @@ -84,14 +96,44 @@ export function OnboardingWindow() {

Friday is your desktop companion for Cursor agents. Paste your{" "} Cursor API key{" "} - from the Cursor dashboard to enable Cloud Agent and related features. - Voice transcription uses a separate optional OpenAI key in Settings. + from the Cursor dashboard to enable Cloud Agent. Local CLI uses{" "} + cursor-agent on your PATH + (no API key required).

+ {cliProbe && ( +
+ {cliProbe.found ? ( + + ) : ( + + )} +
+

+ {cliProbe.found + ? "Cursor CLI detected" + : "Cursor CLI not found"} +

+

+ {cliProbe.found + ? cliProbe.path + : (cliProbe.error ?? + "Install Cursor Agent CLI and ensure cursor-agent is on PATH, or set the path in Settings.")} +

+
+
+ )} + - Cursor API key + Cursor API key (optional) cursor.com/dashboard {" "} - → Integrations → API Keys. Do not paste an OpenAI{" "} - sk- key here. Stored in your OS - credential manager only. + → Integrations → API Keys. Needed for Cloud Agent only. Stored in + your OS credential manager. @@ -134,7 +175,9 @@ export function OnboardingWindow() { Save key & show pet
diff --git a/apps/desktop/src/windows/pet/PetContextMenu.tsx b/apps/desktop/src/windows/pet/PetContextMenu.tsx index 72bcb3e..5812dd7 100644 --- a/apps/desktop/src/windows/pet/PetContextMenu.tsx +++ b/apps/desktop/src/windows/pet/PetContextMenu.tsx @@ -17,7 +17,11 @@ const MENU_ITEMS: { }[] = [ { label: "Quick chat", icon: MessageSquare, action: () => void openQuickBubble() }, { label: "Open panel", icon: PanelTop, action: () => void openPanel() }, - { label: "Hide pet", icon: EyeOff, action: () => void getCurrentWindow().hide() }, + { + label: "Hide pet", + icon: EyeOff, + action: () => void getCurrentWindow().hide(), + }, ]; export function PetContextMenu({ diff --git a/apps/desktop/src/windows/quick-bubble/QuickComposer.tsx b/apps/desktop/src/windows/quick-bubble/QuickComposer.tsx index b5084c3..f04ff66 100644 --- a/apps/desktop/src/windows/quick-bubble/QuickComposer.tsx +++ b/apps/desktop/src/windows/quick-bubble/QuickComposer.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import { ArrowUp, ChevronDown, Square } from "lucide-react"; import type { AgentMode } from "@friday/agent-core"; diff --git a/docs/ADAPTER_SPEC.md b/docs/ADAPTER_SPEC.md index 43595ac..3308554 100644 --- a/docs/ADAPTER_SPEC.md +++ b/docs/ADAPTER_SPEC.md @@ -2,12 +2,12 @@ ## Adapter IDs -| ID | Session Type | v1 Status | +| ID | Session Type | Status | |---|---|---| -| `external-cursor-observer` | external_cli | Live | -| `cursor-cli-local` | friday_owned_cli | Live | +| `external-cursor-observer` | external_cli | Live (observe / attach by PID) | +| `cursor-cli-local` | friday_owned_cli | Live (PTY + stream-json) | | `cursor-sdk-local` | cursor_sdk_local | Stub | -| `cursor-cloud-agent` | cursor_cloud | Stub | +| `cursor-cloud-agent` | cursor_cloud | Live (`api.cursor.com`) | ## AgentAdapter (Rust trait / TS contract) @@ -22,11 +22,19 @@ onEvent(callback): void ## CursorCliLauncher ``` -resolve_executable → validate_repo_path → build_args (from settings.argTemplates) -→ inject_safe_env → start_pty | pipe_fallback → register_session → stream_events +resolve_executable → build_args (from settings.argTemplates) +→ start_pty | pipe_fallback → register_session → stream_events ``` -CLI flags must **not** be hardcoded — use `FridaySettings.cursor.argTemplates.headlessStream`. +Default headless template: + +``` +--print --output-format {outputFormat} --stream-partial-output {prompt} +``` + +If templates omit `{prompt}`, Friday appends the prompt so the task is never dropped. + +Stop behavior (PTY): Ctrl+C → 3s grace → kill. Child PID is recorded and excluded from external discovery. ## Capabilities @@ -36,4 +44,8 @@ See `packages/agent-core/src/capabilities.ts` for `AgentCapabilities` matrix per All adapters map output to `AgentEvent` in `packages/agent-core/src/events.ts`. -UI and storage only consume normalized events. +`cursor-cli-local` parses Cursor `stream-json` NDJSON (`system`, `assistant`, `tool_call`, `result`) into normalized events. UI and storage only consume those events. + +## Approvals + +High-risk shell commands observed from the CLI stream surface an approval card. This is **observe-and-stop**, not a Cursor permission gate: Acknowledge continues watching; Reject stops the Friday-owned session. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 4f5e9ef..441ae31 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -33,7 +33,7 @@ services/sdk-worker/ v2 Node worker stub | external-cursor-observer | external_cli | observe | | cursor-cli-local | friday_owned_cli | full | | cursor-sdk-local | cursor_sdk_local | stub | -| cursor-cloud-agent | cursor_cloud | stub | +| cursor-cloud-agent | cursor_cloud | full (HTTP + SSE) | ## Backend Modules (Rust) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 1a180f7..db3e3b2 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -22,7 +22,7 @@ ## v2 — Cursor Cloud Agent + Immersive Pet (experimental) - `@friday/sdk-worker` Node process + IPC -- `cursor-cloud-agent` adapter live +- `cursor-cloud-agent` adapter **live** (`api.cursor.com`) - API key onboarding (keyring) - PR / artifact events in Panel - Cloud session history diff --git a/docs/SECURITY.md b/docs/SECURITY.md index d672e2a..9674f4f 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -21,13 +21,13 @@ Classifier: `risk_classifier.rs` + patterns in `command_policy.rs`. ## Approval Layer -When risk is high (or medium if setting enabled): +When a high-risk (or medium, if enabled) shell command is observed in the Cursor CLI stream: -1. Emit `approval.required` event -2. Pause session until user approves/rejects -3. UI shows Approve / Reject in Chat + Quick Bubble +1. Emit `approval.required` (non-blocking — the CLI may already be running the command) +2. Mark session `waiting_permission` in the UI +3. **Acknowledge** clears the approval card; **Stop session** kills the Friday-owned agent -MVP uses button confirmation — no MFA. +This is observe-and-stop, not a Cursor permission gate. MVP uses button confirmation — no MFA. ## Secret Redaction @@ -68,15 +68,15 @@ Configured in `bundle/windows/nsis-hooks.nsh`. ## Process Kill Switch -Stop flow: +Stop flow (PTY / owned CLI): -1. Send graceful stop signal -2. Wait 3 seconds -3. Force kill process -4. Mark session `cancelled` +1. Send Ctrl+C (SIGINT) into the PTY +2. Wait up to 3 seconds +3. Force kill if still alive +4. Mark session `stopped` 5. Save logs -All child PIDs tracked in process registry to prevent orphans. +Child PIDs are recorded on the session and excluded from external `cursor-agent` discovery to avoid duplicate observe sessions. ## Settings diff --git a/package-lock.json b/package-lock.json index 5aee0cd..cf6c4bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,7 +101,6 @@ "integrity": "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -112,7 +111,6 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-52.0.49.tgz", "integrity": "sha512-ge3gUnuyGEePWWKzPY7TQ7FsvtFTdmsdYDHeBVUjMr9KIoQig/gf8A03oH26p3UtTL6sUJcyOIg9vwIHGNPSUw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "0.22.28", @@ -254,7 +252,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -267,7 +264,6 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.3.tgz", "integrity": "sha512-0TUhgmlouRNf6yuDIIAdbQl0g1VsONgCMsLs7Et64hjj5VLMCA7np+4dMrZvGZ3wRNqzgeyT9oWJsUm49AcwSQ==", "license": "MIT", - "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native/assets-registry": "0.76.3", @@ -410,7 +406,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -814,6 +809,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" @@ -830,6 +826,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -845,6 +842,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -860,6 +858,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" @@ -876,6 +875,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", @@ -893,6 +893,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" @@ -993,6 +994,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" }, @@ -1113,6 +1115,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1299,6 +1302,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1364,6 +1368,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1410,6 +1415,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -1478,6 +1484,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -1494,6 +1501,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1509,6 +1517,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -1525,6 +1534,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1540,6 +1550,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7" @@ -1556,6 +1567,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1635,6 +1647,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1680,6 +1693,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1695,6 +1709,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -1727,6 +1742,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", @@ -1745,6 +1761,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -1777,6 +1794,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -1841,6 +1859,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7" @@ -1936,6 +1955,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -2061,6 +2081,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -2077,6 +2098,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -2171,6 +2193,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -2186,6 +2209,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -2220,6 +2244,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, @@ -2235,6 +2260,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -2267,6 +2293,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" @@ -2386,6 +2413,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -3389,6 +3417,7 @@ "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-56.0.2.tgz", "integrity": "sha512-ANl4kPdbe0/HQYWkDEN79S6bQhI+i/ZCnPxuC853pPsB4svhINC7Ku9lmGOKPsUUWWnrHg1spkDGQBZ4sD6JxQ==", "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.1.2" }, @@ -3410,6 +3439,7 @@ "resolved": "https://registry.npmjs.org/@expo/dom-webview/-/dom-webview-56.0.5.tgz", "integrity": "sha512-UIEJxkLg6cHqofKrpWpkn9E6ApxVRtCgZhZkARPr9VV7rBVloJgeroTHs31YgU/JpbI5lLQOnfOlGo54W6C2Ew==", "license": "MIT", + "peer": true, "peerDependencies": { "expo": "*", "react": "*", @@ -3433,7 +3463,8 @@ "version": "0.0.9", "resolved": "https://registry.npmjs.org/@expo/expo-modules-macros-plugin/-/expo-modules-macros-plugin-0.0.9.tgz", "integrity": "sha512-odai6D7ng/gA7At8ukFcWcauNEeDdyVqzVPbQxDkyU2NTJ4kgphA4I5iigS5C4LXFicSIzEt2nzdlLM8sjsTdA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@expo/fingerprint": { "version": "0.11.11", @@ -3548,6 +3579,7 @@ "resolved": "https://registry.npmjs.org/@expo/inline-modules/-/inline-modules-0.0.10.tgz", "integrity": "sha512-DKEfq877UTAmL/gOT5aFhlLNDg/EVmTSca7JQMKDGR6mjFSAcrmQf4GJNsi6ztiaqj6cTnIfoSz0hAYdnr6RJQ==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config-plugins": "~56.0.8" } @@ -3557,6 +3589,7 @@ "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config-types": "^56.0.5", "@expo/json-file": "~10.2.0", @@ -3577,13 +3610,15 @@ "version": "56.0.5", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@expo/inline-modules/node_modules/@expo/json-file": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" @@ -3594,6 +3629,7 @@ "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.7.0.tgz", "integrity": "sha512-vrpryU1GoqSIRNqRB2D3IjXDmzNYfiQpEF6AH/xknlD7eiYmEDt3mb26V7cLcedcPG8PY/1xWHdBXVQJfEAh6Q==", "license": "MIT", + "peer": true, "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -3605,6 +3641,7 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" } @@ -3614,6 +3651,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", + "peer": true, "engines": { "node": "18 || 20 || >=22" } @@ -3623,6 +3661,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, @@ -3635,6 +3674,7 @@ "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -3644,6 +3684,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", @@ -3661,6 +3702,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": "20 || >=22" } @@ -3670,6 +3712,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "brace-expansion": "^5.0.5" }, @@ -3685,6 +3728,7 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -3701,6 +3745,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -3713,6 +3758,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.0" } @@ -3741,6 +3787,7 @@ "resolved": "https://registry.npmjs.org/@expo/local-build-cache-provider/-/local-build-cache-provider-56.0.8.tgz", "integrity": "sha512-UsuXwpNi57MNhzZ3be4XThc8xW6nzk3Wu37s1+2qcfZGeJcMLKDFfwO6n8YXeIiGlCsOi0Ee1rsTdgjrKt/YJQ==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config": "~56.0.9", "chalk": "^4.1.2" @@ -3751,6 +3798,7 @@ "resolved": "https://registry.npmjs.org/@expo/config/-/config-56.0.9.tgz", "integrity": "sha512-/lqFeWGSrhpKJVP8tTN8LjuoIe8u8q2w7FzBL0C+wHgl+WM8l1qUIEYWy/sMvsG/NbpUIUsDHJRhQvOkU58eIw==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config-plugins": "~56.0.8", "@expo/config-types": "^56.0.5", @@ -3769,6 +3817,7 @@ "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config-types": "^56.0.5", "@expo/json-file": "~10.2.0", @@ -3789,13 +3838,15 @@ "version": "56.0.5", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@expo/local-build-cache-provider/node_modules/@expo/json-file": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" @@ -3806,6 +3857,7 @@ "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.7.0.tgz", "integrity": "sha512-vrpryU1GoqSIRNqRB2D3IjXDmzNYfiQpEF6AH/xknlD7eiYmEDt3mb26V7cLcedcPG8PY/1xWHdBXVQJfEAh6Q==", "license": "MIT", + "peer": true, "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -3817,6 +3869,7 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" } @@ -3826,6 +3879,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", + "peer": true, "engines": { "node": "18 || 20 || >=22" } @@ -3835,6 +3889,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, @@ -3847,6 +3902,7 @@ "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -3856,6 +3912,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", @@ -3873,6 +3930,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": "20 || >=22" } @@ -3882,6 +3940,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "brace-expansion": "^5.0.5" }, @@ -3897,6 +3956,7 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -3913,6 +3973,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -3925,6 +3986,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.0" } @@ -3934,6 +3996,7 @@ "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-56.0.12.tgz", "integrity": "sha512-budE6AGmJbpOJfGSOz+JVP3+FevElT82IEIg+ukQ4gZpW/dGO7QX1unFjanKdSaYgudBwJ4FCFGMwWhW/1tXVQ==", "license": "MIT", + "peer": true, "dependencies": { "@expo/dom-webview": "^56.0.5", "anser": "^1.4.9", @@ -3951,6 +4014,7 @@ "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-56.0.0.tgz", "integrity": "sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==", "license": "MIT", + "peer": true, "dependencies": { "metro": "0.84.4", "metro-babel-transformer": "0.84.4", @@ -4379,6 +4443,7 @@ "resolved": "https://registry.npmjs.org/@expo/metro-file-map/-/metro-file-map-56.0.3.tgz", "integrity": "sha512-5OGW3z8LgEYgMJOR7F3pC8llFLkb1fVqwAewbCl6S4Vkha8AFQMwOjT+9Wbka+V4rmpljpGqOnMhF4xZbD961w==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.3.4", "fb-watchman": "^2.0.2", @@ -4393,6 +4458,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -4405,19 +4471,22 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@expo/metro/node_modules/hermes-estree": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@expo/metro/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -4427,6 +4496,7 @@ "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz", "integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", @@ -4480,6 +4550,7 @@ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz", "integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==", "license": "MIT", + "peer": true, "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", @@ -4499,6 +4570,7 @@ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -4513,6 +4585,7 @@ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz", "integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -4526,6 +4599,7 @@ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", @@ -4546,6 +4620,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -4562,6 +4637,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -4571,6 +4647,7 @@ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -4583,6 +4660,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.3.0" }, @@ -4724,6 +4802,7 @@ "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.3.tgz", "integrity": "sha512-KyLeOn/zzQSvuPpV5YhB/FPKnpQytno4luN918bGdPDssLBoS3N/0UbC3W0rJAn9kSFu+XpfR81eABRVsSdfgQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.25.2", @@ -4760,7 +4839,8 @@ "version": "56.0.1", "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-56.0.1.tgz", "integrity": "sha512-CZ/+mYbQmWeOnkCGlWy9K+lFxbJSMFY7+TqBZcKzBSTU5Q7IGRvn/sOG3TdNjIdLPmbA8xe7R/c3UUQ28R9i9w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@expo/sdk-runtime-versions": { "version": "1.0.0", @@ -5252,7 +5332,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { "version": "3.0.4", @@ -5265,7 +5346,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { "version": "3.0.4", @@ -5278,7 +5360,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { "version": "3.0.4", @@ -5291,7 +5374,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { "version": "3.0.4", @@ -5304,7 +5388,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { "version": "3.0.4", @@ -5317,7 +5402,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -7123,6 +7209,7 @@ "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.85.3.tgz", "integrity": "sha512-/jRAaT9boiCttIcEwS02WPwYkUihqsjSaK/TMtHz05vT6uMgac9PaQt5kzBQLIABv5aEIa5gtrMmKVz49MjkjQ==", "license": "MIT", + "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "debug": "^4.4.0", @@ -7217,6 +7304,7 @@ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.85.3.tgz", "integrity": "sha512-dsCjI//OIPEUJMyNHp4l7zNLVjCx7bcaRUceOCkU+IB17hkbtbGWvi7HjGFSzy7FJGmS/MOlcfpb72xXiy1Oig==", "license": "MIT", + "peer": true, "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" @@ -8219,7 +8307,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } @@ -8246,7 +8333,6 @@ "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -8257,7 +8343,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -8287,7 +8372,8 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/@urql/core": { "version": "5.2.0", @@ -8385,6 +8471,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 14" } @@ -8670,6 +8757,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8", "core-js-compat": "^3.48.0" @@ -9065,7 +9153,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -10079,7 +10166,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/dnssd-advertise/-/dnssd-advertise-1.1.4.tgz", "integrity": "sha512-AmGyK9WpNf06WeP5TjHZq/wNzP76OuEeaiTlKr9E/EEelYLczywUKoqRz+DPRq/ErssjT4lU+/W7wzJW+7K/ZA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dotenv": { "version": "16.4.7", @@ -10337,6 +10425,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10459,6 +10548,7 @@ "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-56.0.15.tgz", "integrity": "sha512-BHGi2IAOPQTcOelkUdcz1WIknfCTRjkcpYHX1azjMwgYenrVC+J5qcqJGaC8eUOWLCRtkRJWGnmFQRYtLU1nUQ==", "license": "MIT", + "peer": true, "dependencies": { "@expo/image-utils": "^0.10.1", "expo-constants": "~56.0.16" @@ -10474,6 +10564,7 @@ "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.3.0.tgz", "integrity": "sha512-9HnnIbzwTTdbwSjNLXTk0fPm9ZwMJ7c1/31tsni8HZ8Q62KzYCyspahH+V365vg5J6lr001DzNwBxVWSaYCQLg==", "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", @@ -10488,6 +10579,7 @@ "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", "license": "MIT", + "peer": true, "dependencies": { "@expo/require-utils": "^56.1.3", "@expo/spawn-async": "^1.8.0", @@ -10503,6 +10595,7 @@ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.16.tgz", "integrity": "sha512-6tsiN+gmTUPp/atyA+uY9Tg8VOdXdmb4s/3TVGolfn6A/oCAraw1pcPZX5XllyD+xUguxB6eBSFAT8494hZVMA==", "license": "MIT", + "peer": true, "dependencies": { "@expo/env": "~2.3.0" }, @@ -10516,6 +10609,7 @@ "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -10525,6 +10619,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -10551,6 +10646,7 @@ "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-56.0.7.tgz", "integrity": "sha512-dcKzo8ShPloM7jgfnMcJStgQebhP8owVjCkNI/aX6NMFV1CYB8bxKGMdnzJ3mXk5nfaiW+F/lSKr2UIJ02WAUA==", "license": "MIT", + "peer": true, "peerDependencies": { "expo": "*", "react-native": "*" @@ -10576,6 +10672,7 @@ "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-56.0.3.tgz", "integrity": "sha512-CLMJXtEiMKknD3Rpm8CRwE6ZJUzu2yCEmRk1sgfHAJ1zIbuEWY3dpPDubtsnuzWm+2k6Sru+yaFbYsvPWmTiBA==", "license": "MIT", + "peer": true, "peerDependencies": { "expo": "*", "react": "*" @@ -10659,6 +10756,7 @@ "resolved": "https://registry.npmjs.org/expo-modules-jsi/-/expo-modules-jsi-56.0.7.tgz", "integrity": "sha512-iBAj4Xeh/8HT201VVxFlmf+VBfmtQV1ZUoJdLQQENm0+j9gnD2QswZLJyNo3CmNNXl46esJeLR5lpGpYZts/zA==", "license": "MIT", + "peer": true, "peerDependencies": { "react-native": "*" } @@ -10678,6 +10776,7 @@ "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.12.tgz", "integrity": "sha512-Ya/13E1yDx1oAuPw5MDmqzIGyzwSs7KSr1EjgSObOF0VO0GD9jqJjvjOiwurjScLUfxcGZQgq23UzMlBVHwdvA==", "license": "MIT", + "peer": true, "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/config": "~56.0.9", @@ -10759,6 +10858,7 @@ "resolved": "https://registry.npmjs.org/@expo/config/-/config-56.0.9.tgz", "integrity": "sha512-/lqFeWGSrhpKJVP8tTN8LjuoIe8u8q2w7FzBL0C+wHgl+WM8l1qUIEYWy/sMvsG/NbpUIUsDHJRhQvOkU58eIw==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config-plugins": "~56.0.8", "@expo/config-types": "^56.0.5", @@ -10777,6 +10877,7 @@ "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.8.tgz", "integrity": "sha512-phTuyBhgVLfqUHMjQkAfRtbyoY6yTxoKja1awtpVnEkoJDxPJuXx1KX5uvq1eZtt4bJQ08OBJ6P95INqRSHpRg==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config-types": "^56.0.5", "@expo/json-file": "~10.2.0", @@ -10797,13 +10898,15 @@ "version": "56.0.5", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-56.0.5.tgz", "integrity": "sha512-GsAHO/MwW9ZRdgnmyfRXqVGLCP/zejD6rWnp5OROp8mBGRObKm4HfrjlUyT1skjMwCj1OrURx9ZfIc6yeBAkIA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/expo/node_modules/@expo/env": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.3.0.tgz", "integrity": "sha512-9HnnIbzwTTdbwSjNLXTk0fPm9ZwMJ7c1/31tsni8HZ8Q62KzYCyspahH+V365vg5J6lr001DzNwBxVWSaYCQLg==", "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", @@ -10818,6 +10921,7 @@ "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.19.3.tgz", "integrity": "sha512-w9Au2IVrtc0Ct+WRa05DVHGNHXYq6VyPZWuFP+5x055OeZ5q6k5Yg+aJ1gfShmjdOhDftRcsvmWmTdTZlWaTZw==", "license": "MIT", + "peer": true, "dependencies": { "@expo/env": "^2.3.0", "@expo/spawn-async": "^1.8.0", @@ -10840,6 +10944,7 @@ "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.1.tgz", "integrity": "sha512-YDeefvmYdihS7Wp3ESDUVnOgOSWmj2Cczm9lVNDdm4MqQLdAKm/LPYg83HtFQPfefRlAxyHrQR/O9kIXN9C1Wg==", "license": "MIT", + "peer": true, "dependencies": { "@expo/require-utils": "^56.1.3", "@expo/spawn-async": "^1.8.0", @@ -10855,6 +10960,7 @@ "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.2.0.tgz", "integrity": "sha512-S6XzKe3R9GQeHiUPXc3xJjOv2VJhOEwFYf7xdC2z2cUqt3kZJ9mSO877sNQloVdnW/SUCtPY3bexlM7nwq+CAQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" @@ -10865,6 +10971,7 @@ "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-56.0.13.tgz", "integrity": "sha512-OPyNYiex/6Ms8zT2POdIZsLhcAZYk7O+yJvpz5uG/4QRA7aiESfCy1I+0YHewMlR4P1YQeyxIrfTurs6m9xfZA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", @@ -10905,6 +11012,7 @@ "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.7.0.tgz", "integrity": "sha512-vrpryU1GoqSIRNqRB2D3IjXDmzNYfiQpEF6AH/xknlD7eiYmEDt3mb26V7cLcedcPG8PY/1xWHdBXVQJfEAh6Q==", "license": "MIT", + "peer": true, "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -10916,6 +11024,7 @@ "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-56.0.13.tgz", "integrity": "sha512-caR1karpDasbNmM+LrcHKZrSnyEYdmxm7kedq+WjiuZg+9XAW5sbEjojo2i9Dq6cfbDJPyr7I0yEprLabnvmpA==", "license": "MIT", + "peer": true, "dependencies": { "@expo/config": "~56.0.9", "@expo/config-plugins": "~56.0.8", @@ -10934,6 +11043,7 @@ "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-56.0.12.tgz", "integrity": "sha512-RqKV2/Z8BH/z8l0ngSpG6//5xxJPaF5dTQvSfPQ0nrvCjikGMeIvyj3B9BeLnmZZhxb3gBtXqrj3irAoiIp2aQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.3.4" }, @@ -10968,6 +11078,7 @@ "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.85.3.tgz", "integrity": "sha512-Wc94zGfeFG8Njf9SHMPfYZP04kjigkOps6F1TYTvd7ZVXuGxqseCDgxc50LWcOhOCLypI9n3oVVqz81C3p44ZA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@react-native/codegen": "0.85.3" @@ -10981,6 +11092,7 @@ "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.3.tgz", "integrity": "sha512-/JkS1lGLyzBWP1FbgDwaqEf7qShIC6pUC1M0a/YMAd/v4iqR24MRkQWe7jkYvcBQ2LpEhs5NGE9InhxSv21zCA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.29.0", @@ -11002,6 +11114,7 @@ "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.85.3.tgz", "integrity": "sha512-uAu7rM5o/Np1zgp6fi5zM1sP1aB8DcS7DdOLcj/TkSutOAjkMqqd2lWt1/+3S7qXexRHVK5XcP+o3VXo4L/V0A==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -11011,6 +11124,7 @@ "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.85.3.tgz", "integrity": "sha512-JYzBiT4A8w+KQt+dOD5v+ti+tDrGoPnsSTuApq3Ls4RB5sfWbDlYMyz3dbc8qBIHz9tv0sQ5+eOu6Xwqzr5AQA==", "license": "MIT", + "peer": true, "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.85.3", @@ -11034,6 +11148,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.3.0" }, @@ -11054,13 +11169,15 @@ "version": "0.85.3", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.85.3.tgz", "integrity": "sha512-hj0PScZEhIbcOvQV5yMKX3ha4XEIOy/SVE1Rrpp0beW0dpNLOgSC7KDxGewmDnIHK9YdQUXGY9eMEfShUMIaZw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/expo/node_modules/@xmldom/xmldom": { "version": "0.8.13", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" } @@ -11069,13 +11186,15 @@ "version": "0.21.2", "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/expo/node_modules/babel-plugin-syntax-hermes-parser": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz", "integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-parser": "0.33.3" } @@ -11085,6 +11204,7 @@ "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-56.0.13.tgz", "integrity": "sha512-+CxxAQrN95N+/dF4AUJXNxEh5cEv4yhxb4CM5ijdc2OeIIw+hxzYh2OM1X7QHIm6hkT66H4vJCTT636yjJ8MnQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/generator": "^7.20.5", "@babel/helper-module-imports": "^7.25.9", @@ -11152,6 +11272,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", + "peer": true, "engines": { "node": "18 || 20 || >=22" } @@ -11161,6 +11282,7 @@ "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", "license": "MIT", + "peer": true, "dependencies": { "stream-buffers": "2.2.x" } @@ -11170,6 +11292,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, @@ -11182,6 +11305,7 @@ "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.3.0.tgz", "integrity": "sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -11195,6 +11319,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10" } @@ -11218,6 +11343,7 @@ "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-56.0.14.tgz", "integrity": "sha512-9ugtZkheNPYDkW4DZopY1rH2BCbUICaafUEPxRgbLDR5UNRF5K3cdHMIMEt8pxZPq2+eX4wCm+6pbSvdY/DPHg==", "license": "MIT", + "peer": true, "dependencies": { "@expo/require-utils": "^56.1.3", "@expo/spawn-async": "^1.8.0", @@ -11233,6 +11359,7 @@ "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.13.tgz", "integrity": "sha512-3Hgpi9Q1O0XqoesQtgFY7qhfDsNA3bJtdCJotEqdE42+N8Zv/LJACbNgIyFN/XrnMDzfF5rozh0vNWaRT0/eXQ==", "license": "MIT", + "peer": true, "dependencies": { "@expo/expo-modules-macros-plugin": "~0.0.9", "expo-modules-jsi": "~56.0.7", @@ -11254,6 +11381,7 @@ "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -11263,6 +11391,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", @@ -11279,13 +11408,15 @@ "version": "0.33.3", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/expo/node_modules/hermes-parser": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.33.3" } @@ -11295,6 +11426,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": "20 || >=22" } @@ -11304,6 +11436,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "brace-expansion": "^5.0.5" }, @@ -11319,6 +11452,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "license": "MIT", + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -11331,6 +11465,7 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -11357,6 +11492,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -11369,6 +11505,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -11390,6 +11527,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.0" } @@ -11436,6 +11574,7 @@ "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", "license": "(MIT OR Apache-2.0)", + "peer": true, "bin": { "dotslash": "bin/dotslash" }, @@ -11512,7 +11651,8 @@ "version": "0.4.10", "resolved": "https://registry.npmjs.org/fetch-nodeshim/-/fetch-nodeshim-0.4.10.tgz", "integrity": "sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fetch-retry": { "version": "4.1.1", @@ -12013,7 +12153,8 @@ "version": "250829098.0.10", "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.10.tgz", "integrity": "sha512-TcRlZ0/TlyfJqquRFAWoyElVNnkdYRi/sEp4/Qy8/GYxjg8j2cS9D4MjuaQ+qimkmLN7AmO+44IznRf06mAr0w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/hermes-estree": { "version": "0.23.1", @@ -12082,6 +12223,7 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -12881,6 +13023,7 @@ "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.2.1.tgz", "integrity": "sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==", "license": "MIT", + "peer": true, "bin": { "lan-network": "dist/lan-network-cli.js" } @@ -12960,6 +13103,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -12980,6 +13124,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13000,6 +13145,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13020,6 +13166,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13040,6 +13187,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13060,6 +13208,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13080,6 +13229,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13100,6 +13250,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13120,6 +13271,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13140,6 +13292,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13160,6 +13313,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13467,6 +13621,7 @@ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.4.tgz", "integrity": "sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", @@ -13482,13 +13637,15 @@ "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -13498,6 +13655,7 @@ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.4.tgz", "integrity": "sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==", "license": "MIT", + "peer": true, "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", @@ -13513,6 +13671,7 @@ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.4.tgz", "integrity": "sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -13525,6 +13684,7 @@ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -13598,6 +13758,7 @@ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.4.tgz", "integrity": "sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", @@ -13618,6 +13779,7 @@ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.4.tgz", "integrity": "sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" @@ -13631,6 +13793,7 @@ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.4.tgz", "integrity": "sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -13697,6 +13860,7 @@ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.4.tgz", "integrity": "sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", @@ -13717,6 +13881,7 @@ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", @@ -13737,6 +13902,7 @@ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -13749,6 +13915,7 @@ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.4.tgz", "integrity": "sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", @@ -13766,6 +13933,7 @@ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.4.tgz", "integrity": "sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", @@ -13790,6 +13958,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -13802,19 +13971,22 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/metro-transform-worker/node_modules/hermes-estree": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/metro-transform-worker/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -13824,6 +13996,7 @@ "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz", "integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", @@ -13877,6 +14050,7 @@ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz", "integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==", "license": "MIT", + "peer": true, "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", @@ -13896,6 +14070,7 @@ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -13910,6 +14085,7 @@ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz", "integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -13923,6 +14099,7 @@ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", @@ -13943,6 +14120,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -13959,6 +14137,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -13968,6 +14147,7 @@ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -13980,6 +14160,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.3.0" }, @@ -14447,6 +14628,7 @@ "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.0.2.tgz", "integrity": "sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ==", "license": "MIT", + "peer": true, "optionalDependencies": { "msgpackr-extract": "^3.0.4" } @@ -14458,6 +14640,7 @@ "hasInstallScript": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, @@ -14477,7 +14660,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/multitars/-/multitars-1.0.0.tgz", "integrity": "sha512-H/J4fMLedtudftaYMOg7ajzLYgT3/rwbWVJbqr/iUgB8DQztn38ys5HOqI1CzSxx8QhXXwOOnnBvd4v3jG5+Mg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/mz": { "version": "2.7.0", @@ -14592,6 +14776,7 @@ "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "detect-libc": "^2.0.1" }, @@ -15496,7 +15681,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -15537,7 +15721,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -15556,6 +15739,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.85.3.tgz", "integrity": "sha512-HN/fGC+3nZVcDNcw7gfbM/DuqZAvI9Mz+/SxuhODaua4JY0BPzhfTzWXRyTR4mRgMHmShTPpH2PYMTxvZrsdZA==", "license": "MIT", + "peer": true, "dependencies": { "@react-native/assets-registry": "0.85.3", "@react-native/codegen": "0.85.3", @@ -15615,6 +15799,7 @@ "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.85.3.tgz", "integrity": "sha512-u9ZiYP23vA2IFtdFQFmetzSmk6SM0xgKIoiOsr1hXNHjHaLhOm+/Ph1ud57wX6+Dbwdzx8coJgnzSKL3W21PCg==", "license": "MIT", + "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -15624,6 +15809,7 @@ "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.3.tgz", "integrity": "sha512-/JkS1lGLyzBWP1FbgDwaqEf7qShIC6pUC1M0a/YMAd/v4iqR24MRkQWe7jkYvcBQ2LpEhs5NGE9InhxSv21zCA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.29.0", @@ -15645,6 +15831,7 @@ "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.85.3.tgz", "integrity": "sha512-fs85dmbIqNmtzEixDb0g+q6R3Vt4H9eAt8/inIZdDKfjN76+sUJA2r1nxODQ76bU23MrIbz8sI7KFBPaWk/zQw==", "license": "MIT", + "peer": true, "dependencies": { "@react-native/dev-middleware": "0.85.3", "debug": "^4.4.0", @@ -15675,6 +15862,7 @@ "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.85.3.tgz", "integrity": "sha512-uAu7rM5o/Np1zgp6fi5zM1sP1aB8DcS7DdOLcj/TkSutOAjkMqqd2lWt1/+3S7qXexRHVK5XcP+o3VXo4L/V0A==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -15684,6 +15872,7 @@ "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.85.3.tgz", "integrity": "sha512-JYzBiT4A8w+KQt+dOD5v+ti+tDrGoPnsSTuApq3Ls4RB5sfWbDlYMyz3dbc8qBIHz9tv0sQ5+eOu6Xwqzr5AQA==", "license": "MIT", + "peer": true, "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.85.3", @@ -15707,6 +15896,7 @@ "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.85.3.tgz", "integrity": "sha512-39dY2j50Q1pntejzwt3XL7vwXtrj8jcIfHq6E+gyu3jzYxZJVvMkMutQ39vSg6zinIQOX36oQDhidXUbCXzgoA==", "license": "MIT", + "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -15716,6 +15906,7 @@ "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.3.tgz", "integrity": "sha512-U2+aMshIXf1uFn77tpBb/xhHWB9vkVrMpt7kkucAugF8hJKYTDGB587X7WwelHduK2KBfhl4giSv0rzZGoef9A==", "license": "MIT", + "peer": true, "engines": { "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" } @@ -15724,13 +15915,15 @@ "version": "0.85.3", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.85.3.tgz", "integrity": "sha512-hj0PScZEhIbcOvQV5yMKX3ha4XEIOy/SVE1Rrpp0beW0dpNLOgSC7KDxGewmDnIHK9YdQUXGY9eMEfShUMIaZw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-native/node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -15744,6 +15937,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz", "integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-parser": "0.33.3" } @@ -15753,6 +15947,7 @@ "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.3.0.tgz", "integrity": "sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -15765,19 +15960,22 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-native/node_modules/hermes-estree": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-native/node_modules/hermes-parser": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.33.3" } @@ -15787,6 +15985,7 @@ "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz", "integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", @@ -15840,6 +16039,7 @@ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz", "integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==", "license": "MIT", + "peer": true, "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", @@ -15859,6 +16059,7 @@ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -15873,6 +16074,7 @@ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz", "integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -15886,6 +16088,7 @@ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", @@ -15905,13 +16108,15 @@ "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-native/node_modules/metro/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", + "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -15921,6 +16126,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -15937,6 +16143,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "license": "MIT", + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -15949,6 +16156,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -15958,6 +16166,7 @@ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", "license": "MIT", + "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -15970,6 +16179,7 @@ "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", "license": "MIT", + "peer": true, "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -15980,6 +16190,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -15989,6 +16200,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -16001,6 +16213,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.3.0" }, @@ -17257,7 +17470,6 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -17365,7 +17577,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/toqr/-/toqr-0.1.1.tgz", "integrity": "sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tr46": { "version": "0.0.3", @@ -17699,7 +17912,6 @@ "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -17828,7 +18040,8 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/whatwg-url-minimum/-/whatwg-url-minimum-0.1.2.tgz", "integrity": "sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/whatwg-url-without-unicode": { "version": "8.0.0-3", @@ -18111,6 +18324,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/packages/agent-core/src/capabilities.ts b/packages/agent-core/src/capabilities.ts index 8481539..4d4894a 100644 --- a/packages/agent-core/src/capabilities.ts +++ b/packages/agent-core/src/capabilities.ts @@ -1,6 +1,7 @@ import type { ControlLevel } from "./sessions.js"; -export interface AgentCapabilities { canCreate: boolean; +export interface AgentCapabilities { + canCreate: boolean; canAttach: boolean; canObserve: boolean; canSendFollowUp: boolean; @@ -48,7 +49,7 @@ export const FRIDAY_OWNED_CLI_CAPABILITIES: AgentCapabilities = { export const CURSOR_CLOUD_CAPABILITIES: AgentCapabilities = { canCreate: true, - canAttach: true, + canAttach: false, canObserve: true, canSendFollowUp: true, canStop: true,