From 8b7b373a8dd65a68ad41138bab0d84eda960063d Mon Sep 17 00:00:00 2001 From: Alex Holmberg Date: Sat, 28 Mar 2026 13:06:44 +0100 Subject: [PATCH] fix: remove 68 tracked files that are also in .gitignore These files (build artifacts, db files, planning docs) were committed before their .gitignore rules existed. release-plz detects this as uncommitted changes and refuses to run. Files removed from tracking (kept on disk): - docs/superpowers/{plans,specs}/*.md (caught by docs/** rule) - tests/ag-ui-app/*/dist/ (caught by nested .gitignore) - tests/ag-ui-app/*/data/*.db* (caught by nested .gitignore) - tests/ag-ui-app/frontend/todos.json (caught by nested .gitignore) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../plans/2026-03-27-agent-output-pipeline.md | 1262 --------- .../plans/2026-03-27-npx-installer.md | 2286 ----------------- .../plans/2026-03-27-syncable-cli-skills.md | 1349 ---------- ...2026-03-27-agent-output-pipeline-design.md | 194 -- .../specs/2026-03-27-npx-installer-design.md | 403 --- .../2026-03-27-syncable-cli-skills-design.md | 247 -- tests/ag-ui-app/backend/data/smart-reply.db | Bin 40960 -> 0 bytes .../ag-ui-app/backend/data/smart-reply.db-shm | Bin 32768 -> 0 bytes .../ag-ui-app/backend/data/smart-reply.db-wal | Bin 61832 -> 0 bytes tests/ag-ui-app/backend/dist/db/index.d.ts | 12 - tests/ag-ui-app/backend/dist/db/index.js | 78 - tests/ag-ui-app/backend/dist/db/schema.d.ts | 21 - tests/ag-ui-app/backend/dist/db/schema.js | 50 - tests/ag-ui-app/backend/dist/index.d.ts | 1 - tests/ag-ui-app/backend/dist/index.js | 81 - tests/ag-ui-app/backend/dist/lib/openai.d.ts | 11 - tests/ag-ui-app/backend/dist/lib/openai.js | 59 - .../ag-ui-app/backend/dist/lib/services.d.ts | 69 - tests/ag-ui-app/backend/dist/lib/services.js | 186 -- .../backend/dist/routes/history.d.ts | 3 - .../ag-ui-app/backend/dist/routes/history.js | 58 - .../backend/dist/routes/replies.d.ts | 3 - .../ag-ui-app/backend/dist/routes/replies.js | 137 - .../dist/client/assets/agent-Bh38wHiA.js | 1 - .../dist/client/assets/index-CfGsyQoR.js | 62 - .../dist/client/assets/main-DbgxqtWz.js | 18 - .../dist/client/assets/styles-DnGqQgkE.css | 1 - .../frontend/dist/client/assets/x-DtBV5VmR.js | 1 - .../frontend/dist/client/favicon.ico | Bin 3870 -> 0 bytes .../frontend/dist/client/logo192.png | Bin 5347 -> 0 bytes .../frontend/dist/client/logo512.png | Bin 9664 -> 0 bytes .../frontend/dist/client/manifest.json | 25 - .../ag-ui-app/frontend/dist/client/robots.txt | 3 - .../dist/client/tanstack-circle-logo.png | Bin 265387 -> 0 bytes .../dist/client/tanstack-word-logo-white.svg | 1 - .../_tanstack-start-manifest_v-Bh9ReuAT.js | 4 - .../dist/server/assets/agent-M-s1KgCM.js | 502 ---- .../dist/server/assets/api-C6MFPO9a.js | 22 - .../dist/server/assets/index-Cv7xvle-.js | 982 ------- .../dist/server/assets/router-wq7tchx4.js | 210 -- .../dist/server/assets/start-HYkvq4Ni.js | 4 - .../ag-ui-app/frontend/dist/server/server.js | 1256 --------- tests/ag-ui-app/frontend/todos.json | 14 - .../contact-intelligence/data/contacts.db | Bin 4096 -> 0 bytes .../contact-intelligence/data/contacts.db-shm | Bin 32768 -> 0 bytes .../contact-intelligence/data/contacts.db-wal | Bin 65952 -> 0 bytes .../contact-intelligence/dist/db/index.d.ts | 45 - .../contact-intelligence/dist/db/index.js | 102 - .../contact-intelligence/dist/db/schema.d.ts | 2 - .../contact-intelligence/dist/db/schema.js | 40 - .../contact-intelligence/dist/index.d.ts | 1 - .../contact-intelligence/dist/index.js | 170 -- .../contact-intelligence/dist/matcher.d.ts | 14 - .../contact-intelligence/dist/matcher.js | 140 - .../sentiment-analysis/data/sentiment.db | Bin 4096 -> 0 bytes .../sentiment-analysis/data/sentiment.db-shm | Bin 32768 -> 0 bytes .../sentiment-analysis/data/sentiment.db-wal | Bin 243112 -> 0 bytes .../services/writing-style/data/style.db | Bin 4096 -> 0 bytes .../services/writing-style/data/style.db-shm | Bin 32768 -> 0 bytes .../services/writing-style/data/style.db-wal | Bin 103032 -> 0 bytes .../services/writing-style/dist/db/index.d.ts | 40 - .../services/writing-style/dist/db/index.js | 94 - .../writing-style/dist/db/schema.d.ts | 2 - .../services/writing-style/dist/db/schema.js | 47 - .../services/writing-style/dist/index.d.ts | 1 - .../services/writing-style/dist/index.js | 137 - .../services/writing-style/dist/learner.d.ts | 35 - .../services/writing-style/dist/learner.js | 287 --- 68 files changed, 10773 deletions(-) delete mode 100644 docs/superpowers/plans/2026-03-27-agent-output-pipeline.md delete mode 100644 docs/superpowers/plans/2026-03-27-npx-installer.md delete mode 100644 docs/superpowers/plans/2026-03-27-syncable-cli-skills.md delete mode 100644 docs/superpowers/specs/2026-03-27-agent-output-pipeline-design.md delete mode 100644 docs/superpowers/specs/2026-03-27-npx-installer-design.md delete mode 100644 docs/superpowers/specs/2026-03-27-syncable-cli-skills-design.md delete mode 100644 tests/ag-ui-app/backend/data/smart-reply.db delete mode 100644 tests/ag-ui-app/backend/data/smart-reply.db-shm delete mode 100644 tests/ag-ui-app/backend/data/smart-reply.db-wal delete mode 100644 tests/ag-ui-app/backend/dist/db/index.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/db/index.js delete mode 100644 tests/ag-ui-app/backend/dist/db/schema.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/db/schema.js delete mode 100644 tests/ag-ui-app/backend/dist/index.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/index.js delete mode 100644 tests/ag-ui-app/backend/dist/lib/openai.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/lib/openai.js delete mode 100644 tests/ag-ui-app/backend/dist/lib/services.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/lib/services.js delete mode 100644 tests/ag-ui-app/backend/dist/routes/history.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/routes/history.js delete mode 100644 tests/ag-ui-app/backend/dist/routes/replies.d.ts delete mode 100644 tests/ag-ui-app/backend/dist/routes/replies.js delete mode 100644 tests/ag-ui-app/frontend/dist/client/assets/agent-Bh38wHiA.js delete mode 100644 tests/ag-ui-app/frontend/dist/client/assets/index-CfGsyQoR.js delete mode 100644 tests/ag-ui-app/frontend/dist/client/assets/main-DbgxqtWz.js delete mode 100644 tests/ag-ui-app/frontend/dist/client/assets/styles-DnGqQgkE.css delete mode 100644 tests/ag-ui-app/frontend/dist/client/assets/x-DtBV5VmR.js delete mode 100644 tests/ag-ui-app/frontend/dist/client/favicon.ico delete mode 100644 tests/ag-ui-app/frontend/dist/client/logo192.png delete mode 100644 tests/ag-ui-app/frontend/dist/client/logo512.png delete mode 100644 tests/ag-ui-app/frontend/dist/client/manifest.json delete mode 100644 tests/ag-ui-app/frontend/dist/client/robots.txt delete mode 100644 tests/ag-ui-app/frontend/dist/client/tanstack-circle-logo.png delete mode 100644 tests/ag-ui-app/frontend/dist/client/tanstack-word-logo-white.svg delete mode 100644 tests/ag-ui-app/frontend/dist/server/assets/_tanstack-start-manifest_v-Bh9ReuAT.js delete mode 100644 tests/ag-ui-app/frontend/dist/server/assets/agent-M-s1KgCM.js delete mode 100644 tests/ag-ui-app/frontend/dist/server/assets/api-C6MFPO9a.js delete mode 100644 tests/ag-ui-app/frontend/dist/server/assets/index-Cv7xvle-.js delete mode 100644 tests/ag-ui-app/frontend/dist/server/assets/router-wq7tchx4.js delete mode 100644 tests/ag-ui-app/frontend/dist/server/assets/start-HYkvq4Ni.js delete mode 100644 tests/ag-ui-app/frontend/dist/server/server.js delete mode 100644 tests/ag-ui-app/frontend/todos.json delete mode 100644 tests/ag-ui-app/services/contact-intelligence/data/contacts.db delete mode 100644 tests/ag-ui-app/services/contact-intelligence/data/contacts.db-shm delete mode 100644 tests/ag-ui-app/services/contact-intelligence/data/contacts.db-wal delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/db/index.d.ts delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/db/index.js delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/db/schema.d.ts delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/db/schema.js delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/index.d.ts delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/index.js delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/matcher.d.ts delete mode 100644 tests/ag-ui-app/services/contact-intelligence/dist/matcher.js delete mode 100644 tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db delete mode 100644 tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db-shm delete mode 100644 tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db-wal delete mode 100644 tests/ag-ui-app/services/writing-style/data/style.db delete mode 100644 tests/ag-ui-app/services/writing-style/data/style.db-shm delete mode 100644 tests/ag-ui-app/services/writing-style/data/style.db-wal delete mode 100644 tests/ag-ui-app/services/writing-style/dist/db/index.d.ts delete mode 100644 tests/ag-ui-app/services/writing-style/dist/db/index.js delete mode 100644 tests/ag-ui-app/services/writing-style/dist/db/schema.d.ts delete mode 100644 tests/ag-ui-app/services/writing-style/dist/db/schema.js delete mode 100644 tests/ag-ui-app/services/writing-style/dist/index.d.ts delete mode 100644 tests/ag-ui-app/services/writing-style/dist/index.js delete mode 100644 tests/ag-ui-app/services/writing-style/dist/learner.d.ts delete mode 100644 tests/ag-ui-app/services/writing-style/dist/learner.js diff --git a/docs/superpowers/plans/2026-03-27-agent-output-pipeline.md b/docs/superpowers/plans/2026-03-27-agent-output-pipeline.md deleted file mode 100644 index 63bc8594..00000000 --- a/docs/superpowers/plans/2026-03-27-agent-output-pipeline.md +++ /dev/null @@ -1,1262 +0,0 @@ -# Agent Output Pipeline Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Expose the existing internal RAG pipeline (compression + disk storage + retrieval) through CLI flags so external AI agents get compressed output instead of raw stdout blasts, then rewrite all 11 skills to use the new two-step pattern. - -**Architecture:** Add `--agent` flag to 5 scan commands (analyze, security, vulnerabilities, dependencies, optimize) that routes handler output through `compress_tool_output()` / `compress_analysis_output()` + `store_output()`, printing ~15KB compressed JSON to stdout. Add `retrieve` top-level subcommand wrapping existing `retrieve_filtered()` and `list_outputs()`. Fix existing bug in `find_issues_array()` missing `failures`/`diagnostics` fields. Rewrite all 11 skill markdown files to use `--agent` flag and teach agents the retrieve pattern. - -**Tech Stack:** Rust (clap, serde_json), Markdown - -**Spec:** `docs/superpowers/specs/2026-03-27-agent-output-pipeline-design.md` - ---- - -## File Structure - -### Subsystem 1: CLI Changes (Rust) - -| File | Action | Responsibility | -|------|--------|---------------| -| `src/cli.rs` | Modify | Add `agent: bool` field to Analyze, Security, Vulnerabilities, Dependencies, Optimize structs. Add `Retrieve` command variant. | -| `src/main.rs` | Modify | Wire `--agent` flag in 5 command handlers. Add `Retrieve` match arm. Add `handle_agent_output()` helper. Add `handle_retrieve()` handler. | -| `src/agent/tools/output_store.rs` | Modify | Fix `find_issues_array()` to include `failures` and `diagnostics`. Add `resolve_latest()` function. | -| `src/agent/tools/compression.rs` | Modify | Add `compress_tool_output_cli()` variant that produces strict JSON (no plaintext footer, CLI-syntax retrieval hint). | -| `src/handlers/analyze.rs` | Modify | Change return type to return raw JSON Value when called in agent mode. | - -### Subsystem 2: Skill Rewrites (Markdown) - -| File | Action | Responsibility | -|------|--------|---------------| -| `skills/commands/syncable-analyze.md` | Modify | Switch to `--agent`, add Reading Results section | -| `skills/commands/syncable-security.md` | Modify | Switch to `--agent`, add Reading Results section | -| `skills/commands/syncable-vulnerabilities.md` | Modify | Switch to `--agent`, add Reading Results section | -| `skills/commands/syncable-dependencies.md` | Modify | Switch to `--agent`, add Reading Results section | -| `skills/commands/syncable-validate.md` | Modify | Switch to `--agent`, add Reading Results section | -| `skills/commands/syncable-optimize.md` | Modify | Switch to `--agent`, add Reading Results section | -| `skills/commands/syncable-platform.md` | No change | Action commands, no compression needed | -| `skills/workflows/syncable-project-assessment.md` | Modify | Switch to `--agent`, add cross-step retrieval | -| `skills/workflows/syncable-security-audit.md` | Modify | Switch to `--agent`, add cross-step retrieval | -| `skills/workflows/syncable-iac-pipeline.md` | Modify | Switch to `--agent`, add cross-step retrieval | -| `skills/workflows/syncable-deploy-pipeline.md` | Modify | Switch to `--agent`, add cross-step retrieval | - ---- - -## Part 1: CLI Changes (Rust) - -### Task 1: Fix `find_issues_array()` bug in output_store.rs - -**Files:** -- Modify: `src/agent/tools/output_store.rs:291-300` - -This is an existing bug — `find_issues_array()` is missing `"failures"` and `"diagnostics"` fields that `compression.rs::extract_issues()` already handles. This causes retrieval filtering to return empty results for kubelint/hadolint/dclint/helmlint outputs. - -- [ ] **Step 1: Write the failing test** - -Add to the existing `#[cfg(test)]` module in `src/agent/tools/output_store.rs`: - -```rust -#[test] -fn test_find_issues_array_failures_field() { - let data = serde_json::json!({ - "failures": [ - {"code": "DL3008", "severity": "warning", "message": "Pin versions"}, - {"code": "DL3009", "severity": "info", "message": "Delete apt cache"} - ] - }); - let result = find_issues_array(&data); - assert!(result.is_some()); - assert_eq!(result.unwrap().len(), 2); -} - -#[test] -fn test_find_issues_array_diagnostics_field() { - let data = serde_json::json!({ - "diagnostics": [ - {"code": "DC001", "severity": "error", "message": "Invalid compose version"} - ] - }); - let result = find_issues_array(&data); - assert!(result.is_some()); - assert_eq!(result.unwrap().len(), 1); -} -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cargo test --lib -- output_store::tests::test_find_issues_array_failures_field output_store::tests::test_find_issues_array_diagnostics_field` -Expected: FAIL — `find_issues_array` doesn't check `failures` or `diagnostics` fields - -- [ ] **Step 3: Fix `find_issues_array`** - -In `src/agent/tools/output_store.rs:291-300`, add the missing fields to the `issue_fields` array: - -```rust -fn find_issues_array(data: &Value) -> Option> { - let issue_fields = [ - "issues", - "findings", - "violations", - "warnings", - "errors", - "recommendations", - "results", - "failures", - "diagnostics", - ]; - - for field in &issue_fields { - if let Some(arr) = data.get(field).and_then(|v| v.as_array()) { - return Some(arr.clone()); - } - } - - if let Some(arr) = data.as_array() { - return Some(arr.clone()); - } - - None -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cargo test --lib -- output_store::tests::test_find_issues_array` -Expected: PASS - -- [ ] **Step 5: Commit** - -```bash -git add src/agent/tools/output_store.rs -git commit -m "fix: add failures/diagnostics fields to find_issues_array - -Fixes retrieval returning empty results for kubelint, hadolint, dclint, -and helmlint outputs which use 'failures' field instead of 'issues'." -``` - ---- - -### Task 2: Add `resolve_latest()` to output_store.rs - -**Files:** -- Modify: `src/agent/tools/output_store.rs` - -External agents can't use the in-memory session registry across CLI invocations. `resolve_latest()` scans disk files by timestamp to find the most recent stored output. - -- [ ] **Step 1: Write the failing test** - -```rust -#[test] -fn test_resolve_latest_returns_most_recent() { - use std::fs; - use std::path::Path; - - let output_dir = Path::new("/tmp/syncable-cli/outputs"); - fs::create_dir_all(output_dir).unwrap(); - - // Clean up any existing test files - let _ = fs::remove_file(output_dir.join("test_old_aaa111.json")); - let _ = fs::remove_file(output_dir.join("test_new_bbb222.json")); - - let now = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(); - - // Write two files with different timestamps - let old_data = serde_json::json!({ - "ref_id": "test_old_aaa111", - "tool": "test_old", - "timestamp": now - 60, - "data": {} - }); - let new_data = serde_json::json!({ - "ref_id": "test_new_bbb222", - "tool": "test_new", - "timestamp": now, - "data": {} - }); - - fs::write( - output_dir.join("test_old_aaa111.json"), - serde_json::to_string(&old_data).unwrap(), - ).unwrap(); - fs::write( - output_dir.join("test_new_bbb222.json"), - serde_json::to_string(&new_data).unwrap(), - ).unwrap(); - - let latest = resolve_latest(); - assert!(latest.is_some()); - assert_eq!(latest.unwrap(), "test_new_bbb222"); - - // Cleanup - let _ = fs::remove_file(output_dir.join("test_old_aaa111.json")); - let _ = fs::remove_file(output_dir.join("test_new_bbb222.json")); -} - -#[test] -fn test_resolve_latest_empty_dir() { - // When no outputs exist, returns None - // This test is fragile if other tests leave files — just verify it doesn't panic - let result = resolve_latest(); - // Result is either Some or None, but must not panic - let _ = result; -} -``` - -- [ ] **Step 2: Run test to verify it fails** - -Run: `cargo test --lib -- output_store::tests::test_resolve_latest` -Expected: FAIL — `resolve_latest` doesn't exist yet - -- [ ] **Step 3: Implement `resolve_latest()`** - -Add to `src/agent/tools/output_store.rs` near `list_outputs()`: - -```rust -/// Resolve "latest" to the most recent ref_id by scanning disk files. -/// Works across separate CLI invocations (no in-memory state dependency). -pub fn resolve_latest() -> Option { - let output_dir = std::path::Path::new("/tmp/syncable-cli/outputs"); - if !output_dir.exists() { - return None; - } - - let mut newest: Option<(u64, String)> = None; - - if let Ok(entries) = std::fs::read_dir(output_dir) { - for entry in entries.flatten() { - let path = entry.path(); - if path.extension().map_or(true, |e| e != "json") { - continue; - } - - if let Ok(contents) = std::fs::read_to_string(&path) { - if let Ok(data) = serde_json::from_str::(&contents) { - if let Some(ts) = data.get("timestamp").and_then(|v| v.as_u64()) { - if let Some(ref_id) = data.get("ref_id").and_then(|v| v.as_str()) { - match &newest { - Some((best_ts, _)) if ts > *best_ts => { - newest = Some((ts, ref_id.to_string())); - } - None => { - newest = Some((ts, ref_id.to_string())); - } - _ => {} - } - } - } - } - } - } - } - - newest.map(|(_, ref_id)| ref_id) -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cargo test --lib -- output_store::tests::test_resolve_latest` -Expected: PASS - -- [ ] **Step 5: Commit** - -```bash -git add src/agent/tools/output_store.rs -git commit -m "feat: add resolve_latest() for cross-process ref_id resolution - -Scans /tmp/syncable-cli/outputs/ by embedded timestamp to find the most -recent stored output. Enables 'sync-ctl retrieve latest' across separate -CLI invocations." -``` - ---- - -### Task 3: Add `compress_tool_output_cli()` to compression.rs - -**Files:** -- Modify: `src/agent/tools/compression.rs` - -The existing `compress_tool_output()` appends a plaintext footer (`format_session_refs_for_agent()`) to the JSON, making it invalid JSON. For `--agent` CLI mode, we need strict JSON with CLI-syntax retrieval hints. - -- [ ] **Step 1: Write the failing test** - -```rust -#[test] -fn test_compress_tool_output_cli_produces_valid_json() { - let output = serde_json::json!({ - "findings": (0..100).map(|i| serde_json::json!({ - "code": format!("SEC{:03}", i), - "severity": if i < 3 { "critical" } else if i < 15 { "high" } else { "medium" }, - "message": format!("Finding {} with enough text to exceed compression threshold when multiplied", i), - "file": format!("src/file_{}.rs", i), - })).collect::>() - }); - let config = CompressionConfig::default(); - let result = compress_tool_output_cli(&output, "security", &config); - - // Must be valid JSON - let parsed: Result = serde_json::from_str(&result); - assert!(parsed.is_ok(), "CLI output must be valid JSON, got: {}", &result[..200.min(result.len())]); - - let json = parsed.unwrap(); - // Must contain CLI-syntax retrieval hint - let hint = json.get("retrieval_hint").and_then(|v| v.as_str()).unwrap(); - assert!(hint.contains("sync-ctl retrieve"), "Hint should use CLI syntax, got: {}", hint); - assert!(!hint.contains("retrieve_output("), "Hint should NOT use internal tool call syntax"); -} -``` - -- [ ] **Step 2: Run test to verify it fails** - -Run: `cargo test --lib -- compression::tests::test_compress_tool_output_cli_produces_valid_json` -Expected: FAIL — function doesn't exist - -- [ ] **Step 3: Implement `compress_tool_output_cli()`** - -Add to `src/agent/tools/compression.rs` after `compress_tool_output()`: - -```rust -/// CLI variant of compress_tool_output - produces strict JSON for external agents. -/// -/// Differences from compress_tool_output(): -/// 1. Does NOT append format_session_refs_for_agent() plaintext footer -/// 2. Uses CLI-syntax retrieval hints (sync-ctl retrieve '' --query '...') -/// 3. Output is guaranteed valid JSON -pub fn compress_tool_output_cli(output: &Value, tool_name: &str, config: &CompressionConfig) -> String { - let raw_str = serde_json::to_string(output).unwrap_or_default(); - if raw_str.len() <= config.target_size_bytes { - // Still store it for retrieval even if small - let ref_id = output_store::store_output(output, tool_name); - let mut result = output.clone(); - if let Some(obj) = result.as_object_mut() { - obj.insert("full_data_ref".to_string(), Value::String(ref_id.clone())); - obj.insert("retrieval_hint".to_string(), Value::String( - format!("Use `sync-ctl retrieve '{}' --query 'severity:critical'` for filtered details", ref_id) - )); - } - return serde_json::to_string_pretty(&result).unwrap_or(raw_str); - } - - // Run the same compression pipeline as compress_tool_output - let issues = extract_issues(output); - if issues.is_empty() { - let ref_id = output_store::store_output(output, tool_name); - let mut result = output.clone(); - if let Some(obj) = result.as_object_mut() { - obj.insert("full_data_ref".to_string(), Value::String(ref_id.clone())); - obj.insert("retrieval_hint".to_string(), Value::String( - format!("Use `sync-ctl retrieve '{}'` for full data", ref_id) - )); - } - return serde_json::to_string_pretty(&result).unwrap_or(raw_str); - } - - // Store full output for retrieval - let ref_id = output_store::store_output(output, tool_name); - - // Classify by severity — returns tuple: (critical, high, medium, low, info) - let (critical, high, medium, low, info) = classify_by_severity(&issues); - - // Build summary inline (no helper function — matches compress_tool_output pattern) - let summary = SeveritySummary { - total: issues.len(), - critical: critical.len(), - high: high.len(), - medium: medium.len(), - low: low.len(), - info: info.len(), - }; - - // Critical: always full detail - let critical_issues: Vec = critical.clone(); - - // High: full if few, truncate otherwise - let high_issues: Vec = if high.len() <= config.max_high_full { - high.clone() - } else { - high.iter().take(config.max_high_full).cloned().collect() - }; - - // Deduplicate medium/low/info into patterns - let mut all_lower: Vec = Vec::new(); - all_lower.extend(medium.clone()); - all_lower.extend(low.clone()); - all_lower.extend(info.clone()); - if high.len() > config.max_high_full { - all_lower.extend(high.iter().skip(config.max_high_full).cloned()); - } - let patterns = deduplicate_to_patterns(&all_lower, config); - - // Determine status inline - let status = if summary.critical > 0 { - "CRITICAL_ISSUES_FOUND" - } else if summary.high > 0 { - "HIGH_ISSUES_FOUND" - } else if summary.total > 0 { - "ISSUES_FOUND" - } else { - "CLEAN" - }; - - let compressed = CompressedOutput { - tool: tool_name.to_string(), - status: status.to_string(), - summary, - critical_issues, - high_issues, - patterns, - full_data_ref: ref_id.clone(), - retrieval_hint: format!( - "Use `sync-ctl retrieve '{}' --query 'severity:critical'` for full details. Other queries: 'file:', 'code:'", - ref_id - ), - }; - - // Return strict JSON — no plaintext footer (unlike compress_tool_output which appends session refs) - serde_json::to_string_pretty(&compressed).unwrap_or(raw_str) -} -``` - -Also add `compress_analysis_output_cli()` for the analyze command. This mirrors the logic in `compress_analysis_output()` (lines 476-634) but produces strict JSON with CLI-syntax retrieval hints: - -```rust -/// CLI variant of compress_analysis_output - produces strict JSON for external agents. -/// Mirrors compress_analysis_output() logic but: -/// 1. Always stores (even small output) for retrieval consistency -/// 2. Uses CLI-syntax retrieval hints -/// 3. No session refs plaintext footer -pub fn compress_analysis_output_cli(output: &Value, _config: &CompressionConfig) -> String { - let raw_str = serde_json::to_string(output).unwrap_or_default(); - let ref_id = output_store::store_output(output, "analyze_project"); - - // Build minimal summary — same inline extraction as compress_analysis_output() - let mut summary = json!({ - "tool": "analyze_project", - "status": "ANALYSIS_COMPLETE", - "full_data_ref": ref_id.clone(), - "retrieval_hint": format!( - "Use `sync-ctl retrieve '{}' --query 'section:frameworks'` for details. Other queries: 'section:languages', 'language:', 'framework:', 'project:'", - ref_id - ) - }); - - let summary_obj = summary.as_object_mut().unwrap(); - - // Detect output type and extract summary fields - // (Same logic as compress_analysis_output lines 495-587) - let is_monorepo = output.get("projects").is_some() || output.get("is_monorepo").is_some(); - - if is_monorepo { - if let Some(projects) = output.get("projects").and_then(|v| v.as_array()) { - summary_obj.insert("project_count".to_string(), json!(projects.len())); - let mut all_languages: Vec = Vec::new(); - let mut all_frameworks: Vec = Vec::new(); - let mut project_names: Vec = Vec::new(); - for project in projects.iter().take(20) { - if let Some(name) = project.get("name").and_then(|v| v.as_str()) { - project_names.push(name.to_string()); - } - if let Some(analysis) = project.get("analysis") { - if let Some(langs) = analysis.get("languages").and_then(|v| v.as_array()) { - for lang in langs { - if let Some(name) = lang.get("name").and_then(|v| v.as_str()) - && !all_languages.contains(&name.to_string()) { - all_languages.push(name.to_string()); - } - } - } - if let Some(fws) = analysis.get("frameworks").and_then(|v| v.as_array()) { - for fw in fws { - if let Some(name) = fw.get("name").and_then(|v| v.as_str()) - && !all_frameworks.contains(&name.to_string()) { - all_frameworks.push(name.to_string()); - } - } - } - } - } - summary_obj.insert("project_names".to_string(), json!(project_names)); - summary_obj.insert("languages_detected".to_string(), json!(all_languages)); - summary_obj.insert("frameworks_detected".to_string(), json!(all_frameworks)); - } - } else { - // ProjectAnalysis flat structure - if let Some(langs) = output.get("languages").and_then(|v| v.as_array()) { - let names: Vec<&str> = langs.iter() - .filter_map(|l| l.get("name").and_then(|n| n.as_str())) - .collect(); - summary_obj.insert("languages_detected".to_string(), json!(names)); - } - if let Some(techs) = output.get("technologies").and_then(|v| v.as_array()) { - let names: Vec<&str> = techs.iter() - .filter_map(|t| t.get("name").and_then(|n| n.as_str())) - .collect(); - summary_obj.insert("technologies_detected".to_string(), json!(names)); - } - // Extract services (matches compress_analysis_output lines 576-586) - if let Some(services) = output.get("services").and_then(|v| v.as_array()) { - summary_obj.insert("services_count".to_string(), json!(services.len())); - let service_names: Vec<&str> = services.iter() - .filter_map(|s| s.get("name").and_then(|n| n.as_str())) - .collect(); - if !service_names.is_empty() { - summary_obj.insert("services_detected".to_string(), json!(service_names)); - } - } - } - - serde_json::to_string_pretty(&summary).unwrap_or(raw_str) -} -``` - -**Note:** `extract_issues`, `classify_by_severity`, and `deduplicate_to_patterns` are existing private functions in compression.rs. They are already used by `compress_tool_output()`. If the new `_cli` functions are in the same module (which they should be), they can access these private functions directly. - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cargo test --lib -- compression::tests::test_compress_tool_output_cli` -Expected: PASS - -- [ ] **Step 5: Commit** - -```bash -git add src/agent/tools/compression.rs -git commit -m "feat: add CLI variants of compression functions - -compress_tool_output_cli() and compress_analysis_output_cli() produce -strict JSON without plaintext footer and use CLI-syntax retrieval hints -for external AI agents." -``` - ---- - -### Task 4: Add `--agent` flag to CLI command structs - -**Files:** -- Modify: `src/cli.rs` - -- [ ] **Step 1: Add `agent` field to each command struct** - -In `src/cli.rs`, add the following field to each of these command variants: - -**Analyze** (after the `color_scheme` field, around line 66): -```rust - /// Output compressed JSON for AI agent consumption (implies --json) - #[arg(long)] - agent: bool, -``` - -**Dependencies** (after the `format` field, around line 158): -```rust - /// Output compressed JSON for AI agent consumption (implies --json) - #[arg(long)] - agent: bool, -``` - -**Vulnerabilities** (after the `output` field, around line 177): -```rust - /// Output compressed JSON for AI agent consumption (implies --json) - #[arg(long)] - agent: bool, -``` - -**Security** (after the `fail_on_findings` field, around line 224): -```rust - /// Output compressed JSON for AI agent consumption (implies --json) - #[arg(long)] - agent: bool, -``` - -**Validate** (after the `fix` field, around line 116): -```rust - /// Output compressed JSON for AI agent consumption (implies --json) - #[arg(long)] - agent: bool, -``` - -**Optimize** (after the `full` field, around line 314): -```rust - /// Output compressed JSON for AI agent consumption (implies --json) - #[arg(long)] - agent: bool, -``` - -- [ ] **Step 2: Add `Retrieve` command variant** - -Add after the `Optimize` variant (before `Chat`): - -```rust - /// Retrieve stored output from a previous --agent command - Retrieve { - /// Reference ID (e.g., "security_a1b2c3d4") or "latest" for most recent - #[arg(value_name = "REF_ID")] - ref_id: Option, - - /// Filter query (e.g., "severity:critical", "file:path", "section:frameworks") - #[arg(long, short = 'q')] - query: Option, - - /// List all stored outputs - #[arg(long)] - list: bool, - }, -``` - -- [ ] **Step 3: Verify it compiles** - -Run: `cargo check 2>&1 | head -30` -Expected: Compilation errors in `main.rs` because match arms don't destructure the new `agent` field yet — that's expected and will be fixed in Task 5. - -If there are OTHER errors (syntax, type), fix them before proceeding. - -- [ ] **Step 4: Commit** - -```bash -git add src/cli.rs -git commit -m "feat: add --agent flag to 5 scan commands and Retrieve subcommand - -Adds 'agent: bool' field to Analyze, Security, Vulnerabilities, -Dependencies, and Optimize command structs. Adds Retrieve command -variant with ref_id, query, and list fields." -``` - ---- - -### Task 5: Wire `--agent` flag in main.rs command handlers - -**Files:** -- Modify: `src/main.rs` - -This is the biggest task — wiring the `--agent` flag in each command handler and adding the `Retrieve` match arm. - -- [ ] **Step 1: Add imports at top of main.rs** - -Near the existing imports in `main.rs`, add: - -```rust -use syncable_cli::agent::tools::compression::{ - compress_tool_output_cli, compress_analysis_output_cli, CompressionConfig, -}; -use syncable_cli::agent::tools::output_store; -``` - -**Note:** If `compression` or `output_store` modules are not publicly exported from `syncable_cli`, you may need to add `pub mod` re-exports in `src/lib.rs` or `src/agent/mod.rs` → `src/agent/tools/mod.rs`. Check the module visibility chain and add `pub` as needed. - -- [ ] **Step 2: Add `handle_agent_output` helper function** - -Add a helper function near the other `handle_*` functions in `main.rs`: - -```rust -/// Route command output through compression pipeline for --agent mode. -/// Runs cleanup, compresses, stores, and prints strict JSON to stdout. -/// If output_file is provided, writes full uncompressed output to that file. -fn handle_agent_output(json_output: &str, tool_name: &str, output_file: Option<&std::path::Path>) -> syncable_cli::Result<()> { - output_store::cleanup_old_outputs(); - - // If --output was also passed, write full uncompressed output to file - if let Some(path) = output_file { - std::fs::write(path, json_output)?; - eprintln!("Full output saved to: {}", path.display()); - } - - let value: serde_json::Value = serde_json::from_str(json_output) - .map_err(|e| syncable_cli::error::IaCGeneratorError::Analysis( - syncable_cli::error::AnalysisError::InvalidStructure( - format!("Failed to parse output as JSON: {}", e) - ) - ))?; - - let config = CompressionConfig::default(); - let compressed = if tool_name == "analyze_project" { - compress_analysis_output_cli(&value, &config) - } else { - compress_tool_output_cli(&value, tool_name, &config) - }; - - println!("{}", compressed); - Ok(()) -} -``` - -- [ ] **Step 3: Wire --agent in Analyze handler** - -In the `Commands::Analyze` match arm (around line 129), add `agent` to the destructuring pattern and modify the handler call: - -```rust - Commands::Analyze { - path, - json, - detailed, - display, - only, - color_scheme, - agent, - } => { - // ... existing telemetry code ... - - if agent { - // Force JSON mode, call inner handler (returns Result), route through compression - match syncable_cli::handlers::analyze::handle_analyze(path, true, false, None, only, color_scheme) { - Ok(output) => handle_agent_output(&output, "analyze_project", None), - Err(e) => Err(e), - } - } else { - match handle_analyze(path, json, detailed, display, only, color_scheme) { - Ok(_output) => Ok(()), - Err(e) => Err(e), - } - } - } -``` - -**Important:** The `handle_analyze` in `main.rs` is a thin wrapper returning `Result<()>`. For `--agent` mode, call the inner handler directly: `syncable_cli::handlers::analyze::handle_analyze(...)` which returns `crate::Result` — the string is the JSON output when `json: true`. - -- [ ] **Step 4: Wire --agent in Security handler** - -In the `Commands::Security` match arm (around line 365), add `agent` to destructuring and modify: - -```rust - Commands::Security { - path, - mode, - include_low, - no_secrets, - no_code_patterns, - no_infrastructure, - no_compliance, - frameworks, - format, - output, - fail_on_findings, - agent, - } => { - // ... existing telemetry code ... - - if agent { - // Force JSON format, call inner handler (returns Result) - let result = syncable_cli::handlers::security::handle_security( - path, mode, include_low, no_secrets, no_code_patterns, - no_infrastructure, no_compliance, frameworks, - OutputFormat::Json, None, fail_on_findings, - )?; - handle_agent_output(&result, "security", output.as_deref()) - } else { - let effective_format = if cli.json { OutputFormat::Json } else { format }; - // ... rest of existing handler code ... - handle_security( - path, mode, include_low, no_secrets, no_code_patterns, - no_infrastructure, no_compliance, frameworks, - effective_format, output, fail_on_findings, - ).map(|_| ()) - } - } -``` - -**Important:** Same as Analyze — call the inner handler `syncable_cli::handlers::security::handle_security(...)` which returns `crate::Result`. The main.rs wrapper `handle_security()` returns `Result<()>` and discards the string. - -- [ ] **Step 5: Wire --agent in Vulnerabilities handler** - -Find the `Commands::Vulnerabilities` match arm, add `agent` to destructuring. The vulnerabilities handler is `async` and returns `Result<()>` — it currently prints directly. For `--agent` mode, we need to capture its JSON output. Check how the handler produces output: - -```rust - Commands::Vulnerabilities { - path, - severity, - format, - output, - agent, - } => { - // ... existing telemetry code ... - - if agent { - // Force JSON, call inner handler — see Task 6 for handler refactor - // After Task 6 refactor, the handler returns Result in JSON mode - let result = syncable_cli::handlers::vulnerabilities::handle_vulnerabilities( - path, severity, OutputFormat::Json, None, - ).await?; - handle_agent_output(&result, "vulnerabilities", output.as_deref()) - } else { - handle_vulnerabilities(path, severity, format, output).await - } - } -``` - -**Implementation note:** The vulnerabilities handler returns `Result<()>` and prints directly. For `--agent` mode to work, the handler needs to be modified to return the JSON string instead of printing it. This pattern applies to `dependencies` and `optimize` as well. The modification is: when format is JSON, collect output into a String and return it instead of printing. This is a necessary refactor for each affected handler. - -- [ ] **Step 6: Wire --agent in Dependencies handler** - -Same pattern as Vulnerabilities — add `agent` to destructuring, force JSON format when `agent` is true, route through `handle_agent_output()`. - -- [ ] **Step 7: Wire --agent in Optimize handler** - -Same pattern — add `agent` to destructuring, force JSON format when `agent` is true, route through `handle_agent_output()`. - -- [ ] **Step 7b: Wire --agent in Validate handler (stub)** - -The Validate handler is currently a stub (`handle_validate` just prints "not yet implemented"). Add `agent` to the destructuring but ignore it for now: - -```rust - Commands::Validate { path, types, fix, agent: _ } => { - // ... existing telemetry code ... - handle_validate(path, types, fix) - } -``` - -When validate is implemented, the `agent: _` will be replaced with actual `--agent` wiring. - -- [ ] **Step 8: Add Retrieve to command_name telemetry match** - -In main.rs around line 106-123, there's a match for `command_name` used in telemetry. Add: -```rust - Commands::Retrieve { .. } => "retrieve", -``` - -- [ ] **Step 9: Add Retrieve match arm** - -Add after the Optimize match arm: - -```rust - Commands::Retrieve { ref_id, query, list } => { - output_store::cleanup_old_outputs(); - - if list { - let outputs = output_store::list_outputs(); - let json_list: Vec = outputs.iter().map(|o| { - let age_secs = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_secs() - .saturating_sub(o.timestamp); - let age_str = if age_secs < 60 { - format!("{}s ago", age_secs) - } else if age_secs < 3600 { - format!("{}m ago", age_secs / 60) - } else { - format!("{}h ago", age_secs / 3600) - }; - serde_json::json!({ - "ref_id": o.ref_id, - "tool": o.tool, - "timestamp": o.timestamp, - "age": age_str, - "size_bytes": o.size_bytes, - }) - }).collect(); - println!("{}", serde_json::to_string_pretty(&json_list)?); - return Ok(()); - } - - let resolved_ref = match ref_id.as_deref() { - Some("latest") => match output_store::resolve_latest() { - Some(id) => id, - None => { - let error = serde_json::json!({ - "error": "no_outputs", - "message": "No stored outputs found. Run a command with --agent first." - }); - eprintln!("{}", serde_json::to_string_pretty(&error)?); - std::process::exit(1); - } - }, - Some(id) => id.to_string(), - None => { - eprintln!("Usage: sync-ctl retrieve [--query ]"); - eprintln!(" sync-ctl retrieve --list"); - return Ok(()); - } - }; - - match output_store::retrieve_filtered(&resolved_ref, query.as_deref()) { - Some(data) => { - let json_str = serde_json::to_string_pretty(&data)?; - if json_str.len() > 50_000 { - eprintln!("Warning: result is {} bytes. Use a more specific --query to narrow results.", json_str.len()); - } - println!("{}", json_str); - Ok(()) - } - None => { - let available = output_store::list_outputs(); - let available_ids: Vec<&str> = available.iter().map(|o| o.ref_id.as_str()).collect(); - let error = serde_json::json!({ - "error": "not_found", - "message": format!("Output '{}' not found (expired or invalid ref_id)", resolved_ref), - "available": available_ids, - }); - eprintln!("{}", serde_json::to_string_pretty(&error)?); - std::process::exit(1); - } - } - } -``` - -- [ ] **Step 10: Verify it compiles** - -Run: `cargo check` -Expected: PASS (or warnings only). Fix any compilation errors. - -- [ ] **Step 11: Run existing tests** - -Run: `cargo test` -Expected: All existing tests pass. The changes are additive — no existing behavior changed. - -- [ ] **Step 12: Commit** - -```bash -git add src/main.rs src/lib.rs src/agent/mod.rs src/agent/tools/mod.rs -git commit -m "feat: wire --agent flag and retrieve subcommand in main.rs - -Routes --agent output through compression pipeline for analyze, security, -vulnerabilities, dependencies, and optimize commands. Adds retrieve -subcommand with --list, --query, and 'latest' resolution." -``` - ---- - -### Task 6: Handler refactors for --agent JSON capture - -**Files:** -- Modify: `src/handlers/vulnerabilities.rs` -- Modify: `src/handlers/dependencies.rs` -- Modify: `src/handlers/optimize.rs` - -The `vulnerabilities`, `dependencies`, and `optimize` handlers currently return `Result<()>` and print directly to stdout. For `--agent` mode, they need to return the JSON string so main.rs can route it through compression. - -- [ ] **Step 1: Modify vulnerabilities handler** - -In `src/handlers/vulnerabilities.rs`, change the handler to return `Result` when format is JSON: - -The exact changes depend on how the handler currently formats and prints output. The pattern is: -1. When format is JSON, collect the serde_json output into a String -2. Return the String instead of printing -3. Let the caller (main.rs) decide whether to print directly or route through compression - -- [ ] **Step 2: Modify dependencies handler** - -Same pattern in `src/handlers/dependencies.rs`. - -- [ ] **Step 3: Modify optimize handler** - -Same pattern in `src/handlers/optimize.rs`. - -- [ ] **Step 4: Update main.rs callers** - -Update the match arms in main.rs to handle the new return types. - -- [ ] **Step 5: Verify compilation and tests** - -Run: `cargo check && cargo test` -Expected: PASS - -- [ ] **Step 6: Commit** - -```bash -git add src/handlers/vulnerabilities.rs src/handlers/dependencies.rs src/handlers/optimize.rs src/main.rs -git commit -m "refactor: handlers return JSON string for --agent mode capture - -Modified vulnerabilities, dependencies, and optimize handlers to return -JSON output string instead of printing directly, enabling --agent mode -to route through compression pipeline." -``` - ---- - -### Task 7: Module visibility and integration test - -**Files:** -- Modify: `src/lib.rs` and/or `src/agent/mod.rs`, `src/agent/tools/mod.rs` (as needed) -- Create: integration test or manual verification - -- [ ] **Step 1: Ensure module visibility** - -The `compression` and `output_store` modules need to be accessible from `main.rs`. Check the module tree: - -``` -src/lib.rs → pub mod agent -src/agent/mod.rs → pub mod tools -src/agent/tools/mod.rs → pub mod compression, pub mod output_store -``` - -If any of these are `pub(crate)` or private, change to `pub`. The functions `compress_tool_output_cli`, `compress_analysis_output_cli`, `CompressionConfig`, `output_store::cleanup_old_outputs`, `output_store::store_output`, `output_store::retrieve_filtered`, `output_store::list_outputs`, `output_store::resolve_latest` all need to be reachable from main.rs. - -- [ ] **Step 2: Manual integration test** - -Run a full end-to-end test: - -```bash -# Build -cargo build - -# Test --agent on analyze -./target/debug/sync-ctl analyze . --agent - -# Verify output is valid JSON -./target/debug/sync-ctl analyze . --agent | python3 -c "import sys,json; d=json.load(sys.stdin); print('ref:', d.get('full_data_ref')); print('hint:', d.get('retrieval_hint'))" - -# Test retrieve --list -./target/debug/sync-ctl retrieve --list - -# Test retrieve with ref_id from analyze output -./target/debug/sync-ctl retrieve latest --query "section:frameworks" - -# Test error case -./target/debug/sync-ctl retrieve nonexistent_abc123 -``` - -Expected: Each command produces valid JSON output with appropriate fields. - -- [ ] **Step 3: Commit any visibility fixes** - -```bash -git add -A -git commit -m "feat: ensure module visibility for --agent pipeline integration" -``` - ---- - -## Part 2: Skill Rewrites (Markdown) - -### Task 8: Rewrite command skills with --agent pattern - -**Files:** -- Modify: `skills/commands/syncable-analyze.md` -- Modify: `skills/commands/syncable-security.md` -- Modify: `skills/commands/syncable-vulnerabilities.md` -- Modify: `skills/commands/syncable-dependencies.md` -- Modify: `skills/commands/syncable-validate.md` -- Modify: `skills/commands/syncable-optimize.md` - -For each command skill, make these changes: - -1. **Replace all `--json` / `--format json` with `--agent`** in command examples -2. **Add "Reading Results" section** after the command reference -3. **Add skill-specific query filters** for the retrieve command -4. **Remove raw JSON parsing instructions** — agents no longer need to manually parse arrays - -- [ ] **Step 1: Rewrite syncable-analyze.md** - -Replace all command examples using `--json` with `--agent`. Add after the command reference: - -```markdown -## Reading Results - -When you use `--agent`, the output is a compressed summary — not the full analysis. You can act on it directly for most decisions. - -The output JSON includes: -- `summary` — project count, languages, frameworks detected -- `full_data_ref` — reference ID for retrieving full data -- `retrieval_hint` — exact command to get more details - -To drill into specifics: -```bash -# Get framework details -sync-ctl retrieve --query "section:frameworks" - -# Get language breakdown -sync-ctl retrieve --query "section:languages" - -# Get specific project details (monorepos) -sync-ctl retrieve --query "project:" - -# Get specific language details -sync-ctl retrieve --query "language:Go" - -# Get specific framework details -sync-ctl retrieve --query "framework:React" - -# List all stored outputs -sync-ctl retrieve --list -``` - -**Available query filters:** `section:summary`, `section:frameworks`, `section:languages`, `language:`, `framework:`, `project:`, `compact:true` -``` - -- [ ] **Step 2: Rewrite syncable-security.md** - -Replace `--format json` with `--agent`. Add Reading Results section: - -```markdown -## Reading Results - -When you use `--agent`, the output is a compressed summary (~15KB max). All **critical** issues are included in full detail. High-severity issues show the first 10. Medium/low issues are deduplicated into patterns. - -The output JSON includes: -- `status` — e.g., "CRITICAL_ISSUES_FOUND", "HIGH_ISSUES_FOUND", "CLEAN" -- `summary` — counts by severity (total, critical, high, medium, low, info) -- `critical_issues` — full details for every critical finding -- `high_issues` — first 10 high-severity findings -- `patterns` — deduplicated medium/low findings with counts -- `full_data_ref` — reference ID for retrieving full data -- `retrieval_hint` — exact command for drill-down - -To drill into specifics: -```bash -# Get all critical findings (already in summary, but also via retrieve) -sync-ctl retrieve --query "severity:critical" - -# Get findings for a specific file -sync-ctl retrieve --query "file:src/auth.rs" - -# Get findings by rule code -sync-ctl retrieve --query "code:hardcoded-secret" -``` - -**Available query filters:** `severity:critical|high|medium|low|info`, `file:`, `code:` -``` - -- [ ] **Step 3: Rewrite syncable-vulnerabilities.md** - -Same pattern as security — replace format flags with `--agent`, add Reading Results with filters: `severity:`, `file:`. - -- [ ] **Step 4: Rewrite syncable-dependencies.md** - -Replace `--format json` with `--agent`, add Reading Results with filters: `severity:`, `file:`. - -- [ ] **Step 5: Rewrite syncable-validate.md** - -Replace command invocations with `--agent`. Note: the validate CLI command is currently a stub, but the skill should still document the `--agent` flag for when it's implemented. Add Reading Results with filters: `severity:`, `file:`, `code:`. - -- [ ] **Step 6: Rewrite syncable-optimize.md** - -Replace `--format json` with `--agent`, add Reading Results with filters: `severity:`, `container:`. - -- [ ] **Step 7: Verify no remaining --json/--format json in command skills** - -```bash -grep -r "\-\-json\|\-\-format json" skills/commands/ -``` - -Expected: Only `syncable-platform.md` should still reference `--json` (platform commands don't get `--agent`). All other command skills should use `--agent`. - -- [ ] **Step 8: Commit** - -```bash -git add skills/commands/ -git commit -m "feat: rewrite command skills to use --agent flag - -All 6 scan command skills now use --agent instead of --json/--format json. -Each skill includes a Reading Results section documenting compressed output -format and available retrieve query filters." -``` - ---- - -### Task 9: Rewrite workflow skills with --agent and cross-step retrieval - -**Files:** -- Modify: `skills/workflows/syncable-project-assessment.md` -- Modify: `skills/workflows/syncable-security-audit.md` -- Modify: `skills/workflows/syncable-iac-pipeline.md` -- Modify: `skills/workflows/syncable-deploy-pipeline.md` - -Workflow skills get the same `--agent` switch plus cross-step retrieval instructions. - -- [ ] **Step 1: Rewrite syncable-project-assessment.md** - -Replace all `--json` / `--format json` with `--agent` in each step's commands. Add cross-step retrieval: - -After Step 1 (Analyze), add: -```markdown -Save the `full_data_ref` from the analyze output — you'll use it to retrieve details without re-running analyze. -``` - -After all steps, add or update the Report Synthesis section: -```markdown -## Cross-Step Retrieval - -Each step produces a `full_data_ref` in its output. You can retrieve details from any previous step at any time: - -```bash -# Check what data is available from all steps -sync-ctl retrieve --list - -# Get framework details from Step 1 (analyze) -sync-ctl retrieve --query "section:frameworks" - -# Get critical security findings from Step 2 -sync-ctl retrieve --query "severity:critical" - -# Get vulnerability details from Step 3 -sync-ctl retrieve --query "severity:high" -``` - -Do NOT re-run a command just to get more detail — use `sync-ctl retrieve` instead. -``` - -- [ ] **Step 2: Rewrite syncable-security-audit.md** - -Same pattern — `--agent` flags, cross-step retrieval. - -- [ ] **Step 3: Rewrite syncable-iac-pipeline.md** - -Same pattern — `--agent` flags, cross-step retrieval. Emphasize that the analyze step's ref_id can be reused in later steps. - -- [ ] **Step 4: Rewrite syncable-deploy-pipeline.md** - -Same pattern — `--agent` flags, cross-step retrieval. The deploy pipeline's security gate step should reference the security output's `status` field to check for critical issues without needing to parse raw arrays. - -Update the security gate decision logic: -```markdown -**CRITICAL GATE:** Check the security output's `status` field: -- If `status` is "CRITICAL_ISSUES_FOUND": present findings to user, warn, require confirmation -- If `status` is "HIGH_ISSUES_FOUND": warn but allow deployment -- If `status` is "CLEAN": proceed to deploy - -All critical findings are in the `critical_issues` array of the compressed output — no retrieval needed for the gate decision. -``` - -- [ ] **Step 5: Verify no remaining --json/--format json in workflow skills** - -```bash -grep -r "\-\-json\|\-\-format json" skills/workflows/ -``` - -Expected: No matches. - -- [ ] **Step 6: Commit** - -```bash -git add skills/workflows/ -git commit -m "feat: rewrite workflow skills with --agent and cross-step retrieval - -All 4 workflow skills now use --agent and teach agents to reuse ref_ids -across steps via sync-ctl retrieve. Security gate in deploy pipeline uses -compressed output status field instead of raw JSON parsing." -``` - ---- - -### Task 10: Final verification - -**Files:** None (verification only) - -- [ ] **Step 1: Verify all skills use --agent pattern** - -```bash -# Should find --agent in all command skills except platform -grep -l "\-\-agent" skills/commands/*.md - -# Should find --agent in all workflow skills -grep -l "\-\-agent" skills/workflows/*.md - -# Should find NO --json in skills except platform -grep -rn "\-\-json" skills/ | grep -v platform -``` - -- [ ] **Step 2: Verify Rust tests pass** - -```bash -cargo test -``` - -- [ ] **Step 3: Verify npm skills build** - -```bash -cd installer && npm run build && cd .. -``` - -- [ ] **Step 4: Commit any final fixes** - -Only if needed from verification. diff --git a/docs/superpowers/plans/2026-03-27-npx-installer.md b/docs/superpowers/plans/2026-03-27-npx-installer.md deleted file mode 100644 index 6ea5309d..00000000 --- a/docs/superpowers/plans/2026-03-27-npx-installer.md +++ /dev/null @@ -1,2286 +0,0 @@ -# NPX Installer Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build an npx-installable TypeScript CLI (`syncable-cli-skills`) that installs the Syncable CLI, detects AI coding agents, and installs skills in each agent's native format. - -**Architecture:** Commander-based CLI with four commands (install/uninstall/update/status). Modular design: agent detection, format transformers, prerequisite installers, and skill loading are isolated units. Pure-function transformers make testing straightforward. ESM throughout (chalk@5, ora@8, inquirer@9 are ESM-only). - -**Tech Stack:** TypeScript (ESM), commander, inquirer, ora, chalk, fs-extra, vitest (testing) - -**Spec:** `docs/superpowers/specs/2026-03-27-npx-installer-design.md` - ---- - -## File Structure - -``` -installer/ -├── package.json -├── tsconfig.json -├── vitest.config.ts -├── .gitignore -├── scripts/ -│ └── copy-skills.js # prebuild: copy ../skills/ into installer/skills/ -├── src/ -│ ├── index.ts # CLI entrypoint (commander setup) -│ ├── constants.ts # MIN_SYNC_CTL_VERSION, SKILL_MARKER_START/END -│ ├── utils.ts # shell exec, version parsing, platform helpers -│ ├── skills.ts # load bundled skill files, parse frontmatter -│ ├── agents/ -│ │ ├── types.ts # AgentConfig interface, AgentName enum -│ │ ├── detect.ts # detect all installed agents -│ │ ├── claude.ts # Claude Code agent config -│ │ ├── cursor.ts # Cursor agent config -│ │ ├── windsurf.ts # Windsurf agent config -│ │ ├── codex.ts # Codex agent config -│ │ └── gemini.ts # Gemini CLI agent config -│ ├── transformers/ -│ │ ├── types.ts # Skill type, TransformResult type -│ │ ├── claude.ts # no-op (native format) -│ │ ├── codex.ts # wrap in SKILL.md directory structure -│ │ ├── cursor.ts # convert to .mdc format -│ │ ├── windsurf.ts # convert to windsurf rule format -│ │ └── gemini.ts # concatenate into GEMINI.md section -│ ├── prerequisites/ -│ │ ├── check.ts # check cargo, sync-ctl presence and versions -│ │ ├── install-rustup.ts # install Rust toolchain via rustup -│ │ └── install-cli.ts # cargo install syncable-cli -│ └── commands/ -│ ├── install.ts # install command (default) -│ ├── uninstall.ts # remove skills from agents -│ ├── update.ts # uninstall + install -│ └── status.ts # show what's installed where -├── tests/ -│ ├── utils.test.ts -│ ├── skills.test.ts -│ ├── agents/ -│ │ └── detect.test.ts -│ ├── transformers/ -│ │ ├── claude.test.ts -│ │ ├── codex.test.ts -│ │ ├── cursor.test.ts -│ │ ├── windsurf.test.ts -│ │ └── gemini.test.ts -│ ├── prerequisites/ -│ │ └── check.test.ts -│ └── commands/ -│ ├── install.test.ts -│ ├── uninstall.test.ts -│ └── status.test.ts -├── skills/ # build artifact (gitignored), copied from ../skills/ -└── dist/ # compiled JS output (gitignored) -``` - -Each unit has a single responsibility. Transformers are pure functions. Agent configs are data objects with detection logic. Commands orchestrate the pieces. - ---- - -### Task 1: Project scaffolding - -**Files:** -- Create: `installer/package.json` -- Create: `installer/tsconfig.json` -- Create: `installer/vitest.config.ts` -- Create: `installer/.gitignore` -- Create: `installer/scripts/copy-skills.js` - -- [ ] **Step 1: Create `installer/package.json`** - -```json -{ - "name": "syncable-cli-skills", - "version": "0.1.0", - "type": "module", - "description": "Install Syncable CLI skills for AI coding agents", - "bin": { - "syncable-cli-skills": "./dist/index.js" - }, - "files": [ - "dist/", - "skills/" - ], - "engines": { - "node": ">=18.0.0" - }, - "scripts": { - "prebuild": "node scripts/copy-skills.js", - "build": "tsc", - "test": "vitest run", - "test:watch": "vitest", - "prepublishOnly": "npm run build" - }, - "dependencies": { - "commander": "^12.0.0", - "inquirer": "^9.0.0", - "ora": "^8.0.0", - "chalk": "^5.0.0", - "fs-extra": "^11.0.0" - }, - "devDependencies": { - "@types/fs-extra": "^11.0.0", - "@types/node": "^20.0.0", - "typescript": "^5.0.0", - "vitest": "^3.0.0" - } -} -``` - -- [ ] **Step 2: Create `installer/tsconfig.json`** - -```json -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "declaration": true, - "sourceMap": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "tests", "skills"] -} -``` - -- [ ] **Step 3: Create `installer/vitest.config.ts`** - -```typescript -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - root: '.', - include: ['tests/**/*.test.ts'], - }, -}); -``` - -- [ ] **Step 4: Create `installer/.gitignore`** - -``` -node_modules/ -dist/ -skills/ -``` - -- [ ] **Step 5: Create `installer/scripts/copy-skills.js`** - -This script copies `../skills/` into `installer/skills/` at build time using `fs-extra`. Cross-platform safe. - -```javascript -import { copySync, removeSync, existsSync } from 'fs-extra'; -import { resolve, dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const source = resolve(__dirname, '..', '..', 'skills'); -const dest = resolve(__dirname, '..', 'skills'); - -if (!existsSync(source)) { - console.error('Error: skills/ directory not found at', source); - process.exit(1); -} - -removeSync(dest); -copySync(source, dest); - -console.log(`Copied skills from ${source} to ${dest}`); -``` - -- [ ] **Step 6: Install dependencies** - -Run: `cd installer && npm install` - -- [ ] **Step 7: Verify TypeScript compiles (empty project)** - -Create a minimal `installer/src/index.ts`: - -```typescript -#!/usr/bin/env node -console.log('syncable-cli-skills'); -``` - -Run: `cd installer && npx tsc --noEmit` -Expected: No errors. - -- [ ] **Step 8: Verify tests run (empty)** - -Run: `cd installer && npm test` -Expected: vitest runs, 0 tests found. - -- [ ] **Step 9: Verify copy-skills script works** - -Run: `cd installer && node scripts/copy-skills.js` -Expected: `Copied skills from ...` message. `installer/skills/` directory now contains `commands/` and `workflows/` subdirs with 11 `.md` files. - -- [ ] **Step 10: Commit** - -```bash -git add installer/ -git commit -m "chore(installer): scaffold npx installer project" -``` - ---- - -### Task 2: Constants and utilities module - -**Files:** -- Create: `installer/src/constants.ts` -- Create: `installer/src/utils.ts` -- Test: `installer/tests/utils.test.ts` - -- [ ] **Step 1: Write the failing tests for utils** - -```typescript -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { parseVersion, compareVersions, isWindows } from '../src/utils.js'; - -describe('parseVersion', () => { - it('parses semver string', () => { - expect(parseVersion('1.2.3')).toEqual({ major: 1, minor: 2, patch: 3 }); - }); - - it('extracts version from prefixed string', () => { - expect(parseVersion('sync-ctl 0.36.0')).toEqual({ major: 0, minor: 36, patch: 0 }); - }); - - it('extracts version from cargo output', () => { - expect(parseVersion('cargo 1.79.0 (ffa9cf99a 2024-06-03)')).toEqual({ major: 1, minor: 79, patch: 0 }); - }); - - it('returns null for unparseable string', () => { - expect(parseVersion('no version here')).toBeNull(); - }); -}); - -describe('compareVersions', () => { - it('returns 0 for equal versions', () => { - expect(compareVersions({ major: 1, minor: 2, patch: 3 }, { major: 1, minor: 2, patch: 3 })).toBe(0); - }); - - it('returns positive when a > b', () => { - expect(compareVersions({ major: 2, minor: 0, patch: 0 }, { major: 1, minor: 9, patch: 9 })).toBeGreaterThan(0); - }); - - it('returns negative when a < b', () => { - expect(compareVersions({ major: 0, minor: 35, patch: 0 }, { major: 0, minor: 36, patch: 0 })).toBeLessThan(0); - }); - - it('compares patch level', () => { - expect(compareVersions({ major: 1, minor: 0, patch: 1 }, { major: 1, minor: 0, patch: 0 })).toBeGreaterThan(0); - }); -}); - -describe('isWindows', () => { - it('returns a boolean', () => { - expect(typeof isWindows()).toBe('boolean'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL — modules not found. - -- [ ] **Step 3: Write `installer/src/constants.ts`** - -```typescript -export const MIN_SYNC_CTL_VERSION = '0.35.0'; -export const SKILL_MARKER_START = ''; -export const SKILL_MARKER_END = ''; -``` - -- [ ] **Step 4: Write `installer/src/utils.ts`** - -```typescript -import { exec as execCb } from 'child_process'; -import { promisify } from 'util'; -import os from 'os'; -import path from 'path'; - -const execAsync = promisify(execCb); - -export interface Version { - major: number; - minor: number; - patch: number; -} - -export function parseVersion(input: string): Version | null { - const match = input.match(/(\d+)\.(\d+)\.(\d+)/); - if (!match) return null; - return { - major: parseInt(match[1], 10), - minor: parseInt(match[2], 10), - patch: parseInt(match[3], 10), - }; -} - -export function compareVersions(a: Version, b: Version): number { - if (a.major !== b.major) return a.major - b.major; - if (a.minor !== b.minor) return a.minor - b.minor; - return a.patch - b.patch; -} - -export function isWindows(): boolean { - return process.platform === 'win32'; -} - -export function homedir(): string { - return os.homedir(); -} - -export function cargoBinDir(): string { - return path.join(os.homedir(), '.cargo', 'bin'); -} - -export async function execCommand(command: string): Promise<{ stdout: string; stderr: string }> { - return execAsync(command, { timeout: 300_000 }); -} - -export async function commandExists(command: string): Promise { - try { - const cmd = isWindows() ? `where ${command}` : `which ${command}`; - await execAsync(cmd); - return true; - } catch { - return false; - } -} - -export function prependCargoToPath(): void { - process.env.PATH = `${cargoBinDir()}${path.delimiter}${process.env.PATH}`; -} -``` - -- [ ] **Step 5: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All 8 tests PASS. - -- [ ] **Step 6: Commit** - -```bash -git add installer/src/constants.ts installer/src/utils.ts installer/tests/utils.test.ts -git commit -m "feat(installer): add constants and utils module with version parsing" -``` - ---- - -### Task 3: Skill loader module - -**Files:** -- Create: `installer/src/skills.ts` -- Test: `installer/tests/skills.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect } from 'vitest'; -import { fileURLToPath } from 'url'; -import { parseFrontmatter, loadSkills } from '../src/skills.js'; - -describe('parseFrontmatter', () => { - it('extracts name and description from frontmatter', () => { - const content = `--- -name: syncable-analyze -description: Use when analyzing a project ---- - -## Purpose - -Analyze stuff.`; - - const result = parseFrontmatter(content); - expect(result.frontmatter.name).toBe('syncable-analyze'); - expect(result.frontmatter.description).toBe('Use when analyzing a project'); - expect(result.body).toContain('## Purpose'); - expect(result.body).toContain('Analyze stuff.'); - }); - - it('handles multi-line description', () => { - const content = `--- -name: test-skill -description: First line and more text here. ---- - -Body here.`; - - const result = parseFrontmatter(content); - expect(result.frontmatter.name).toBe('test-skill'); - expect(result.frontmatter.description).toContain('First line'); - expect(result.body).toBe('Body here.'); - }); - - it('throws on missing frontmatter', () => { - expect(() => parseFrontmatter('no frontmatter here')).toThrow(); - }); -}); - -describe('loadSkills', () => { - it('loads skills from a directory with commands/ and workflows/', () => { - // This test requires the copy-skills script to have run - // The skills/ dir should exist after build - // We'll test with the actual skills dir from the repo root - const skills = loadSkills(fileURLToPath(new URL('../../skills/', import.meta.url))); - expect(skills.length).toBe(11); - - const names = skills.map((s) => s.frontmatter.name); - expect(names).toContain('syncable-analyze'); - expect(names).toContain('syncable-deploy-pipeline'); - }); - - it('categorizes skills as command or workflow', () => { - const skills = loadSkills(fileURLToPath(new URL('../../skills/', import.meta.url))); - const commands = skills.filter((s) => s.category === 'command'); - const workflows = skills.filter((s) => s.category === 'workflow'); - expect(commands.length).toBe(7); - expect(workflows.length).toBe(4); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL — module not found. - -- [ ] **Step 3: Write `installer/src/skills.ts`** - -```typescript -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -export interface SkillFrontmatter { - name: string; - description: string; -} - -export interface Skill { - frontmatter: SkillFrontmatter; - body: string; - category: 'command' | 'workflow'; - filename: string; -} - -export function parseFrontmatter(content: string): { frontmatter: SkillFrontmatter; body: string } { - const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); - if (!match) throw new Error('No frontmatter found'); - - const frontmatterRaw = match[1]; - const body = match[2].trim(); - - const nameMatch = frontmatterRaw.match(/^name:\s*(.+)$/m); - const descMatch = frontmatterRaw.match(/^description:\s*(.+)$/m); - - if (!nameMatch || !descMatch) { - throw new Error('Frontmatter must contain name and description'); - } - - return { - frontmatter: { - name: nameMatch[1].trim(), - description: descMatch[1].trim(), - }, - body, - }; -} - -export function loadSkills(skillsDir: string): Skill[] { - const skills: Skill[] = []; - - for (const category of ['commands', 'workflows'] as const) { - const dir = path.join(skillsDir, category); - if (!fs.existsSync(dir)) continue; - - const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')); - for (const file of files) { - const content = fs.readFileSync(path.join(dir, file), 'utf-8'); - const { frontmatter, body } = parseFrontmatter(content); - const cat = category === 'commands' ? 'command' : 'workflow'; - skills.push({ frontmatter, body, category: cat, filename: file }); - } - } - - return skills; -} - -export function getBundledSkillsDir(): string { - return fileURLToPath(new URL('../skills/', import.meta.url)); -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS (including the 5 new skill loader tests + 8 previous utils tests). - -- [ ] **Step 5: Commit** - -```bash -git add installer/src/skills.ts installer/tests/skills.test.ts -git commit -m "feat(installer): add skill loader with frontmatter parsing" -``` - ---- - -### Task 4: Agent types and detection - -**Files:** -- Create: `installer/src/agents/types.ts` -- Create: `installer/src/agents/claude.ts` -- Create: `installer/src/agents/cursor.ts` -- Create: `installer/src/agents/windsurf.ts` -- Create: `installer/src/agents/codex.ts` -- Create: `installer/src/agents/gemini.ts` -- Create: `installer/src/agents/detect.ts` -- Test: `installer/tests/agents/detect.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { AgentName } from '../src/agents/types.js'; -import { allAgents } from '../src/agents/detect.js'; - -// We test the agent configs are well-formed and the detection logic -describe('agent configs', () => { - it('each agent has required fields', async () => { - const agents = allAgents(); - - for (const agent of agents) { - expect(agent.name).toBeDefined(); - expect(agent.displayName).toBeDefined(); - expect(agent.installType).toMatch(/^(global|project)$/); - expect(typeof agent.detect).toBe('function'); - expect(typeof agent.getSkillPath).toBe('function'); - } - }); - - it('has 5 agents total', async () => { - expect(allAgents().length).toBe(5); - }); - - it('claude and codex are global, others are project', async () => { - const agents = allAgents(); - const globalAgents = agents.filter((a) => a.installType === 'global'); - const projectAgents = agents.filter((a) => a.installType === 'project'); - - expect(globalAgents.map((a) => a.name)).toContain('claude'); - expect(globalAgents.map((a) => a.name)).toContain('codex'); - expect(projectAgents.map((a) => a.name)).toContain('cursor'); - expect(projectAgents.map((a) => a.name)).toContain('windsurf'); - expect(projectAgents.map((a) => a.name)).toContain('gemini'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL — modules not found. - -- [ ] **Step 3: Write `installer/src/agents/types.ts`** - -```typescript -export type AgentName = 'claude' | 'cursor' | 'windsurf' | 'codex' | 'gemini'; - -export interface AgentConfig { - name: AgentName; - displayName: string; - installType: 'global' | 'project'; - detect: () => Promise; - getSkillPath: () => string; -} -``` - -- [ ] **Step 4: Write the 5 agent config files** - -`installer/src/agents/claude.ts`: - -```typescript -import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { AgentConfig } from './types.js'; - -export const claudeAgent: AgentConfig = { - name: 'claude', - displayName: 'Claude Code', - installType: 'global', - detect: async () => { - return fs.existsSync(path.join(os.homedir(), '.claude')); - }, - getSkillPath: () => { - return path.join(os.homedir(), '.claude', 'skills', 'syncable'); - }, -}; -``` - -`installer/src/agents/codex.ts`: - -```typescript -import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { AgentConfig } from './types.js'; -import { commandExists } from '../utils.js'; - -export const codexAgent: AgentConfig = { - name: 'codex', - displayName: 'Codex', - installType: 'global', - detect: async () => { - return fs.existsSync(path.join(os.homedir(), '.codex')) || await commandExists('codex'); - }, - getSkillPath: () => { - return path.join(os.homedir(), '.codex', 'skills'); - }, -}; -``` - -`installer/src/agents/cursor.ts`: - -```typescript -import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { AgentConfig } from './types.js'; - -export const cursorAgent: AgentConfig = { - name: 'cursor', - displayName: 'Cursor', - installType: 'project', - detect: async () => { - return fs.existsSync(path.join(os.homedir(), '.cursor')); - }, - getSkillPath: () => { - return path.join(process.cwd(), '.cursor', 'rules'); - }, -}; -``` - -`installer/src/agents/windsurf.ts`: - -```typescript -import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { AgentConfig } from './types.js'; - -export const windsurfAgent: AgentConfig = { - name: 'windsurf', - displayName: 'Windsurf', - installType: 'project', - detect: async () => { - return fs.existsSync(path.join(os.homedir(), '.codeium', 'windsurf')); - }, - getSkillPath: () => { - return path.join(process.cwd(), '.windsurf', 'rules'); - }, -}; -``` - -`installer/src/agents/gemini.ts`: - -```typescript -import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { AgentConfig } from './types.js'; -import { commandExists } from '../utils.js'; - -export const geminiAgent: AgentConfig = { - name: 'gemini', - displayName: 'Gemini CLI', - installType: 'project', - detect: async () => { - return fs.existsSync(path.join(os.homedir(), '.gemini')) || await commandExists('gemini'); - }, - getSkillPath: () => { - return path.join(process.cwd(), 'GEMINI.md'); - }, -}; -``` - -- [ ] **Step 5: Write `installer/src/agents/detect.ts`** - -```typescript -import { AgentConfig, AgentName } from './types.js'; -import { claudeAgent } from './claude.js'; -import { codexAgent } from './codex.js'; -import { cursorAgent } from './cursor.js'; -import { windsurfAgent } from './windsurf.js'; -import { geminiAgent } from './gemini.js'; - -export function allAgents(): AgentConfig[] { - return [claudeAgent, cursorAgent, windsurfAgent, codexAgent, geminiAgent]; -} - -export function getAgent(name: AgentName): AgentConfig | undefined { - return allAgents().find((a) => a.name === name); -} - -export interface DetectionResult { - agent: AgentConfig; - detected: boolean; -} - -export async function detectAgents(): Promise { - const agents = allAgents(); - const results: DetectionResult[] = []; - - for (const agent of agents) { - const detected = await agent.detect(); - results.push({ agent, detected }); - } - - return results; -} -``` - -- [ ] **Step 6: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 7: Commit** - -```bash -git add installer/src/agents/ installer/tests/agents/ -git commit -m "feat(installer): add agent detection for 5 AI coding agents" -``` - ---- - -### Task 5: Format transformers — types and Claude (no-op) - -**Files:** -- Create: `installer/src/transformers/types.ts` -- Create: `installer/src/transformers/claude.ts` -- Test: `installer/tests/transformers/claude.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect } from 'vitest'; -import { transformForClaude } from '../src/transformers/claude.js'; - -const sampleSkill = { - frontmatter: { name: 'syncable-analyze', description: 'Use when analyzing a project' }, - body: '## Purpose\n\nAnalyze stuff.', - category: 'command' as const, - filename: 'syncable-analyze.md', -}; - -describe('transformForClaude', () => { - it('returns files preserving directory structure', () => { - const result = transformForClaude(sampleSkill); - expect(result.length).toBe(1); - expect(result[0].relativePath).toBe('commands/syncable-analyze.md'); - }); - - it('preserves content exactly (no-op transform)', () => { - const result = transformForClaude(sampleSkill); - expect(result[0].content).toContain('---'); - expect(result[0].content).toContain('name: syncable-analyze'); - expect(result[0].content).toContain('## Purpose'); - expect(result[0].content).toContain('Analyze stuff.'); - }); - - it('uses workflows/ for workflow skills', () => { - const workflow = { ...sampleSkill, category: 'workflow' as const }; - const result = transformForClaude(workflow); - expect(result[0].relativePath).toBe('workflows/syncable-analyze.md'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/transformers/types.ts`** - -```typescript -import { Skill } from '../skills.js'; - -export interface TransformResult { - relativePath: string; - content: string; -} - -export type TransformFn = (skill: Skill) => TransformResult[]; -``` - -- [ ] **Step 4: Write `installer/src/transformers/claude.ts`** - -```typescript -import { Skill } from '../skills.js'; -import { TransformResult } from './types.js'; - -export function transformForClaude(skill: Skill): TransformResult[] { - const dir = skill.category === 'command' ? 'commands' : 'workflows'; - const content = `---\nname: ${skill.frontmatter.name}\ndescription: ${skill.frontmatter.description}\n---\n\n${skill.body}`; - return [{ relativePath: `${dir}/${skill.filename}`, content }]; -} -``` - -- [ ] **Step 5: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 6: Commit** - -```bash -git add installer/src/transformers/types.ts installer/src/transformers/claude.ts installer/tests/transformers/claude.test.ts -git commit -m "feat(installer): add Claude Code transformer (no-op, native format)" -``` - ---- - -### Task 6: Format transformers — Codex - -**Files:** -- Create: `installer/src/transformers/codex.ts` -- Test: `installer/tests/transformers/codex.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect } from 'vitest'; -import { transformForCodex } from '../src/transformers/codex.js'; - -const sampleSkill = { - frontmatter: { name: 'syncable-analyze', description: 'Use when analyzing a project' }, - body: '## Purpose\n\nAnalyze stuff.', - category: 'command' as const, - filename: 'syncable-analyze.md', -}; - -describe('transformForCodex', () => { - it('creates a directory with SKILL.md', () => { - const result = transformForCodex(sampleSkill); - expect(result.length).toBe(1); - expect(result[0].relativePath).toBe('syncable-analyze/SKILL.md'); - }); - - it('preserves frontmatter in SKILL.md', () => { - const result = transformForCodex(sampleSkill); - expect(result[0].content).toContain('name: syncable-analyze'); - expect(result[0].content).toContain('description: Use when analyzing a project'); - }); - - it('preserves body content', () => { - const result = transformForCodex(sampleSkill); - expect(result[0].content).toContain('## Purpose'); - expect(result[0].content).toContain('Analyze stuff.'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/transformers/codex.ts`** - -```typescript -import { Skill } from '../skills.js'; -import { TransformResult } from './types.js'; - -export function transformForCodex(skill: Skill): TransformResult[] { - const content = `---\nname: ${skill.frontmatter.name}\ndescription: ${skill.frontmatter.description}\n---\n\n${skill.body}`; - return [{ relativePath: `${skill.frontmatter.name}/SKILL.md`, content }]; -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 5: Commit** - -```bash -git add installer/src/transformers/codex.ts installer/tests/transformers/codex.test.ts -git commit -m "feat(installer): add Codex transformer (SKILL.md directory format)" -``` - ---- - -### Task 7: Format transformers — Cursor - -**Files:** -- Create: `installer/src/transformers/cursor.ts` -- Test: `installer/tests/transformers/cursor.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect } from 'vitest'; -import { transformForCursor } from '../src/transformers/cursor.js'; - -const sampleSkill = { - frontmatter: { name: 'syncable-analyze', description: 'Use when analyzing a project' }, - body: '## Purpose\n\nAnalyze stuff.', - category: 'command' as const, - filename: 'syncable-analyze.md', -}; - -describe('transformForCursor', () => { - it('creates a .mdc file', () => { - const result = transformForCursor(sampleSkill); - expect(result.length).toBe(1); - expect(result[0].relativePath).toBe('syncable-analyze.mdc'); - }); - - it('uses alwaysApply: true frontmatter', () => { - const result = transformForCursor(sampleSkill); - expect(result[0].content).toContain('alwaysApply: true'); - }); - - it('prefixes description with "Syncable CLI: "', () => { - const result = transformForCursor(sampleSkill); - expect(result[0].content).toContain('description: "Syncable CLI: Use when analyzing a project"'); - }); - - it('includes empty globs field', () => { - const result = transformForCursor(sampleSkill); - expect(result[0].content).toContain('globs:'); - }); - - it('drops name from frontmatter', () => { - const result = transformForCursor(sampleSkill); - // The .mdc frontmatter should NOT contain "name:" - const frontmatterSection = result[0].content.split('---')[1]; - expect(frontmatterSection).not.toContain('name:'); - }); - - it('preserves body content', () => { - const result = transformForCursor(sampleSkill); - expect(result[0].content).toContain('## Purpose'); - expect(result[0].content).toContain('Analyze stuff.'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/transformers/cursor.ts`** - -```typescript -import { Skill } from '../skills.js'; -import { TransformResult } from './types.js'; - -export function transformForCursor(skill: Skill): TransformResult[] { - const filename = skill.frontmatter.name + '.mdc'; - const content = `---\ndescription: "Syncable CLI: ${skill.frontmatter.description}"\nglobs:\nalwaysApply: true\n---\n\n${skill.body}`; - return [{ relativePath: filename, content }]; -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 5: Commit** - -```bash -git add installer/src/transformers/cursor.ts installer/tests/transformers/cursor.test.ts -git commit -m "feat(installer): add Cursor transformer (.mdc format with alwaysApply)" -``` - ---- - -### Task 8: Format transformers — Windsurf and Gemini - -**Files:** -- Create: `installer/src/transformers/windsurf.ts` -- Create: `installer/src/transformers/gemini.ts` -- Test: `installer/tests/transformers/windsurf.test.ts` -- Test: `installer/tests/transformers/gemini.test.ts` - -- [ ] **Step 1: Write the failing tests for Windsurf** - -```typescript -import { describe, it, expect } from 'vitest'; -import { transformForWindsurf } from '../src/transformers/windsurf.js'; - -const sampleSkill = { - frontmatter: { name: 'syncable-analyze', description: 'Use when analyzing a project' }, - body: '## Purpose\n\nAnalyze stuff.', - category: 'command' as const, - filename: 'syncable-analyze.md', -}; - -describe('transformForWindsurf', () => { - it('creates a .md file with syncable- prefix', () => { - const result = transformForWindsurf(sampleSkill); - expect(result.length).toBe(1); - expect(result[0].relativePath).toBe('syncable-analyze.md'); - }); - - it('uses trigger: always', () => { - const result = transformForWindsurf(sampleSkill); - expect(result[0].content).toContain('trigger: always'); - }); - - it('prefixes description with "Syncable CLI: "', () => { - const result = transformForWindsurf(sampleSkill); - expect(result[0].content).toContain('description: "Syncable CLI: Use when analyzing a project"'); - }); - - it('preserves body', () => { - const result = transformForWindsurf(sampleSkill); - expect(result[0].content).toContain('Analyze stuff.'); - }); -}); -``` - -- [ ] **Step 2: Write the failing tests for Gemini** - -```typescript -import { describe, it, expect } from 'vitest'; -import { transformForGemini } from '../src/transformers/gemini.js'; -import { Skill } from '../src/skills.js'; - -const skills: Skill[] = [ - { - frontmatter: { name: 'syncable-analyze', description: 'Analyze stuff' }, - body: '## Purpose\n\nAnalyze.', - category: 'command', - filename: 'syncable-analyze.md', - }, - { - frontmatter: { name: 'syncable-security', description: 'Security scan' }, - body: '## Purpose\n\nScan.', - category: 'command', - filename: 'syncable-security.md', - }, -]; - -describe('transformForGemini', () => { - it('produces a single content block with markers', () => { - const result = transformForGemini(skills); - expect(result).toContain(''); - expect(result).toContain(''); - }); - - it('includes all skills as sections', () => { - const result = transformForGemini(skills); - expect(result).toContain('### syncable-analyze'); - expect(result).toContain('### syncable-security'); - }); - - it('includes skill body content', () => { - const result = transformForGemini(skills); - expect(result).toContain('Analyze.'); - expect(result).toContain('Scan.'); - }); - - it('has header text', () => { - const result = transformForGemini(skills); - expect(result).toContain('## Syncable CLI Skills'); - expect(result).toContain('The following skills describe how to use the Syncable CLI'); - }); -}); -``` - -- [ ] **Step 3: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 4: Write `installer/src/transformers/windsurf.ts`** - -```typescript -import { Skill } from '../skills.js'; -import { TransformResult } from './types.js'; - -export function transformForWindsurf(skill: Skill): TransformResult[] { - const filename = skill.frontmatter.name + '.md'; - const content = `---\ntrigger: always\ndescription: "Syncable CLI: ${skill.frontmatter.description}"\n---\n\n${skill.body}`; - return [{ relativePath: filename, content }]; -} -``` - -- [ ] **Step 5: Write `installer/src/transformers/gemini.ts`** - -```typescript -import { Skill } from '../skills.js'; -import { SKILL_MARKER_START, SKILL_MARKER_END } from '../constants.js'; - -export function transformForGemini(skills: Skill[]): string { - const sections = skills - .map((s) => `### ${s.frontmatter.name}\n\n${s.body}`) - .join('\n\n'); - - return `${SKILL_MARKER_START} -## Syncable CLI Skills - -The following skills describe how to use the Syncable CLI (sync-ctl) toolbox. - -${sections} -${SKILL_MARKER_END}`; -} -``` - -- [ ] **Step 6: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 7: Commit** - -```bash -git add installer/src/transformers/windsurf.ts installer/src/transformers/gemini.ts installer/tests/transformers/ -git commit -m "feat(installer): add Windsurf and Gemini format transformers" -``` - ---- - -### Task 9: Prerequisites checker - -**Files:** -- Create: `installer/src/prerequisites/check.ts` -- Create: `installer/src/prerequisites/install-rustup.ts` -- Create: `installer/src/prerequisites/install-cli.ts` -- Test: `installer/tests/prerequisites/check.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect, vi } from 'vitest'; -import { checkNodeVersion, PrereqStatus } from '../src/prerequisites/check.js'; - -describe('checkNodeVersion', () => { - it('returns ok for current Node version (>=18)', () => { - const result = checkNodeVersion(); - expect(result.status).toBe('ok'); - expect(result.version).toBeDefined(); - }); -}); - -describe('PrereqStatus', () => { - it('has expected shape', () => { - const status: PrereqStatus = { - status: 'ok', - version: '1.0.0', - }; - expect(status.status).toBe('ok'); - }); - - it('can represent missing', () => { - const status: PrereqStatus = { - status: 'missing', - }; - expect(status.status).toBe('missing'); - }); - - it('can represent outdated', () => { - const status: PrereqStatus = { - status: 'outdated', - version: '0.30.0', - }; - expect(status.status).toBe('outdated'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/prerequisites/check.ts`** - -```typescript -import { execCommand, commandExists, parseVersion, compareVersions, cargoBinDir } from '../utils.js'; -import { MIN_SYNC_CTL_VERSION } from '../constants.js'; -import fs from 'fs'; -import path from 'path'; - -export interface PrereqStatus { - status: 'ok' | 'missing' | 'outdated'; - version?: string; -} - -export function checkNodeVersion(): PrereqStatus { - const version = process.version; - const parsed = parseVersion(version); - if (!parsed || parsed.major < 18) { - return { status: 'outdated', version }; - } - return { status: 'ok', version }; -} - -export async function checkCargo(): Promise { - try { - const { stdout } = await execCommand('cargo --version'); - const version = parseVersion(stdout); - return { status: 'ok', version: version ? `${version.major}.${version.minor}.${version.patch}` : stdout.trim() }; - } catch { - // Check if cargo exists at the known cargo bin path - const cargoPath = path.join(cargoBinDir(), 'cargo'); - if (fs.existsSync(cargoPath)) { - return { status: 'ok', version: 'unknown' }; - } - return { status: 'missing' }; - } -} - -export async function checkSyncCtl(): Promise { - try { - const { stdout } = await execCommand('sync-ctl --version'); - const version = parseVersion(stdout); - if (!version) { - return { status: 'ok', version: stdout.trim() }; - } - - const minVersion = parseVersion(MIN_SYNC_CTL_VERSION); - if (minVersion && compareVersions(version, minVersion) < 0) { - return { status: 'outdated', version: `${version.major}.${version.minor}.${version.patch}` }; - } - - return { status: 'ok', version: `${version.major}.${version.minor}.${version.patch}` }; - } catch { - return { status: 'missing' }; - } -} -``` - -- [ ] **Step 4: Write `installer/src/prerequisites/install-rustup.ts`** - -```typescript -import { execCommand, isWindows, prependCargoToPath } from '../utils.js'; - -export async function installRustup(): Promise { - try { - if (isWindows()) { - // Step 1: Try winget - try { - await execCommand('winget install Rustlang.Rustup --accept-source-agreements --accept-package-agreements'); - prependCargoToPath(); - return true; - } catch { - // winget unavailable - } - - // Step 2: Try downloading rustup-init.exe - try { - await execCommand('curl -sSf https://win.rustup.rs/x86_64 -o rustup-init.exe && .\\rustup-init.exe -y && del rustup-init.exe'); - prependCargoToPath(); - return true; - } catch { - // Download failed - } - - // Step 3: Manual instructions - console.error('Could not install Rust automatically. Install manually: https://rustup.rs'); - return false; - } else { - await execCommand('curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y'); - prependCargoToPath(); - return true; - } - } catch { - return false; - } -} -``` - -- [ ] **Step 5: Write `installer/src/prerequisites/install-cli.ts`** - -```typescript -import { execCommand } from '../utils.js'; - -export async function installSyncCtl(force: boolean = false): Promise { - try { - const cmd = force - ? 'cargo install syncable-cli --force' - : 'cargo install syncable-cli'; - await execCommand(cmd); - return true; - } catch { - return false; - } -} -``` - -- [ ] **Step 6: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 7: Commit** - -```bash -git add installer/src/prerequisites/ installer/tests/prerequisites/ -git commit -m "feat(installer): add prerequisite check and installation modules" -``` - ---- - -### Task 10: Install command - -**Files:** -- Create: `installer/src/commands/install.ts` -- Test: `installer/tests/commands/install.test.ts` - -This is the largest command. It orchestrates prerequisites, agent detection, skill installation. - -- [ ] **Step 1: Write the failing tests** - -Focus on the skill-writing logic (the testable core), not the interactive prompts. - -```typescript -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { writeSkillsForClaude, writeSkillsForCodex, writeSkillsForCursor, writeSkillsForWindsurf, writeSkillsForGemini } from '../src/commands/install.js'; -import { Skill } from '../src/skills.js'; -import fs from 'fs'; -import path from 'path'; -import os from 'os'; - -const tmpDir = path.join(os.tmpdir(), 'syncable-installer-test-' + Date.now()); - -const sampleSkills: Skill[] = [ - { - frontmatter: { name: 'syncable-analyze', description: 'Analyze' }, - body: '## Purpose\n\nAnalyze.', - category: 'command', - filename: 'syncable-analyze.md', - }, - { - frontmatter: { name: 'syncable-project-assessment', description: 'Assess' }, - body: '## Purpose\n\nAssess.', - category: 'workflow', - filename: 'syncable-project-assessment.md', - }, -]; - -beforeEach(() => { - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterEach(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -describe('writeSkillsForClaude', () => { - it('writes skills preserving commands/ and workflows/ structure', () => { - writeSkillsForClaude(sampleSkills, tmpDir); - expect(fs.existsSync(path.join(tmpDir, 'commands', 'syncable-analyze.md'))).toBe(true); - expect(fs.existsSync(path.join(tmpDir, 'workflows', 'syncable-project-assessment.md'))).toBe(true); - }); - - it('preserves skill content', () => { - writeSkillsForClaude(sampleSkills, tmpDir); - const content = fs.readFileSync(path.join(tmpDir, 'commands', 'syncable-analyze.md'), 'utf-8'); - expect(content).toContain('name: syncable-analyze'); - expect(content).toContain('Analyze.'); - }); -}); - -describe('writeSkillsForCodex', () => { - it('writes each skill as a directory with SKILL.md', () => { - writeSkillsForCodex(sampleSkills, tmpDir); - expect(fs.existsSync(path.join(tmpDir, 'syncable-analyze', 'SKILL.md'))).toBe(true); - expect(fs.existsSync(path.join(tmpDir, 'syncable-project-assessment', 'SKILL.md'))).toBe(true); - }); -}); - -describe('writeSkillsForCursor', () => { - it('writes .mdc files', () => { - writeSkillsForCursor(sampleSkills, tmpDir); - expect(fs.existsSync(path.join(tmpDir, 'syncable-analyze.mdc'))).toBe(true); - expect(fs.existsSync(path.join(tmpDir, 'syncable-project-assessment.mdc'))).toBe(true); - }); - - it('uses alwaysApply frontmatter', () => { - writeSkillsForCursor(sampleSkills, tmpDir); - const content = fs.readFileSync(path.join(tmpDir, 'syncable-analyze.mdc'), 'utf-8'); - expect(content).toContain('alwaysApply: true'); - }); -}); - -describe('writeSkillsForWindsurf', () => { - it('writes .md files with trigger: always', () => { - writeSkillsForWindsurf(sampleSkills, tmpDir); - const content = fs.readFileSync(path.join(tmpDir, 'syncable-analyze.md'), 'utf-8'); - expect(content).toContain('trigger: always'); - }); -}); - -describe('writeSkillsForGemini', () => { - it('writes content with markers to a file', () => { - const filePath = path.join(tmpDir, 'GEMINI.md'); - writeSkillsForGemini(sampleSkills, filePath); - const content = fs.readFileSync(filePath, 'utf-8'); - expect(content).toContain(''); - expect(content).toContain(''); - expect(content).toContain('### syncable-analyze'); - }); - - it('appends to existing file without destroying content', () => { - const filePath = path.join(tmpDir, 'GEMINI.md'); - fs.writeFileSync(filePath, '# My Project\n\nExisting content.\n'); - writeSkillsForGemini(sampleSkills, filePath); - const content = fs.readFileSync(filePath, 'utf-8'); - expect(content).toContain('# My Project'); - expect(content).toContain('Existing content.'); - expect(content).toContain(''); - }); - - it('replaces existing Syncable section on re-install', () => { - const filePath = path.join(tmpDir, 'GEMINI.md'); - fs.writeFileSync(filePath, '# Header\n\nold content\n\n# Footer\n'); - writeSkillsForGemini(sampleSkills, filePath); - const content = fs.readFileSync(filePath, 'utf-8'); - expect(content).toContain('# Header'); - expect(content).toContain('# Footer'); - expect(content).not.toContain('old content'); - expect(content).toContain('### syncable-analyze'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/commands/install.ts`** - -```typescript -import fs from 'fs'; -import path from 'path'; -import { Skill, loadSkills, getBundledSkillsDir } from '../skills.js'; -import { transformForClaude } from '../transformers/claude.js'; -import { transformForCodex } from '../transformers/codex.js'; -import { transformForCursor } from '../transformers/cursor.js'; -import { transformForWindsurf } from '../transformers/windsurf.js'; -import { transformForGemini } from '../transformers/gemini.js'; -import { SKILL_MARKER_START, SKILL_MARKER_END } from '../constants.js'; - -export function writeSkillsForClaude(skills: Skill[], destDir: string): void { - for (const skill of skills) { - const results = transformForClaude(skill); - for (const { relativePath, content } of results) { - const fullPath = path.join(destDir, relativePath); - fs.mkdirSync(path.dirname(fullPath), { recursive: true }); - fs.writeFileSync(fullPath, content); - } - } -} - -export function writeSkillsForCodex(skills: Skill[], destDir: string): void { - for (const skill of skills) { - const results = transformForCodex(skill); - for (const { relativePath, content } of results) { - const fullPath = path.join(destDir, relativePath); - fs.mkdirSync(path.dirname(fullPath), { recursive: true }); - fs.writeFileSync(fullPath, content); - } - } -} - -export function writeSkillsForCursor(skills: Skill[], destDir: string): void { - fs.mkdirSync(destDir, { recursive: true }); - for (const skill of skills) { - const results = transformForCursor(skill); - for (const { relativePath, content } of results) { - fs.writeFileSync(path.join(destDir, relativePath), content); - } - } -} - -export function writeSkillsForWindsurf(skills: Skill[], destDir: string): void { - fs.mkdirSync(destDir, { recursive: true }); - for (const skill of skills) { - const results = transformForWindsurf(skill); - for (const { relativePath, content } of results) { - fs.writeFileSync(path.join(destDir, relativePath), content); - } - } -} - -export function writeSkillsForGemini(skills: Skill[], filePath: string): void { - const geminiContent = transformForGemini(skills); - let existing = ''; - - if (fs.existsSync(filePath)) { - existing = fs.readFileSync(filePath, 'utf-8'); - - // Replace existing section if present - const startIdx = existing.indexOf(SKILL_MARKER_START); - const endIdx = existing.indexOf(SKILL_MARKER_END); - if (startIdx !== -1 && endIdx !== -1) { - const before = existing.slice(0, startIdx); - const after = existing.slice(endIdx + SKILL_MARKER_END.length); - fs.writeFileSync(filePath, before + geminiContent + after); - return; - } - } - - // Append to existing or create new - const separator = existing && !existing.endsWith('\n') ? '\n\n' : existing ? '\n' : ''; - fs.writeFileSync(filePath, existing + separator + geminiContent + '\n'); -} - -export interface InstallOptions { - skipCli: boolean; - dryRun: boolean; - agents?: string[]; - globalOnly: boolean; - projectOnly: boolean; - yes: boolean; - verbose: boolean; -} - -export type AgentWriter = (skills: Skill[], destOrPath: string) => void; - -export const agentWriters: Record = { - claude: writeSkillsForClaude, - codex: writeSkillsForCodex, - cursor: writeSkillsForCursor, - windsurf: writeSkillsForWindsurf, - gemini: writeSkillsForGemini, -}; -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 5: Commit** - -```bash -git add installer/src/commands/install.ts installer/tests/commands/install.test.ts -git commit -m "feat(installer): add install command with skill writers for all 5 agents" -``` - ---- - -### Task 11: Uninstall command - -**Files:** -- Create: `installer/src/commands/uninstall.ts` -- Test: `installer/tests/commands/uninstall.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { removeSyncableSkills, removeGeminiSection } from '../src/commands/uninstall.js'; -import fs from 'fs'; -import path from 'path'; -import os from 'os'; - -const tmpDir = path.join(os.tmpdir(), 'syncable-uninstall-test-' + Date.now()); - -beforeEach(() => { - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterEach(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -describe('removeSyncableSkills', () => { - it('removes a directory and its contents', () => { - const skillDir = path.join(tmpDir, 'syncable'); - fs.mkdirSync(path.join(skillDir, 'commands'), { recursive: true }); - fs.writeFileSync(path.join(skillDir, 'commands', 'test.md'), 'test'); - removeSyncableSkills(skillDir); - expect(fs.existsSync(skillDir)).toBe(false); - }); - - it('removes glob-matched files', () => { - fs.writeFileSync(path.join(tmpDir, 'syncable-analyze.mdc'), 'test'); - fs.writeFileSync(path.join(tmpDir, 'syncable-security.mdc'), 'test'); - fs.writeFileSync(path.join(tmpDir, 'other-rule.mdc'), 'keep'); - removeSyncableSkills(tmpDir, 'syncable-*.mdc'); - expect(fs.existsSync(path.join(tmpDir, 'syncable-analyze.mdc'))).toBe(false); - expect(fs.existsSync(path.join(tmpDir, 'syncable-security.mdc'))).toBe(false); - expect(fs.existsSync(path.join(tmpDir, 'other-rule.mdc'))).toBe(true); - }); - - it('no-ops when directory does not exist', () => { - expect(() => removeSyncableSkills(path.join(tmpDir, 'nonexistent'))).not.toThrow(); - }); -}); - -describe('removeGeminiSection', () => { - it('removes content between markers', () => { - const filePath = path.join(tmpDir, 'GEMINI.md'); - fs.writeFileSync(filePath, '# Header\n\nstuff\n\n# Footer\n'); - removeGeminiSection(filePath); - const content = fs.readFileSync(filePath, 'utf-8'); - expect(content).toContain('# Header'); - expect(content).toContain('# Footer'); - expect(content).not.toContain('stuff'); - expect(content).not.toContain('SYNCABLE-CLI-SKILLS'); - }); - - it('no-ops when file does not exist', () => { - expect(() => removeGeminiSection(path.join(tmpDir, 'nope.md'))).not.toThrow(); - }); - - it('no-ops when no markers found', () => { - const filePath = path.join(tmpDir, 'GEMINI.md'); - fs.writeFileSync(filePath, '# Just a normal file\n'); - removeGeminiSection(filePath); - const content = fs.readFileSync(filePath, 'utf-8'); - expect(content).toBe('# Just a normal file\n'); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/commands/uninstall.ts`** - -```typescript -import fs from 'fs'; -import path from 'path'; -import { SKILL_MARKER_START, SKILL_MARKER_END } from '../constants.js'; - -export function removeSyncableSkills(dirPath: string, globPattern?: string): void { - if (!fs.existsSync(dirPath)) return; - - if (globPattern) { - // Match entries by prefix pattern (e.g., "syncable-*.mdc" or "syncable-*") - const prefix = globPattern.split('*')[0]; // "syncable-" - const suffix = globPattern.split('*')[1]; // ".mdc" or "" - const entries = fs.readdirSync(dirPath); - for (const entry of entries) { - if (entry.startsWith(prefix) && entry.endsWith(suffix)) { - const fullPath = path.join(dirPath, entry); - const stat = fs.statSync(fullPath); - if (stat.isDirectory()) { - fs.rmSync(fullPath, { recursive: true, force: true }); - } else { - fs.unlinkSync(fullPath); - } - } - } - } else { - // Remove the entire directory - fs.rmSync(dirPath, { recursive: true, force: true }); - } -} - -export function removeGeminiSection(filePath: string): void { - if (!fs.existsSync(filePath)) return; - - const content = fs.readFileSync(filePath, 'utf-8'); - const startIdx = content.indexOf(SKILL_MARKER_START); - const endIdx = content.indexOf(SKILL_MARKER_END); - - if (startIdx === -1 || endIdx === -1) return; - - const before = content.slice(0, startIdx); - const after = content.slice(endIdx + SKILL_MARKER_END.length); - - // Clean up extra blank lines left behind - const cleaned = (before + after).replace(/\n{3,}/g, '\n\n').trim() + '\n'; - fs.writeFileSync(filePath, cleaned); -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 5: Commit** - -```bash -git add installer/src/commands/uninstall.ts installer/tests/commands/uninstall.test.ts -git commit -m "feat(installer): add uninstall command with glob removal and Gemini marker cleanup" -``` - ---- - -### Task 12: Status command - -**Files:** -- Create: `installer/src/commands/status.ts` -- Test: `installer/tests/commands/status.test.ts` - -- [ ] **Step 1: Write the failing tests** - -```typescript -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { countInstalledSkills } from '../src/commands/status.js'; -import fs from 'fs'; -import path from 'path'; -import os from 'os'; - -const tmpDir = path.join(os.tmpdir(), 'syncable-status-test-' + Date.now()); - -beforeEach(() => { - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterEach(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -describe('countInstalledSkills', () => { - it('counts .md files in commands/ and workflows/ (Claude format)', () => { - fs.mkdirSync(path.join(tmpDir, 'commands'), { recursive: true }); - fs.mkdirSync(path.join(tmpDir, 'workflows'), { recursive: true }); - fs.writeFileSync(path.join(tmpDir, 'commands', 'a.md'), ''); - fs.writeFileSync(path.join(tmpDir, 'commands', 'b.md'), ''); - fs.writeFileSync(path.join(tmpDir, 'workflows', 'c.md'), ''); - expect(countInstalledSkills(tmpDir, 'claude')).toBe(3); - }); - - it('counts directories (Codex format)', () => { - fs.mkdirSync(path.join(tmpDir, 'syncable-analyze'), { recursive: true }); - fs.writeFileSync(path.join(tmpDir, 'syncable-analyze', 'SKILL.md'), ''); - fs.mkdirSync(path.join(tmpDir, 'syncable-security'), { recursive: true }); - fs.writeFileSync(path.join(tmpDir, 'syncable-security', 'SKILL.md'), ''); - expect(countInstalledSkills(tmpDir, 'codex')).toBe(2); - }); - - it('counts .mdc files (Cursor format)', () => { - fs.writeFileSync(path.join(tmpDir, 'syncable-analyze.mdc'), ''); - fs.writeFileSync(path.join(tmpDir, 'syncable-security.mdc'), ''); - fs.writeFileSync(path.join(tmpDir, 'other.mdc'), ''); - expect(countInstalledSkills(tmpDir, 'cursor')).toBe(2); - }); - - it('returns 0 when directory does not exist', () => { - expect(countInstalledSkills('/nonexistent', 'claude')).toBe(0); - }); -}); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `cd installer && npm test` -Expected: FAIL. - -- [ ] **Step 3: Write `installer/src/commands/status.ts`** - -```typescript -import fs from 'fs'; -import path from 'path'; -import { AgentName } from '../agents/types.js'; -import { SKILL_MARKER_START } from '../constants.js'; - -export function countInstalledSkills(dirOrPath: string, agent: AgentName | string): number { - if (!fs.existsSync(dirOrPath)) return 0; - - switch (agent) { - case 'claude': { - let count = 0; - for (const sub of ['commands', 'workflows']) { - const dir = path.join(dirOrPath, sub); - if (fs.existsSync(dir)) { - count += fs.readdirSync(dir).filter((f) => f.endsWith('.md')).length; - } - } - return count; - } - - case 'codex': { - return fs.readdirSync(dirOrPath) - .filter((f) => f.startsWith('syncable-') && fs.statSync(path.join(dirOrPath, f)).isDirectory()) - .length; - } - - case 'cursor': { - return fs.readdirSync(dirOrPath) - .filter((f) => f.startsWith('syncable-') && f.endsWith('.mdc')) - .length; - } - - case 'windsurf': { - return fs.readdirSync(dirOrPath) - .filter((f) => f.startsWith('syncable-') && f.endsWith('.md')) - .length; - } - - case 'gemini': { - // For Gemini, the path is the GEMINI.md file itself - if (!fs.existsSync(dirOrPath)) return 0; - const content = fs.readFileSync(dirOrPath, 'utf-8'); - if (content.includes(SKILL_MARKER_START)) { - // Count ### headings between markers - const start = content.indexOf(SKILL_MARKER_START); - const end = content.indexOf(''); - if (start !== -1 && end !== -1) { - const section = content.slice(start, end); - return (section.match(/^### /gm) || []).length; - } - } - return 0; - } - - default: - return 0; - } -} -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `cd installer && npm test` -Expected: All tests PASS. - -- [ ] **Step 5: Commit** - -```bash -git add installer/src/commands/status.ts installer/tests/commands/status.test.ts -git commit -m "feat(installer): add status command with per-agent skill counting" -``` - ---- - -### Task 13: Update command - -**Files:** -- Create: `installer/src/commands/update.ts` - -- [ ] **Step 1: Write `installer/src/commands/update.ts`** - -The update command is a thin orchestrator: uninstall then install. No separate tests needed — it composes already-tested functions. - -```typescript -// Update is: uninstall + install for the same agents -// This is a thin wrapper used by the CLI entrypoint. -// The actual logic is in install.ts and uninstall.ts. - -export { removeSyncableSkills, removeGeminiSection } from './uninstall.js'; -export { writeSkillsForClaude, writeSkillsForCodex, writeSkillsForCursor, writeSkillsForWindsurf, writeSkillsForGemini } from './install.js'; -``` - -- [ ] **Step 2: Commit** - -```bash -git add installer/src/commands/update.ts -git commit -m "feat(installer): add update command (re-exports uninstall + install)" -``` - ---- - -### Task 14: CLI entrypoint - -**Files:** -- Modify: `installer/src/index.ts` - -This is the interactive orchestration layer. It uses commander for CLI parsing, inquirer for prompts, ora for spinners, and chalk for colors. This wires together all the modules. - -- [ ] **Step 1: Write the full CLI entrypoint** - -```typescript -#!/usr/bin/env node - -import { Command } from 'commander'; -import inquirer from 'inquirer'; -import ora from 'ora'; -import chalk from 'chalk'; -import { createRequire } from 'module'; -import { checkNodeVersion, checkCargo, checkSyncCtl } from './prerequisites/check.js'; -import { installRustup } from './prerequisites/install-rustup.js'; -import { installSyncCtl } from './prerequisites/install-cli.js'; -import { detectAgents, allAgents } from './agents/detect.js'; -import { AgentConfig, AgentName } from './agents/types.js'; -import { loadSkills, getBundledSkillsDir } from './skills.js'; -import { - writeSkillsForClaude, - writeSkillsForCodex, - writeSkillsForCursor, - writeSkillsForWindsurf, - writeSkillsForGemini, - InstallOptions, -} from './commands/install.js'; -import { removeSyncableSkills, removeGeminiSection } from './commands/uninstall.js'; -import { countInstalledSkills } from './commands/status.js'; - -const require = createRequire(import.meta.url); -const pkg = require('../package.json'); - -const program = new Command(); - -program - .name('syncable-cli-skills') - .description('Install Syncable CLI skills for AI coding agents') - .version(pkg.version); - -program - .command('install', { isDefault: true }) - .description('Install sync-ctl and skills') - .option('--skip-cli', 'Skip sync-ctl installation check') - .option('--dry-run', 'Show what would be done without doing it') - .option('--agents ', 'Comma-separated agent list') - .option('--global-only', 'Only install global skills') - .option('--project-only', 'Only install project-level rules') - .option('-y, --yes', 'Skip confirmations') - .option('--verbose', 'Show detailed output') - .action(async (opts) => { - console.log(chalk.bold('\n Syncable CLI Skills Installer')); - console.log(' ' + '─'.repeat(29) + '\n'); - - // Check Node.js version - const nodeCheck = checkNodeVersion(); - if (nodeCheck.status === 'outdated') { - console.error(chalk.red(` Node.js >= 18.0.0 required. Found: ${nodeCheck.version}`)); - process.exit(1); - } - console.log(chalk.green(` ✓ Node.js ${nodeCheck.version}`)); - - // Check prerequisites - if (!opts.skipCli) { - const cargoStatus = await checkCargo(); - const syncCtlStatus = await checkSyncCtl(); - - if (cargoStatus.status === 'ok') { - console.log(chalk.green(` ✓ cargo ${cargoStatus.version}`)); - } else { - console.log(chalk.red(' ✗ cargo not found')); - } - - if (syncCtlStatus.status === 'ok') { - console.log(chalk.green(` ✓ sync-ctl v${syncCtlStatus.version}`)); - } else if (syncCtlStatus.status === 'outdated') { - console.log(chalk.yellow(` ⚠ sync-ctl v${syncCtlStatus.version} (outdated)`)); - } else { - console.log(chalk.red(' ✗ sync-ctl not found')); - } - - // Install missing prerequisites - if (cargoStatus.status === 'missing') { - console.log(chalk.yellow('\n sync-ctl requires Rust\'s cargo package manager.\n')); - const { installRust } = opts.yes - ? { installRust: true } - : await inquirer.prompt([{ type: 'confirm', name: 'installRust', message: 'Install Rust toolchain via rustup?', default: true }]); - - if (installRust) { - const spinner = ora(' Installing rustup...').start(); - const success = await installRustup(); - if (success) { - spinner.succeed(' Rust toolchain installed'); - } else { - spinner.fail(' Failed to install Rust. Install manually: https://rustup.rs'); - } - } - } - - if (syncCtlStatus.status === 'missing' || syncCtlStatus.status === 'outdated') { - const cargoNow = await checkCargo(); - if (cargoNow.status === 'ok') { - const message = syncCtlStatus.status === 'outdated' - ? 'Update syncable-cli via cargo?' - : 'Install syncable-cli via cargo?'; - const { installCli } = opts.yes - ? { installCli: true } - : await inquirer.prompt([{ type: 'confirm', name: 'installCli', message, default: true }]); - - if (installCli) { - const spinner = ora(' Running: cargo install syncable-cli').start(); - const force = syncCtlStatus.status === 'outdated'; - const success = await installSyncCtl(force); - if (success) { - spinner.succeed(' sync-ctl installed'); - } else { - spinner.fail(' Failed to install sync-ctl. Try: cargo install syncable-cli'); - } - } - } - } - } - - // Detect agents - console.log(chalk.bold('\n Detecting AI coding agents...\n')); - const detectionResults = await detectAgents(); - - for (const { agent, detected } of detectionResults) { - if (detected) { - console.log(chalk.green(` ✓ ${agent.displayName} detected`)); - } else { - console.log(chalk.dim(` ✗ ${agent.displayName} not detected`)); - } - } - - // Determine which agents to install for - let selectedAgents: AgentConfig[]; - - if (opts.agents) { - const names = opts.agents.split(',').map((n: string) => n.trim()) as AgentName[]; - selectedAgents = allAgents().filter((a) => names.includes(a.name)); - } else if (opts.globalOnly) { - selectedAgents = detectionResults.filter((r) => r.detected && r.agent.installType === 'global').map((r) => r.agent); - } else if (opts.projectOnly) { - selectedAgents = detectionResults.filter((r) => r.detected && r.agent.installType === 'project').map((r) => r.agent); - } else if (opts.yes) { - selectedAgents = detectionResults.filter((r) => r.detected).map((r) => r.agent); - } else { - const choices = detectionResults.map((r) => ({ - name: `${r.agent.displayName} — ${r.agent.installType} install`, - value: r.agent.name, - checked: r.detected, - })); - - const { agents } = await inquirer.prompt([{ - type: 'checkbox', - name: 'agents', - message: 'Which agents should receive Syncable skills?', - choices, - }]); - - selectedAgents = allAgents().filter((a) => agents.includes(a.name)); - } - - if (selectedAgents.length === 0) { - console.log(chalk.yellow('\n No agents selected. Nothing to install.')); - return; - } - - // Load and install skills - const skills = loadSkills(getBundledSkillsDir()); - const commandCount = skills.filter((s) => s.category === 'command').length; - const workflowCount = skills.filter((s) => s.category === 'workflow').length; - - for (const agent of selectedAgents) { - const spinner = ora(` Installing skills for ${agent.displayName}...`).start(); - - if (opts.dryRun) { - spinner.info(` Would install ${skills.length} skills for ${agent.displayName}`); - continue; - } - - try { - const dest = agent.getSkillPath(); - switch (agent.name) { - case 'claude': - writeSkillsForClaude(skills, dest); - break; - case 'codex': - writeSkillsForCodex(skills, dest); - break; - case 'cursor': - writeSkillsForCursor(skills, dest); - break; - case 'windsurf': - writeSkillsForWindsurf(skills, dest); - break; - case 'gemini': - writeSkillsForGemini(skills, dest); - break; - } - spinner.succeed(` ${skills.length} skills installed for ${agent.displayName}`); - } catch (err) { - spinner.fail(` Failed to install skills for ${agent.displayName}: ${err}`); - } - } - - // Summary - console.log('\n ' + '─'.repeat(29)); - console.log(chalk.green.bold(' ✓ Setup complete!\n')); - console.log(` Installed:`); - console.log(` • ${commandCount} command skills + ${workflowCount} workflow skills`); - console.log(` • Agents: ${selectedAgents.map((a) => a.displayName).join(', ')}`); - console.log(`\n Try it: Open Claude Code and say "assess this project"\n`); - }); - -program - .command('uninstall') - .description('Remove skills from agents') - .option('--agents ', 'Comma-separated agent list') - .option('-y, --yes', 'Skip confirmations') - .action(async (opts) => { - const agents = opts.agents - ? allAgents().filter((a) => opts.agents.split(',').includes(a.name)) - : allAgents(); - - if (!opts.yes) { - const { confirm } = await inquirer.prompt([{ - type: 'confirm', - name: 'confirm', - message: `Remove Syncable skills from ${agents.map((a) => a.displayName).join(', ')}?`, - default: false, - }]); - if (!confirm) return; - } - - for (const agent of agents) { - const spinner = ora(` Removing skills from ${agent.displayName}...`).start(); - try { - const dest = agent.getSkillPath(); - switch (agent.name) { - case 'claude': - removeSyncableSkills(dest); - break; - case 'codex': - removeSyncableSkills(dest, 'syncable-*'); - break; - case 'cursor': - removeSyncableSkills(dest, 'syncable-*.mdc'); - break; - case 'windsurf': - removeSyncableSkills(dest, 'syncable-*.md'); - break; - case 'gemini': - removeGeminiSection(dest); - break; - } - spinner.succeed(` Skills removed from ${agent.displayName}`); - } catch (err) { - spinner.fail(` Failed to remove skills from ${agent.displayName}: ${err}`); - } - } - }); - -program - .command('update') - .description('Update skills to latest version') - .option('--agents ', 'Comma-separated agent list') - .option('-y, --yes', 'Skip confirmations') - .action(async (opts) => { - // Uninstall then install - const yesFlag = opts.yes ? ['--yes'] : []; - const agentsFlag = opts.agents ? ['--agents', opts.agents] : []; - await program.commands.find((c) => c.name() === 'uninstall')!.parseAsync(['node', 'x', ...agentsFlag, ...yesFlag]); - await program.commands.find((c) => c.name() === 'install')!.parseAsync(['node', 'x', '--skip-cli', ...agentsFlag, ...yesFlag]); - }); - -program - .command('status') - .description('Show what is installed and where') - .action(async () => { - console.log(chalk.bold('\n Syncable CLI Skills Status\n')); - - const detectionResults = await detectAgents(); - const syncCtlStatus = await checkSyncCtl(); - const cargoStatus = await checkCargo(); - - console.log(' Agent Status Location'); - console.log(' ' + '─'.repeat(60)); - - for (const { agent } of detectionResults) { - const dest = agent.getSkillPath(); - const count = countInstalledSkills(dest, agent.name); - if (count > 0) { - console.log(` ${agent.displayName.padEnd(14)} ${chalk.green('✓ installed')} ${dest} (${count} skills)`); - } else { - console.log(` ${agent.displayName.padEnd(14)} ${chalk.dim('✗ not installed')}`); - } - } - - console.log(); - if (syncCtlStatus.status === 'ok') { - console.log(` sync-ctl ${chalk.green('✓')} v${syncCtlStatus.version}`); - } else { - console.log(` sync-ctl ${chalk.red('✗ not found')}`); - } - if (cargoStatus.status === 'ok') { - console.log(` cargo ${chalk.green('✓')} v${cargoStatus.version}`); - } else { - console.log(` cargo ${chalk.red('✗ not found')}`); - } - console.log(); - }); - -program.parse(); -``` - -- [ ] **Step 2: Verify TypeScript compiles** - -Run: `cd installer && npx tsc --noEmit` -Expected: No errors. - -- [ ] **Step 3: Test the CLI locally** - -Run: `cd installer && node scripts/copy-skills.js && npx tsc && node dist/index.js --help` -Expected: Shows help text with install/uninstall/update/status commands. - -Run: `cd installer && node dist/index.js status` -Expected: Shows agent status table. - -- [ ] **Step 4: Commit** - -```bash -git add installer/src/index.ts -git commit -m "feat(installer): add CLI entrypoint with commander, inquirer, ora, chalk" -``` - ---- - -### Task 15: Integration test and final verification - -**Files:** -- None new — verification of everything working together. - -- [ ] **Step 1: Run the full test suite** - -Run: `cd installer && npm test` -Expected: All tests PASS. Verify count matches expectations (~35-40 tests across all files). - -- [ ] **Step 2: Run a dry-run install** - -Run: `cd installer && node dist/index.js install --dry-run --yes` -Expected: Shows what would be installed for detected agents without writing files. - -- [ ] **Step 3: Run the copy-skills script and verify skill count** - -Run: `cd installer && node scripts/copy-skills.js && ls skills/commands/ skills/workflows/` -Expected: 7 command files + 4 workflow files. - -- [ ] **Step 4: Verify .gitignore excludes build artifacts** - -Run: `cd installer && cat .gitignore` -Expected: Contains `node_modules/`, `dist/`, `skills/`. - -- [ ] **Step 5: Final commit if any fixes were needed** - -```bash -git add -A installer/ -git commit -m "chore(installer): final integration fixes" -``` - ---- - -## Summary - -| Task | Component | Files | Tests | -|------|-----------|-------|-------| -| 1 | Project scaffolding | 5 | 0 (infra) | -| 2 | Constants + utils | 2 | ~8 | -| 3 | Skill loader | 1 | ~5 | -| 4 | Agent types + detection | 7 | ~3 | -| 5 | Claude transformer | 2 | ~3 | -| 6 | Codex transformer | 1 | ~3 | -| 7 | Cursor transformer | 1 | ~6 | -| 8 | Windsurf + Gemini transformers | 2 | ~8 | -| 9 | Prerequisites | 3 | ~4 | -| 10 | Install command | 1 | ~8 | -| 11 | Uninstall command | 1 | ~5 | -| 12 | Status command | 1 | ~4 | -| 13 | Update command | 1 | 0 (thin wrapper) | -| 14 | CLI entrypoint | 1 | 0 (integration) | -| 15 | Integration verification | 0 | manual | - -**Total: ~30 source files, ~57 tests, 15 tasks, ~15 commits** diff --git a/docs/superpowers/plans/2026-03-27-syncable-cli-skills.md b/docs/superpowers/plans/2026-03-27-syncable-cli-skills.md deleted file mode 100644 index a1bbe6f0..00000000 --- a/docs/superpowers/plans/2026-03-27-syncable-cli-skills.md +++ /dev/null @@ -1,1349 +0,0 @@ -# Syncable CLI Skills Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build 7 command skills and 4 workflow skills that teach AI agents when and how to use the Syncable CLI (`sync-ctl`) as a toolbox. - -**Architecture:** Layered skill model — command skills are atomic wrappers around individual CLI commands, workflow skills orchestrate command skills for multi-step patterns. All skills are markdown files following the Claude Code skill format with frontmatter, structured sections, and concrete examples. - -**Tech Stack:** Markdown (Claude Code skill format), Bash (CLI invocations in examples) - -**Spec:** `docs/superpowers/specs/2026-03-27-syncable-cli-skills-design.md` - ---- - -## File Structure - -``` -skills/ -├── commands/ -│ ├── syncable-analyze.md # Project stack analysis -│ ├── syncable-security.md # Secret/code pattern scanning -│ ├── syncable-vulnerabilities.md # CVE dependency scanning -│ ├── syncable-dependencies.md # License/dependency audit -│ ├── syncable-validate.md # IaC linting (hadolint/dclint/terraform) -│ ├── syncable-optimize.md # K8s optimization & cost analysis -│ └── syncable-platform.md # Auth/project/org/env/deploy -│ -└── workflows/ - ├── syncable-project-assessment.md # Full health check - ├── syncable-security-audit.md # Deep security review - ├── syncable-iac-pipeline.md # IaC validation pipeline - └── syncable-deploy-pipeline.md # End-to-end deploy -``` - -Each file is a self-contained Claude Code skill (markdown with YAML frontmatter). No code, no tests — these are pure prompt/documentation files. - ---- - -### Task 1: Create directory structure - -**Files:** -- Create: `skills/commands/` (directory) -- Create: `skills/workflows/` (directory) - -- [ ] **Step 1: Create the skills directories** - -```bash -mkdir -p skills/commands skills/workflows -``` - -- [ ] **Step 2: Commit** - -```bash -git add skills/ -git commit -m "chore: scaffold skills directory structure" -``` - ---- - -### Task 2: Write `syncable-analyze` command skill - -**Files:** -- Create: `skills/commands/syncable-analyze.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-analyze -description: Use when analyzing a project's tech stack, detecting languages, frameworks, runtimes, or dependencies using Syncable CLI. Trigger on: "what stack is this", "analyze this project", "detect frameworks", "what languages does this use". ---- - -## Purpose - -Analyze a project directory to detect its tech stack: programming languages, frameworks, runtimes, package managers, dependencies, Docker presence, and monorepo structure. This is the foundation skill — most workflows start here to understand what they're working with. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory - -## Commands - -### Basic analysis (JSON output for agent consumption) - -```bash -sync-ctl analyze --json -``` - -### Human-readable matrix view - -```bash -sync-ctl analyze --display matrix -``` - -### Filtered analysis (only specific aspects) - -```bash -sync-ctl analyze --json --only languages,frameworks -sync-ctl analyze --json --only dependencies -``` - -### Key Flags - -| Flag | Purpose | -|------|---------| -| `--json` | Machine-readable JSON output (always use when processing results) | -| `--detailed` | Show detailed analysis (legacy vertical format) | -| `--display {matrix\|detailed\|summary}` | Display format for human-readable output | -| `--only ` | Comma-separated: `languages`, `frameworks`, `dependencies` | - -## Output Interpretation - -The JSON output contains: - -- **languages** — detected programming languages with file counts and percentages -- **frameworks** — detected frameworks with versions where available -- **dependencies** — package managers found and dependency counts -- **runtimes** — detected runtime versions (Node.js, Python, Go, Rust, Java) -- **docker** — whether Dockerfiles or Docker Compose files exist -- **monorepo** — whether the project is a monorepo and its structure - -When reporting to the user, prioritize: primary language, main framework, runtime version, and whether Docker/K8s infrastructure exists. - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `No such file or directory` | Invalid path | Ask user to verify the project path | -| Empty output | No recognizable project files | Tell user the directory may not contain a supported project. Run `sync-ctl support` to show supported technologies | -| Timeout | Very large monorepo | Try `--only languages` for a faster partial scan | - -## Examples - -**Analyze current directory:** -```bash -sync-ctl analyze . --json -``` - -**Analyze a specific project:** -```bash -sync-ctl analyze /path/to/project --json -``` - -**Quick language-only check:** -```bash -sync-ctl analyze . --json --only languages -``` -``` - -- [ ] **Step 2: Verify the file is valid markdown with correct frontmatter** - -Run: `head -5 skills/commands/syncable-analyze.md` -Expected: YAML frontmatter with `---`, `name:`, `description:`, `---` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-analyze.md -git commit -m "feat(skills): add syncable-analyze command skill" -``` - ---- - -### Task 3: Write `syncable-security` command skill - -**Files:** -- Create: `skills/commands/syncable-security.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-security -description: Use when scanning code for secrets, credentials, API keys, or insecure code patterns using Syncable CLI. Trigger on: "scan for secrets", "find leaked credentials", "security scan", "is this code secure", "check for hardcoded passwords". ---- - -## Purpose - -Perform security analysis on a codebase: detect leaked secrets (API keys, tokens, passwords, private keys), identify insecure code patterns, and analyze configuration security. Returns findings with severity levels, file locations, and remediation guidance. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory - -## Commands - -### Standard security scan - -```bash -sync-ctl security --mode balanced --format json -``` - -### Mode Selection Guide - -Always pass `--mode` explicitly. Choose based on context: - -| Mode | When to use | Speed | -|------|------------|-------| -| `lightning` | Quick check, only critical files (.env, configs) | Fastest | -| `fast` | Smart sampling, good for large repos during development | Fast | -| `balanced` | **Default choice.** Good coverage with optimizations | Medium | -| `thorough` | Pre-deployment reviews, PR security checks | Slow | -| `paranoid` | Compliance audits, production security reviews | Slowest | - -### Key Flags - -| Flag | Purpose | -|------|---------| -| `--mode {lightning\|fast\|balanced\|thorough\|paranoid}` | Scan depth (always specify) | -| `--format json` | Machine-readable output (always use when processing results) | -| `--include-low` | Include low-severity findings (off by default) | -| `--no-secrets` | Skip secrets detection (only code patterns) | -| `--no-code-patterns` | Skip code pattern analysis (only secrets) | -| `--fail-on-findings` | Exit with error code if findings exist (for CI) | -| `--output ` | Write report to file | - -## Output Interpretation - -The JSON output contains: - -- **findings** — array of security issues, each with: - - `severity` — Critical, High, Medium, Low, Info - - `category` — secrets, code_pattern, configuration, infrastructure - - `file` — exact file path - - `line` — line number - - `description` — what was found - - `remediation` — how to fix it -- **summary** — total counts by severity -- **score** — overall security score (0-100) - -**Priority for reporting to user:** -1. Critical findings first (leaked secrets, hardcoded credentials) -2. High findings (insecure patterns) -3. Summary with score -4. Remediation steps for top findings - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `No such file or directory` | Invalid path | Ask user to verify the project path | -| Very slow scan | Large repo with `thorough`/`paranoid` mode | Suggest trying `balanced` or `fast` mode first | -| No findings | Clean project or scan mode too light | If `lightning`/`fast`, suggest re-running with `balanced` for deeper coverage | - -## Examples - -**Quick secrets check on current directory:** -```bash -sync-ctl security . --mode balanced --format json -``` - -**Deep pre-deploy audit:** -```bash -sync-ctl security . --mode paranoid --format json -``` - -**Secrets-only scan (skip code patterns):** -```bash -sync-ctl security . --mode thorough --no-code-patterns --format json -``` - -**Save report to file:** -```bash -sync-ctl security . --mode thorough --format json --output security-report.json -``` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/commands/syncable-security.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-security.md -git commit -m "feat(skills): add syncable-security command skill" -``` - ---- - -### Task 4: Write `syncable-vulnerabilities` command skill - -**Files:** -- Create: `skills/commands/syncable-vulnerabilities.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-vulnerabilities -description: Use when checking project dependencies for known CVEs or security vulnerabilities using Syncable CLI. Trigger on: "check for CVEs", "vulnerable dependencies", "dependency security", "are my packages safe", "npm audit", "cargo audit". ---- - -## Purpose - -Scan project dependencies for known CVEs (Common Vulnerabilities and Exposures) across npm, pip, cargo, go, and java ecosystems. Returns vulnerable packages with severity, affected versions, and available fixes. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory -- Language-specific scanning tools should be installed. If a scan fails with "tool not found", run `sync-ctl tools install` to install missing scanners. - -## Commands - -### Scan for vulnerabilities - -```bash -sync-ctl vulnerabilities --format json -``` - -### Filter by severity - -```bash -sync-ctl vulnerabilities --severity high --format json -``` - -### Key Flags - -| Flag | Purpose | -|------|---------| -| `--format json` | Machine-readable output (always use) | -| `--severity {low\|medium\|high\|critical}` | Only show findings at or above this severity | -| `--output ` | Write report to file | - -## Output Interpretation - -The JSON output contains an array of vulnerability findings, each with: - -- **package** — affected dependency name -- **version** — installed version -- **severity** — Critical, High, Medium, Low -- **cve** — CVE identifier (e.g., CVE-2024-1234) -- **description** — what the vulnerability is -- **fix_version** — version that resolves it (if available) -- **ecosystem** — npm, pip, cargo, go, java - -**Priority for reporting to user:** -1. Critical/High CVEs with available fixes — actionable immediately -2. Critical/High CVEs without fixes — flag as risk -3. Medium/Low — mention count, don't enumerate unless asked - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `tool not found` or scanner missing | Language-specific audit tool not installed | Run `sync-ctl tools install` to install missing scanners, then retry | -| `No dependencies found` | No package manager files detected | Run `sync-ctl analyze --json` first to verify the project has dependencies | -| Timeout | Very large dependency tree | Try scanning specific subdirectories in a monorepo | - -## Examples - -**Scan current project:** -```bash -sync-ctl vulnerabilities . --format json -``` - -**Only critical and high severity:** -```bash -sync-ctl vulnerabilities . --severity high --format json -``` - -**Save report:** -```bash -sync-ctl vulnerabilities . --format json --output vuln-report.json -``` - -**Install missing scanners first:** -```bash -sync-ctl tools install --yes -sync-ctl vulnerabilities . --format json -``` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/commands/syncable-vulnerabilities.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-vulnerabilities.md -git commit -m "feat(skills): add syncable-vulnerabilities command skill" -``` - ---- - -### Task 5: Write `syncable-dependencies` command skill - -**Files:** -- Create: `skills/commands/syncable-dependencies.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-dependencies -description: Use when auditing project dependencies for licenses, production/dev split, or detailed dependency analysis using Syncable CLI. Trigger on: "license audit", "list dependencies", "dependency analysis", "what licenses am I using", "show me all packages". ---- - -## Purpose - -Analyze project dependencies in detail: list all packages, check license types, separate production from development dependencies, and optionally flag vulnerabilities inline. Use this for license compliance and dependency inventory. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory - -## Commands - -### Full dependency analysis with licenses - -```bash -sync-ctl dependencies --licenses --format json -``` - -### Production dependencies only - -```bash -sync-ctl dependencies --licenses --prod-only --format json -``` - -### Key Flags - -| Flag | Purpose | -|------|---------| -| `--format json` | Machine-readable output (always use) | -| `--licenses` | Include license information for each dependency | -| `--vulnerabilities` | Quick inline vulnerability check (for thorough CVE scanning, use the standalone `sync-ctl vulnerabilities` command instead) | -| `--prod-only` | Show only production dependencies | -| `--dev-only` | Show only development dependencies | - -## Output Interpretation - -The JSON output contains: - -- **dependencies** — array of packages with name, version, license, and prod/dev classification -- **summary** — total counts, license distribution - -**Priority for reporting to user:** -1. License concerns (copyleft in commercial projects, unknown licenses) -2. Dependency counts (prod vs dev) -3. Specific packages only if asked - -**When to use `--vulnerabilities` vs standalone `vulnerabilities` command:** -- Use `--vulnerabilities` here for a quick inline check alongside license info -- Use `sync-ctl vulnerabilities` for a dedicated, thorough CVE scan - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `No dependencies found` | No package manager files | Verify project path, run `sync-ctl analyze` to check for supported package managers | -| Incomplete results | Some package managers not fully parsed | Note which ecosystems were scanned and which may be missing | - -## Examples - -**Full audit with licenses:** -```bash -sync-ctl dependencies . --licenses --format json -``` - -**Production-only for license compliance:** -```bash -sync-ctl dependencies . --licenses --prod-only --format json -``` - -**Quick vulnerability check alongside deps:** -```bash -sync-ctl dependencies . --licenses --vulnerabilities --format json -``` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/commands/syncable-dependencies.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-dependencies.md -git commit -m "feat(skills): add syncable-dependencies command skill" -``` - ---- - -### Task 6: Write `syncable-validate` command skill - -**Files:** -- Create: `skills/commands/syncable-validate.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-validate -description: Use when linting or validating Dockerfiles, Docker Compose files, Terraform configs, or Kubernetes manifests using Syncable CLI. Trigger on: "lint Dockerfile", "validate compose", "check terraform", "is my IaC correct", "lint my infrastructure files". ---- - -## Purpose - -Validate Infrastructure-as-Code files against best practices. Covers Dockerfiles (via native hadolint), Docker Compose files (via native dclint), and Terraform configurations. Reports violations with severity, file locations, and auto-fix suggestions. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory -- Project must contain IaC files (Dockerfiles, docker-compose.yml, *.tf files) - -## Commands - -### Validate all IaC files in a directory - -```bash -sync-ctl validate -``` - -### Validate specific types only - -```bash -sync-ctl validate --types dockerfile -sync-ctl validate --types dockerfile,compose -sync-ctl validate --types terraform -``` - -### Auto-fix issues where possible - -```bash -sync-ctl validate --fix -``` - -### Key Flags - -| Flag | Purpose | -|------|---------| -| `--types ` | Filter to specific IaC types: `dockerfile`, `compose`, `terraform` | -| `--fix` | Automatically fix issues where possible | - -**Note:** The `validate` command does not support `--format json`. Output is terminal text with structured lint violations. When processing results, parse the text output rather than expecting JSON. - -## Output Interpretation - -Output contains lint violations (as terminal text, not JSON), each with: - -- **file** — path to the IaC file -- **line** — line number of the violation -- **rule** — rule ID (e.g., DL3006 for hadolint, DCL001 for dclint) -- **severity** — Error, Warning, Info -- **message** — description of the issue -- **fix** — suggested fix (if available) - -**What gets checked:** - -| Type | Linter | Example checks | -|------|--------|---------------| -| Dockerfile | hadolint (native Rust) | Pin versions, avoid `latest` tag, use COPY not ADD, multi-stage best practices | -| Docker Compose | dclint (native Rust) | Service naming, volume declarations, network configuration, 15 configurable rules | -| Terraform | Terraform validator | Syntax validation, provider configuration, resource definitions | - -**Priority for reporting to user:** -1. Errors first — these will cause build/deploy failures -2. Warnings — best practice violations -3. Info — suggestions for improvement - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `No IaC files found` | Directory has no Dockerfiles, Compose, or Terraform files | Run `sync-ctl analyze --json` to verify what IaC exists | -| `Unknown type` | Invalid `--types` value | Valid types are: `dockerfile`, `compose`, `terraform` | - -## Examples - -**Lint all IaC in current directory:** -```bash -sync-ctl validate . -``` - -**Lint only Dockerfiles:** -```bash -sync-ctl validate . --types dockerfile -``` - -**Auto-fix Docker Compose issues:** -```bash -sync-ctl validate . --types compose --fix -``` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/commands/syncable-validate.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-validate.md -git commit -m "feat(skills): add syncable-validate command skill" -``` - ---- - -### Task 7: Write `syncable-optimize` command skill - -**Files:** -- Create: `skills/commands/syncable-optimize.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-optimize -description: Use when optimizing Kubernetes resource requests/limits, analyzing costs, or detecting configuration drift using Syncable CLI. Trigger on: "optimize k8s", "right-size pods", "k8s cost analysis", "resource recommendations", "over-provisioned containers". ---- - -## Purpose - -Analyze Kubernetes manifests and optionally live cluster metrics to recommend resource right-sizing, estimate costs, and detect configuration drift. Can also run kubelint security checks and helmlint validation with `--full` flag. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- For static analysis: K8s manifest files (YAML) in the project -- For live cluster analysis: valid kubeconfig with cluster access -- For cost estimation: `--cloud-provider` flag - -## Commands - -### Static manifest analysis - -```bash -sync-ctl optimize --format json -``` - -### Live cluster analysis - -```bash -sync-ctl optimize --cluster --format json -sync-ctl optimize --cluster my-context --namespace default --format json -``` - -### With Prometheus metrics - -```bash -sync-ctl optimize --cluster --prometheus http://localhost:9090 --period 30d --format json -``` - -### Full analysis (includes kubelint + helmlint) - -```bash -sync-ctl optimize --full --format json -``` - -### Cost estimation - -```bash -sync-ctl optimize --cluster --cloud-provider aws --region us-east-1 --format json -``` - -### Key Flags - -| Flag | Purpose | -|------|---------| -| `--format json` | Machine-readable output (always use) | -| `--cluster [CONTEXT]` | Connect to live K8s cluster (uses current context if no name given) | -| `--prometheus ` | Prometheus URL for historical metrics | -| `--namespace ` | Target namespace (or `*` for all) | -| `--period ` | Analysis period for metrics (e.g., `7d`, `30d`) | -| `--full` | Include kubelint security checks + helmlint validation | -| `--cloud-provider {aws\|gcp\|azure\|onprem}` | Cloud provider for cost estimation | -| `--region ` | Region for pricing (default: `us-east-1`) | -| `--fix` | Generate fix suggestions | -| `--apply` | **DANGEROUS:** Apply fixes to manifest files. Requires `--fix`. Never use without explicit user confirmation. | -| `--dry-run` | Preview changes without applying | -| `--severity ` | Minimum severity to report | -| `--threshold <0-100>` | Minimum waste percentage threshold | - -## Output Interpretation - -The JSON output contains: - -- **recommendations** — array of optimization suggestions with: - - Resource right-sizing (CPU/memory requests/limits) - - Confidence score - - Current vs recommended values - - Estimated savings -- **costs** — cost attribution per workload (if `--cloud-provider` set) -- **drift** — configuration drift between manifests and running state (if `--cluster` set) -- **security** — kubelint findings (if `--full` set) - -**Priority for reporting to user:** -1. High-confidence right-sizing recommendations with cost savings -2. Critical security findings (from `--full`) -3. Drift detection issues -4. Cost breakdown summary - -## Safety - -- `--fix` only generates suggestions — it does NOT modify files -- `--apply` (requires `--fix`) writes changes to files — always confirm with user first -- `--dry-run` previews what `--apply` would do — use this to show the user before applying -- Recommend: always run `--fix --dry-run` first, show output, then `--fix --apply` only after user approval - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `No Kubernetes manifests found` | No YAML with K8s resources | Run `sync-ctl analyze --json` to check for K8s presence | -| `Cannot connect to cluster` | Invalid kubeconfig or cluster unreachable | Check `kubectl cluster-info` works, verify context name | -| `Prometheus unreachable` | Wrong URL or Prometheus not running | Verify URL, fall back to static analysis without `--prometheus` | - -## Examples - -**Quick static analysis:** -```bash -sync-ctl optimize . --format json -``` - -**Full analysis with live cluster and cost estimation:** -```bash -sync-ctl optimize . --cluster --cloud-provider aws --full --format json -``` - -**Preview fixes before applying:** -```bash -sync-ctl optimize . --fix --dry-run --format json -``` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/commands/syncable-optimize.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-optimize.md -git commit -m "feat(skills): add syncable-optimize command skill" -``` - ---- - -### Task 8: Write `syncable-platform` command skill - -**Files:** -- Create: `skills/commands/syncable-platform.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-platform -description: Use when authenticating with Syncable, managing projects/orgs/environments, or deploying services through the Syncable platform. Trigger on: "syncable login", "select project", "deploy to syncable", "list environments", "switch organization". ---- - -## Purpose - -Manage Syncable platform operations: authenticate, select organizations/projects/environments, and trigger deployments. This skill covers the full platform lifecycle from login to deploy. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Internet access for Syncable API -- For deployment: authenticated session with project and environment selected - -## Commands - -### Authentication - -```bash -# Check if authenticated -sync-ctl auth status - -# Log in (opens browser) -sync-ctl auth login - -# Log in without auto-opening browser -sync-ctl auth login --no-browser - -# Log out -sync-ctl auth logout - -# Get access token (for scripting) -sync-ctl auth token --raw -``` - -### Organization Management - -```bash -# List organizations -sync-ctl org list - -# Select an organization -sync-ctl org select -``` - -### Project Management - -```bash -# List projects in current org -sync-ctl project list - -# List projects in a specific org -sync-ctl project list --org-id - -# Select a project -sync-ctl project select - -# Show current context (org + project) -sync-ctl project current - -# Show project details -sync-ctl project info -sync-ctl project info -``` - -### Environment Management - -```bash -# List environments in current project -sync-ctl env list - -# Select an environment -sync-ctl env select -``` - -### Deployment - -```bash -# Launch interactive deployment wizard -sync-ctl deploy wizard - -# Create a new environment -sync-ctl deploy new-env - -# Check deployment status -sync-ctl deploy status - -# Watch deployment status until complete -sync-ctl deploy status --watch -``` - -## Auth-First Flow - -**Always check auth status before any platform operation.** Follow this sequence: - -1. Run `sync-ctl auth status` — if not authenticated, guide user through `sync-ctl auth login` -2. Run `sync-ctl project current` — if no project selected, list projects and ask user to select -3. For deployment: ensure environment is selected via `sync-ctl env list` + `sync-ctl env select` - -## Output Interpretation - -- **auth status** — shows whether user is logged in, token expiry -- **org/project/env list** — shows available items with IDs and names -- **project current** — shows currently selected org, project, and environment -- **deploy status** — shows deployment progress, logs, and final status - -## Error Handling - -| Error | Cause | Action | -|-------|-------|--------| -| `Not authenticated` | No valid session | Run `sync-ctl auth login` | -| `Token expired` | Session timed out | Run `sync-ctl auth login` to re-authenticate | -| `No project selected` | Project context not set | Run `sync-ctl project list` then `sync-ctl project select ` | -| `No environment selected` | Environment not set | Run `sync-ctl env list` then `sync-ctl env select ` | -| `Deployment failed` | Build or infra error | Check `sync-ctl deploy status ` for error details | - -## Examples - -**Full login-to-deploy flow:** -```bash -sync-ctl auth login -sync-ctl org list -sync-ctl org select -sync-ctl project list -sync-ctl project select -sync-ctl env list -sync-ctl env select -sync-ctl deploy wizard -``` - -**Check current context:** -```bash -sync-ctl auth status -sync-ctl project current -``` - -**Monitor a deployment:** -```bash -sync-ctl deploy status --watch -``` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/commands/syncable-platform.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/commands/syncable-platform.md -git commit -m "feat(skills): add syncable-platform command skill" -``` - ---- - -### Task 9: Write `syncable-project-assessment` workflow skill - -**Files:** -- Create: `skills/workflows/syncable-project-assessment.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-project-assessment -description: Use when a user wants a comprehensive project health check - combines stack analysis, security scanning, vulnerability checks, and dependency auditing via Syncable CLI. Trigger on: "assess this project", "full health check", "project overview", "what's the state of this codebase", "onboard me to this repo". ---- - -## Purpose - -Run a comprehensive project health check by chaining multiple Syncable CLI commands. Produces a unified report covering tech stack, security posture, vulnerability status, and dependency health. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory - -## Workflow Steps - -### Step 1: Analyze the project stack - -```bash -sync-ctl analyze --json -``` - -Parse the output to understand: -- What languages and frameworks are present -- Whether dependencies exist (needed for steps 3 and 4) -- Whether secrets-capable files exist (affects step 2 mode) - -### Step 2: Security scan - -```bash -sync-ctl security --mode balanced --format json -``` - -**Decision point:** If step 1 shows no config files, secrets files, or environment files, use `--mode lightning` instead of `--mode balanced` to save time. - -### Step 3: Vulnerability scan - -```bash -sync-ctl vulnerabilities --format json -``` - -**Decision point:** If step 1 detected no dependencies (no package.json, requirements.txt, Cargo.toml, go.mod, etc.), **skip this step entirely** and note "No dependencies detected" in the report. - -### Step 4: Dependency audit - -```bash -sync-ctl dependencies --licenses --format json -``` - -**Decision point:** Same as step 3 — skip if no dependencies detected. - -## Decision Points Summary - -| Condition | Action | -|-----------|--------| -| No dependencies detected in step 1 | Skip steps 3 and 4 | -| No secrets-capable files in step 1 | Use `--mode lightning` in step 2 | -| Vulnerability scanner missing | Run `sync-ctl tools install --yes`, then retry step 3 | - -## Report Synthesis - -After all steps complete, synthesize a unified report for the user: - -1. **Tech Stack** — primary language, frameworks, runtimes -2. **Security Score** — score from security scan, critical/high finding count -3. **Vulnerabilities** — critical/high CVE count, packages with available fixes -4. **Dependencies** — total count, license concerns (copyleft, unknown) -5. **Recommendations** — top 3-5 actionable items prioritized by severity - -## Examples - -**Assess current directory:** - -The agent runs these commands in sequence, skipping steps based on decision points: - -```bash -sync-ctl analyze . --json -sync-ctl security . --mode balanced --format json -sync-ctl vulnerabilities . --format json -sync-ctl dependencies . --licenses --format json -``` - -Then synthesizes the results into a single report for the user. -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/workflows/syncable-project-assessment.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/workflows/syncable-project-assessment.md -git commit -m "feat(skills): add syncable-project-assessment workflow skill" -``` - ---- - -### Task 10: Write `syncable-security-audit` workflow skill - -**Files:** -- Create: `skills/workflows/syncable-security-audit.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-security-audit -description: Use when performing a thorough pre-deployment or compliance security review - combines deep security scan, CVE checks, and IaC validation via Syncable CLI. Trigger on: "security audit", "is this production-ready", "pre-deploy security check", "compliance review", "full security review". ---- - -## Purpose - -Perform a deep, multi-layered security review suitable for pre-deployment gates or compliance audits. Goes deeper than the project assessment by using thorough/paranoid scan modes and including IaC validation. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory - -## Workflow Steps - -### Step 1: Analyze the project - -```bash -sync-ctl analyze --json -``` - -Parse the output to determine: -- What IaC files exist (Dockerfiles, Compose, Terraform, K8s manifests) — needed for step 4 -- What dependencies exist — needed for step 3 - -### Step 2: Deep security scan - -Choose mode based on context: - -**For PR reviews / pre-merge:** -```bash -sync-ctl security --mode thorough --format json -``` - -**For production deployment / compliance:** -```bash -sync-ctl security --mode paranoid --format json -``` - -### Step 3: Vulnerability scan - -```bash -sync-ctl vulnerabilities --format json -``` - -### Step 4: IaC validation - -**Decision point:** Only run if step 1 detected Docker, Compose, Terraform, or K8s files. - -```bash -sync-ctl validate -``` - -If specific types are known from step 1, filter: -```bash -sync-ctl validate --types dockerfile,compose -``` - -## Decision Points Summary - -| Condition | Action | -|-----------|--------| -| PR review context | Use `--mode thorough` in step 2 | -| Pre-deploy / compliance context | Use `--mode paranoid` in step 2 | -| No IaC files detected in step 1 | Skip step 4 | -| No dependencies detected in step 1 | Skip step 3 | - -## Report Synthesis - -Produce a security audit report: - -1. **Security Findings** — all Critical and High findings with file locations and remediation -2. **Vulnerability Status** — CVEs by severity, packages needing updates -3. **IaC Compliance** — lint violations in Dockerfiles, Compose, Terraform -4. **Overall Verdict** — PASS (no critical/high findings), WARN (high findings but no critical), FAIL (critical findings present) -5. **Remediation Priority** — ordered list of actions to resolve findings - -**If critical findings exist:** Explicitly warn the user. If this audit is part of a deploy pipeline, recommend blocking deployment until critical issues are resolved. -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/workflows/syncable-security-audit.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/workflows/syncable-security-audit.md -git commit -m "feat(skills): add syncable-security-audit workflow skill" -``` - ---- - -### Task 11: Write `syncable-iac-pipeline` workflow skill - -**Files:** -- Create: `skills/workflows/syncable-iac-pipeline.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-iac-pipeline -description: Use when validating all infrastructure-as-code files in a project - combines IaC linting with Kubernetes optimization and security checks via Syncable CLI. Trigger on: "validate infrastructure", "lint all IaC", "check my k8s and docker files", "infrastructure review". ---- - -## Purpose - -Validate all infrastructure-as-code files in a project by chaining IaC linting with Kubernetes optimization and security checks. Covers Dockerfiles, Docker Compose, Terraform, K8s manifests, and Helm charts. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Agent has access to the project directory -- Project must contain IaC files - -## Workflow Steps - -### Step 1: Analyze the project - -```bash -sync-ctl analyze --json -``` - -Parse the output to determine: -- Which IaC types exist (Dockerfile, Compose, Terraform, K8s manifests) -- Whether K8s manifests are present — needed for step 3 - -### Step 2: Validate IaC files - -```bash -sync-ctl validate -``` - -If step 1 revealed specific types, you can filter: -```bash -sync-ctl validate --types dockerfile,compose,terraform -``` - -### Step 3: Kubernetes optimization (conditional) - -**Decision point:** Only run if step 1 detected Kubernetes manifests or Helm charts. - -```bash -sync-ctl optimize --full --format json -``` - -The `--full` flag includes kubelint security checks and helmlint validation on top of resource optimization. - -## Decision Points Summary - -| Condition | Action | -|-----------|--------| -| No K8s manifests or Helm charts in step 1 | Skip step 3 | -| No IaC files at all in step 1 | Abort workflow, tell user no IaC files found | - -## Report Synthesis - -Produce an IaC validation report: - -1. **Dockerfile Issues** — hadolint violations by severity -2. **Docker Compose Issues** — dclint violations -3. **Terraform Issues** — validation errors -4. **Kubernetes Issues** — kubelint security findings and resource optimization recommendations (if step 3 ran) -5. **Actionable Fixes** — which issues can be auto-fixed with `--fix` -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/workflows/syncable-iac-pipeline.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/workflows/syncable-iac-pipeline.md -git commit -m "feat(skills): add syncable-iac-pipeline workflow skill" -``` - ---- - -### Task 12: Write `syncable-deploy-pipeline` workflow skill - -**Files:** -- Create: `skills/workflows/syncable-deploy-pipeline.md` - -- [ ] **Step 1: Write the skill file** - -```markdown ---- -name: syncable-deploy-pipeline -description: Use when deploying a project through Syncable - orchestrates auth, analysis, security gating, and deployment via Syncable CLI. Trigger on: "deploy this", "push to syncable", "set up deployment", "deploy my project". ---- - -## Purpose - -Orchestrate a full deployment pipeline through the Syncable platform: authenticate, analyze the project, run a security audit as a gate, then deploy. Ensures no deployment happens without authentication and security review. - -## Prerequisites - -- `sync-ctl` binary installed and on PATH -- Internet access for Syncable API -- Agent has access to the project directory - -## Workflow Steps - -### Step 1: Check authentication and platform context - -```bash -sync-ctl auth status -``` - -**Decision point:** If not authenticated: -```bash -sync-ctl auth login -``` - -Then verify project/environment context: -```bash -sync-ctl project current -``` - -**Decision point:** If no project selected: -```bash -sync-ctl org list -# Ask user which org -sync-ctl org select -sync-ctl project list -# Ask user which project -sync-ctl project select -sync-ctl env list -# Ask user which environment -sync-ctl env select -``` - -### Step 2: Analyze the project - -```bash -sync-ctl analyze --json -``` - -### Step 3: Pre-deploy security audit - -Execute the `syncable-security-audit` workflow inline (all its steps and decision logic). **Note:** Step 2's analyze output is reused here — do not re-run analyze. - -1. `sync-ctl security --mode paranoid --format json` -2. `sync-ctl vulnerabilities --format json` -3. `sync-ctl validate ` (if IaC files exist per Step 2's analysis) - -**CRITICAL GATE:** If the security audit finds **critical** findings: -- Present all critical findings to the user -- Explicitly warn: "Critical security findings detected. Deploying with these issues is not recommended." -- Ask the user whether to proceed or abort -- **Never deploy silently when critical findings exist** - -### Step 4: Deploy - -```bash -sync-ctl deploy wizard -``` - -Then monitor: -```bash -sync-ctl deploy status --watch -``` - -## Decision Points Summary - -| Condition | Action | -|-----------|--------| -| Not authenticated | Run `sync-ctl auth login` first | -| No project/env selected | Guide user through selection | -| Critical security findings | Warn user, require explicit confirmation to proceed | -| High security findings (no critical) | Warn user but allow deployment | -| Clean security audit | Proceed to deploy | - -## Safety - -- **Never deploy without the security gate.** Even if the user says "just deploy", run at least a fast security scan. -- **Always confirm with the user before triggering deployment.** Show them what will be deployed, to which environment. -- **Monitor deployment status** after triggering — don't fire-and-forget. -``` - -- [ ] **Step 2: Verify frontmatter** - -Run: `head -5 skills/workflows/syncable-deploy-pipeline.md` - -- [ ] **Step 3: Commit** - -```bash -git add skills/workflows/syncable-deploy-pipeline.md -git commit -m "feat(skills): add syncable-deploy-pipeline workflow skill" -``` - ---- - -### Task 13: Final verification and summary commit - -**Files:** -- Verify: all 11 skill files exist in `skills/` - -- [ ] **Step 1: Verify all files exist** - -```bash -ls -la skills/commands/ skills/workflows/ -``` - -Expected: -- `skills/commands/`: 7 files (analyze, security, vulnerabilities, dependencies, validate, optimize, platform) -- `skills/workflows/`: 4 files (project-assessment, security-audit, iac-pipeline, deploy-pipeline) - -- [ ] **Step 2: Verify all frontmatter is valid** - -```bash -for f in skills/commands/*.md skills/workflows/*.md; do echo "=== $f ==="; head -4 "$f"; echo; done -``` - -Expected: Each file starts with `---`, `name: syncable-*`, `description: ...`, `---` - -- [ ] **Step 3: Verify git status is clean** - -```bash -git log --oneline -15 -``` - -Expected: 13 commits on this branch (1 spec + 1 scaffold + 7 command skills + 4 workflow skills) diff --git a/docs/superpowers/specs/2026-03-27-agent-output-pipeline-design.md b/docs/superpowers/specs/2026-03-27-agent-output-pipeline-design.md deleted file mode 100644 index d52ee251..00000000 --- a/docs/superpowers/specs/2026-03-27-agent-output-pipeline-design.md +++ /dev/null @@ -1,194 +0,0 @@ -# Agent Output Pipeline Design - -## Problem - -When external AI agents (Claude Code, Cursor, Windsurf, Codex, Gemini) run `sync-ctl` commands via the installed skills, raw command output goes directly to stdout. A full project scan can produce hundreds of thousands of tokens, which: - -1. Overflows the agent's context window -2. Burns through the user's token budget -3. Degrades agent reasoning quality (buried in noise) - -The syncable-cli already has a production-grade RAG pipeline for its **internal** agent (`output_store.rs`, `compression.rs`, `retrieve.rs`, `truncation.rs`). But this pipeline is only wired to the internal agent tools — it's invisible to external agents running `sync-ctl` as a shell command. - -## Solution - -Expose the existing RAG pipeline through two new CLI features: - -1. **`--agent` flag** on all scan commands — routes output through compression + disk storage, returns a ~15KB compressed summary to stdout -2. **`retrieve` subcommand** — lets agents query stored full data by ref_id with filtering - -External agents get the exact same compressed format (`CompressedOutput`) that the internal syncable agent already uses. No new compression logic. No new storage format. Just CLI plumbing to expose what exists. - -## Design Decisions - -| Decision | Choice | Rationale | -|----------|--------|-----------| -| Output format for external agents | Same `CompressedOutput` as internal agent | Reuse existing compression, no format duplication | -| `--agent` behavior on stdout | Replaces stdout with compressed summary | Whole point is agents never see full blast | -| Retrieval interface | Top-level `retrieve` subcommand | Separate concern from scan commands; matches internal `RetrieveOutputTool` | -| Opt-in vs default | New `--agent` flag (opt-in) | `--json` stays untouched for human scripts; `--agent` implies `--json` | - -## Subsystem 1: CLI Changes (Rust) - -### `--agent` Flag - -**Added to:** `analyze`, `security`, `vulnerabilities`, `dependencies`, `validate`, `optimize` - -**Behavior when `--agent` is passed:** - -1. Run the command normally, produce full output internally -2. Route through existing `compress_tool_output()` (for scan/lint commands) or `compress_analysis_output()` (for analyze) -3. Store full output to disk via `output_store::store_output()` at `/tmp/syncable-cli/outputs/{ref_id}.json` (1hr TTL) -4. Register in session via `output_store::register_session_ref()` (no-op for cross-process retrieval — kept for code path reuse with internal agent; `latest` resolution uses disk scan instead) -5. Print `CompressedOutput` JSON to stdout - -**`--agent` implies `--json`** — no need to pass both. - -**Existing flags unchanged:** `--json`, `--format`, `--display` all work exactly as before. - -**`--agent` + `--output` interaction:** When both are passed, the compressed summary goes to stdout and the full uncompressed output is written to the `--output` file. This lets agents get the summary for reasoning while preserving full data in a file for human review. - -**Strict JSON output:** `--agent` mode produces valid JSON only — no appended plaintext (the `format_session_refs_for_agent()` footer used by the internal agent is skipped). The `retrieval_hint` field inside the JSON uses CLI syntax (`sync-ctl retrieve '' --query '...'`) instead of the internal tool call format. - -**TTL cleanup:** At the start of `--agent` processing, call `cleanup_old_outputs()` to remove expired files from `/tmp/syncable-cli/outputs/`. This prevents unbounded disk growth since external CLI invocations are short-lived (unlike the internal agent which runs cleanup periodically). - -**Files to modify:** - -- `src/cli.rs` — Add `agent: bool` field to `Analyze`, `Security`, `Vulnerabilities`, `Dependencies`, `Validate`, `Optimize` command structs -- `src/main.rs` — In each command handler, check `agent` flag; if true, route output through compression pipeline instead of direct printing -- `src/agent/tools/output_store.rs` — Fix `find_issues_array()` to include `"failures"` and `"diagnostics"` fields (currently missing, causing retrieval to return empty for kubelint/hadolint/dclint/helmlint outputs) -- `src/agent/tools/compression.rs` — When called from `--agent` mode, skip `format_session_refs_for_agent()` plaintext footer so output is strict JSON; add CLI-syntax `retrieval_hint` (e.g., `sync-ctl retrieve '' --query '...'` instead of internal `retrieve_output(...)` tool call format) - -### `CompressedOutput` Format (already defined in `compression.rs`) - -For scan/lint commands: -```json -{ - "tool": "security", - "status": "CRITICAL_ISSUES_FOUND", - "summary": { "total": 47, "critical": 3, "high": 12, "medium": 20, "low": 8, "info": 4 }, - "critical_issues": [ "...full details for all critical..." ], - "high_issues": [ "...first 10 high issues..." ], - "patterns": [ { "code": "hardcoded-secret", "count": 5, "severity": "High", "message": "...", "affected_files": ["..."] } ], - "full_data_ref": "security_a1b2c3d4", - "retrieval_hint": "Use `sync-ctl retrieve 'security_a1b2c3d4' --query 'severity:critical'` for full details" -} -``` - -For analyze: -```json -{ - "tool": "analyze_project", - "summary": { "project_count": 3, "languages": ["Rust", "TypeScript"], "frameworks": ["Axum", "React"] }, - "full_data_ref": "analyze_e5f6g7h8", - "retrieval_hint": "Use `sync-ctl retrieve 'analyze_e5f6g7h8' --query 'section:frameworks'` for details" -} -``` - -### `retrieve` Subcommand - -**Definition in `src/cli.rs`:** -``` -Retrieve { - ref_id: Option, // e.g., "security_a1b2c3d4" or "latest" - query: Option, // e.g., "severity:critical", "file:path" - list: bool, // --list: show all stored outputs -} -``` - -**Behavior:** - -- `sync-ctl retrieve --list` — Calls `output_store::list_outputs()`, prints table of ref_id / tool / age / size -- `sync-ctl retrieve ` — Calls `output_store::retrieve_output(ref_id)`, prints full stored JSON -- `sync-ctl retrieve --query ""` — Calls retrieval with filtering from `retrieve.rs` -- `sync-ctl retrieve latest --query ""` — Resolves `latest` by scanning `/tmp/syncable-cli/outputs/*.json` files, sorting by embedded timestamp, and using the most recent. This works across separate CLI invocations (no in-memory state needed). - -**Query filters (already implemented in `retrieve.rs`):** - -| Filter | Example | Works with | -|--------|---------|-----------| -| `severity:` | `severity:critical` | security, vulnerabilities, validate, optimize | -| `file:` | `file:deployment.yaml` | All scan commands | -| `code:` | `code:DL3008` | validate, security | -| `container:` | `container:nginx` | optimize | -| `section:` | `section:frameworks` | analyze | -| `language:` | `language:Go` | analyze | -| `framework:` | `framework:React` | analyze | -| `project:` | `project:api-gateway` | analyze | -| `compact:true` | `compact:true` | analyze | - -**Output:** Always JSON. If filtered result > 50KB, prints warning suggesting more specific query. - -**`retrieve --list` format:** Always JSON (array of objects with `ref_id`, `tool`, `timestamp`, `age`, `size_bytes` fields). External agents consume this programmatically. - -**Error handling:** If `ref_id` is not found (expired or invalid), return JSON error object `{ "error": "not_found", "message": "..." , "available": [...] }` with non-zero exit code. The `available` array lists valid ref_ids so the agent can self-correct. - -**Handler in `src/main.rs`:** Thin wrapper calling existing `output_store` and `retrieve` module functions. The logic already exists — this just exposes it through CLI args. - -**TTL cleanup:** At the start of `retrieve` subcommand execution, call `cleanup_old_outputs()` to remove expired files. - -**`latest` resolution:** Implemented by scanning all `*.json` files in `/tmp/syncable-cli/outputs/`, reading the `timestamp` field from each, and selecting the most recent. This is disk-based and works across separate process invocations — no dependency on the in-memory `SESSION_REGISTRY`. - -## Subsystem 2: Skill Rewrites (Markdown) - -### Command Skills (7 files) - -Each command skill changes: - -1. **Command invocations** switch from `--json` to `--agent`: - - Before: `sync-ctl security --mode paranoid --format json` - - After: `sync-ctl security --mode paranoid --agent` - -2. **New "Reading Results" section** added to each skill, teaching the agent: - - The output is a compressed summary — act on it directly for triage and decisions - - All critical issues are always included in full - - To drill into specifics: `sync-ctl retrieve --query ""` - - Skill-specific query filters documented (each skill lists its applicable filters) - -3. **Raw JSON parsing instructions removed** — no more "parse the findings array" or "count the issues manually" - -### Workflow Skills (4 files) - -Same changes as command skills, plus: - -1. **Cross-step retrieval** — Workflows teach agents to reuse `ref_id` from earlier steps. Example in project-assessment: "The analyze step returned `full_data_ref`. Use this ref_id with `sync-ctl retrieve` if you need details later — don't re-run analyze." - -2. **`sync-ctl retrieve --list`** — Workflows teach agents to check what data is already stored before running redundant commands. - -### Skill-Specific Query Filter Documentation - -| Skill | Filters to Document | -|-------|-------------------| -| `syncable-analyze` | `section:summary`, `section:frameworks`, `section:languages`, `language:`, `framework:`, `project:`, `compact:true` | -| `syncable-security` | `severity:`, `file:`, `code:` | -| `syncable-vulnerabilities` | `severity:`, `file:` | -| `syncable-dependencies` | `severity:`, `file:` (new wiring needed — dependency audit findings follow the same issues/findings array pattern as other scan commands, but `compress_tool_output()` is not yet called from the dependencies code path) | -| `syncable-validate` | `severity:`, `file:`, `code:` | -| `syncable-optimize` | `severity:`, `container:` | -| `syncable-platform` | N/A (action commands — auth/deploy are imperative, not scan/analysis, so `--agent` compression is not applicable) | - -### Files Modified - -- `skills/commands/syncable-analyze.md` -- `skills/commands/syncable-security.md` -- `skills/commands/syncable-vulnerabilities.md` -- `skills/commands/syncable-dependencies.md` -- `skills/commands/syncable-validate.md` -- `skills/commands/syncable-optimize.md` -- `skills/commands/syncable-platform.md` (no `--agent` flag — these are action commands, not scans. Skill unchanged except a note that other skills use `--agent`) -- `skills/workflows/syncable-project-assessment.md` -- `skills/workflows/syncable-security-audit.md` -- `skills/workflows/syncable-iac-pipeline.md` -- `skills/workflows/syncable-deploy-pipeline.md` - -## Implementation Order - -**Subsystem 1 first** (CLI must support `--agent` and `retrieve` before skills can reference them), **then Subsystem 2**. - -## Not In Scope - -- Changing compression algorithms or thresholds (already tuned at 15KB target) -- Changing storage location or TTL (1hr at `/tmp/syncable-cli/outputs/`) -- Changes to the internal agent's RAG pipeline (stays identical) -- Changes to the npx installer (skills are content-only changes, format is unchanged) -- New agent detection or format transformer logic diff --git a/docs/superpowers/specs/2026-03-27-npx-installer-design.md b/docs/superpowers/specs/2026-03-27-npx-installer-design.md deleted file mode 100644 index 114ad604..00000000 --- a/docs/superpowers/specs/2026-03-27-npx-installer-design.md +++ /dev/null @@ -1,403 +0,0 @@ -# Syncable CLI Skills NPX Installer Design - -## Context - -The Syncable CLI (`sync-ctl`) ships with 11 skills (7 command + 4 workflow) that teach AI coding agents how to use the CLI toolbox. Users need a way to: - -1. Install `sync-ctl` (and its Rust toolchain dependency) if missing -2. Detect which AI coding agents they have installed -3. Install skills in the correct format for each agent -4. Keep skills updated - -This installer lives inside the `syncable-cli` repo at `installer/` and is published to npm as `syncable-cli-skills`. - -## Package Details - -- **npm name:** `syncable-cli-skills` -- **Usage:** `npx syncable-cli-skills` -- **Location in repo:** `installer/` -- **Tech stack:** TypeScript, commander, inquirer, ora, chalk -- **Source:** open source (part of syncable-cli repo) - -## Directory Structure - -``` -installer/ -├── package.json -├── tsconfig.json -├── src/ -│ ├── index.ts # CLI entrypoint (commander setup) -│ ├── commands/ -│ │ ├── install.ts # install command (default) -│ │ ├── uninstall.ts # remove skills from agents -│ │ ├── update.ts # update skills (uninstall + install) -│ │ └── status.ts # show what's installed where -│ ├── prerequisites/ -│ │ ├── check.ts # check rustup, cargo, sync-ctl -│ │ ├── install-rustup.ts # install Rust toolchain -│ │ └── install-cli.ts # cargo install syncable-cli -│ ├── agents/ -│ │ ├── types.ts # AgentConfig interface -│ │ ├── detect.ts # detect all installed agents -│ │ ├── claude.ts # Claude Code config -│ │ ├── cursor.ts # Cursor config -│ │ ├── windsurf.ts # Windsurf config -│ │ ├── codex.ts # Codex config -│ │ └── gemini.ts # Gemini CLI config -│ ├── transformers/ -│ │ ├── claude.ts # no-op (native format) -│ │ ├── codex.ts # wrap in SKILL.md directory -│ │ ├── cursor.ts # convert to .mdc format -│ │ ├── windsurf.ts # convert to windsurf rule format -│ │ └── gemini.ts # concatenate into GEMINI.md section -│ ├── skills.ts # load bundled skill files, parse frontmatter -│ └── utils.ts # shell exec, spinner, platform helpers -├── skills/ # bundled copy of ../skills/ (build artifact) -└── dist/ # compiled JS output -``` - -## CLI Interface - -``` -npx syncable-cli-skills [command] [options] - -Commands: - install Install sync-ctl and skills (default) - uninstall Remove skills from agents - update Update skills to latest version - status Show what's installed and where - -Options: - --skip-cli Skip sync-ctl installation check - --dry-run Show what would be done without doing it - --agents Comma-separated: claude,cursor,windsurf,codex,gemini - --global-only Only install global skills (Claude, Codex) - --project-only Only install project-level rules (Cursor, Windsurf, Gemini) - -y, --yes Skip confirmations (auto-yes) - --verbose Show detailed output for debugging - -h, --help Show help - -v, --version Show version -``` - -Running with no arguments defaults to `install`. - -## Interactive Install Flow - -``` -$ npx syncable-cli-skills - - Syncable CLI Skills Installer - ───────────────────────────── - - Checking prerequisites... - - ✓ Node.js v20.11.0 - ✗ sync-ctl not found - ✗ cargo not found - - sync-ctl (Syncable CLI) is required but not installed. - This requires Rust's cargo package manager. - - ? Install Rust toolchain via rustup? (Y/n) Y - - ◐ Installing rustup... - ✓ Rust toolchain installed (cargo 1.79.0) - - ? Install syncable-cli via cargo? (Y/n) Y - - ◐ Running: cargo install syncable-cli - ✓ sync-ctl installed (v0.36.0) - - Detecting AI coding agents... - - ✓ Claude Code detected (~/.claude/) - ✓ Cursor detected (~/.cursor/) - ✗ Windsurf not detected - ✓ Codex detected (~/.codex/) - ✗ Gemini CLI not detected - - ? Which agents should receive Syncable skills? - ◉ Claude Code — global install (~/.claude/skills/syncable/) - ◉ Cursor — project install (.cursor/rules/syncable-*.mdc) - ◉ Codex — global install (~/.codex/skills/syncable-*/) - ────────────── - ◯ Windsurf (not detected — install anyway?) - ◯ Gemini CLI (not detected — install anyway?) - - ◐ Installing skills for Claude Code... - ✓ 11 skills installed to ~/.claude/skills/syncable/ - - ◐ Installing skills for Cursor... - ✓ 11 skills installed to .cursor/rules/ - - ◐ Installing skills for Codex... - ✓ 11 skills installed to ~/.codex/skills/ - - ───────────────────────────── - ✓ Setup complete! - - Installed: - • sync-ctl v0.36.0 - • 7 command skills + 4 workflow skills - • Agents: Claude Code, Cursor, Codex - - Try it: Open Claude Code and say "assess this project" -``` - -## Agent Detection & Installation - -### Agent Categories - -**Global install (skills persist across all projects):** - -| Agent | Detection | Skill Path | Format | -|-------|-----------|-----------|--------| -| Claude Code | `~/.claude/` exists | `~/.claude/skills/syncable/commands/*.md`, `~/.claude/skills/syncable/workflows/*.md` | Native — our format matches | -| Codex | `~/.codex/` exists | `~/.codex/skills/syncable-/SKILL.md` | Each skill is a directory with SKILL.md | - -**Project install (skills are per-repo, installed into current working directory):** - -| Agent | Detection | Skill Path | Format | -|-------|-----------|-----------|--------| -| Cursor | `~/.cursor/` exists | `.cursor/rules/syncable-.mdc` | `.mdc` with `description`/`globs`/`alwaysApply` frontmatter | -| Windsurf | `~/.codeium/windsurf/` exists | `.windsurf/rules/syncable-.md` | `.md` with `trigger`/`description` frontmatter | -| Gemini CLI | `~/.gemini/` exists | `GEMINI.md` in project root (append) | Concatenated markdown block with markers | - -### Platform Paths - -- macOS/Linux: `~/` expands to `os.homedir()` -- Windows: `os.homedir()` returns `C:\Users\` -- All paths use `path.join()` for cross-platform safety - -## Skill Format Transformers - -### Claude Code (no transform) - -Skills copy as-is. The `commands/` and `workflows/` directory structure is preserved under `~/.claude/skills/syncable/`. - -### Codex - -Each skill becomes a directory under `~/.codex/skills/`: - -``` -~/.codex/skills/syncable-analyze/ -└── SKILL.md -``` - -Frontmatter stays the same (`name`, `description`). Body stays the same. - -### Cursor - -Each skill becomes a `.mdc` file. Frontmatter is transformed: - -**From (our format):** -```yaml ---- -name: syncable-analyze -description: Use when analyzing... ---- -``` - -**To (.mdc format):** -```yaml ---- -description: "Syncable CLI: Use when analyzing..." -globs: -alwaysApply: true ---- -``` - -The `name` field is dropped (filename is the identifier). `globs` is empty (skills apply globally). `alwaysApply: true` ensures the agent always has access to the skill context. - -### Windsurf - -Each skill becomes a `.md` file in `.windsurf/rules/`. Frontmatter is transformed: - -**To:** -```yaml ---- -trigger: always -description: "Syncable CLI: Use when analyzing..." ---- -``` - -### Gemini CLI - -All skills are concatenated into a single block and appended to `GEMINI.md`: - -```markdown - -## Syncable CLI Skills - -The following skills describe how to use the Syncable CLI (sync-ctl) toolbox. - -### syncable-analyze -[full skill content] - -### syncable-security -[full skill content] - -... - -``` - -The HTML comment markers allow `uninstall` and `update` to cleanly find and remove/replace the section without touching user content. - -If `GEMINI.md` doesn't exist, create it. If it exists, append (preserving existing content). - -## Prerequisite Installation - -### Rustup / Cargo - -Detection: check if `cargo` is on PATH or at `$HOME/.cargo/bin/cargo`. - -If missing, install via: - -**macOS/Linux:** -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -``` - -**Windows (fallback chain):** -1. Try `winget install Rustlang.Rustup` (requires no admin on modern Windows) -2. If winget unavailable, download and run `rustup-init.exe` from https://rustup.rs -3. If both fail, show manual install instructions and skip - -**PATH propagation after install:** Node's `child_process.exec` spawns a new shell each time, so `source ~/.cargo/env` has no effect. After rustup completes, the installer must prepend `$HOME/.cargo/bin` to `process.env.PATH` in the Node process so subsequent `exec` calls find `cargo`. - -```typescript -process.env.PATH = `${path.join(os.homedir(), '.cargo', 'bin')}${path.delimiter}${process.env.PATH}`; -``` - -### sync-ctl - -Detection: `sync-ctl --version` (or check `$HOME/.cargo/bin/sync-ctl`). - -If missing (but cargo exists): -```bash -cargo install syncable-cli -``` - -If already installed, parse version from `sync-ctl --version` output. Only offer `cargo install syncable-cli --force` if the installed version is older than the minimum required version. This avoids unnecessary multi-minute recompiles. - -The npm package should declare a `MIN_SYNC_CTL_VERSION` constant that matches the skills it bundles. - -## Commands - -### install (default) - -1. Check prerequisites (Node.js, cargo, sync-ctl) -2. Offer to install missing prerequisites interactively -3. Detect installed agents -4. Present agent checklist (detected pre-selected, undetected available) -5. For each selected agent: transform and install skills -6. Print summary - -### uninstall - -1. Detect where skills are installed -2. Confirm removal -3. For global agents: remove `~/.claude/skills/syncable/`, `~/.codex/skills/syncable-*/` -4. For project agents: remove `.cursor/rules/syncable-*.mdc`, `.windsurf/rules/syncable-*.md` -5. For Gemini: remove content between `SYNCABLE-CLI-SKILLS-START` and `SYNCABLE-CLI-SKILLS-END` markers in `GEMINI.md` - -### update - -Uninstall then install. Uses bundled skills from the npm package (which reflect the version at publish time). - -### status - -Show a table: -``` - Agent Status Location - ───────────── ─────────── ────────────────────────── - Claude Code ✓ installed ~/.claude/skills/syncable/ (11 skills) - Cursor ✓ installed .cursor/rules/ (11 .mdc files) - Windsurf ✗ not installed - Codex ✓ installed ~/.codex/skills/ (11 skills) - Gemini CLI ✗ not installed - - sync-ctl ✓ v0.36.0 - cargo ✓ v1.79.0 -``` - -## Build & Publish - -### Build script (package.json) - -```json -{ - "scripts": { - "prebuild": "node scripts/copy-skills.js", - "build": "tsc", - "prepublishOnly": "npm run build" - } -} -``` - -The `prebuild` step runs a Node script (`scripts/copy-skills.js`) that copies `../skills/` into `installer/skills/` using `fs-extra`. This is cross-platform safe (no reliance on Unix `cp`). The `installer/skills/` directory must be in `.gitignore` to avoid duplicating the canonical source. - -### Module system: ESM - -The package uses `"type": "module"` since `chalk@5`, `ora@8`, and `inquirer@9` are ESM-only. TypeScript compiles to ESM output (`"module": "NodeNext"` in tsconfig). Requires Node.js >= 18. - -### package.json key fields - -```json -{ - "name": "syncable-cli-skills", - "version": "0.1.0", - "type": "module", - "description": "Install Syncable CLI skills for AI coding agents", - "bin": { - "syncable-cli-skills": "./dist/index.js" - }, - "files": [ - "dist/", - "skills/" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "commander": "^12.0.0", - "inquirer": "^9.0.0", - "ora": "^8.0.0", - "chalk": "^5.0.0", - "fs-extra": "^11.0.0" - }, - "devDependencies": { - "@types/fs-extra": "^11.0.0", - "typescript": "^5.0.0" - } -} -``` - -## Error Handling - -| Scenario | Behavior | -|----------|----------| -| No internet (can't install rustup/cargo) | Show manual install instructions, skip to agent detection | -| cargo install fails | Show error, suggest `cargo install syncable-cli` manually, continue with skills | -| Agent directory not writable | Warn and skip that agent | -| Skill file already exists | Overwrite (with confirmation unless `--yes`) | -| GEMINI.md has existing Syncable section | Replace between markers | -| Windows without curl | Use `winget` for rustup, fall back to manual instructions | -| Node.js too old | Check `>=18.0.0` at startup, exit with clear message | - -## Design Decisions - -1. **Skills bundled in npm package** — no network fetch at install time. Skills version matches the npm package version. -2. **Interactive by default** — every destructive or install action requires confirmation. `--yes` flag for CI/scripting. -3. **Per-project for Cursor/Windsurf/Gemini** — these agents don't have global skill directories, so skills must be installed per-project. The installer makes this clear. -4. **Gemini uses comment markers** — allows clean uninstall/update without destroying user content in GEMINI.md. -5. **Transformers are pure functions** — each takes a parsed skill (frontmatter + body) and returns the target format string. Easy to test, easy to add new agents. -6. **Codex gets directory-per-skill** — matches Codex's native SKILL.md convention. -7. **Cursor uses alwaysApply: true** — skills should always be available, not scoped to specific file globs. -8. **Uninstall only removes skills** — `sync-ctl`, `cargo`, and `rustup` are intentionally left installed. They are general-purpose tools the user may rely on beyond Syncable. -9. **Dynamic skill count** — the installer counts bundled skills at runtime rather than hardcoding "11 skills". If skills are added or removed, output reflects the actual count. -10. **No telemetry** — the installer does not phone home, collect analytics, or send any data. It runs entirely locally. -11. **`--agents` takes precedence over `--global-only`/`--project-only`** — if `--agents` is specified, it overrides the global/project filters. The flags are for convenience when `--agents` is not used. -12. **Agent detection uses both directory and PATH** — Codex is detected via `~/.codex/` OR `codex` on PATH. Gemini CLI via `~/.gemini/` OR `gemini` on PATH. This avoids false positives from other tools that may use the same directories. -13. **ESM module system** — the package uses `"type": "module"` because `chalk@5`, `ora@8`, and `inquirer@9` are ESM-only. Requires Node.js >= 18. -14. **`installer/skills/` is gitignored** — this directory is a build artifact copied from `../skills/` at build time. It must be in `.gitignore`. diff --git a/docs/superpowers/specs/2026-03-27-syncable-cli-skills-design.md b/docs/superpowers/specs/2026-03-27-syncable-cli-skills-design.md deleted file mode 100644 index 63482e80..00000000 --- a/docs/superpowers/specs/2026-03-27-syncable-cli-skills-design.md +++ /dev/null @@ -1,247 +0,0 @@ -# Syncable CLI Skills Design - -## Context - -Syncable CLI (`sync-ctl`) is a Rust-based DevOps toolbox with 260+ language/framework detection, security scanning, vulnerability checking, Dockerfile/Compose/K8s linting (native Rust implementations of hadolint, dclint, kubelint, helmlint), Kubernetes optimization, and Syncable platform integration. - -Rather than competing with agent frameworks or building an MCP server, we're pivoting to a **skills-first approach**: the CLI stays a pure toolbox, and we ship Skills that teach any agent (Claude Code, Cursor, etc.) when and how to shell out to `sync-ctl`. - -### Why Skills Over MCP - -- No server to host, maintain, or pay for -- No auth layer needed -- Full local file access — the CLI reads the repo directly -- No latency shipping file contents over HTTP -- Works offline -- Simpler for users — install CLI + skills, done - -## Architecture - -### Layered Skill Model - -Two layers: - -1. **Command skills** — one per CLI command, the atomic building blocks. Each skill is the single source of truth for its command's syntax, flags, output interpretation, and error handling. -2. **Workflow skills** — orchestrate command skills for common multi-step patterns. They define sequencing, decision logic, and short-circuit conditions. - -The agent can invoke command skills individually for granular tasks, or invoke workflow skills for end-to-end patterns. Workflow skills reference command skills by name. - -### Directory Structure - -``` -skills/ -├── commands/ -│ ├── syncable-analyze.md -│ ├── syncable-security.md -│ ├── syncable-vulnerabilities.md -│ ├── syncable-dependencies.md -│ ├── syncable-validate.md -│ ├── syncable-optimize.md -│ └── syncable-platform.md -│ -└── workflows/ - ├── syncable-project-assessment.md - ├── syncable-security-audit.md - ├── syncable-iac-pipeline.md - └── syncable-deploy-pipeline.md -``` - -### Skill File Format - -Every skill follows the Claude Code skill format: - -```markdown ---- -name: syncable- -description: ---- - -## Purpose -## Prerequisites -## Commands -## Output Interpretation -## Error Handling -## Examples -``` - -Workflow skills add: - -```markdown -## Workflow Steps -## Decision Points -``` - -## Command Skills (7) - -### syncable-analyze - -- **Triggers:** analyze project stack, detect languages/frameworks/runtimes/dependencies, "what is this project?" -- **Description:** `Use when analyzing a project's tech stack, detecting languages, frameworks, runtimes, or dependencies using Syncable CLI. Trigger on: "what stack is this", "analyze this project", "detect frameworks".` -- **Command:** `sync-ctl analyze --json` -- **Key flags:** `--json`, `--detailed`, `--display {matrix|detailed|summary}`, `--only ` -- **Output:** languages, frameworks, dependencies, runtimes, monorepo structure, Docker presence -- **Role:** Foundation skill — almost every workflow starts here. - -### syncable-security - -- **Triggers:** secret scanning, credential leaks, code security patterns, "is this project secure?" -- **Description:** `Use when scanning code for secrets, credentials, API keys, or insecure code patterns using Syncable CLI. Trigger on: "scan for secrets", "find leaked credentials", "security scan".` -- **Command:** `sync-ctl security --mode --format json` -- **Key flags:** `--mode {lightning|fast|balanced|thorough|paranoid}`, `--include-low`, `--no-secrets`, `--no-code-patterns`, `--fail-on-findings` -- **Output:** findings with severity, file locations, remediation steps, security score -- **Mode selection guidance:** - - `lightning` — quick check, critical files only (.env, configs) - - `fast` — smart sampling with priority patterns - - `balanced` — recommended default - - `thorough` — comprehensive, pre-deploy - - `paranoid` — compliance/audit scenarios - -### syncable-vulnerabilities - -- **Triggers:** CVE scanning, dependency vulnerabilities, "are my dependencies safe?" -- **Description:** `Use when checking project dependencies for known CVEs or security vulnerabilities using Syncable CLI. Trigger on: "check for CVEs", "vulnerable dependencies", "dependency security".` -- **Command:** `sync-ctl vulnerabilities --format json` -- **Key flags:** `--severity {low|medium|high|critical}`, `--output ` -- **Output:** CVEs per dependency, severity, affected versions, fix versions -- **Note:** Supports npm, pip, cargo, go, java ecosystems. - -### syncable-dependencies - -- **Triggers:** license audit, dependency listing, "what dependencies do I have?" -- **Description:** `Use when auditing project dependencies for licenses, production/dev split, or detailed dependency analysis using Syncable CLI. Trigger on: "license audit", "list dependencies", "dependency analysis".` -- **Command:** `sync-ctl dependencies --licenses --format json` -- **Key flags:** `--licenses`, `--vulnerabilities`, `--prod-only`, `--dev-only` -- **Output:** dependency tree, license types, prod vs dev split - -### syncable-validate - -- **Triggers:** lint Dockerfiles, validate Compose, check Terraform, "is my IaC correct?" -- **Description:** `Use when linting or validating Dockerfiles, Docker Compose files, Terraform configs, or Kubernetes manifests using Syncable CLI. Trigger on: "lint Dockerfile", "validate compose", "check terraform".` -- **Command:** `sync-ctl validate --types ` -- **Key flags:** `--types {comma-separated}`, `--fix` -- **Output:** lint violations, best practice issues, auto-fix suggestions -- **Note:** Covers hadolint (Dockerfiles), dclint (Docker Compose), Terraform validation in one command. - -### syncable-optimize - -- **Triggers:** Kubernetes optimization, resource right-sizing, cost analysis, drift detection -- **Description:** `Use when optimizing Kubernetes resource requests/limits, analyzing costs, or detecting configuration drift using Syncable CLI. Trigger on: "optimize k8s", "right-size pods", "k8s cost analysis".` -- **Command:** `sync-ctl optimize ` with various flags -- **Key flags:** `--cluster`, `--prometheus `, `--namespace`, `--full`, `--cloud-provider {aws|gcp|azure|onprem}`, `--fix`, `--apply` (requires `--fix`), `--dry-run` -- **Output:** recommendations, cost savings estimates, drift detection results -- **Important:** `--fix` generates fix suggestions only. `--apply` (which requires `--fix`) actually writes changes to manifest files. The agent must never use `--apply` without explicit user confirmation. -- **Note:** `--full` adds kubelint security checks + helmlint validation. - -### syncable-platform - -- **Triggers:** Syncable authentication, project/org/env management, deployment -- **Description:** `Use when authenticating with Syncable, managing projects/orgs/environments, or deploying services through the Syncable platform. Trigger on: "syncable login", "select project", "deploy to syncable".` -- **Commands:** - - `sync-ctl auth login|logout|status|token` - - `sync-ctl org list|select ` - - `sync-ctl project list|select |current|info` - - `sync-ctl env list|select ` - - `sync-ctl deploy wizard|new-env|status ` -- **Note:** Auth-first flow — skill checks auth status before any platform operation. - -## Workflow Skills (4) - -### syncable-project-assessment - -- **Triggers:** "assess this project", "full health check", "what's the state of this codebase?", onboarding to a new repo -- **Description:** `Use when a user wants a comprehensive project health check — combines stack analysis, security scanning, vulnerability checks, and dependency auditing via Syncable CLI. Trigger on: "assess this project", "full health check", "project overview".` -- **Steps:** - 1. `syncable-analyze` → understand the stack - 2. `syncable-security` (balanced mode) → scan for secrets and code patterns - 3. `syncable-vulnerabilities` → check dependency CVEs - 4. `syncable-dependencies` → license audit -- **Decision logic:** - - If analyze shows no dependencies → skip vulnerabilities and dependencies steps - - If no secrets-capable files found → use lightning mode for security -- **Output:** Agent synthesizes a unified project health report. - -### syncable-security-audit - -- **Triggers:** "run a full security audit", "is this production-ready?", pre-merge security review -- **Description:** `Use when performing a thorough pre-deployment or compliance security review — combines deep security scan, CVE checks, and IaC validation via Syncable CLI. Trigger on: "security audit", "is this production-ready", "pre-deploy security check".` -- **Steps:** - 1. `syncable-analyze` → understand what we're scanning - 2. `syncable-security` (thorough or paranoid mode) → deep secret + pattern scan - 3. `syncable-vulnerabilities` → CVE scan - 4. `syncable-validate` → lint IaC files if any exist -- **Decision logic:** - - If analyze detects Docker/K8s/Terraform files → include validate step, else skip - - Severity threshold driven by context: PR review = thorough, pre-deploy = paranoid - -### syncable-iac-pipeline - -- **Triggers:** "validate my infrastructure", "check my Dockerfiles and K8s manifests", "lint all IaC" -- **Description:** `Use when validating all infrastructure-as-code files in a project — combines IaC linting with Kubernetes optimization and security checks via Syncable CLI. Trigger on: "validate infrastructure", "lint all IaC", "check my k8s and docker files".` -- **Steps:** - 1. `syncable-analyze` → detect which IaC types exist - 2. `syncable-validate` → lint all detected types - 3. `syncable-optimize` (with `--full` if K8s manifests found) → kubelint + helmlint + resource optimization -- **Decision logic:** - - Only run optimize if K8s manifests detected by analyze - -### syncable-deploy-pipeline - -- **Triggers:** "deploy this project", "set up deployment", "push to Syncable" -- **Description:** `Use when deploying a project through Syncable — orchestrates auth, analysis, security gating, and deployment via Syncable CLI. Trigger on: "deploy this", "push to syncable", "set up deployment".` -- **Steps:** - 1. `syncable-platform` → check auth status, ensure project/org/env selected - 2. `syncable-analyze` → understand the project - 3. `syncable-security-audit` (workflow) → pre-deploy security gate - 4. `syncable-platform` → trigger deployment -- **Decision logic:** - - If auth missing → guide through login first - - If security audit finds critical findings → warn user, require confirmation before proceeding - - Never deploy silently - -## Installation Model - -Skills ship inside the `syncable-cli` repo under `skills/`. A future `npx syncable-skills install` command will: - -1. Copy skill files from `skills/` into the agent's skill directory (e.g., `~/.claude/skills/` for Claude Code) -2. Verify `sync-ctl` is available on PATH -3. Optionally add a CLAUDE.md entry pointing to the skills - -## Global Prerequisites - -Every skill assumes: -- `sync-ctl` binary is installed and on PATH -- The agent has shell access (Bash tool or equivalent) -- The agent has access to the project directory being analyzed -- For platform skills: internet access for Syncable API -- All command skills default to `--json` or `--format json` output for machine-readable results the agent can parse - -## Design Decisions - -1. **No generate skills for now** — `generate iac` and `generate ci` are excluded from skills. They stay in the CLI but aren't surfaced to agents yet. -2. **Platform commands consolidated** — auth/project/org/env/deploy are one skill (`syncable-platform`) because they're always used together in a flow. -3. **JSON-first output** — all command skills instruct the agent to use `--json` or `--format json` so the agent can parse structured output, not terminal tables. Note: `analyze` uses a `--json` boolean flag while other commands use `--format json` via the OutputFormat enum. -4. **Analyze is the foundation** — most workflows start with analyze. The skill explicitly tells the agent to run it first if it hasn't been run in the current session. -5. **Security mode guidance** — the security skill provides explicit guidance on which scan mode to use based on context, rather than leaving the agent to guess. -6. **Global flags** — all commands support `--verbose` (`-v`/`-vv`/`-vvv`), `--quiet`, and `--config ` at the CLI level. JSON output is command-specific: `analyze` uses a `--json` boolean flag, while `security`, `vulnerabilities`, `dependencies`, and `optimize` use `--format json` via the OutputFormat enum. Each skill must document the correct JSON flag for its command. -7. **Dependencies --vulnerabilities vs standalone vulnerabilities** — `dependencies --vulnerabilities` gives a quick inline check alongside license info; the standalone `vulnerabilities` command is a deeper dedicated CVE scan. Skills should guide agents to use the standalone command for thorough checks and the flag for quick inline context. - -## CLI Branch State - -All commands and flags referenced in this spec exist on both `main` and `develop` branches. The `develop` branch is ahead of `main` with additional features (agent improvements, framework detection, etc.) but the skills surface area is fully available on `main`. This skills branch can target either branch. - -## Excluded Commands - -The following CLI commands are intentionally excluded from the skills surface: - -- **`generate`** — IaC and CI generation are excluded for now (design decision #1). -- **`support`** — lists supported technologies. Low agent utility; agents can infer support from `analyze` results. -- **`tools`** — manages vulnerability scanning tool installation. Rather than a dedicated skill, `syncable-vulnerabilities` includes error handling guidance that directs agents to run `sync-ctl tools install` when a scanner is missing. -- **`chat` / `agent`** — the AI agent and AG-UI server. Excluded because the entire point of this skills approach is to replace agent-in-agent with direct CLI toolbox usage. - -## Workflow Composition - -Workflow skills can reference other workflow skills as steps (e.g., `syncable-deploy-pipeline` calls `syncable-security-audit`). When a workflow references another workflow, the inner workflow's full step sequence and decision logic is executed inline — the agent treats it as expanding the inner workflow's steps into the outer workflow at that position. - -## Security Mode Default - -The CLI defaults `--mode` to `thorough`. Skills should always pass `--mode` explicitly rather than relying on the CLI default, since the skill's recommended mode may differ from the CLI default (e.g., `balanced` for project assessments, `paranoid` for compliance audits). diff --git a/tests/ag-ui-app/backend/data/smart-reply.db b/tests/ag-ui-app/backend/data/smart-reply.db deleted file mode 100644 index e6efd712724a12808296a368afe1e9abfb29dc99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 zcmeI%Z*SU00LO8grjW3<=`%lwD()GuWz$$6^}*CqxO7no>A=z2N0tYd%o4Gw?M8|A zDABsNu{W~4hDm!hd$_Z)4bC5_)1*B>UrWIG?(D;#-yO*E`1^quD{&lzr*15s8jp=i z#dslvVHg$ts_R$2Rdrz_-_f7uO8Ks0#rW#i&mV68ZES7*X*~LA`;P~IJ$m-p7Y}}` z|6YHttJn}g009ILKmY**{+ocmRkNNp%uCDjr|R6Bo;$AZ&Msna5;^Kj`EjvZ?~P>F zmSTU{mv6-_J;mWr6un#N!A^WJSG!^oDmPYB$Bo-RZ`Q2mP4n_eG2SHb&r}$>u^0G} zlXWdsZrvEU)SMraj9+SKEyopoIqtPjYc=cJ9rJR#7(7(-nWyxyD_12I8%j9QK)7^*xi={xwI3lx=1%~Ds1`I7H>xT2i?&- z@mjvy&6<|Hi!Q@MTMUl|1JV0V_Fjt>ejS)-HBXeULLEYLS2QQV{37{`)J#p{q%_qf z-PUgBr@wY7=@@z2sfyjH8@r{-Rcbv;?EM3&&G))T16%ZtMk6`2ouqPXcMsm=XWBa) z$^BQuor!6FmFr-yKvGk zt~0ddD>*7okjK2*=cTlNka8%#7TsZAe6!55Sbd`Z|3=g6j(gp{TqN`5M%C)>+?7lw z>vXch{i~5#vzkrwmr0t+DrZ#Msgx2bHGN7>LFk=$`X|phRZ--gsN1thgF0UXmFMFv zv&*aEz`u5aJgBrKU5WEBIMzeyUpse}ti9vVQ~q?8u1I$t%z_i8e|?h{H;PmkduQpQ z_0@+s)JLqW(SMWcB7cfs8FPgQAbfX&%{y)5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjZBQXo&6 z5CUZiw9=r=#&t~~rV8a*LqdQ60RjXF5GX;Qlg1^qsYe1A1iI<|!mcd>1PBmlvOxQH zrgW1#)Ej}n1bQh?VHW}=3gnvugg}`B`OXX>K!5-N0t5&UAV7cs0RjXF5FkK+009C7 L2oNAZpxFXH8k8ab diff --git a/tests/ag-ui-app/backend/data/smart-reply.db-wal b/tests/ag-ui-app/backend/data/smart-reply.db-wal deleted file mode 100644 index 277251ce284070951ca479fef4e49a861a0ce06a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61832 zcmeI5U2Gd!702zQO*U?KgH&k)TdE!`p(I^9YT|9vL_#5P+$6h6n#OLoe5}m%%(Xqu zc*dC-$8lFHnl4{&NGm`>JS^~lKnsG0g%?08fp}qs5b&_9K&z@eAf8r2JRlGf|9i*d z*sUve0ihtBkrU6{xu3st?zxkjx%bR|a_CMheeb=o*n48?_}DkUlpA|aPCxO&E6(80 z@18@@KzDe4&RseE=HTBRMPPjBdog_YU;<2l2`~XBaAXO*v=L9t-Z}O1_=_h#n6X>C zo6WMh+A4~?XAN*aW!EFWh9ENBT3tAn^r;hM$~_9CsQFX$ zY*hsJZhsl^N|J)ABvG$>t}v=P2k8XTt9zD@)B$2rQ#b($$0SEelZ=KRxHapR2rSoG z*l_)Tq*JkQ&$FCLSOk*zfuv0O0-2)aqdHF6sx)+jmFyNW3YxCBqvDq&YAwl>b=z&J z06{*!Y^f3j0{MCrxP& zQ`A0HisL$vL6;ty(y)A}my(+nAPQKLRSSBcqWgw)lqNLMSsHE7)x&@JhqBf#pRuLEfb_D;%<5rDjq*P61k4(!pBw7!HMSW(i}k_&A_R9j!? z7}k?YE!0W(yq-MLTAh{sN8jF4AR{$9td87omnQ;kEBuC67KYUMEJCMiTJe->sTK&k zsVuj)N}v;7?ZMcZ-6jfH=FTgIPyuv6w{6EE+O9qh+GZ<7Hq0&Zo!b@((aN!S?SqEF_1>R-qv19WeXKpw(>=Xv-LyFHhE8FM#z+ zUw{Ebf^Z;#wmXnf#jyfq-&FN9eHhwE$tG0yToGQNMxmMWGZzlC?jG|XI4L}%I;k`c zHVB`l8aVanX}j)@Pqu|20p-;_flay2`bpYO3lG zC==zDQ(;TcDKL5%7}o$Fj1k8rrB&$u+D-^_PmQU_9(@^tg^lx7JyElo8st0o!&22F z#o>dBf&6r=GRAzuxZpHw;lW7PT#N)5FA=+nUl=dL&TQzhHGFW;&4kL8)sX6N;QyPzdk1Gn`u-OM%^JZ^BAOQ%e{1Y#t*L|2a9-d~vC%*Me>a?K z&jgqN6JP>NfC(@GCcp%k025#WOn?c1z~h6Nk^b)s3@JX*yuhhbrQiJEW zV)*dE1egF5U;<2l2`~XBzyz286JP>NfC>Ej1P-2v56_;NJ($cmOIt;I{prlEpLe{i z^!W=HuU3Kz)?#^j$wHq+kNfC(@GCcp%k025#WOn?b6fp<*c?6EVMk?56#{_hLKRc6uq0#D36I{Aq=PHp6` zu3OlsQ*Bg<4H7f(W-u@i`!Errs(lJi;wL)PI)r1<1-^BDUDy3i2V+y8Irr%3Ut=R9 zzl=Rr9sTjiUq3c?;>nXQCw`PzKoB2HfC(@GCcp%k025#W{}};iWOyudHhwS$mjT%I z-rO%@vwpjU&A$C2b`*y%i|8_u-Oeo*a6{F;izqBEt>+He(L{puVVP9O-6+u3_S(kc_DATM+(%OFTZeeVD4SObw0UhEZa!9X z*=MK=KLkb-WQm_Sdl<5w!!~iGcbMq)18;07O!6ZTE^Wx~U3l6?MfS!$NK)x82Dg5_5N)rCj0J zTyB%j)8ghb%^b=y+&v$?kCy+pqRB4ivy02QP%`ObLt~4lA10YL+2VBjGg_mP$8Hzvp70M?yWf-ie%C&w?8uVHewP+V_`*7Sw(ys7OBWMia>{3euhjbeD}J z9SKU`(LJ&I1%K7|%AN&R1h(#xqVC!*T$)&hx+8qJIZ$36)P*A(fhX+lKKgFt8p>d9 zU}$V({9!WaVON~)xm17KKhhp*di)eV|CdblE3cQvkK<;1^zSOl$?&Tko4=R<6JP>N zfC(HS0_>-d{S>mFLiGU4ehS%7A^Ry*F8$a~AspkepF;Li2&bd(!cQTd>Q1Qt?f3X8 z{NiWNy_LQ3mB-aOf`QRr#72L?o(hl9<1bf*2`~XBzyz286JP>NfC(@GCcp%kz{3f= zZ*Vq#Y5US#!^#K6=6hz7uz`ycS*jYz#z4aGsXW?&u2??446DkH2{0Os4O9a|JgLw%sDpl)hWj+v<{ELlCySs?~*qy~)V++Ciq= zqdB(_bIf>oV1a|3a%_kBUpCrpE zdnnjLM717QL4aFiC-7)&mVP7SjUUt2v@Jv#NiDbGQPy@F zCT=@~&6xI(8Pyvl+cK2U3}o$F*xHSnR#2tYLSZY9#uap{{DvJYBoaI~z;gpUH{keI zh35u%Zh+?oobiBoZU6&+zqx^EFT_6Y5#WJJ`4i~8FYuG*txq0bO?_X@3k*E=^Vq0? zUwkkDCcp%k025#WOn?b60Vco%m;e(v@&pbZ9~hoJGkfrX%-Y<|YrDHMEBRaHo8I^mu<_Z{pzD}fBu6hLPk!QMM)ixc=>MUf{LZ=-tuR zdJEwzCcp%k025#WOn?b60Vco%m;e)C0!-kD5TIjcGV$n*gFf#n44g%Tk%vZ@x%5x& C_}3Ev diff --git a/tests/ag-ui-app/backend/dist/db/index.d.ts b/tests/ag-ui-app/backend/dist/db/index.d.ts deleted file mode 100644 index 7759d61d..00000000 --- a/tests/ag-ui-app/backend/dist/db/index.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Database from 'better-sqlite3'; -import { type Tone, type Conversation, type Reply, type ConversationWithReplies } from './schema.js'; -export declare function getDb(): Database.Database; -export declare const dbOps: { - createConversation(message: string, tone: Tone, context?: string, intent?: string): string; - saveReplies(conversationId: string, replies: string[]): void; - getHistory(limit?: number): ConversationWithReplies[]; - getConversation(id: string): ConversationWithReplies | null; - deleteConversation(id: string): boolean; - logEvent(eventType: "generate" | "copy" | "select" | "delete", conversationId?: string, metadata?: object): void; -}; -export type { Tone, Conversation, Reply, ConversationWithReplies }; diff --git a/tests/ag-ui-app/backend/dist/db/index.js b/tests/ag-ui-app/backend/dist/db/index.js deleted file mode 100644 index b7100ebf..00000000 --- a/tests/ag-ui-app/backend/dist/db/index.js +++ /dev/null @@ -1,78 +0,0 @@ -import { initDb } from './schema.js'; -import { nanoid } from 'nanoid'; -// Singleton database instance -let db = null; -export function getDb() { - if (!db) { - db = initDb(); - } - return db; -} -// Database operations -export const dbOps = { - // Create a new conversation - createConversation(message, tone, context, intent) { - const db = getDb(); - const id = nanoid(); - db.prepare('INSERT INTO conversations (id, original_message, context, intent, tone) VALUES (?, ?, ?, ?, ?)') - .run(id, message, context || null, intent || null, tone); - return id; - }, - // Save generated replies - saveReplies(conversationId, replies) { - const db = getDb(); - const stmt = db.prepare('INSERT INTO replies (id, conversation_id, content, reply_index) VALUES (?, ?, ?, ?)'); - const insertMany = db.transaction((items) => { - items.forEach((content, index) => { - stmt.run(nanoid(), conversationId, content, index); - }); - }); - insertMany(replies); - }, - // Get all conversations with their replies - getHistory(limit = 50) { - const db = getDb(); - const conversations = db.prepare(` - SELECT c.*, - json_group_array( - json_object( - 'id', r.id, - 'content', r.content, - 'reply_index', r.reply_index, - 'created_at', r.created_at - ) - ) as replies_json - FROM conversations c - LEFT JOIN replies r ON c.id = r.conversation_id - GROUP BY c.id - ORDER BY c.created_at DESC - LIMIT ? - `).all(limit); - return conversations.map(conv => ({ - ...conv, - replies: JSON.parse(conv.replies_json) - .filter((r) => r.id !== null) - .sort((a, b) => a.reply_index - b.reply_index) - })); - }, - // Get a single conversation with replies - getConversation(id) { - const db = getDb(); - const conversation = db.prepare('SELECT * FROM conversations WHERE id = ?').get(id); - if (!conversation) - return null; - const replies = db.prepare('SELECT * FROM replies WHERE conversation_id = ? ORDER BY reply_index').all(id); - return { ...conversation, replies }; - }, - // Delete a conversation (cascades to replies) - deleteConversation(id) { - const db = getDb(); - const result = db.prepare('DELETE FROM conversations WHERE id = ?').run(id); - return result.changes > 0; - }, - // Log analytics event - logEvent(eventType, conversationId, metadata) { - const db = getDb(); - db.prepare('INSERT INTO analytics (id, event_type, conversation_id, metadata) VALUES (?, ?, ?, ?)').run(nanoid(), eventType, conversationId || null, metadata ? JSON.stringify(metadata) : null); - } -}; diff --git a/tests/ag-ui-app/backend/dist/db/schema.d.ts b/tests/ag-ui-app/backend/dist/db/schema.d.ts deleted file mode 100644 index 32d22b91..00000000 --- a/tests/ag-ui-app/backend/dist/db/schema.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Database from 'better-sqlite3'; -export type Tone = 'professional' | 'friendly' | 'apologetic' | 'assertive' | 'neutral'; -export interface Conversation { - id: string; - original_message: string; - context: string | null; - intent: string | null; - tone: Tone; - created_at: string; -} -export interface Reply { - id: string; - conversation_id: string; - content: string; - reply_index: number; - created_at: string; -} -export interface ConversationWithReplies extends Conversation { - replies: Reply[]; -} -export declare function initDb(): Database.Database; diff --git a/tests/ag-ui-app/backend/dist/db/schema.js b/tests/ag-ui-app/backend/dist/db/schema.js deleted file mode 100644 index b541e737..00000000 --- a/tests/ag-ui-app/backend/dist/db/schema.js +++ /dev/null @@ -1,50 +0,0 @@ -import Database from 'better-sqlite3'; -import path from 'path'; -import { fileURLToPath } from 'url'; -// Initialize database -export function initDb() { - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const dbPath = path.join(__dirname, '..', '..', 'data', 'smart-reply.db'); - const db = new Database(dbPath); - // Enable WAL mode for better concurrent performance - db.pragma('journal_mode = WAL'); - db.pragma('foreign_keys = ON'); - // Create tables - db.exec(` - CREATE TABLE IF NOT EXISTS conversations ( - id TEXT PRIMARY KEY, - original_message TEXT NOT NULL, - context TEXT, - intent TEXT, - tone TEXT NOT NULL CHECK (tone IN ('professional', 'friendly', 'apologetic', 'assertive', 'neutral')), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP - ); - - -- Add context and intent columns if they don't exist (for existing databases) - -- SQLite doesn't support IF NOT EXISTS for ALTER TABLE, so we handle this in code - - - CREATE TABLE IF NOT EXISTS replies ( - id TEXT PRIMARY KEY, - conversation_id TEXT NOT NULL, - content TEXT NOT NULL, - reply_index INTEGER NOT NULL CHECK (reply_index BETWEEN 0 AND 2), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE - ); - - CREATE TABLE IF NOT EXISTS analytics ( - id TEXT PRIMARY KEY, - event_type TEXT NOT NULL CHECK (event_type IN ('generate', 'copy', 'select', 'delete')), - conversation_id TEXT, - metadata TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE SET NULL - ); - - CREATE INDEX IF NOT EXISTS idx_replies_conversation ON replies(conversation_id); - CREATE INDEX IF NOT EXISTS idx_conversations_created ON conversations(created_at DESC); - CREATE INDEX IF NOT EXISTS idx_analytics_event ON analytics(event_type, created_at); - `); - return db; -} diff --git a/tests/ag-ui-app/backend/dist/index.d.ts b/tests/ag-ui-app/backend/dist/index.d.ts deleted file mode 100644 index 0b172b7b..00000000 --- a/tests/ag-ui-app/backend/dist/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -import 'dotenv/config'; diff --git a/tests/ag-ui-app/backend/dist/index.js b/tests/ag-ui-app/backend/dist/index.js deleted file mode 100644 index a104a360..00000000 --- a/tests/ag-ui-app/backend/dist/index.js +++ /dev/null @@ -1,81 +0,0 @@ -import { serve } from '@hono/node-server'; -import { Hono } from 'hono'; -import { cors } from 'hono/cors'; -import { logger } from 'hono/logger'; -import { repliesRouter } from './routes/replies.js'; -import { historyRouter } from './routes/history.js'; -import { checkServicesHealth } from './lib/services.js'; -import 'dotenv/config'; -// Create Hono app -const app = new Hono(); -// Middleware -app.use('*', logger()); -app.use('*', cors({ - origin: ['http://localhost:3000', 'http://127.0.0.1:3000'], - allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'], - allowHeaders: ['Content-Type', 'Authorization'], - exposeHeaders: ['Content-Length'], - maxAge: 86400, - credentials: true -})); -// Health check endpoint -app.get('/health', (c) => { - return c.json({ - status: 'ok', - timestamp: new Date().toISOString(), - service: 'smart-reply-backend' - }); -}); -// Services health check (includes microservices) -app.get('/health/services', async (c) => { - const services = await checkServicesHealth(); - const allHealthy = services.sentiment && services.contacts && services.style; - return c.json({ - status: allHealthy ? 'ok' : 'degraded', - timestamp: new Date().toISOString(), - services: { - 'sentiment-analysis': services.sentiment ? 'up' : 'down', - 'contact-intelligence': services.contacts ? 'up' : 'down', - 'writing-style': services.style ? 'up' : 'down' - } - }); -}); -// API routes -app.route('/api/replies', repliesRouter); -app.route('/api/history', historyRouter); -// 404 handler -app.notFound((c) => { - return c.json({ error: 'Not found' }, 404); -}); -// Error handler -app.onError((err, c) => { - console.error('Unhandled error:', err); - return c.json({ error: 'Internal server error' }, 500); -}); -// Start server -const port = parseInt(process.env.PORT || '3001', 10); -serve({ fetch: app.fetch, port }, (info) => { - console.log(` - ╔═══════════════════════════════════════════════════════╗ - ║ ║ - ║ Smart Reply Backend ║ - ║ Running at http://localhost:${info.port} ║ - ║ ║ - ║ Endpoints: ║ - ║ • GET /health - Health check ║ - ║ • GET /health/services - Services status ║ - ║ • POST /api/replies/generate - Generate replies ║ - ║ • POST /api/replies/save - Save replies ║ - ║ • POST /api/replies/learn - Learn from reply ║ - ║ • GET /api/history - List conversations ║ - ║ • GET /api/history/:id - Get conversation ║ - ║ • DELETE /api/history/:id - Delete conversation ║ - ║ ║ - ║ Microservices: ║ - ║ • Sentiment Analysis - localhost:3002 ║ - ║ • Contact Intelligence - localhost:3003 ║ - ║ • Writing Style - localhost:3004 ║ - ║ ║ - ╚═══════════════════════════════════════════════════════╝ - `); -}); diff --git a/tests/ag-ui-app/backend/dist/lib/openai.d.ts b/tests/ag-ui-app/backend/dist/lib/openai.d.ts deleted file mode 100644 index 2bf87fcf..00000000 --- a/tests/ag-ui-app/backend/dist/lib/openai.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Tone } from '../db/index.js'; -/** - * Create OpenAI adapter using TanStack AI - * Model is passed directly as string argument - */ -export declare function getOpenAIAdapter(): import("@tanstack/ai-openai").OpenAITextAdapter<"gpt-4o-mini">; -export declare const TONE_SYSTEM_PROMPTS: Record; -/** - * Build the complete system prompt for generating smart replies - */ -export declare function buildSmartReplyPrompt(tone: Tone): string; diff --git a/tests/ag-ui-app/backend/dist/lib/openai.js b/tests/ag-ui-app/backend/dist/lib/openai.js deleted file mode 100644 index bb6d8a89..00000000 --- a/tests/ag-ui-app/backend/dist/lib/openai.js +++ /dev/null @@ -1,59 +0,0 @@ -import { openaiText } from '@tanstack/ai-openai'; -/** - * Create OpenAI adapter using TanStack AI - * Model is passed directly as string argument - */ -export function getOpenAIAdapter() { - return openaiText('gpt-4o-mini'); -} -// Tone-specific system prompts for smart reply generation -export const TONE_SYSTEM_PROMPTS = { - professional: `You craft professional, business-appropriate replies. Use formal language, be concise and respectful. -Avoid casual expressions, slang, or overly familiar language. Maintain a courteous yet efficient tone.`, - friendly: `You craft warm, friendly replies. Use casual but appropriate language, show genuine interest and enthusiasm. -Be personable and approachable while remaining respectful. Include warm greetings when appropriate.`, - apologetic: `You craft sincere, apologetic replies. Express genuine remorse and understanding of the issue. -Take responsibility where appropriate, offer solutions or next steps, and show empathy for any inconvenience caused.`, - assertive: `You craft confident, assertive replies. Be direct, clear, and firm while remaining professional. -State your position clearly, set boundaries respectfully, and avoid being passive or aggressive.`, - neutral: `You craft balanced, neutral replies. Be objective and straightforward without emotional language. -Present information clearly, avoid taking strong positions, and maintain a calm, measured tone.` -}; -/** - * Build the complete system prompt for generating smart replies - */ -export function buildSmartReplyPrompt(tone) { - return `You are a reply assistant that helps users craft the perfect response to messages they've received. - -TONE GUIDELINES: -${TONE_SYSTEM_PROMPTS[tone]} - -YOUR TASK: -You will receive: -1. CONVERSATION CONTEXT (optional): Background information or previous messages in the conversation thread -2. MESSAGE RECEIVED: The specific message the user needs to reply to -3. WHAT I WANT TO COMMUNICATE (optional): The user's intended message or key points they want to convey - -Using ALL provided information, generate exactly 3 different reply options that: -- Match the ${tone} tone -- Address the received message directly -- Incorporate the user's intended points if provided -- Consider the conversation context for appropriate follow-up -- Are complete, polished, and ready to send - -OUTPUT FORMAT: -Return ONLY a valid JSON array containing exactly 3 strings. Each string is a complete reply. - -Example format: -["Short reply option here.", "Medium length reply with more detail here.", "Detailed reply with full context and explanation here."] - -GUIDELINES: -- Option 1: Short and concise (1-2 sentences) -- Option 2: Medium length with appropriate detail (2-3 sentences) -- Option 3: Detailed and comprehensive (3-4 sentences) -- Make each reply natural and conversational -- If context is provided, reference it appropriately -- If the user's intent is provided, make sure all replies convey that intent -- Do NOT include any explanation, markdown, or text outside the JSON array -- Output ONLY the JSON array, nothing else`; -} diff --git a/tests/ag-ui-app/backend/dist/lib/services.d.ts b/tests/ag-ui-app/backend/dist/lib/services.d.ts deleted file mode 100644 index 667742c4..00000000 --- a/tests/ag-ui-app/backend/dist/lib/services.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Service Client Helpers - * Query the microservices for enhanced reply generation - */ -export interface SentimentResult { - sentiment: 'positive' | 'negative' | 'neutral' | 'mixed'; - confidence: number; - emotions: Array<{ - emotion: string; - score: number; - }>; - urgency: 'low' | 'medium' | 'high' | 'critical'; - keyPoints: string[]; - suggestedApproach: string; -} -export interface ContactMatchResult { - matchedContact: { - id: string; - name: string; - relationship: string; - company: string | null; - formality: string; - use_emojis: boolean; - preferred_tone: string | null; - } | null; - confidence: number; - relationshipContext: string; -} -export interface StyleProfile { - totalSamples: number; - averageSentenceLength: number; - commonGreetings: string[]; - commonSignoffs: string[]; - vocabularyLevel: 'professional' | 'casual' | 'mixed'; - usesEmojis: boolean; - commonPhrases: string[]; - punctuationStyle: { - exclamationFrequency: number; - questionFrequency: number; - }; -} -/** - * Analyze sentiment of a message - */ -export declare function analyzeSentiment(message: string): Promise; -/** - * Match message to a known contact - */ -export declare function matchContact(message: string): Promise; -/** - * Get the user's writing style profile - */ -export declare function getStyleProfile(): Promise; -/** - * Learn from a selected/edited reply - */ -export declare function learnFromReply(content: string, type: 'selected_reply' | 'custom_edit' | 'sent_message'): Promise; -/** - * Check health of all services - */ -export declare function checkServicesHealth(): Promise<{ - sentiment: boolean; - contacts: boolean; - style: boolean; -}>; -/** - * Build enhanced context from all services - */ -export declare function buildEnhancedContext(message: string): Promise; diff --git a/tests/ag-ui-app/backend/dist/lib/services.js b/tests/ag-ui-app/backend/dist/lib/services.js deleted file mode 100644 index 0be05c5d..00000000 --- a/tests/ag-ui-app/backend/dist/lib/services.js +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Service Client Helpers - * Query the microservices for enhanced reply generation - */ -const SENTIMENT_URL = process.env.SENTIMENT_SERVICE_URL || 'http://localhost:3002'; -const CONTACTS_URL = process.env.CONTACTS_SERVICE_URL || 'http://localhost:3003'; -const STYLE_URL = process.env.STYLE_SERVICE_URL || 'http://localhost:3004'; -// Timeout for service calls (don't block reply generation for too long) -const SERVICE_TIMEOUT = 3000; -/** - * Fetch with timeout - */ -async function fetchWithTimeout(url, options = {}) { - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), SERVICE_TIMEOUT); - try { - const response = await fetch(url, { - ...options, - signal: controller.signal - }); - return response; - } - finally { - clearTimeout(timeout); - } -} -/** - * Analyze sentiment of a message - */ -export async function analyzeSentiment(message) { - try { - const response = await fetchWithTimeout(`${SENTIMENT_URL}/api/analyze`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) - }); - if (!response.ok) { - console.warn('[Services] Sentiment analysis failed:', response.status); - return null; - } - return await response.json(); - } - catch (error) { - if (error instanceof Error && error.name === 'AbortError') { - console.warn('[Services] Sentiment analysis timed out'); - } - else { - console.warn('[Services] Sentiment analysis error:', error); - } - return null; - } -} -/** - * Match message to a known contact - */ -export async function matchContact(message) { - try { - const response = await fetchWithTimeout(`${CONTACTS_URL}/api/contacts/match`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) - }); - if (!response.ok) { - console.warn('[Services] Contact matching failed:', response.status); - return null; - } - return await response.json(); - } - catch (error) { - if (error instanceof Error && error.name === 'AbortError') { - console.warn('[Services] Contact matching timed out'); - } - else { - console.warn('[Services] Contact matching error:', error); - } - return null; - } -} -/** - * Get the user's writing style profile - */ -export async function getStyleProfile() { - try { - const response = await fetchWithTimeout(`${STYLE_URL}/api/profile`); - if (!response.ok) { - console.warn('[Services] Style profile fetch failed:', response.status); - return null; - } - return await response.json(); - } - catch (error) { - if (error instanceof Error && error.name === 'AbortError') { - console.warn('[Services] Style profile timed out'); - } - else { - console.warn('[Services] Style profile error:', error); - } - return null; - } -} -/** - * Learn from a selected/edited reply - */ -export async function learnFromReply(content, type) { - try { - const response = await fetchWithTimeout(`${STYLE_URL}/api/learn`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ content, type }) - }); - return response.ok; - } - catch (error) { - console.warn('[Services] Learn from reply error:', error); - return false; - } -} -/** - * Check health of all services - */ -export async function checkServicesHealth() { - const results = await Promise.allSettled([ - fetchWithTimeout(`${SENTIMENT_URL}/health`), - fetchWithTimeout(`${CONTACTS_URL}/health`), - fetchWithTimeout(`${STYLE_URL}/health`) - ]); - return { - sentiment: results[0].status === 'fulfilled' && results[0].value.ok, - contacts: results[1].status === 'fulfilled' && results[1].value.ok, - style: results[2].status === 'fulfilled' && results[2].value.ok - }; -} -/** - * Build enhanced context from all services - */ -export async function buildEnhancedContext(message) { - // Query all services in parallel - const [sentiment, contact, style] = await Promise.all([ - analyzeSentiment(message), - matchContact(message), - getStyleProfile() - ]); - const contextParts = []; - // Add sentiment context - if (sentiment) { - const emotionList = sentiment.emotions - .slice(0, 3) - .map(e => `${e.emotion} (${Math.round(e.score * 100)}%)`) - .join(', '); - contextParts.push(`SENTIMENT ANALYSIS: -- Overall sentiment: ${sentiment.sentiment} (confidence: ${Math.round(sentiment.confidence * 100)}%) -- Detected emotions: ${emotionList || 'none detected'} -- Urgency level: ${sentiment.urgency} -${sentiment.keyPoints.length > 0 ? `- Key points: ${sentiment.keyPoints.join(', ')}` : ''} -- Suggested approach: ${sentiment.suggestedApproach}`); - } - // Add contact context - if (contact && contact.matchedContact) { - contextParts.push(`RELATIONSHIP CONTEXT: -${contact.relationshipContext}`); - } - // Add style context - if (style && style.totalSamples >= 3) { - const styleHints = []; - if (style.commonGreetings.length > 0) { - styleHints.push(`Preferred greetings: ${style.commonGreetings.slice(0, 3).join(', ')}`); - } - if (style.commonSignoffs.length > 0) { - styleHints.push(`Preferred sign-offs: ${style.commonSignoffs.slice(0, 3).join(', ')}`); - } - if (style.vocabularyLevel !== 'mixed') { - styleHints.push(`Writing style: ${style.vocabularyLevel}`); - } - if (style.usesEmojis) { - styleHints.push('User likes to use emojis'); - } - if (style.commonPhrases.length > 0) { - styleHints.push(`Common phrases: "${style.commonPhrases.slice(0, 2).join('", "')}"`); - } - if (styleHints.length > 0) { - contextParts.push(`USER'S WRITING STYLE: -${styleHints.join('\n')}`); - } - } - return contextParts.join('\n\n'); -} diff --git a/tests/ag-ui-app/backend/dist/routes/history.d.ts b/tests/ag-ui-app/backend/dist/routes/history.d.ts deleted file mode 100644 index a64d6018..00000000 --- a/tests/ag-ui-app/backend/dist/routes/history.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Hono } from 'hono'; -declare const historyRouter: Hono; -export { historyRouter }; diff --git a/tests/ag-ui-app/backend/dist/routes/history.js b/tests/ag-ui-app/backend/dist/routes/history.js deleted file mode 100644 index 4c518312..00000000 --- a/tests/ag-ui-app/backend/dist/routes/history.js +++ /dev/null @@ -1,58 +0,0 @@ -import { Hono } from 'hono'; -import { dbOps } from '../db/index.js'; -const historyRouter = new Hono(); -// GET /api/history - Get all conversations with replies -historyRouter.get('/', (c) => { - try { - const limitParam = c.req.query('limit'); - const limit = limitParam ? Math.min(Math.max(parseInt(limitParam, 10), 1), 100) : 50; - const conversations = dbOps.getHistory(limit); - return c.json({ - success: true, - data: conversations, - count: conversations.length - }); - } - catch (error) { - console.error('Get history error:', error); - return c.json({ error: 'Failed to fetch history' }, 500); - } -}); -// GET /api/history/:id - Get single conversation with replies -historyRouter.get('/:id', (c) => { - try { - const { id } = c.req.param(); - const conversation = dbOps.getConversation(id); - if (!conversation) { - return c.json({ error: 'Conversation not found' }, 404); - } - return c.json({ - success: true, - data: conversation - }); - } - catch (error) { - console.error('Get conversation error:', error); - return c.json({ error: 'Failed to fetch conversation' }, 500); - } -}); -// DELETE /api/history/:id - Delete a conversation -historyRouter.delete('/:id', (c) => { - try { - const { id } = c.req.param(); - const deleted = dbOps.deleteConversation(id); - if (!deleted) { - return c.json({ error: 'Conversation not found' }, 404); - } - dbOps.logEvent('delete', id); - return c.json({ - success: true, - message: 'Conversation deleted successfully' - }); - } - catch (error) { - console.error('Delete conversation error:', error); - return c.json({ error: 'Failed to delete conversation' }, 500); - } -}); -export { historyRouter }; diff --git a/tests/ag-ui-app/backend/dist/routes/replies.d.ts b/tests/ag-ui-app/backend/dist/routes/replies.d.ts deleted file mode 100644 index 58458e1f..00000000 --- a/tests/ag-ui-app/backend/dist/routes/replies.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Hono } from 'hono'; -declare const repliesRouter: Hono; -export { repliesRouter }; diff --git a/tests/ag-ui-app/backend/dist/routes/replies.js b/tests/ag-ui-app/backend/dist/routes/replies.js deleted file mode 100644 index 7191831d..00000000 --- a/tests/ag-ui-app/backend/dist/routes/replies.js +++ /dev/null @@ -1,137 +0,0 @@ -import { Hono } from 'hono'; -import { chat, toServerSentEventsResponse } from '@tanstack/ai'; -import { dbOps } from '../db/index.js'; -import { getOpenAIAdapter, buildSmartReplyPrompt } from '../lib/openai.js'; -import { buildEnhancedContext, learnFromReply } from '../lib/services.js'; -const repliesRouter = new Hono(); -// Validate tone -function isValidTone(tone) { - return ['professional', 'friendly', 'apologetic', 'assertive', 'neutral'].includes(tone); -} -/** - * POST /api/replies/generate - * Generate smart replies using TanStack AI with streaming - */ -repliesRouter.post('/generate', async (c) => { - try { - const body = await c.req.json(); - const { message, tone, context, intent } = body; - // Validate input - if (!message?.trim()) { - return c.json({ error: 'Message is required' }, 400); - } - if (!tone || !isValidTone(tone)) { - return c.json({ - error: 'Invalid tone. Must be one of: professional, friendly, apologetic, assertive, neutral' - }, 400); - } - if (message.length > 5000) { - return c.json({ error: 'Message too long. Maximum 5000 characters.' }, 400); - } - // Create conversation record with context and intent - const conversationId = dbOps.createConversation(message.trim(), tone, context?.trim() || undefined, intent?.trim() || undefined); - dbOps.logEvent('generate', conversationId, { - tone, - messageLength: message.length, - hasContext: !!context, - hasIntent: !!intent - }); - // Get OpenAI adapter from TanStack AI - const adapter = getOpenAIAdapter(); - // Build system prompt - const systemPrompt = buildSmartReplyPrompt(tone); - // Query microservices for enhanced context (non-blocking, with timeout) - const enhancedContext = await buildEnhancedContext(message.trim()); - // Build user message with context, intent, and service insights - const userMessage = buildUserMessage(message.trim(), context?.trim(), intent?.trim(), enhancedContext); - // Create streaming chat with TanStack AI - const stream = chat({ - adapter, - systemPrompts: [systemPrompt], - messages: [ - { - role: 'user', - content: userMessage - } - ], - temperature: 0.8, - maxTokens: 2000, - }); - // Convert to Server-Sent Events response using TanStack AI utility - const response = toServerSentEventsResponse(stream); - // Add custom header with conversation ID - const headers = new Headers(response.headers); - headers.set('X-Conversation-Id', conversationId); - headers.set('Access-Control-Expose-Headers', 'X-Conversation-Id'); - return new Response(response.body, { - status: response.status, - headers - }); - } - catch (error) { - console.error('Generate endpoint error:', error); - return c.json({ error: 'Internal server error' }, 500); - } -}); -/** - * Build the user message with context, intent, and service insights - */ -function buildUserMessage(message, context, intent, enhancedContext) { - let parts = []; - // Add AI-analyzed context from microservices - if (enhancedContext) { - parts.push(`AI-ANALYZED INSIGHTS:\n${enhancedContext}`); - } - if (context) { - parts.push(`CONVERSATION CONTEXT (from user):\n${context}`); - } - parts.push(`MESSAGE RECEIVED:\n"${message}"`); - if (intent) { - parts.push(`WHAT I WANT TO COMMUNICATE:\n${intent}`); - } - parts.push('Generate 3 reply options based on the above information. Consider the sentiment, relationship context, and user\'s writing style if provided:'); - return parts.join('\n\n'); -} -/** - * POST /api/replies/save - * Save generated replies to database (called after streaming completes) - */ -repliesRouter.post('/save', async (c) => { - try { - const { conversationId, replies } = await c.req.json(); - if (!conversationId || !replies || !Array.isArray(replies)) { - return c.json({ error: 'Invalid request body' }, 400); - } - // Save replies to database - dbOps.saveReplies(conversationId, replies.slice(0, 3)); - return c.json({ success: true }); - } - catch (error) { - console.error('Save replies error:', error); - return c.json({ error: 'Failed to save replies' }, 500); - } -}); -/** - * POST /api/replies/learn - * Learn from a selected/copied reply to improve style matching - */ -repliesRouter.post('/learn', async (c) => { - try { - const { content, type } = await c.req.json(); - if (!content?.trim()) { - return c.json({ error: 'Content is required' }, 400); - } - const validTypes = ['selected_reply', 'custom_edit', 'sent_message']; - if (!type || !validTypes.includes(type)) { - return c.json({ error: 'Invalid type' }, 400); - } - // Send to writing style service (non-blocking) - const success = await learnFromReply(content.trim(), type); - return c.json({ success, learned: success }); - } - catch (error) { - console.error('Learn endpoint error:', error); - return c.json({ error: 'Failed to learn from reply' }, 500); - } -}); -export { repliesRouter }; diff --git a/tests/ag-ui-app/frontend/dist/client/assets/agent-Bh38wHiA.js b/tests/ag-ui-app/frontend/dist/client/assets/agent-Bh38wHiA.js deleted file mode 100644 index c5b243b6..00000000 --- a/tests/ag-ui-app/frontend/dist/client/assets/agent-Bh38wHiA.js +++ /dev/null @@ -1 +0,0 @@ -import{r as p,u as w,_ as k,j as e}from"./main-DbgxqtWz.js";import{c as x,L as _,X as E,C}from"./x-DtBV5VmR.js";const z=[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]],T=x("bot",z);const O=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],g=x("chevron-down",O);const H=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],A=x("circle-check",H);const G=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],L=x("circle",G);const q=[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]],S=x("code-xml",q);const U=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]],M=x("database",U);const V=[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",key:"ct8e1f"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242",key:"151rxh"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143",key:"13bj9a"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],W=x("eye-off",V);const D=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],B=x("eye",D);const F=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",key:"1oefj6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5",key:"wfsgrz"}],["path",{d:"M10 12.5 8 15l2 2.5",key:"1tg20x"}],["path",{d:"m14 12.5 2 2.5-2 2.5",key:"yinavb"}]],j=x("file-code",F);const K=[["path",{d:"M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 15 3h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z",key:"hod4my"}],["path",{d:"M20 21a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-2.9a1 1 0 0 1-.88-.55l-.42-.85a1 1 0 0 0-.92-.6H13a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z",key:"w4yl2u"}],["path",{d:"M3 5a2 2 0 0 0 2 2h3",key:"f2jnh7"}],["path",{d:"M3 3v13a2 2 0 0 0 2 2h3",key:"k8epm1"}]],b=x("folder-tree",K);const J=[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]],y=x("git-branch",J);const X=[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z",key:"1a0edw"}],["path",{d:"M12 22V12",key:"d0xqtd"}],["polyline",{points:"3.29 7 12 12 20.71 7",key:"ousv84"}],["path",{d:"m7.5 4.27 9 5.15",key:"1c824w"}]],f=x("package",X);const Z=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],I=x("server",Z);const Y=[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],P=x("settings",Y);const Q=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]],$=x("shield",Q);const ee=[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]],N=x("terminal",ee);const se=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],te=x("triangle-alert",se);const ae=[["path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z",key:"1ngwbx"}]],re=x("wrench",ae);function le({onClose:s}){const{settings:t,setProvider:i,setModel:r,setApiKey:n,setAwsRegion:o,availableModels:m}=w(),[c,l]=p.useState(!1);return e.jsx("div",{className:"absolute inset-0 bg-slate-950/90 backdrop-blur-sm z-20 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-slate-900 border border-slate-700 rounded-2xl p-6 w-full max-w-md shadow-2xl",children:[e.jsxs("div",{className:"flex items-center justify-between mb-6",children:[e.jsxs("h2",{className:"text-lg font-semibold text-white flex items-center gap-2",children:[e.jsx(P,{className:"w-5 h-5 text-emerald-400"}),"Agent Settings"]}),e.jsx("button",{onClick:s,className:"p-1 rounded-lg hover:bg-slate-800 text-slate-400 hover:text-white transition-colors",children:e.jsx(E,{className:"w-5 h-5"})})]}),e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-slate-300 mb-2",children:"Provider"}),e.jsxs("div",{className:"relative",children:[e.jsxs("select",{value:t.provider,onChange:a=>i(a.target.value),className:"w-full px-4 py-2.5 bg-slate-800 border border-slate-600 rounded-lg text-white appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent",children:[e.jsx("option",{value:"openai",children:"OpenAI"}),e.jsx("option",{value:"anthropic",children:"Anthropic"}),e.jsx("option",{value:"bedrock",children:"AWS Bedrock"})]}),e.jsx(g,{className:"absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none"})]})]}),e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-slate-300 mb-2",children:"Model"}),e.jsxs("div",{className:"relative",children:[e.jsx("select",{value:t.model,onChange:a=>r(a.target.value),className:"w-full px-4 py-2.5 bg-slate-800 border border-slate-600 rounded-lg text-white appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent",children:m.map(a=>e.jsx("option",{value:a.id,children:a.name},a.id))}),e.jsx(g,{className:"absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none"})]})]}),t.provider!=="bedrock"&&e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-slate-300 mb-2",children:"API Key"}),e.jsxs("div",{className:"relative",children:[e.jsx("input",{type:c?"text":"password",value:t.apiKey,onChange:a=>n(a.target.value),placeholder:t.provider==="openai"?"sk-...":"sk-ant-...",className:"w-full px-4 py-2.5 pr-10 bg-slate-800 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent"}),e.jsx("button",{type:"button",onClick:()=>l(!c),className:"absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white",children:c?e.jsx(W,{className:"w-4 h-4"}):e.jsx(B,{className:"w-4 h-4"})})]}),e.jsx("p",{className:"mt-1.5 text-xs text-slate-500",children:t.provider==="openai"?"Get your API key from platform.openai.com":"Get your API key from console.anthropic.com"})]}),t.provider==="bedrock"&&e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-slate-300 mb-2",children:"AWS Region"}),e.jsxs("div",{className:"relative",children:[e.jsxs("select",{value:t.awsRegion||"us-east-1",onChange:a=>o(a.target.value),className:"w-full px-4 py-2.5 bg-slate-800 border border-slate-600 rounded-lg text-white appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent",children:[e.jsx("option",{value:"us-east-1",children:"US East (N. Virginia)"}),e.jsx("option",{value:"us-west-2",children:"US West (Oregon)"}),e.jsx("option",{value:"eu-west-1",children:"EU (Ireland)"}),e.jsx("option",{value:"eu-central-1",children:"EU (Frankfurt)"}),e.jsx("option",{value:"ap-northeast-1",children:"Asia Pacific (Tokyo)"})]}),e.jsx(g,{className:"absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none"})]}),e.jsx("p",{className:"mt-1.5 text-xs text-slate-500",children:"Uses AWS credentials from ~/.aws/credentials or environment variables"})]}),e.jsx("button",{onClick:s,className:"w-full mt-2 px-4 py-2.5 bg-emerald-600 hover:bg-emerald-500 text-white font-medium rounded-lg transition-colors",children:"Save Settings"})]})})}function u(s){if(typeof s=="object"&&s!==null&&!Array.isArray(s)){const t=s;if("raw"in t&&typeof t.raw=="string")try{return JSON.parse(t.raw)}catch{return t}return t}if(typeof s=="string")try{return JSON.parse(s)}catch{return{raw:s}}return{}}function ne({result:s}){const t=u(s);console.log("AnalyzeProjectCard data:",t);const i=t.is_monorepo,r=t.project_count||(t.project_names||[]).length||1,n=t.project_names||[],o=t.root_path||"",m=t.status,c=t.frameworks_detected||[],l=t.languages_detected||[],a=o?o.split("/").pop()||"Project":"Project Analysis";return e.jsxs("div",{className:"bg-gradient-to-br from-slate-800/90 to-slate-900/90 border border-emerald-500/40 rounded-xl overflow-hidden shadow-lg",children:[e.jsx("div",{className:"px-4 py-3 bg-emerald-500/10 border-b border-emerald-500/20",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2.5 bg-emerald-500/20 rounded-xl",children:e.jsx(b,{className:"w-6 h-6 text-emerald-400"})}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-white text-lg",children:a}),e.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[i&&e.jsx("span",{className:"px-2 py-0.5 bg-purple-500/20 border border-purple-500/30 rounded text-xs text-purple-300",children:"Monorepo"}),e.jsxs("span",{className:"text-xs text-slate-400",children:[r," project",r!==1?"s":""]}),m==="ANALYSIS_COMPLETE"&&e.jsx(A,{className:"w-3.5 h-3.5 text-emerald-400"})]})]})]})}),e.jsxs("div",{className:"p-4 space-y-4",children:[l.length>0&&e.jsxs("div",{children:[e.jsxs("h4",{className:"text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5",children:[e.jsx(S,{className:"w-3.5 h-3.5"}),"Languages"]}),e.jsx("div",{className:"flex flex-wrap gap-2",children:l.map((d,h)=>e.jsx("span",{className:"px-3 py-1.5 bg-blue-500/15 border border-blue-500/30 rounded-lg text-sm text-blue-300 font-medium",children:d},h))})]}),c.length>0&&e.jsxs("div",{children:[e.jsxs("h4",{className:"text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5",children:[e.jsx(f,{className:"w-3.5 h-3.5"}),"Frameworks & Libraries (",c.length,")"]}),e.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[c.slice(0,20).map((d,h)=>e.jsx("span",{className:"px-2 py-1 bg-cyan-500/10 border border-cyan-500/25 rounded text-xs text-cyan-300",children:d},h)),c.length>20&&e.jsxs("span",{className:"px-2 py-1 text-xs text-slate-500",children:["+",c.length-20," more"]})]})]}),n.length>1&&e.jsxs("div",{children:[e.jsxs("h4",{className:"text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5",children:[e.jsx(y,{className:"w-3.5 h-3.5"}),"Projects (",n.length,")"]}),e.jsx("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-1.5 max-h-[120px] overflow-y-auto",children:n.map((d,h)=>e.jsxs("div",{className:"flex items-center gap-1.5 px-2 py-1.5 bg-slate-800/60 rounded text-xs text-slate-300 truncate",children:[e.jsx(j,{className:"w-3 h-3 text-slate-500 shrink-0"}),e.jsx("span",{className:"truncate",children:d})]},h))})]}),o&&e.jsx("div",{className:"pt-2 border-t border-slate-700/50",children:e.jsxs("div",{className:"flex items-center gap-2 text-xs text-slate-500",children:[e.jsx(N,{className:"w-3.5 h-3.5"}),e.jsx("code",{className:"font-mono",children:o})]})})]})]})}function ce({result:s}){const[t,i]=p.useState(!0),r=u(s),n=r.vulnerabilities||[],o=r.findings||[],m=r.security_score||r.score,c=n.filter(a=>a.severity==="critical"||a.severity==="CRITICAL").length,l=n.filter(a=>a.severity==="high"||a.severity==="HIGH").length;return e.jsxs("div",{className:"bg-gradient-to-br from-slate-800/80 to-slate-900/80 border border-orange-500/30 rounded-xl overflow-hidden",children:[e.jsxs("button",{onClick:()=>i(!t),className:"w-full px-4 py-3 flex items-center justify-between hover:bg-slate-700/30 transition-colors",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 bg-orange-500/20 rounded-lg",children:e.jsx($,{className:"w-5 h-5 text-orange-400"})}),e.jsxs("div",{className:"text-left",children:[e.jsx("h3",{className:"font-semibold text-white",children:"Security Scan"}),e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[c>0&&e.jsxs("span",{className:"text-red-400",children:[c," Critical"]}),l>0&&e.jsxs("span",{className:"text-orange-400",children:[l," High"]}),m!==void 0&&e.jsxs("span",{className:"text-emerald-400",children:["Score: ",m]})]})]})]}),e.jsx(C,{className:`w-5 h-5 text-slate-400 transition-transform ${t?"rotate-90":""}`})]}),t&&(n.length>0||o.length>0)&&e.jsx("div",{className:"px-4 pb-4",children:e.jsxs("div",{className:"space-y-2 max-h-[200px] overflow-y-auto",children:[n.slice(0,5).map((a,d)=>e.jsxs("div",{className:`p-3 rounded-lg border ${a.severity==="critical"||a.severity==="CRITICAL"?"bg-red-500/10 border-red-500/30":a.severity==="high"||a.severity==="HIGH"?"bg-orange-500/10 border-orange-500/30":"bg-yellow-500/10 border-yellow-500/30"}`,children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(te,{className:`w-4 h-4 ${a.severity==="critical"||a.severity==="CRITICAL"?"text-red-400":a.severity==="high"||a.severity==="HIGH"?"text-orange-400":"text-yellow-400"}`}),e.jsx("span",{className:"text-sm font-medium text-white",children:a.title})]}),a.description&&e.jsx("p",{className:"mt-1 text-xs text-slate-400 line-clamp-2",children:a.description})]},d)),o.slice(0,5).map((a,d)=>e.jsx("div",{className:"p-3 rounded-lg bg-slate-700/30 border border-slate-600/30",children:e.jsx("span",{className:"text-sm text-slate-300",children:a.message})},d))]})})]})}function ie({result:s,args:t}){const i=u(s);t.query;const r=i.projects||[];if(r.length>0)return e.jsxs("div",{className:"bg-gradient-to-br from-slate-800/80 to-slate-900/80 border border-violet-500/30 rounded-xl overflow-hidden",children:[e.jsx("div",{className:"px-4 py-3 bg-violet-500/10 border-b border-violet-500/20",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(I,{className:"w-5 h-5 text-violet-400"}),e.jsx("h3",{className:"font-semibold text-white",children:"Project Details"}),e.jsxs("span",{className:"text-xs text-slate-400",children:["(",r.length," projects)"]})]})}),e.jsx("div",{className:"p-3 space-y-2 max-h-[300px] overflow-y-auto",children:r.map((l,a)=>e.jsxs("div",{className:"p-3 bg-slate-800/50 rounded-lg border border-slate-700/50",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"font-medium text-white",children:l.name}),e.jsx("span",{className:`px-2 py-0.5 rounded text-xs ${l.category==="Frontend"?"bg-blue-500/20 text-blue-300":l.category==="Backend"?"bg-green-500/20 text-green-300":l.category==="Tool"?"bg-orange-500/20 text-orange-300":"bg-slate-500/20 text-slate-300"}`,children:l.category})]}),l.languages.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1 mb-1",children:l.languages.map((d,h)=>e.jsx("span",{className:"px-1.5 py-0.5 bg-blue-500/10 rounded text-xs text-blue-300",children:d},h))}),l.frameworks.length>0&&e.jsxs("div",{className:"flex flex-wrap gap-1",children:[l.frameworks.slice(0,5).map((d,h)=>e.jsx("span",{className:"px-1.5 py-0.5 bg-cyan-500/10 rounded text-xs text-cyan-300",children:d},h)),l.frameworks.length>5&&e.jsxs("span",{className:"text-xs text-slate-500",children:["+",l.frameworks.length-5]})]}),l.path&&e.jsx("div",{className:"mt-1.5 text-xs text-slate-500 font-mono truncate",children:l.path||"./"})]},a))})]});const n=i.is_monorepo,o=i.project_count||0,m=i.project_names||[],c=i.root_path||"";if(m.length>0){const l=c?c.split("/").pop()||"Workspace":"Workspace Analysis";return e.jsxs("div",{className:"bg-gradient-to-br from-slate-800/90 to-slate-900/90 border border-indigo-500/40 rounded-xl overflow-hidden shadow-lg",children:[e.jsx("div",{className:"px-4 py-3 bg-indigo-500/10 border-b border-indigo-500/20",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2.5 bg-indigo-500/20 rounded-xl",children:e.jsx(b,{className:"w-6 h-6 text-indigo-400"})}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-white text-lg",children:l}),e.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[n&&e.jsx("span",{className:"px-2 py-0.5 bg-purple-500/20 border border-purple-500/30 rounded text-xs text-purple-300",children:"Monorepo"}),e.jsxs("span",{className:"text-xs text-slate-400",children:[o||m.length," projects detected"]})]})]})]})}),e.jsxs("div",{className:"p-4 space-y-4",children:[e.jsx("div",{className:"flex items-center gap-4 text-sm",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-8 h-8 rounded-lg bg-emerald-500/20 flex items-center justify-center",children:e.jsx(f,{className:"w-4 h-4 text-emerald-400"})}),e.jsxs("div",{children:[e.jsx("div",{className:"text-white font-semibold",children:m.length}),e.jsx("div",{className:"text-xs text-slate-500",children:"Projects"})]})]})}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5",children:[e.jsx(y,{className:"w-3.5 h-3.5"}),"Projects"]}),e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-1.5 max-h-[200px] overflow-y-auto",children:[m.slice(0,30).map((a,d)=>e.jsxs("div",{className:"flex items-center gap-1.5 px-2 py-1.5 bg-slate-800/60 rounded text-xs text-slate-300 truncate",children:[e.jsx(j,{className:"w-3 h-3 text-slate-500 shrink-0"}),e.jsx("span",{className:"truncate",children:a})]},d)),m.length>30&&e.jsxs("div",{className:"flex items-center justify-center px-2 py-1.5 bg-slate-800/40 rounded text-xs text-slate-500",children:["+",m.length-30," more"]})]})]}),c&&e.jsx("div",{className:"pt-2 border-t border-slate-700/50",children:e.jsxs("div",{className:"flex items-center gap-2 text-xs text-slate-500",children:[e.jsx(N,{className:"w-3.5 h-3.5"}),e.jsx("code",{className:"font-mono truncate",children:c})]})})]})]})}return null}function v({toolName:s,result:t,isError:i}){const[r,n]=p.useState(!1),o=u(t),m="raw"in o&&typeof o.raw=="string"&&Object.keys(o).length===1,c=()=>s.includes("k8s")||s.includes("kube")?e.jsx(I,{className:"w-5 h-5"}):s.includes("terraform")?e.jsx(S,{className:"w-5 h-5"}):s.includes("docker")||s.includes("hadolint")?e.jsx(f,{className:"w-5 h-5"}):s.includes("git")?e.jsx(y,{className:"w-5 h-5"}):s.includes("file")||s.includes("read")?e.jsx(j,{className:"w-5 h-5"}):s.includes("security")||s.includes("vuln")?e.jsx($,{className:"w-5 h-5"}):s.includes("list")||s.includes("directory")?e.jsx(b,{className:"w-5 h-5"}):e.jsx(M,{className:"w-5 h-5"}),l=i&&typeof o=="object"&&o!==null&&"error"in o,a=l?"border-red-500/30":"border-slate-600/50",d=l?"bg-red-500/20":"bg-slate-700/50",h=l?"text-red-400":"text-slate-400";return e.jsxs("div",{className:`bg-gradient-to-br from-slate-800/60 to-slate-900/60 border ${a} rounded-xl overflow-hidden`,children:[e.jsxs("button",{onClick:()=>n(!r),className:"w-full px-4 py-3 flex items-center justify-between hover:bg-slate-700/30 transition-colors",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:`p-2 ${d} rounded-lg ${h}`,children:c()}),e.jsxs("div",{className:"text-left",children:[e.jsx("h3",{className:"font-medium text-white",children:s.replace(/_/g," ")}),l&&e.jsx("span",{className:"text-xs text-red-400",children:"Error"})]})]}),e.jsx(C,{className:`w-5 h-5 text-slate-400 transition-transform ${r?"rotate-90":""}`})]}),r&&e.jsx("div",{className:"px-4 pb-4",children:e.jsx("pre",{className:"p-3 bg-slate-900/80 rounded-lg text-xs text-slate-300 overflow-x-auto max-h-[300px] overflow-y-auto whitespace-pre-wrap break-words",children:m?o.raw:JSON.stringify(o,null,2)})})]})}function oe(s){const t=u(s),i=t.projects||[],r=t.project_names||[];return i.length>0||r.length>0}function de({toolResult:s}){const{tool_name:t,args:i,result:r,is_error:n}=s;switch(t){case"analyze_project":return e.jsx(ne,{result:r});case"retrieve_output":return oe(r)?e.jsx(ie,{result:r,args:i}):e.jsx(v,{toolName:t,result:r,isError:n});case"security_scan":case"check_vulnerabilities":return e.jsx(ce,{result:r});default:return e.jsx(v,{toolName:t,result:r,isError:n})}}function xe(s){const t=s?.steps&&s.steps.length>0,i=s?.tool_results&&s.tool_results.length>0;if(!t&&!i)return null;const r=s?.steps?.filter(c=>c.status==="completed").length||0,n=s?.steps?.length||0,o=n>0?r/n*100:0,m=n>0&&r===n;return e.jsxs("div",{className:"space-y-4 mb-4",children:[t&&!m&&e.jsxs("div",{className:"p-4 bg-slate-900/80 border border-slate-700/50 rounded-xl backdrop-blur-sm",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(re,{className:"w-4 h-4 text-cyan-400 animate-pulse"}),e.jsx("span",{className:"text-sm font-medium text-slate-200",children:"Agent Progress"})]}),e.jsxs("span",{className:"text-xs text-slate-400",children:[r,"/",n," complete"]})]}),e.jsx("div",{className:"h-1.5 bg-slate-700 rounded-full overflow-hidden mb-3",children:e.jsx("div",{className:"h-full bg-gradient-to-r from-emerald-500 to-cyan-500 rounded-full transition-all duration-500 ease-out",style:{width:`${o}%`}})}),e.jsx("div",{className:"space-y-2 max-h-[150px] overflow-y-auto",children:s.steps?.map((c,l)=>{const a=c.status==="completed",h=c.status==="pending"&&l===s.steps?.findIndex(R=>R.status==="pending");return e.jsxs("div",{className:`flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-300 ${a?"bg-emerald-500/10 border border-emerald-500/20":h?"bg-cyan-500/10 border border-cyan-500/30":"bg-slate-800/50 border border-slate-700/30"}`,children:[a?e.jsx(A,{className:"w-4 h-4 text-emerald-400 shrink-0"}):h?e.jsx(_,{className:"w-4 h-4 text-cyan-400 animate-spin shrink-0"}):e.jsx(L,{className:"w-4 h-4 text-slate-500 shrink-0"}),e.jsx("span",{className:`text-sm truncate ${a?"text-emerald-300":h?"text-cyan-300":"text-slate-400"}`,children:c.description})]},l)})}),s.current_tool&&e.jsx("div",{className:"mt-3 pt-3 border-t border-slate-700/50",children:e.jsxs("div",{className:"flex items-center gap-2 text-xs text-slate-400",children:[e.jsx("span",{className:"w-2 h-2 bg-cyan-400 rounded-full animate-pulse"}),e.jsxs("span",{children:["Running: ",e.jsx("code",{className:"text-cyan-400",children:s.current_tool})]})]})})]}),i&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-xs text-slate-400 uppercase tracking-wider font-medium px-1",children:[e.jsx(M,{className:"w-3.5 h-3.5"}),e.jsx("span",{children:"Tool Results"})]}),s.tool_results.map((c,l)=>e.jsx(de,{toolResult:c},l))]})]})}function me({useCoAgentStateRender:s,CopilotChat:t}){return s({name:"syncable",render:({state:i})=>xe(i)}),e.jsx(t,{className:"h-full",labels:{title:"Syncable Agent",initial:"Hi! I'm the Syncable agent. How can I help you today?",placeholder:"Type your message..."}})}function he({CopilotChat:s}){const[t,i]=p.useState(null);return p.useEffect(()=>{k(()=>import("@copilotkit/react-core"),[]).then(r=>{i(()=>r.useCoAgentStateRender)})},[]),t?e.jsx(me,{useCoAgentStateRender:t,CopilotChat:s}):e.jsx(s,{className:"h-full",labels:{title:"Syncable Agent",initial:"Hi! I'm the Syncable agent. How can I help you today?",placeholder:"Type your message..."}})}function ge(){const[s,t]=p.useState(null),[i,r]=p.useState(!1),{settings:n}=w();return p.useEffect(()=>{k(()=>import("@copilotkit/react-ui"),[]).then(o=>{t(()=>o.CopilotChat)})},[]),e.jsxs("main",{className:"min-h-screen bg-slate-950 relative overflow-hidden",children:[e.jsx("div",{className:"absolute inset-0 bg-linear-to-br from-slate-950 via-slate-900 to-slate-950"}),e.jsx("div",{className:"absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(34,211,238,0.1),transparent_50%)]"}),e.jsxs("div",{className:"relative z-10 max-w-5xl mx-auto px-4 sm:px-6 py-8",children:[e.jsxs("header",{className:"flex flex-col items-center text-center mb-6",children:[e.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[e.jsx("div",{className:"p-3 rounded-2xl bg-linear-to-br from-emerald-500/20 to-cyan-600/20 border border-emerald-500/30 shadow-[0_0_30px_rgba(16,185,129,0.15)]",children:e.jsx(T,{className:"w-8 h-8 text-emerald-400"})}),e.jsx("h1",{className:"text-4xl font-bold tracking-tight bg-linear-to-r from-emerald-400 via-cyan-400 to-blue-400 bg-clip-text text-transparent",children:"Agent Chat"})]}),e.jsx("p",{className:"text-slate-400 max-w-md text-base leading-relaxed",children:"Chat with the Syncable agent via AG-UI protocol. Messages are processed by the AG-UI server and streamed back in real-time."})]}),e.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-slate-400",children:[e.jsx("span",{className:"px-2 py-1 bg-slate-800/50 rounded text-xs",children:n.provider==="openai"?"OpenAI":n.provider==="anthropic"?"Anthropic":"Bedrock"}),e.jsx("span",{className:"px-2 py-1 bg-slate-800/50 rounded text-xs font-mono truncate max-w-[200px]",children:n.model.split("/").pop()?.split(":")[0]||n.model})]}),e.jsxs("button",{onClick:()=>r(!0),className:"flex items-center gap-2 px-3 py-1.5 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-sm text-slate-300 hover:text-white transition-colors",children:[e.jsx(P,{className:"w-4 h-4"}),"Settings"]})]}),e.jsxs("div",{className:"bg-slate-900/50 border border-slate-800 rounded-2xl overflow-hidden h-[calc(100vh-280px)] min-h-[500px] relative",children:[i&&e.jsx(le,{onClose:()=>r(!1)}),s?e.jsx(he,{CopilotChat:s}):e.jsx("div",{className:"h-full flex items-center justify-center",children:e.jsx(_,{className:"w-8 h-8 text-emerald-400 animate-spin"})})]}),e.jsxs("div",{className:"mt-6 p-4 bg-slate-900/30 border border-slate-800/50 rounded-xl",children:[e.jsxs("div",{className:"flex items-center gap-2 text-slate-400 text-sm",children:[e.jsx(N,{className:"w-4 h-4"}),e.jsx("span",{children:"AG-UI Server: "}),e.jsx("code",{className:"px-2 py-0.5 bg-slate-800 rounded text-emerald-400 text-xs",children:"http://localhost:9090"})]}),e.jsx("p",{className:"mt-2 text-xs text-slate-500",children:"Messages are sent via POST /message and responses streamed via SSE/WebSocket."})]})]})]})}export{ge as component}; diff --git a/tests/ag-ui-app/frontend/dist/client/assets/index-CfGsyQoR.js b/tests/ag-ui-app/frontend/dist/client/assets/index-CfGsyQoR.js deleted file mode 100644 index 61ab8686..00000000 --- a/tests/ag-ui-app/frontend/dist/client/assets/index-CfGsyQoR.js +++ /dev/null @@ -1,62 +0,0 @@ -import{j as e,p as X,T as V,g as Q,i as Z,c as ee,r as d}from"./main-DbgxqtWz.js";import{c as f,L as W,C as J,X as te}from"./x-DtBV5VmR.js";function U(t){if(Array.isArray(t))return t.flatMap(c=>U(c));if(typeof t!="string")return[];const a=[];let s=0,n,l,i,r,o;const u=()=>{for(;s(l=t.charAt(s),l!=="="&&l!==";"&&l!==",");for(;s=t.length)&&a.push(t.slice(n))}return a}function se(t){return t instanceof Headers?new Headers(t):Array.isArray(t)?new Headers(t):typeof t=="object"?new Headers(t):new Headers}function ae(...t){return t.reduce((a,s)=>{const n=se(s);for(const[l,i]of n.entries())l==="set-cookie"?U(i).forEach(o=>a.append("set-cookie",o)):a.set(l,i);return a},new Headers)}const re=[["path",{d:"M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16",key:"jecpp"}],["rect",{width:"20",height:"14",x:"2",y:"6",rx:"2",key:"i6l2r4"}]],ne=f("briefcase",re);const oe=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],le=f("check",oe);const ie=[["path",{d:"M12 6v6l4 2",key:"mmk7yg"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],T=f("clock",ie);const ce=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],de=f("copy",ce);const ue=[["path",{d:"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5",key:"mvr1a0"}]],he=f("heart",ue);const xe=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]],pe=f("history",xe);const me=[["path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5",key:"1gvzjb"}],["path",{d:"M9 18h6",key:"x1upvd"}],["path",{d:"M10 22h4",key:"ceow96"}]],fe=f("lightbulb",me);const be=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]],z=f("message-square",be);const ye=[["path",{d:"M5 12h14",key:"1ays0h"}]],ge=f("minus",ye);const we=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],ve=f("refresh-cw",we);const je=[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",key:"1ffxy3"}],["path",{d:"m21.854 2.147-10.94 10.939",key:"12cjpa"}]],Ne=f("send",je);const ke=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M8 14s1.5 2 4 2 4-2 4-2",key:"1y1vjs"}],["line",{x1:"9",x2:"9.01",y1:"9",y2:"9",key:"yxxnd0"}],["line",{x1:"15",x2:"15.01",y1:"9",y2:"9",key:"1p4y9e"}]],Ce=f("smile",ke);const _e=[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",key:"1s2grr"}],["path",{d:"M20 2v4",key:"1rf3ol"}],["path",{d:"M22 4h-4",key:"gwowj6"}],["circle",{cx:"4",cy:"20",r:"2",key:"6kqj1y"}]],G=f("sparkles",_e);const Me=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]],Se=f("square",Me);const $e=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],Oe=f("trash-2",$e);const Ee=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]],Re=f("zap",Ee);function Fe(){return e.jsxs("div",{className:"absolute inset-0 pointer-events-none overflow-hidden",children:[e.jsx("div",{className:"absolute -top-48 -left-48 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl animate-slow-pulse","aria-hidden":"true"}),e.jsx("div",{className:"absolute -bottom-48 -right-48 w-96 h-96 bg-violet-500/10 rounded-full blur-3xl animate-slow-pulse",style:{animationDelay:"4s"},"aria-hidden":"true"}),e.jsx("div",{className:"absolute top-1/2 -left-24 w-64 h-64 bg-teal-500/5 rounded-full blur-3xl animate-slow-pulse",style:{animationDelay:"2s"},"aria-hidden":"true"}),e.jsx("div",{className:"absolute -top-24 right-1/4 w-48 h-48 bg-fuchsia-500/5 rounded-full blur-3xl animate-slow-pulse",style:{animationDelay:"6s"},"aria-hidden":"true"}),e.jsx("div",{className:"absolute inset-0 opacity-30",style:{backgroundImage:` - linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px), - linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px) - `,backgroundSize:"60px 60px"},"aria-hidden":"true"}),e.jsx("div",{className:"absolute inset-0 bg-gradient-to-b from-transparent via-slate-950/50 to-slate-950","aria-hidden":"true"})]})}function Ae({message:t,context:a,intent:s,onMessageChange:n,onContextChange:l,onIntentChange:i,disabled:r=!1}){return e.jsxs("div",{className:"space-y-5 animate-glass-reveal",children:[e.jsxs("div",{className:"bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-5 transition-all duration-300 hover:bg-white/[0.07] hover:border-white/15",children:[e.jsxs("label",{htmlFor:"context-input",className:"flex items-center gap-2 text-sm font-medium text-slate-300 mb-3",children:[e.jsx(pe,{className:"w-4 h-4 text-violet-400"}),"Conversation context",e.jsx("span",{className:"text-slate-500 font-normal",children:"(optional)"})]}),e.jsx("textarea",{id:"context-input",value:a,onChange:o=>l(o.target.value),disabled:r,placeholder:`Provide background or previous messages in the conversation... - -e.g., 'We've been discussing the Q3 budget. They initially proposed $50k but I countered with $35k. This is their response to my counter-offer.'`,rows:3,className:` - w-full bg-slate-800/60 backdrop-blur-sm border border-white/10 rounded-xl p-4 - text-slate-100 text-[15px] leading-relaxed placeholder:text-slate-500 resize-none - focus:outline-none focus:border-violet-500/50 focus:shadow-[0_0_30px_rgba(139,92,246,0.1)] - transition-all duration-200 - disabled:opacity-50 disabled:cursor-not-allowed - `})]}),e.jsxs("div",{className:"bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-5 transition-all duration-300 hover:bg-white/[0.07] hover:border-white/15",children:[e.jsxs("label",{htmlFor:"message-input",className:"flex items-center gap-2 text-sm font-medium text-slate-300 mb-3",children:[e.jsx(z,{className:"w-4 h-4 text-cyan-400"}),"Message you received",e.jsx("span",{className:"text-rose-400",children:"*"})]}),e.jsx("textarea",{id:"message-input",value:t,onChange:o=>n(o.target.value),disabled:r,placeholder:`Paste the message you need to reply to... - -e.g., 'Hi, I wanted to follow up on our meeting yesterday. When would be a good time to reschedule?'`,rows:4,className:` - w-full bg-slate-800/60 backdrop-blur-sm border border-white/10 rounded-xl p-4 - text-slate-100 text-[15px] leading-relaxed placeholder:text-slate-500 resize-none - focus:outline-none focus:border-cyan-500/50 focus:shadow-[0_0_30px_rgba(34,211,238,0.1)] - transition-all duration-200 - disabled:opacity-50 disabled:cursor-not-allowed - `})]}),e.jsxs("div",{className:"bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-5 transition-all duration-300 hover:bg-white/[0.07] hover:border-white/15",children:[e.jsxs("label",{htmlFor:"intent-input",className:"flex items-center gap-2 text-sm font-medium text-slate-300 mb-3",children:[e.jsx(fe,{className:"w-4 h-4 text-amber-400"}),"What you want to say",e.jsx("span",{className:"text-slate-500 font-normal",children:"(optional)"})]}),e.jsx("textarea",{id:"intent-input",value:s,onChange:o=>i(o.target.value),disabled:r,placeholder:`Describe what you want to communicate in your reply... - -e.g., 'I want to accept their offer but negotiate a faster timeline. Also mention I need the contract by Friday.'`,rows:3,className:` - w-full bg-slate-800/60 backdrop-blur-sm border border-white/10 rounded-xl p-4 - text-slate-100 text-[15px] leading-relaxed placeholder:text-slate-500 resize-none - focus:outline-none focus:border-amber-500/50 focus:shadow-[0_0_30px_rgba(245,158,11,0.1)] - transition-all duration-200 - disabled:opacity-50 disabled:cursor-not-allowed - `})]})]})}const He=[{value:"professional",label:"Professional",icon:e.jsx(ne,{className:"w-4 h-4"}),description:"Formal, business-appropriate"},{value:"friendly",label:"Friendly",icon:e.jsx(Ce,{className:"w-4 h-4"}),description:"Warm and personable"},{value:"apologetic",label:"Apologetic",icon:e.jsx(he,{className:"w-4 h-4"}),description:"Sincere and understanding"},{value:"assertive",label:"Assertive",icon:e.jsx(Re,{className:"w-4 h-4"}),description:"Direct and confident"},{value:"neutral",label:"Neutral",icon:e.jsx(ge,{className:"w-4 h-4"}),description:"Balanced and objective"}];function Ie({selected:t,onChange:a,disabled:s=!1}){return e.jsxs("div",{className:"space-y-3 animate-fade-in-up",style:{animationDelay:"100ms"},children:[e.jsx("label",{className:"text-sm font-medium text-slate-300",children:"Select tone"}),e.jsx("div",{className:"flex flex-wrap gap-2",role:"radiogroup","aria-label":"Reply tone selection",children:He.map(n=>{const l=t===n.value;return e.jsxs("button",{onClick:()=>a(n.value),disabled:s,role:"radio","aria-checked":l,title:n.description,className:` - group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium - transition-all duration-200 border cursor-pointer - ${l?"bg-gradient-to-r from-cyan-500/20 to-violet-500/20 border-cyan-500/40 text-cyan-300 shadow-[0_0_20px_rgba(34,211,238,0.15)]":"bg-white/5 border-white/10 text-slate-400 hover:bg-white/10 hover:border-white/20 hover:text-slate-200"} - disabled:opacity-50 disabled:cursor-not-allowed - focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:ring-offset-2 focus:ring-offset-slate-950 - `,children:[e.jsx("span",{className:`transition-all duration-200 ${l?"text-cyan-400 scale-110":"text-slate-500 group-hover:text-slate-300 group-hover:scale-110"}`,children:n.icon}),e.jsx("span",{children:n.label})]},n.value)})})]})}function De({onClick:t,onStop:a,isLoading:s=!1,disabled:n=!1}){const l=()=>{s&&a?a():t()};return e.jsxs("button",{onClick:l,disabled:n&&!s,className:` - group relative flex items-center justify-center gap-3 px-8 py-4 rounded-2xl font-semibold text-base - transition-all duration-300 overflow-hidden cursor-pointer - ${s?"bg-slate-800/80 border border-cyan-500/40 text-cyan-300 animate-glow-pulse":"bg-gradient-to-r from-cyan-500 to-violet-600 text-white shadow-lg shadow-cyan-500/25 hover:shadow-[0_0_40px_rgba(34,211,238,0.4)] hover:from-cyan-400 hover:to-violet-500"} - ${!s&&!n?"transform hover:scale-[1.03] active:scale-[0.98]":""} - disabled:opacity-40 disabled:cursor-not-allowed disabled:transform-none disabled:shadow-none - focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:ring-offset-2 focus:ring-offset-slate-950 - `,"aria-label":s?"Stop generating":"Generate reply suggestions",children:[!s&&!n&&e.jsx("div",{className:"absolute inset-0 -translate-x-full group-hover:translate-x-full transition-transform duration-700 bg-gradient-to-r from-transparent via-white/20 to-transparent"}),e.jsx("span",{className:"relative flex items-center gap-3",children:s?e.jsxs(e.Fragment,{children:[e.jsx(W,{className:"w-5 h-5 animate-spin"}),e.jsx("span",{children:"Generating..."}),a&&e.jsx(Se,{className:"w-4 h-4 ml-1 opacity-70"})]}):e.jsxs(e.Fragment,{children:[e.jsx(Ne,{className:"w-5 h-5 transition-transform duration-200 group-hover:translate-x-0.5"}),e.jsx("span",{children:"Generate Replies"})]})})]})}const Te={professional:"bg-blue-500/10 text-blue-300 border-blue-500/20",friendly:"bg-emerald-500/10 text-emerald-300 border-emerald-500/20",apologetic:"bg-amber-500/10 text-amber-300 border-amber-500/20",assertive:"bg-rose-500/10 text-rose-300 border-rose-500/20",neutral:"bg-slate-500/10 text-slate-300 border-slate-500/20"};function Ge({reply:t,tone:a,isCopied:s,onCopy:n,onSelect:l,animationDelay:i=0}){return e.jsxs("div",{className:`group bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-6 - hover:bg-white/[0.08] hover:border-cyan-500/30 hover:shadow-[0_0_40px_rgba(34,211,238,0.1)] - cursor-pointer transition-all duration-300 animate-fade-in-up`,style:{animationDelay:`${i}ms`,animationFillMode:"both"},onClick:l,role:"button",tabIndex:0,onKeyDown:r=>{(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),l())},"aria-label":`Reply option ${t.reply_index+1}. ${t.content}`,children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsxs("span",{className:`px-3 py-1.5 rounded-full text-xs font-medium border ${Te[a]}`,children:["Option ",t.reply_index+1]}),e.jsx("button",{onClick:r=>{r.stopPropagation(),n()},className:`flex items-center gap-2 px-3 py-1.5 rounded-lg cursor-pointer - bg-white/5 hover:bg-white/10 - text-slate-400 hover:text-slate-200 - opacity-0 group-hover:opacity-100 - transition-all duration-200 - focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:opacity-100`,"aria-label":s?"Copied to clipboard":"Copy to clipboard",children:s?e.jsxs(e.Fragment,{children:[e.jsx(le,{className:"w-4 h-4 text-emerald-400"}),e.jsx("span",{className:"text-xs font-medium text-emerald-400",children:"Copied!"})]}):e.jsxs(e.Fragment,{children:[e.jsx(de,{className:"w-4 h-4"}),e.jsx("span",{className:"text-xs font-medium",children:"Copy"})]})})]}),e.jsx("p",{className:"text-slate-100 leading-relaxed mb-5 whitespace-pre-wrap text-[15px]",children:t.content}),e.jsx("div",{className:"flex justify-end",children:e.jsxs("button",{onClick:r=>{r.stopPropagation(),l()},className:`flex items-center gap-1.5 text-sm font-medium text-cyan-400 hover:text-cyan-300 - transition-all duration-200 cursor-pointer group/btn`,children:[e.jsx("span",{children:"Use this reply"}),e.jsx(J,{className:"w-4 h-4 transition-transform duration-200 group-hover/btn:translate-x-1"})]})})]})}function Le({delay:t=0}){return e.jsxs("div",{className:"bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-6 animate-fade-in-up",style:{animationDelay:`${t}ms`,animationFillMode:"both"},children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsx("div",{className:"h-7 w-20 bg-slate-700/50 rounded-full animate-shimmer"}),e.jsx("div",{className:"h-8 w-16 bg-slate-700/50 rounded-lg animate-shimmer"})]}),e.jsxs("div",{className:"space-y-3 mb-5",children:[e.jsx("div",{className:"h-4 bg-slate-700/50 rounded-lg w-full animate-shimmer"}),e.jsx("div",{className:"h-4 bg-slate-700/50 rounded-lg w-11/12 animate-shimmer"}),e.jsx("div",{className:"h-4 bg-slate-700/50 rounded-lg w-4/5 animate-shimmer"})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx("div",{className:"h-5 w-28 bg-slate-700/50 rounded-lg animate-shimmer"})})]})}function ze({replies:t,isLoading:a,tone:s,copiedId:n,onCopy:l,onSelect:i,streamedContent:r}){return a?e.jsxs("section",{"aria-live":"polite","aria-busy":"true",className:"animate-fade-in-up",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-4",children:[e.jsx(G,{className:"w-5 h-5 text-cyan-400 animate-pulse"}),e.jsx("h2",{className:"text-xl font-semibold text-slate-100",children:"Generating replies..."})]}),r&&e.jsxs("div",{className:"mb-4 p-4 bg-slate-800/50 rounded-xl border border-cyan-500/20",children:[e.jsx("p",{className:"text-sm text-slate-400 mb-2",children:"AI is thinking..."}),e.jsxs("p",{className:"text-slate-300 text-sm font-mono whitespace-pre-wrap",children:[r,e.jsx("span",{className:"inline-block w-2 h-4 bg-cyan-400 ml-1 animate-pulse"})]})]}),e.jsx("div",{className:"flex flex-col gap-4",children:[0,1,2].map(o=>e.jsx(Le,{delay:o*100},o))})]}):t.length===0?e.jsxs("section",{className:"flex flex-col items-center justify-center py-16 text-center animate-fade-in-up",children:[e.jsx("div",{className:"w-20 h-20 rounded-2xl bg-gradient-to-br from-white/5 to-white/[0.02] border border-white/10 flex items-center justify-center mb-5",children:e.jsx(z,{className:"w-10 h-10 text-slate-500"})}),e.jsx("h3",{className:"text-xl font-medium text-slate-300 mb-2",children:"No replies yet"}),e.jsxs("p",{className:"text-sm text-slate-500 max-w-sm leading-relaxed",children:["Paste a message above, select your preferred tone, and click",e.jsx("span",{className:"text-cyan-400 font-medium",children:' "Generate Replies" '}),"to get AI-powered suggestions."]}),e.jsx("div",{className:"flex gap-2 mt-6",children:[0,1,2].map(o=>e.jsx("div",{className:"w-2 h-2 rounded-full bg-slate-700",style:{opacity:.3+o*.2}},o))})]}):e.jsxs("section",{"aria-live":"polite",children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(G,{className:"w-5 h-5 text-cyan-400"}),e.jsxs("h2",{className:"text-xl font-semibold text-slate-100",children:[t.length," reply suggestion",t.length!==1?"s":""]})]}),e.jsx("span",{className:"text-xs text-slate-500 bg-white/5 px-3 py-1 rounded-full",children:"Click to copy"})]}),e.jsx("div",{className:"flex flex-col gap-4",children:t.map((o,u)=>e.jsx(Ge,{reply:o,tone:s,isCopied:n===o.id,onCopy:()=>l(o.id,o.content),onSelect:()=>i(o.id,o.content),animationDelay:u*100},o.id))})]})}const Pe={professional:"bg-blue-500/20 text-blue-300",friendly:"bg-emerald-500/20 text-emerald-300",apologetic:"bg-amber-500/20 text-amber-300",assertive:"bg-rose-500/20 text-rose-300",neutral:"bg-slate-500/20 text-slate-300"};function Ve(t){const a=new Date(t),n=new Date().getTime()-a.getTime(),l=Math.floor(n/6e4),i=Math.floor(l/60),r=Math.floor(i/24);return l<1?"Just now":l<60?`${l}m ago`:i<24?`${i}h ago`:r<7?`${r}d ago`:a.toLocaleDateString("en-US",{month:"short",day:"numeric"})}function qe(t,a){return t.length<=a?t:t.slice(0,a).trim()+"..."}function Be({conversation:t,onClick:a,onDelete:s,animationDelay:n=0}){const l=t.replies?.length||0;return e.jsxs("div",{className:`group relative bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 - rounded-xl p-3 cursor-pointer transition-all duration-200 animate-fade-in-up`,style:{animationDelay:`${n}ms`,animationFillMode:"both"},onClick:a,role:"button",tabIndex:0,onKeyDown:i=>{(i.key==="Enter"||i.key===" ")&&(i.preventDefault(),a())},children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:`px-2 py-0.5 rounded text-xs font-medium ${Pe[t.tone]}`,children:t.tone}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-slate-500",children:Ve(t.created_at)}),e.jsx("button",{onClick:i=>{i.stopPropagation(),s()},className:`p-1.5 rounded-lg opacity-0 group-hover:opacity-100 hover:bg-rose-500/20 - text-slate-500 hover:text-rose-400 transition-all duration-200 cursor-pointer - focus:outline-none focus:opacity-100 focus:ring-2 focus:ring-rose-500/50`,"aria-label":"Delete conversation",children:e.jsx(Oe,{className:"w-3.5 h-3.5"})})]})]}),e.jsx("p",{className:"text-sm text-slate-300 leading-relaxed mb-2",children:qe(t.original_message,80)}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-slate-500",children:[e.jsx(z,{className:"w-3.5 h-3.5"}),e.jsxs("span",{children:[l," repl",l!==1?"ies":"y"]})]}),e.jsxs("div",{className:"flex items-center gap-1 text-xs text-cyan-400 opacity-0 group-hover:opacity-100 transition-opacity",children:[e.jsx("span",{children:"Load"}),e.jsx(J,{className:"w-3.5 h-3.5"})]})]})]})}function We({isOpen:t,onClose:a,history:s,isLoading:n,onSelectConversation:l,onDeleteConversation:i}){return t?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"fixed inset-0 bg-black/70 backdrop-blur-sm z-40 animate-backdrop-fade-in cursor-pointer",onClick:a,"aria-hidden":"true"}),e.jsxs("aside",{className:`fixed top-0 right-0 h-full w-full sm:w-[400px] bg-slate-900/95 backdrop-blur-xl - border-l border-white/10 shadow-2xl z-50 flex flex-col animate-slide-in-right`,role:"dialog","aria-modal":"true","aria-labelledby":"history-title",children:[e.jsxs("div",{className:"flex items-center justify-between p-5 border-b border-white/10",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-xl bg-cyan-500/10 border border-cyan-500/20",children:e.jsx(T,{className:"w-5 h-5 text-cyan-400"})}),e.jsx("h2",{id:"history-title",className:"text-lg font-semibold text-slate-100",children:"History"})]}),e.jsx("button",{onClick:a,className:`p-2.5 rounded-xl hover:bg-white/10 text-slate-400 hover:text-slate-100 - transition-all duration-200 cursor-pointer - focus:outline-none focus:ring-2 focus:ring-cyan-500/50`,"aria-label":"Close history",children:e.jsx(te,{className:"w-5 h-5"})})]}),e.jsx("div",{className:"flex-1 overflow-y-auto custom-scrollbar p-4",children:n?e.jsxs("div",{className:"flex flex-col items-center justify-center h-48 text-slate-400",children:[e.jsx(W,{className:"w-8 h-8 animate-spin mb-3 text-cyan-400"}),e.jsx("span",{className:"text-sm",children:"Loading history..."})]}):s.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center h-48 text-center",children:[e.jsx("div",{className:"w-16 h-16 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center mb-4",children:e.jsx(T,{className:"w-8 h-8 text-slate-600"})}),e.jsx("p",{className:"text-slate-300 font-medium",children:"No history yet"}),e.jsx("p",{className:"text-slate-500 text-sm mt-1",children:"Your generated replies will appear here"})]}):e.jsx("div",{className:"space-y-3",children:s.map((r,o)=>e.jsx(Be,{conversation:r,onClick:()=>l(r),onDelete:()=>i(r.id),animationDelay:o*50},r.id))})}),s.length>0&&e.jsx("div",{className:"p-4 border-t border-white/10",children:e.jsxs("p",{className:"text-xs text-slate-500 text-center",children:[s.length," conversation",s.length!==1?"s":""," saved"]})})]})]}):null}const Y=()=>{throw new Error("createServerOnlyFn() functions can only be called on the server!")};function q(t){return t!=="__proto__"&&t!=="constructor"&&t!=="prototype"}function L(t,a){const s=Object.create(null);if(t)for(const n of Object.keys(t))q(n)&&(s[n]=t[n]);if(a&&typeof a=="object")for(const n of Object.keys(a))q(n)&&(s[n]=a[n]);return s}function K(t){return Object.create(null)}const A=(t,a)=>{const s=a||t||{};return typeof s.method>"u"&&(s.method="GET"),Object.assign(i=>{const r={...s,...i};return A(void 0,r)},{options:s,middleware:i=>{const r=[...s.middleware||[]];i.map(p=>{V in p?p.options.middleware&&r.push(...p.options.middleware):r.push(p)});const o={...s,middleware:r},u=A(void 0,o);return u[V]=!0,u},inputValidator:i=>{const r={...s,inputValidator:i};return A(void 0,r)},handler:(...i)=>{const[r,o]=i,u={...s,extractedFn:r,serverFn:o},p=[...u.middleware||[],Ye(u)];return Object.assign(async c=>{const x=await B(p,"client",{...r,...u,data:c?.data,headers:c?.headers,signal:c?.signal,context:K()}),h=X(x.error);if(h)throw h;if(x.error)throw x.error;return x.result},{...r,__executeServer:async(c,x)=>{const h=Y(),m=h.contextAfterGlobalMiddlewares,k={...r,...c,context:L(m,c.context),signal:x,request:h.request};return await B(p,"server",k).then(C=>({result:C.result,error:C.error,context:C.sendContext}))}})}})};async function B(t,a,s){const n=Q()?.functionMiddleware||[];let l=Je([...n,...t]);if(a==="server"){const r=Y();r?.executedRequestMiddlewares&&(l=l.filter(o=>!r.executedRequestMiddlewares.has(o)))}const i=async r=>{const o=l.shift();if(!o)return r;try{"inputValidator"in o.options&&o.options.inputValidator&&a==="server"&&(r.data=await Ue(o.options.inputValidator,r.data));let u;if(a==="client"?"client"in o.options&&(u=o.options.client):"server"in o.options&&(u=o.options.server),u){const c=await u({...r,next:async(x={})=>{const h={...r,...x,context:L(r.context,x.context),sendContext:L(r.sendContext,x.sendContext),headers:ae(r.headers,x.headers),result:x.result!==void 0?x.result:x instanceof Response?x:r.result,error:x.error??r.error};try{return await i(h)}catch(m){return{...h,error:m}}}});if(Z(c))return{...r,error:c};if(c instanceof Response)return{...r,result:c};if(!c)throw new Error("User middleware returned undefined. You must call next() or return a result in your middlewares.");return c}return i(r)}catch(u){return{...r,error:u}}};return i({...s,headers:s.headers||{},sendContext:s.sendContext||{},context:s.context||K()})}function Je(t,a=100){const s=new Set,n=[],l=(i,r)=>{if(r>a)throw new Error(`Middleware nesting depth exceeded maximum of ${a}. Check for circular references.`);i.forEach(o=>{o.options.middleware&&l(o.options.middleware,r+1),s.has(o)||(s.add(o),n.push(o))})};return l(t,0),n}async function Ue(t,a){if(t==null)return{};if("~standard"in t){const s=await t["~standard"].validate(a);if(s.issues)throw new Error(JSON.stringify(s.issues,void 0,2));return s.value}if("parse"in t)return t.parse(a);if(typeof t=="function")return t(a);throw new Error("Invalid validator type!")}function Ye(t){return{"~types":void 0,options:{inputValidator:t.inputValidator,client:async({next:a,sendContext:s,...n})=>{const l={...n,context:s},i=await t.extractedFn?.(l);return a(i)},server:async({next:a,...s})=>{const n=await t.serverFn?.(s);return a({...s,result:n})}}}}const M=A({method:"GET"}).handler(ee("cc35041ac8534be1df357d50dcecc9c79d69232d0d2da0a89a0d930737c7ec36")),$={generateReplies:async(t,a,s,n,l)=>fetch(`${await M()}/replies/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:t,tone:a,context:s,intent:n}),signal:l}),getHistory:async(t=50)=>{const a=await fetch(`${await M()}/history?limit=${t}`);if(!a.ok)throw new Error("Failed to fetch history");return(await a.json()).data},getConversation:async t=>{const a=await fetch(`${await M()}/history/${t}`);if(!a.ok)throw new Error("Conversation not found");return(await a.json()).data},deleteConversation:async t=>{if(!(await fetch(`${await M()}/history/${t}`,{method:"DELETE"})).ok)throw new Error("Failed to delete conversation")},saveReplies:async(t,a)=>{if(!(await fetch(`${await M()}/replies/save`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({conversationId:t,replies:a})})).ok)throw new Error("Failed to save replies")},healthCheck:async()=>{try{return(await fetch(`${await M()}.replace('/api', '')}/health`)).ok}catch{return!1}}};function Ke(){const[t,a]=d.useState(!1),[s,n]=d.useState(""),[l,i]=d.useState([]),[r,o]=d.useState(null),[u,p]=d.useState(null),[c,x]=d.useState(null),h=d.useCallback(async(O,C,E,H)=>{a(!0),i([]),o(null),n(""),p(null);const R=new AbortController;x(R);try{const y=await $.generateReplies(O,C,E,H,R.signal);if(!y.ok){const N=await y.json().catch(()=>({}));throw new Error(N.error||`HTTP ${y.status}`)}const g=y.headers.get("X-Conversation-Id");g&&o(g);const S=y.body?.getReader();if(!S)throw new Error("No response body");const F=new TextDecoder;let j="";for(;;){const{done:N,value:I}=await S.read();if(N)break;const D=F.decode(I,{stream:!0}).split(` -`);for(const b of D)if(b.startsWith("data: ")){const w=b.slice(6).trim();if(w==="[DONE]")continue;try{const v=JSON.parse(w);if(v.type==="text"&&v.content)j+=v.content,n(j);else if(v.type==="content"&&v.content)j=v.content,n(j);else if(v.type==="error")throw new Error(v.error?.message||"Stream error")}catch{w&&!w.startsWith("{")&&(j+=w,n(j))}}}const _=Xe(j);if(i(_),g&&_.length>0)try{await $.saveReplies(g,_.map(N=>N.content))}catch(N){console.error("Failed to save replies:",N)}}catch(y){if(y instanceof Error&&y.name==="AbortError")return;const g=y instanceof Error?y.message:"Failed to generate replies";p(g),console.error("Generation error:",y)}finally{a(!1),x(null)}},[]),m=d.useCallback(()=>{c&&(c.abort(),a(!1))},[c]),k=d.useCallback(()=>{m(),n(""),i([]),o(null),p(null)},[m]);return{isGenerating:t,streamedContent:s,replies:l,conversationId:r,error:u,generate:h,stop:m,reset:k,setReplies:i}}function Xe(t){try{const a=t.match(/\[[\s\S]*\]/);if(a){const s=JSON.parse(a[0]);if(Array.isArray(s))return s.slice(0,3).map((n,l)=>({id:`reply-${Date.now()}-${l}`,content:String(n),reply_index:l}))}}catch(a){console.error("Failed to parse replies:",a,t)}return[]}function Qe(){const[t,a]=d.useState([]),[s,n]=d.useState(!1),[l,i]=d.useState(!1),[r,o]=d.useState(null),u=d.useCallback(async()=>{i(!0),o(null);try{const h=await $.getHistory(50);a(h)}catch(h){const m=h instanceof Error?h.message:"Failed to fetch history";o(m),console.error("History fetch error:",h)}finally{i(!1)}},[]),p=d.useCallback(async h=>{try{return await $.getConversation(h)}catch(m){return console.error("Load conversation error:",m),null}},[]),c=d.useCallback(async h=>{try{await $.deleteConversation(h),a(m=>m.filter(k=>k.id!==h))}catch(m){throw console.error("Delete conversation error:",m),m}},[]),x=u;return d.useEffect(()=>{s&&u()},[s,u]),{history:t,isOpen:s,isLoading:l,error:r,setIsOpen:n,fetchHistory:u,loadConversation:p,deleteConversation:c,refreshHistory:x}}function tt(){const[t,a]=d.useState(""),[s,n]=d.useState(""),[l,i]=d.useState(""),[r,o]=d.useState("professional"),[u,p]=d.useState(null),{isGenerating:c,streamedContent:x,replies:h,error:m,generate:k,stop:O,reset:C,setReplies:E}=Ke(),{history:H,isOpen:R,isLoading:y,setIsOpen:g,deleteConversation:S,refreshHistory:F}=Qe(),j=d.useCallback(async()=>{!t.trim()||c||(await k(t,r,s,l),F())},[t,r,s,l,c,k,F]),_=d.useCallback(async(b,w)=>{try{await navigator.clipboard.writeText(w),p(b),setTimeout(()=>p(null),2e3)}catch(v){console.error("Failed to copy:",v)}},[]),N=d.useCallback(async(b,w)=>{await _(b,w)},[_]),I=d.useCallback(b=>{a(b.original_message),n(b.context||""),i(b.intent||""),o(b.tone),E(b.replies),g(!1)},[E,g]),P=d.useCallback(async b=>{try{await S(b)}catch(w){console.error("Failed to delete:",w)}},[S]),D=d.useCallback(()=>{a(""),n(""),i(""),C()},[C]);return e.jsxs("main",{className:"min-h-screen bg-slate-950 relative overflow-hidden",children:[e.jsx(Fe,{}),e.jsxs("div",{className:"relative z-10 max-w-2xl mx-auto px-4 sm:px-6 py-12 sm:py-16",children:[e.jsxs("header",{className:"flex flex-col items-center text-center mb-10 animate-fade-in-up",children:[e.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[e.jsx("div",{className:"p-3 rounded-2xl bg-gradient-to-br from-cyan-500/20 to-violet-600/20 border border-cyan-500/30 shadow-[0_0_30px_rgba(34,211,238,0.15)]",children:e.jsx(G,{className:"w-8 h-8 text-cyan-400"})}),e.jsx("h1",{className:"text-4xl sm:text-5xl font-bold tracking-tight bg-gradient-to-r from-cyan-400 via-blue-400 to-violet-400 bg-clip-text text-transparent",children:"Smart Reply"})]}),e.jsx("p",{className:"text-slate-400 max-w-md text-base sm:text-lg leading-relaxed",children:"Provide context and intent to get AI-powered reply suggestions tailored to your needs."}),e.jsxs("div",{className:"flex items-center gap-3 mt-6",children:[e.jsxs("button",{onClick:()=>g(!0),className:`flex items-center gap-2 px-4 py-2.5 rounded-xl bg-white/5 border border-white/10 - text-slate-400 hover:text-slate-100 hover:bg-white/10 hover:border-white/20 - transition-all duration-200 text-sm font-medium cursor-pointer - focus:outline-none focus:ring-2 focus:ring-cyan-500/50`,"aria-label":"Open history",children:[e.jsx(T,{className:"w-4 h-4"}),e.jsx("span",{children:"History"})]}),(t||s||l||h.length>0)&&e.jsxs("button",{onClick:D,className:`flex items-center gap-2 px-4 py-2.5 rounded-xl bg-white/5 border border-white/10 - text-slate-400 hover:text-slate-100 hover:bg-white/10 hover:border-white/20 - transition-all duration-200 text-sm font-medium cursor-pointer - focus:outline-none focus:ring-2 focus:ring-cyan-500/50`,"aria-label":"Reset form",children:[e.jsx(ve,{className:"w-4 h-4"}),e.jsx("span",{children:"Reset"})]})]})]}),e.jsxs("div",{className:"space-y-8",children:[e.jsx(Ae,{message:t,context:s,intent:l,onMessageChange:a,onContextChange:n,onIntentChange:i,disabled:c}),e.jsx(Ie,{selected:r,onChange:o,disabled:c}),e.jsx("div",{className:"flex justify-center pt-4",children:e.jsx(De,{onClick:j,onStop:O,isLoading:c,disabled:!t.trim()})}),m&&e.jsx("div",{className:"bg-rose-500/10 border border-rose-500/20 rounded-2xl p-5 animate-fade-in-up",children:e.jsx("p",{className:"text-rose-400 text-center text-sm font-medium",children:m})}),e.jsx(ze,{replies:h,isLoading:c,tone:r,copiedId:u,onCopy:_,onSelect:N,streamedContent:x})]}),e.jsx("footer",{className:"mt-16 text-center",children:e.jsx("p",{className:"text-xs text-slate-600",children:"Powered by AI"})})]}),e.jsx(We,{isOpen:R,onClose:()=>g(!1),history:H,isLoading:y,onSelectConversation:I,onDeleteConversation:P})]})}export{tt as component}; diff --git a/tests/ag-ui-app/frontend/dist/client/assets/main-DbgxqtWz.js b/tests/ag-ui-app/frontend/dist/client/assets/main-DbgxqtWz.js deleted file mode 100644 index 7fd76170..00000000 --- a/tests/ag-ui-app/frontend/dist/client/assets/main-DbgxqtWz.js +++ /dev/null @@ -1,18 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/agent-Bh38wHiA.js","assets/x-DtBV5VmR.js","assets/index-CfGsyQoR.js"])))=>i.map(i=>d[i]); -function t0(a){return a&&a.__esModule&&Object.prototype.hasOwnProperty.call(a,"default")?a.default:a}var Jc={exports:{}},Ei={};var jm;function e0(){if(jm)return Ei;jm=1;var a=Symbol.for("react.transitional.element"),u=Symbol.for("react.fragment");function i(c,o,d){var p=null;if(d!==void 0&&(p=""+d),o.key!==void 0&&(p=""+o.key),"key"in o){d={};for(var m in o)m!=="key"&&(d[m]=o[m])}else d=o;return o=d.ref,{$$typeof:a,type:c,key:p,ref:o!==void 0?o:null,props:d}}return Ei.Fragment=u,Ei.jsx=i,Ei.jsxs=i,Ei}var Hm;function n0(){return Hm||(Hm=1,Jc.exports=e0()),Jc.exports}var K=n0(),kc={exports:{}},ot={};var qm;function a0(){if(qm)return ot;qm=1;var a=Symbol.for("react.transitional.element"),u=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),o=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),p=Symbol.for("react.context"),m=Symbol.for("react.forward_ref"),h=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),_=Symbol.for("react.lazy"),S=Symbol.for("react.activity"),v=Symbol.iterator;function A(M){return M===null||typeof M!="object"?null:(M=v&&M[v]||M["@@iterator"],typeof M=="function"?M:null)}var L={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},x=Object.assign,D={};function V(M,q,P){this.props=M,this.context=q,this.refs=D,this.updater=P||L}V.prototype.isReactComponent={},V.prototype.setState=function(M,q){if(typeof M!="object"&&typeof M!="function"&&M!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,M,q,"setState")},V.prototype.forceUpdate=function(M){this.updater.enqueueForceUpdate(this,M,"forceUpdate")};function Z(){}Z.prototype=V.prototype;function Q(M,q,P){this.props=M,this.context=q,this.refs=D,this.updater=P||L}var Y=Q.prototype=new Z;Y.constructor=Q,x(Y,V.prototype),Y.isPureReactComponent=!0;var $=Array.isArray;function k(){}var X={H:null,A:null,T:null,S:null},G=Object.prototype.hasOwnProperty;function F(M,q,P){var W=P.ref;return{$$typeof:a,type:M,key:q,ref:W!==void 0?W:null,props:P}}function st(M,q){return F(M.type,q,M.props)}function lt(M){return typeof M=="object"&&M!==null&&M.$$typeof===a}function ct(M){var q={"=":"=0",":":"=2"};return"$"+M.replace(/[=:]/g,function(P){return q[P]})}var qt=/\/+/g;function St(M,q){return typeof M=="object"&&M!==null&&M.key!=null?ct(""+M.key):q.toString(36)}function Ut(M){switch(M.status){case"fulfilled":return M.value;case"rejected":throw M.reason;default:switch(typeof M.status=="string"?M.then(k,k):(M.status="pending",M.then(function(q){M.status==="pending"&&(M.status="fulfilled",M.value=q)},function(q){M.status==="pending"&&(M.status="rejected",M.reason=q)})),M.status){case"fulfilled":return M.value;case"rejected":throw M.reason}}throw M}function U(M,q,P,W,ut){var rt=typeof M;(rt==="undefined"||rt==="boolean")&&(M=null);var yt=!1;if(M===null)yt=!0;else switch(rt){case"bigint":case"string":case"number":yt=!0;break;case"object":switch(M.$$typeof){case a:case u:yt=!0;break;case _:return yt=M._init,U(yt(M._payload),q,P,W,ut)}}if(yt)return ut=ut(M),yt=W===""?"."+St(M,0):W,$(ut)?(P="",yt!=null&&(P=yt.replace(qt,"$&/")+"/"),U(ut,q,P,"",function(We){return We})):ut!=null&&(lt(ut)&&(ut=st(ut,P+(ut.key==null||M&&M.key===ut.key?"":(""+ut.key).replace(qt,"$&/")+"/")+yt)),q.push(ut)),1;yt=0;var Kt=W===""?".":W+":";if($(M))for(var zt=0;zt>>1,xt=U[At];if(0>>1;Ato(P,it))Wo(ut,P)?(U[At]=ut,U[W]=it,At=W):(U[At]=P,U[q]=it,At=q);else if(Wo(ut,it))U[At]=ut,U[W]=it,At=W;else break t}}return J}function o(U,J){var it=U.sortIndex-J.sortIndex;return it!==0?it:U.id-J.id}if(a.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;a.unstable_now=function(){return d.now()}}else{var p=Date,m=p.now();a.unstable_now=function(){return p.now()-m}}var h=[],y=[],_=1,S=null,v=3,A=!1,L=!1,x=!1,D=!1,V=typeof setTimeout=="function"?setTimeout:null,Z=typeof clearTimeout=="function"?clearTimeout:null,Q=typeof setImmediate<"u"?setImmediate:null;function Y(U){for(var J=i(y);J!==null;){if(J.callback===null)c(y);else if(J.startTime<=U)c(y),J.sortIndex=J.expirationTime,u(h,J);else break;J=i(y)}}function $(U){if(x=!1,Y(U),!L)if(i(h)!==null)L=!0,k||(k=!0,ct());else{var J=i(y);J!==null&&Ut($,J.startTime-U)}}var k=!1,X=-1,G=5,F=-1;function st(){return D?!0:!(a.unstable_now()-FU&&st());){var At=S.callback;if(typeof At=="function"){S.callback=null,v=S.priorityLevel;var xt=At(S.expirationTime<=U);if(U=a.unstable_now(),typeof xt=="function"){S.callback=xt,Y(U),J=!0;break e}S===i(h)&&c(h),Y(U)}else c(h);S=i(h)}if(S!==null)J=!0;else{var M=i(y);M!==null&&Ut($,M.startTime-U),J=!1}}break t}finally{S=null,v=it,A=!1}J=void 0}}finally{J?ct():k=!1}}}var ct;if(typeof Q=="function")ct=function(){Q(lt)};else if(typeof MessageChannel<"u"){var qt=new MessageChannel,St=qt.port2;qt.port1.onmessage=lt,ct=function(){St.postMessage(null)}}else ct=function(){V(lt,0)};function Ut(U,J){X=V(function(){U(a.unstable_now())},J)}a.unstable_IdlePriority=5,a.unstable_ImmediatePriority=1,a.unstable_LowPriority=4,a.unstable_NormalPriority=3,a.unstable_Profiling=null,a.unstable_UserBlockingPriority=2,a.unstable_cancelCallback=function(U){U.callback=null},a.unstable_forceFrameRate=function(U){0>U||125At?(U.sortIndex=it,u(y,U),i(h)===null&&U===i(y)&&(x?(Z(X),X=-1):x=!0,Ut($,it-At))):(U.sortIndex=xt,u(h,U),L||A||(L=!0,k||(k=!0,ct()))),U},a.unstable_shouldYield=st,a.unstable_wrapCallback=function(U){var J=v;return function(){var it=v;v=J;try{return U.apply(this,arguments)}finally{v=it}}}})($c)),$c}var Vm;function i0(){return Vm||(Vm=1,Pc.exports=l0()),Pc.exports}var Wc={exports:{}},re={};var Xm;function u0(){if(Xm)return re;Xm=1;var a=Ui();function u(h){var y="https://react.dev/errors/"+h;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(a)}catch(u){console.error(u)}}return a(),Wc.exports=u0(),Wc.exports}var Zm;function s0(){if(Zm)return Ri;Zm=1;var a=i0(),u=Ui(),i=xy();function c(t){var e="https://react.dev/errors/"+t;if(1xt||(t.current=At[xt],At[xt]=null,xt--)}function P(t,e){xt++,At[xt]=t.current,t.current=e}var W=M(null),ut=M(null),rt=M(null),yt=M(null);function Kt(t,e){switch(P(rt,e),P(ut,t),P(W,null),e.nodeType){case 9:case 11:t=(t=e.documentElement)&&(t=t.namespaceURI)?um(t):0;break;default:if(t=e.tagName,e=e.namespaceURI)e=um(e),t=sm(e,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}q(W),P(W,t)}function zt(){q(W),q(ut),q(rt)}function We(t){t.memoizedState!==null&&P(yt,t);var e=W.current,n=sm(e,t.type);e!==n&&(P(ut,t),P(W,n))}function Rn(t){ut.current===t&&(q(W),q(ut)),yt.current===t&&(q(yt),vi._currentValue=it)}var Ve,ji;function Ie(t){if(Ve===void 0)try{throw Error()}catch(n){var e=n.stack.trim().match(/\n( *(at )?)/);Ve=e&&e[1]||"",ji=-1)":-1s||E[l]!==w[s]){var B=` -`+E[l].replace(" at new "," at ");return t.displayName&&B.includes("")&&(B=B.replace("",t.displayName)),B}while(1<=l&&0<=s);break}}}finally{La=!1,Error.prepareStackTrace=n}return(n=t?t.displayName||t.name:"")?Ie(n):""}function Ns(t,e){switch(t.tag){case 26:case 27:case 5:return Ie(t.type);case 16:return Ie("Lazy");case 13:return t.child!==e&&e!==null?Ie("Suspense Fallback"):Ie("Suspense");case 19:return Ie("SuspenseList");case 0:case 15:return xl(t.type,!1);case 11:return xl(t.type.render,!1);case 1:return xl(t.type,!0);case 31:return Ie("Activity");default:return""}}function Hi(t){try{var e="",n=null;do e+=Ns(t,n),n=t,t=t.return;while(t);return e}catch(l){return` -Error generating stack: `+l.message+` -`+l.stack}}var Tn=Object.prototype.hasOwnProperty,An=a.unstable_scheduleCallback,Na=a.unstable_cancelCallback,qi=a.unstable_shouldYield,wt=a.unstable_requestPaint,Et=a.unstable_now,Se=a.unstable_getCurrentPriorityLevel,zl=a.unstable_ImmediatePriority,Ho=a.unstable_UserBlockingPriority,Yi=a.unstable_NormalPriority,Up=a.unstable_LowPriority,qo=a.unstable_IdlePriority,Bp=a.log,jp=a.unstable_setDisableYieldValue,wl=null,be=null;function Mn(t){if(typeof Bp=="function"&&jp(t),be&&typeof be.setStrictMode=="function")try{be.setStrictMode(wl,t)}catch{}}var _e=Math.clz32?Math.clz32:Yp,Hp=Math.log,qp=Math.LN2;function Yp(t){return t>>>=0,t===0?32:31-(Hp(t)/qp|0)|0}var Gi=256,Vi=262144,Xi=4194304;function ia(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function Qi(t,e,n){var l=t.pendingLanes;if(l===0)return 0;var s=0,r=t.suspendedLanes,f=t.pingedLanes;t=t.warmLanes;var g=l&134217727;return g!==0?(l=g&~r,l!==0?s=ia(l):(f&=g,f!==0?s=ia(f):n||(n=g&~t,n!==0&&(s=ia(n))))):(g=l&~r,g!==0?s=ia(g):f!==0?s=ia(f):n||(n=l&~t,n!==0&&(s=ia(n)))),s===0?0:e!==0&&e!==s&&(e&r)===0&&(r=s&-s,n=e&-e,r>=n||r===32&&(n&4194048)!==0)?e:s}function Cl(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function Gp(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Yo(){var t=Xi;return Xi<<=1,(Xi&62914560)===0&&(Xi=4194304),t}function Us(t){for(var e=[],n=0;31>n;n++)e.push(t);return e}function Dl(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Vp(t,e,n,l,s,r){var f=t.pendingLanes;t.pendingLanes=n,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=n,t.entangledLanes&=n,t.errorRecoveryDisabledLanes&=n,t.shellSuspendCounter=0;var g=t.entanglements,E=t.expirationTimes,w=t.hiddenUpdates;for(n=f&~n;0"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var kp=/[\n"\\]/g;function Ce(t){return t.replace(kp,function(e){return"\\"+e.charCodeAt(0).toString(16)+" "})}function Gs(t,e,n,l,s,r,f,g){t.name="",f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"?t.type=f:t.removeAttribute("type"),e!=null?f==="number"?(e===0&&t.value===""||t.value!=e)&&(t.value=""+we(e)):t.value!==""+we(e)&&(t.value=""+we(e)):f!=="submit"&&f!=="reset"||t.removeAttribute("value"),e!=null?Vs(t,f,we(e)):n!=null?Vs(t,f,we(n)):l!=null&&t.removeAttribute("value"),s==null&&r!=null&&(t.defaultChecked=!!r),s!=null&&(t.checked=s&&typeof s!="function"&&typeof s!="symbol"),g!=null&&typeof g!="function"&&typeof g!="symbol"&&typeof g!="boolean"?t.name=""+we(g):t.removeAttribute("name")}function Io(t,e,n,l,s,r,f,g){if(r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"&&(t.type=r),e!=null||n!=null){if(!(r!=="submit"&&r!=="reset"||e!=null)){Ys(t);return}n=n!=null?""+we(n):"",e=e!=null?""+we(e):n,g||e===t.value||(t.value=e),t.defaultValue=e}l=l??s,l=typeof l!="function"&&typeof l!="symbol"&&!!l,t.checked=g?t.checked:!!l,t.defaultChecked=!!l,f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"&&(t.name=f),Ys(t)}function Vs(t,e,n){e==="number"&&Ji(t.ownerDocument)===t||t.defaultValue===""+n||(t.defaultValue=""+n)}function Ya(t,e,n,l){if(t=t.options,e){e={};for(var s=0;s"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Js=!1;if(nn)try{var Bl={};Object.defineProperty(Bl,"passive",{get:function(){Js=!0}}),window.addEventListener("test",Bl,Bl),window.removeEventListener("test",Bl,Bl)}catch{Js=!1}var xn=null,ks=null,Fi=null;function sf(){if(Fi)return Fi;var t,e=ks,n=e.length,l,s="value"in xn?xn.value:xn.textContent,r=s.length;for(t=0;t=ql),hf=" ",mf=!1;function yf(t,e){switch(t){case"keyup":return Eg.indexOf(e.keyCode)!==-1;case"keydown":return e.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function pf(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Qa=!1;function Tg(t,e){switch(t){case"compositionend":return pf(e);case"keypress":return e.which!==32?null:(mf=!0,hf);case"textInput":return t=e.data,t===hf&&mf?null:t;default:return null}}function Ag(t,e){if(Qa)return t==="compositionend"||!Is&&yf(t,e)?(t=sf(),Fi=ks=xn=null,Qa=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(e.ctrlKey||e.altKey||e.metaKey)||e.ctrlKey&&e.altKey){if(e.char&&1=e)return{node:n,offset:e-t};t=l}t:{for(;n;){if(n.nextSibling){n=n.nextSibling;break t}n=n.parentNode}n=void 0}n=Tf(n)}}function Mf(t,e){return t&&e?t===e?!0:t&&t.nodeType===3?!1:e&&e.nodeType===3?Mf(t,e.parentNode):"contains"in t?t.contains(e):t.compareDocumentPosition?!!(t.compareDocumentPosition(e)&16):!1:!1}function Of(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var e=Ji(t.document);e instanceof t.HTMLIFrameElement;){try{var n=typeof e.contentWindow.location.href=="string"}catch{n=!1}if(n)t=e.contentWindow;else break;e=Ji(t.document)}return e}function nr(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&(e==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||e==="textarea"||t.contentEditable==="true")}var Lg=nn&&"documentMode"in document&&11>=document.documentMode,Za=null,ar=null,Xl=null,lr=!1;function xf(t,e,n){var l=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;lr||Za==null||Za!==Ji(l)||(l=Za,"selectionStart"in l&&nr(l)?l={start:l.selectionStart,end:l.selectionEnd}:(l=(l.ownerDocument&&l.ownerDocument.defaultView||window).getSelection(),l={anchorNode:l.anchorNode,anchorOffset:l.anchorOffset,focusNode:l.focusNode,focusOffset:l.focusOffset}),Xl&&Vl(Xl,l)||(Xl=l,l=Vu(ar,"onSelect"),0>=f,s-=f,Ke=1<<32-_e(e)+s|n<dt?(gt=tt,tt=null):gt=tt.sibling;var _t=C(O,tt,z[dt],j);if(_t===null){tt===null&&(tt=gt);break}t&&tt&&_t.alternate===null&&e(O,tt),R=r(_t,R,dt),bt===null?nt=_t:bt.sibling=_t,bt=_t,tt=gt}if(dt===z.length)return n(O,tt),vt&&ln(O,dt),nt;if(tt===null){for(;dtdt?(gt=tt,tt=null):gt=tt.sibling;var Pn=C(O,tt,_t.value,j);if(Pn===null){tt===null&&(tt=gt);break}t&&tt&&Pn.alternate===null&&e(O,tt),R=r(Pn,R,dt),bt===null?nt=Pn:bt.sibling=Pn,bt=Pn,tt=gt}if(_t.done)return n(O,tt),vt&&ln(O,dt),nt;if(tt===null){for(;!_t.done;dt++,_t=z.next())_t=H(O,_t.value,j),_t!==null&&(R=r(_t,R,dt),bt===null?nt=_t:bt.sibling=_t,bt=_t);return vt&&ln(O,dt),nt}for(tt=l(tt);!_t.done;dt++,_t=z.next())_t=N(tt,O,dt,_t.value,j),_t!==null&&(t&&_t.alternate!==null&&tt.delete(_t.key===null?dt:_t.key),R=r(_t,R,dt),bt===null?nt=_t:bt.sibling=_t,bt=_t);return t&&tt.forEach(function(Iv){return e(O,Iv)}),vt&&ln(O,dt),nt}function Lt(O,R,z,j){if(typeof z=="object"&&z!==null&&z.type===x&&z.key===null&&(z=z.props.children),typeof z=="object"&&z!==null){switch(z.$$typeof){case A:t:{for(var nt=z.key;R!==null;){if(R.key===nt){if(nt=z.type,nt===x){if(R.tag===7){n(O,R.sibling),j=s(R,z.props.children),j.return=O,O=j;break t}}else if(R.elementType===nt||typeof nt=="object"&&nt!==null&&nt.$$typeof===G&&pa(nt)===R.type){n(O,R.sibling),j=s(R,z.props),Fl(j,z),j.return=O,O=j;break t}n(O,R);break}else e(O,R);R=R.sibling}z.type===x?(j=fa(z.props.children,O.mode,j,z.key),j.return=O,O=j):(j=iu(z.type,z.key,z.props,null,O.mode,j),Fl(j,z),j.return=O,O=j)}return f(O);case L:t:{for(nt=z.key;R!==null;){if(R.key===nt)if(R.tag===4&&R.stateNode.containerInfo===z.containerInfo&&R.stateNode.implementation===z.implementation){n(O,R.sibling),j=s(R,z.children||[]),j.return=O,O=j;break t}else{n(O,R);break}else e(O,R);R=R.sibling}j=fr(z,O.mode,j),j.return=O,O=j}return f(O);case G:return z=pa(z),Lt(O,R,z,j)}if(Ut(z))return I(O,R,z,j);if(ct(z)){if(nt=ct(z),typeof nt!="function")throw Error(c(150));return z=nt.call(z),at(O,R,z,j)}if(typeof z.then=="function")return Lt(O,R,du(z),j);if(z.$$typeof===Q)return Lt(O,R,ru(O,z),j);hu(O,z)}return typeof z=="string"&&z!==""||typeof z=="number"||typeof z=="bigint"?(z=""+z,R!==null&&R.tag===6?(n(O,R.sibling),j=s(R,z),j.return=O,O=j):(n(O,R),j=or(z,O.mode,j),j.return=O,O=j),f(O)):n(O,R)}return function(O,R,z,j){try{kl=0;var nt=Lt(O,R,z,j);return nl=null,nt}catch(tt){if(tt===el||tt===ou)throw tt;var bt=Re(29,tt,null,O.mode);return bt.lanes=j,bt.return=O,bt}}}var va=$f(!0),Wf=$f(!1),Ln=!1;function Rr(t){t.updateQueue={baseState:t.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Tr(t,e){t=t.updateQueue,e.updateQueue===t&&(e.updateQueue={baseState:t.baseState,firstBaseUpdate:t.firstBaseUpdate,lastBaseUpdate:t.lastBaseUpdate,shared:t.shared,callbacks:null})}function Nn(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function Un(t,e,n){var l=t.updateQueue;if(l===null)return null;if(l=l.shared,(Rt&2)!==0){var s=l.pending;return s===null?e.next=e:(e.next=s.next,s.next=e),l.pending=e,e=lu(t),Uf(t,null,n),e}return au(t,l,e,n),lu(t)}function Pl(t,e,n){if(e=e.updateQueue,e!==null&&(e=e.shared,(n&4194048)!==0)){var l=e.lanes;l&=t.pendingLanes,n|=l,e.lanes=n,Vo(t,n)}}function Ar(t,e){var n=t.updateQueue,l=t.alternate;if(l!==null&&(l=l.updateQueue,n===l)){var s=null,r=null;if(n=n.firstBaseUpdate,n!==null){do{var f={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};r===null?s=r=f:r=r.next=f,n=n.next}while(n!==null);r===null?s=r=e:r=r.next=e}else s=r=e;n={baseState:l.baseState,firstBaseUpdate:s,lastBaseUpdate:r,shared:l.shared,callbacks:l.callbacks},t.updateQueue=n;return}t=n.lastBaseUpdate,t===null?n.firstBaseUpdate=e:t.next=e,n.lastBaseUpdate=e}var Mr=!1;function $l(){if(Mr){var t=tl;if(t!==null)throw t}}function Wl(t,e,n,l){Mr=!1;var s=t.updateQueue;Ln=!1;var r=s.firstBaseUpdate,f=s.lastBaseUpdate,g=s.shared.pending;if(g!==null){s.shared.pending=null;var E=g,w=E.next;E.next=null,f===null?r=w:f.next=w,f=E;var B=t.alternate;B!==null&&(B=B.updateQueue,g=B.lastBaseUpdate,g!==f&&(g===null?B.firstBaseUpdate=w:g.next=w,B.lastBaseUpdate=E))}if(r!==null){var H=s.baseState;f=0,B=w=E=null,g=r;do{var C=g.lane&-536870913,N=C!==g.lane;if(N?(pt&C)===C:(l&C)===C){C!==0&&C===Ia&&(Mr=!0),B!==null&&(B=B.next={lane:0,tag:g.tag,payload:g.payload,callback:null,next:null});t:{var I=t,at=g;C=e;var Lt=n;switch(at.tag){case 1:if(I=at.payload,typeof I=="function"){H=I.call(Lt,H,C);break t}H=I;break t;case 3:I.flags=I.flags&-65537|128;case 0:if(I=at.payload,C=typeof I=="function"?I.call(Lt,H,C):I,C==null)break t;H=S({},H,C);break t;case 2:Ln=!0}}C=g.callback,C!==null&&(t.flags|=64,N&&(t.flags|=8192),N=s.callbacks,N===null?s.callbacks=[C]:N.push(C))}else N={lane:C,tag:g.tag,payload:g.payload,callback:g.callback,next:null},B===null?(w=B=N,E=H):B=B.next=N,f|=C;if(g=g.next,g===null){if(g=s.shared.pending,g===null)break;N=g,g=N.next,N.next=null,s.lastBaseUpdate=N,s.shared.pending=null}}while(!0);B===null&&(E=H),s.baseState=E,s.firstBaseUpdate=w,s.lastBaseUpdate=B,r===null&&(s.shared.lanes=0),Yn|=f,t.lanes=f,t.memoizedState=H}}function If(t,e){if(typeof t!="function")throw Error(c(191,t));t.call(e)}function td(t,e){var n=t.callbacks;if(n!==null)for(t.callbacks=null,t=0;tr?r:8;var f=U.T,g={};U.T=g,Zr(t,!1,e,n);try{var E=s(),w=U.S;if(w!==null&&w(g,E),E!==null&&typeof E=="object"&&typeof E.then=="function"){var B=Vg(E,l);ei(t,e,B,xe(t))}else ei(t,e,l,xe(t))}catch(H){ei(t,e,{then:function(){},status:"rejected",reason:H},xe())}finally{J.p=r,f!==null&&g.types!==null&&(f.types=g.types),U.T=f}}function kg(){}function Xr(t,e,n,l){if(t.tag!==5)throw Error(c(476));var s=Dd(t).queue;Cd(t,s,e,it,n===null?kg:function(){return Ld(t),n(l)})}function Dd(t){var e=t.memoizedState;if(e!==null)return e;e={memoizedState:it,baseState:it,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cn,lastRenderedState:it},next:null};var n={};return e.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cn,lastRenderedState:n},next:null},t.memoizedState=e,t=t.alternate,t!==null&&(t.memoizedState=e),e}function Ld(t){var e=Dd(t);e.next===null&&(e=t.alternate.memoizedState),ei(t,e.next.queue,{},xe())}function Qr(){return le(vi)}function Nd(){return Qt().memoizedState}function Ud(){return Qt().memoizedState}function Fg(t){for(var e=t.return;e!==null;){switch(e.tag){case 24:case 3:var n=xe();t=Nn(n);var l=Un(e,t,n);l!==null&&(ge(l,e,n),Pl(l,e,n)),e={cache:Sr()},t.payload=e;return}e=e.return}}function Pg(t,e,n){var l=xe();n={lane:l,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Ru(t)?jd(e,n):(n=rr(t,e,n,l),n!==null&&(ge(n,t,l),Hd(n,e,l)))}function Bd(t,e,n){var l=xe();ei(t,e,n,l)}function ei(t,e,n,l){var s={lane:l,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ru(t))jd(e,s);else{var r=t.alternate;if(t.lanes===0&&(r===null||r.lanes===0)&&(r=e.lastRenderedReducer,r!==null))try{var f=e.lastRenderedState,g=r(f,n);if(s.hasEagerState=!0,s.eagerState=g,Ee(g,f))return au(t,e,s,0),Nt===null&&nu(),!1}catch{}if(n=rr(t,e,s,l),n!==null)return ge(n,t,l),Hd(n,e,l),!0}return!1}function Zr(t,e,n,l){if(l={lane:2,revertLane:Rc(),gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},Ru(t)){if(e)throw Error(c(479))}else e=rr(t,n,l,2),e!==null&&ge(e,t,2)}function Ru(t){var e=t.alternate;return t===ft||e!==null&&e===ft}function jd(t,e){ll=pu=!0;var n=t.pending;n===null?e.next=e:(e.next=n.next,n.next=e),t.pending=e}function Hd(t,e,n){if((n&4194048)!==0){var l=e.lanes;l&=t.pendingLanes,n|=l,e.lanes=n,Vo(t,n)}}var ni={readContext:le,use:Su,useCallback:Gt,useContext:Gt,useEffect:Gt,useImperativeHandle:Gt,useLayoutEffect:Gt,useInsertionEffect:Gt,useMemo:Gt,useReducer:Gt,useRef:Gt,useState:Gt,useDebugValue:Gt,useDeferredValue:Gt,useTransition:Gt,useSyncExternalStore:Gt,useId:Gt,useHostTransitionStatus:Gt,useFormState:Gt,useActionState:Gt,useOptimistic:Gt,useMemoCache:Gt,useCacheRefresh:Gt};ni.useEffectEvent=Gt;var qd={readContext:le,use:Su,useCallback:function(t,e){return ce().memoizedState=[t,e===void 0?null:e],t},useContext:le,useEffect:Ed,useImperativeHandle:function(t,e,n){n=n!=null?n.concat([t]):null,_u(4194308,4,Md.bind(null,e,t),n)},useLayoutEffect:function(t,e){return _u(4194308,4,t,e)},useInsertionEffect:function(t,e){_u(4,2,t,e)},useMemo:function(t,e){var n=ce();e=e===void 0?null:e;var l=t();if(Sa){Mn(!0);try{t()}finally{Mn(!1)}}return n.memoizedState=[l,e],l},useReducer:function(t,e,n){var l=ce();if(n!==void 0){var s=n(e);if(Sa){Mn(!0);try{n(e)}finally{Mn(!1)}}}else s=e;return l.memoizedState=l.baseState=s,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:s},l.queue=t,t=t.dispatch=Pg.bind(null,ft,t),[l.memoizedState,t]},useRef:function(t){var e=ce();return t={current:t},e.memoizedState=t},useState:function(t){t=Hr(t);var e=t.queue,n=Bd.bind(null,ft,e);return e.dispatch=n,[t.memoizedState,n]},useDebugValue:Gr,useDeferredValue:function(t,e){var n=ce();return Vr(n,t,e)},useTransition:function(){var t=Hr(!1);return t=Cd.bind(null,ft,t.queue,!0,!1),ce().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,e,n){var l=ft,s=ce();if(vt){if(n===void 0)throw Error(c(407));n=n()}else{if(n=e(),Nt===null)throw Error(c(349));(pt&127)!==0||ud(l,e,n)}s.memoizedState=n;var r={value:n,getSnapshot:e};return s.queue=r,Ed(rd.bind(null,l,r,t),[t]),l.flags|=2048,ul(9,{destroy:void 0},sd.bind(null,l,r,n,e),null),n},useId:function(){var t=ce(),e=Nt.identifierPrefix;if(vt){var n=Je,l=Ke;n=(l&~(1<<32-_e(l)-1)).toString(32)+n,e="_"+e+"R_"+n,n=gu++,0<\/script>",r=r.removeChild(r.firstChild);break;case"select":r=typeof l.is=="string"?f.createElement("select",{is:l.is}):f.createElement("select"),l.multiple?r.multiple=!0:l.size&&(r.size=l.size);break;default:r=typeof l.is=="string"?f.createElement(s,{is:l.is}):f.createElement(s)}}r[ne]=e,r[fe]=l;t:for(f=e.child;f!==null;){if(f.tag===5||f.tag===6)r.appendChild(f.stateNode);else if(f.tag!==4&&f.tag!==27&&f.child!==null){f.child.return=f,f=f.child;continue}if(f===e)break t;for(;f.sibling===null;){if(f.return===null||f.return===e)break t;f=f.return}f.sibling.return=f.return,f=f.sibling}e.stateNode=r;t:switch(ue(r,s,l),s){case"button":case"input":case"select":case"textarea":l=!!l.autoFocus;break t;case"img":l=!0;break t;default:l=!1}l&&fn(e)}}return Ht(e),ic(e,e.type,t===null?null:t.memoizedProps,e.pendingProps,n),null;case 6:if(t&&e.stateNode!=null)t.memoizedProps!==l&&fn(e);else{if(typeof l!="string"&&e.stateNode===null)throw Error(c(166));if(t=rt.current,$a(e)){if(t=e.stateNode,n=e.memoizedProps,l=null,s=ae,s!==null)switch(s.tag){case 27:case 5:l=s.memoizedProps}t[ne]=e,t=!!(t.nodeValue===n||l!==null&&l.suppressHydrationWarning===!0||lm(t.nodeValue,n)),t||Cn(e,!0)}else t=Xu(t).createTextNode(l),t[ne]=e,e.stateNode=t}return Ht(e),null;case 31:if(n=e.memoizedState,t===null||t.memoizedState!==null){if(l=$a(e),n!==null){if(t===null){if(!l)throw Error(c(318));if(t=e.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(c(557));t[ne]=e}else da(),(e.flags&128)===0&&(e.memoizedState=null),e.flags|=4;Ht(e),t=!1}else n=yr(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=n),t=!0;if(!t)return e.flags&256?(Ae(e),e):(Ae(e),null);if((e.flags&128)!==0)throw Error(c(558))}return Ht(e),null;case 13:if(l=e.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(s=$a(e),l!==null&&l.dehydrated!==null){if(t===null){if(!s)throw Error(c(318));if(s=e.memoizedState,s=s!==null?s.dehydrated:null,!s)throw Error(c(317));s[ne]=e}else da(),(e.flags&128)===0&&(e.memoizedState=null),e.flags|=4;Ht(e),s=!1}else s=yr(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=s),s=!0;if(!s)return e.flags&256?(Ae(e),e):(Ae(e),null)}return Ae(e),(e.flags&128)!==0?(e.lanes=n,e):(n=l!==null,t=t!==null&&t.memoizedState!==null,n&&(l=e.child,s=null,l.alternate!==null&&l.alternate.memoizedState!==null&&l.alternate.memoizedState.cachePool!==null&&(s=l.alternate.memoizedState.cachePool.pool),r=null,l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(r=l.memoizedState.cachePool.pool),r!==s&&(l.flags|=2048)),n!==t&&n&&(e.child.flags|=8192),xu(e,e.updateQueue),Ht(e),null);case 4:return zt(),t===null&&Oc(e.stateNode.containerInfo),Ht(e),null;case 10:return sn(e.type),Ht(e),null;case 19:if(q(Xt),l=e.memoizedState,l===null)return Ht(e),null;if(s=(e.flags&128)!==0,r=l.rendering,r===null)if(s)li(l,!1);else{if(Vt!==0||t!==null&&(t.flags&128)!==0)for(t=e.child;t!==null;){if(r=yu(t),r!==null){for(e.flags|=128,li(l,!1),t=r.updateQueue,e.updateQueue=t,xu(e,t),e.subtreeFlags=0,t=n,n=e.child;n!==null;)Bf(n,t),n=n.sibling;return P(Xt,Xt.current&1|2),vt&&ln(e,l.treeForkCount),e.child}t=t.sibling}l.tail!==null&&Et()>Lu&&(e.flags|=128,s=!0,li(l,!1),e.lanes=4194304)}else{if(!s)if(t=yu(r),t!==null){if(e.flags|=128,s=!0,t=t.updateQueue,e.updateQueue=t,xu(e,t),li(l,!0),l.tail===null&&l.tailMode==="hidden"&&!r.alternate&&!vt)return Ht(e),null}else 2*Et()-l.renderingStartTime>Lu&&n!==536870912&&(e.flags|=128,s=!0,li(l,!1),e.lanes=4194304);l.isBackwards?(r.sibling=e.child,e.child=r):(t=l.last,t!==null?t.sibling=r:e.child=r,l.last=r)}return l.tail!==null?(t=l.tail,l.rendering=t,l.tail=t.sibling,l.renderingStartTime=Et(),t.sibling=null,n=Xt.current,P(Xt,s?n&1|2:n&1),vt&&ln(e,l.treeForkCount),t):(Ht(e),null);case 22:case 23:return Ae(e),xr(),l=e.memoizedState!==null,t!==null?t.memoizedState!==null!==l&&(e.flags|=8192):l&&(e.flags|=8192),l?(n&536870912)!==0&&(e.flags&128)===0&&(Ht(e),e.subtreeFlags&6&&(e.flags|=8192)):Ht(e),n=e.updateQueue,n!==null&&xu(e,n.retryQueue),n=null,t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),l=null,e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),l!==n&&(e.flags|=2048),t!==null&&q(ya),null;case 24:return n=null,t!==null&&(n=t.memoizedState.cache),e.memoizedState.cache!==n&&(e.flags|=2048),sn(Jt),Ht(e),null;case 25:return null;case 30:return null}throw Error(c(156,e.tag))}function ev(t,e){switch(hr(e),e.tag){case 1:return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 3:return sn(Jt),zt(),t=e.flags,(t&65536)!==0&&(t&128)===0?(e.flags=t&-65537|128,e):null;case 26:case 27:case 5:return Rn(e),null;case 31:if(e.memoizedState!==null){if(Ae(e),e.alternate===null)throw Error(c(340));da()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 13:if(Ae(e),t=e.memoizedState,t!==null&&t.dehydrated!==null){if(e.alternate===null)throw Error(c(340));da()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 19:return q(Xt),null;case 4:return zt(),null;case 10:return sn(e.type),null;case 22:case 23:return Ae(e),xr(),t!==null&&q(ya),t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 24:return sn(Jt),null;case 25:return null;default:return null}}function ch(t,e){switch(hr(e),e.tag){case 3:sn(Jt),zt();break;case 26:case 27:case 5:Rn(e);break;case 4:zt();break;case 31:e.memoizedState!==null&&Ae(e);break;case 13:Ae(e);break;case 19:q(Xt);break;case 10:sn(e.type);break;case 22:case 23:Ae(e),xr(),t!==null&&q(ya);break;case 24:sn(Jt)}}function ii(t,e){try{var n=e.updateQueue,l=n!==null?n.lastEffect:null;if(l!==null){var s=l.next;n=s;do{if((n.tag&t)===t){l=void 0;var r=n.create,f=n.inst;l=r(),f.destroy=l}n=n.next}while(n!==s)}}catch(g){Ot(e,e.return,g)}}function Hn(t,e,n){try{var l=e.updateQueue,s=l!==null?l.lastEffect:null;if(s!==null){var r=s.next;l=r;do{if((l.tag&t)===t){var f=l.inst,g=f.destroy;if(g!==void 0){f.destroy=void 0,s=e;var E=n,w=g;try{w()}catch(B){Ot(s,E,B)}}}l=l.next}while(l!==r)}}catch(B){Ot(e,e.return,B)}}function oh(t){var e=t.updateQueue;if(e!==null){var n=t.stateNode;try{td(e,n)}catch(l){Ot(t,t.return,l)}}}function fh(t,e,n){n.props=ba(t.type,t.memoizedProps),n.state=t.memoizedState;try{n.componentWillUnmount()}catch(l){Ot(t,e,l)}}function ui(t,e){try{var n=t.ref;if(n!==null){switch(t.tag){case 26:case 27:case 5:var l=t.stateNode;break;case 30:l=t.stateNode;break;default:l=t.stateNode}typeof n=="function"?t.refCleanup=n(l):n.current=l}}catch(s){Ot(t,e,s)}}function ke(t,e){var n=t.ref,l=t.refCleanup;if(n!==null)if(typeof l=="function")try{l()}catch(s){Ot(t,e,s)}finally{t.refCleanup=null,t=t.alternate,t!=null&&(t.refCleanup=null)}else if(typeof n=="function")try{n(null)}catch(s){Ot(t,e,s)}else n.current=null}function dh(t){var e=t.type,n=t.memoizedProps,l=t.stateNode;try{t:switch(e){case"button":case"input":case"select":case"textarea":n.autoFocus&&l.focus();break t;case"img":n.src?l.src=n.src:n.srcSet&&(l.srcset=n.srcSet)}}catch(s){Ot(t,t.return,s)}}function uc(t,e,n){try{var l=t.stateNode;Rv(l,t.type,n,e),l[fe]=e}catch(s){Ot(t,t.return,s)}}function hh(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&Zn(t.type)||t.tag===4}function sc(t){t:for(;;){for(;t.sibling===null;){if(t.return===null||hh(t.return))return null;t=t.return}for(t.sibling.return=t.return,t=t.sibling;t.tag!==5&&t.tag!==6&&t.tag!==18;){if(t.tag===27&&Zn(t.type)||t.flags&2||t.child===null||t.tag===4)continue t;t.child.return=t,t=t.child}if(!(t.flags&2))return t.stateNode}}function rc(t,e,n){var l=t.tag;if(l===5||l===6)t=t.stateNode,e?(n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n).insertBefore(t,e):(e=n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n,e.appendChild(t),n=n._reactRootContainer,n!=null||e.onclick!==null||(e.onclick=en));else if(l!==4&&(l===27&&Zn(t.type)&&(n=t.stateNode,e=null),t=t.child,t!==null))for(rc(t,e,n),t=t.sibling;t!==null;)rc(t,e,n),t=t.sibling}function zu(t,e,n){var l=t.tag;if(l===5||l===6)t=t.stateNode,e?n.insertBefore(t,e):n.appendChild(t);else if(l!==4&&(l===27&&Zn(t.type)&&(n=t.stateNode),t=t.child,t!==null))for(zu(t,e,n),t=t.sibling;t!==null;)zu(t,e,n),t=t.sibling}function mh(t){var e=t.stateNode,n=t.memoizedProps;try{for(var l=t.type,s=e.attributes;s.length;)e.removeAttributeNode(s[0]);ue(e,l,n),e[ne]=t,e[fe]=n}catch(r){Ot(t,t.return,r)}}var dn=!1,Pt=!1,cc=!1,yh=typeof WeakSet=="function"?WeakSet:Set,It=null;function nv(t,e){if(t=t.containerInfo,wc=Pu,t=Of(t),nr(t)){if("selectionStart"in t)var n={start:t.selectionStart,end:t.selectionEnd};else t:{n=(n=t.ownerDocument)&&n.defaultView||window;var l=n.getSelection&&n.getSelection();if(l&&l.rangeCount!==0){n=l.anchorNode;var s=l.anchorOffset,r=l.focusNode;l=l.focusOffset;try{n.nodeType,r.nodeType}catch{n=null;break t}var f=0,g=-1,E=-1,w=0,B=0,H=t,C=null;e:for(;;){for(var N;H!==n||s!==0&&H.nodeType!==3||(g=f+s),H!==r||l!==0&&H.nodeType!==3||(E=f+l),H.nodeType===3&&(f+=H.nodeValue.length),(N=H.firstChild)!==null;)C=H,H=N;for(;;){if(H===t)break e;if(C===n&&++w===s&&(g=f),C===r&&++B===l&&(E=f),(N=H.nextSibling)!==null)break;H=C,C=H.parentNode}H=N}n=g===-1||E===-1?null:{start:g,end:E}}else n=null}n=n||{start:0,end:0}}else n=null;for(Cc={focusedElem:t,selectionRange:n},Pu=!1,It=e;It!==null;)if(e=It,t=e.child,(e.subtreeFlags&1028)!==0&&t!==null)t.return=e,It=t;else for(;It!==null;){switch(e=It,r=e.alternate,t=e.flags,e.tag){case 0:if((t&4)!==0&&(t=e.updateQueue,t=t!==null?t.events:null,t!==null))for(n=0;n title"))),ue(r,l,n),r[ne]=t,Wt(r),l=r;break t;case"link":var f=_m("link","href",s).get(l+(n.href||""));if(f){for(var g=0;gLt&&(f=Lt,Lt=at,at=f);var O=Af(g,at),R=Af(g,Lt);if(O&&R&&(N.rangeCount!==1||N.anchorNode!==O.node||N.anchorOffset!==O.offset||N.focusNode!==R.node||N.focusOffset!==R.offset)){var z=H.createRange();z.setStart(O.node,O.offset),N.removeAllRanges(),at>Lt?(N.addRange(z),N.extend(R.node,R.offset)):(z.setEnd(R.node,R.offset),N.addRange(z))}}}}for(H=[],N=g;N=N.parentNode;)N.nodeType===1&&H.push({element:N,left:N.scrollLeft,top:N.scrollTop});for(typeof g.focus=="function"&&g.focus(),g=0;gn?32:n,U.T=null,n=pc,pc=null;var r=Vn,f=gn;if($t=0,fl=Vn=null,gn=0,(Rt&6)!==0)throw Error(c(331));var g=Rt;if(Rt|=4,Mh(r.current),Rh(r,r.current,f,n),Rt=g,di(0,!1),be&&typeof be.onPostCommitFiberRoot=="function")try{be.onPostCommitFiberRoot(wl,r)}catch{}return!0}finally{J.p=s,U.T=l,Qh(t,e)}}function Kh(t,e,n){e=Le(n,e),e=Fr(t.stateNode,e,2),t=Un(t,e,2),t!==null&&(Dl(t,2),Fe(t))}function Ot(t,e,n){if(t.tag===3)Kh(t,t,n);else for(;e!==null;){if(e.tag===3){Kh(e,t,n);break}else if(e.tag===1){var l=e.stateNode;if(typeof e.type.getDerivedStateFromError=="function"||typeof l.componentDidCatch=="function"&&(Gn===null||!Gn.has(l))){t=Le(n,t),n=Jd(2),l=Un(e,n,2),l!==null&&(kd(n,l,e,t),Dl(l,2),Fe(l));break}}e=e.return}}function bc(t,e,n){var l=t.pingCache;if(l===null){l=t.pingCache=new iv;var s=new Set;l.set(e,s)}else s=l.get(e),s===void 0&&(s=new Set,l.set(e,s));s.has(n)||(dc=!0,s.add(n),t=ov.bind(null,t,e,n),e.then(t,t))}function ov(t,e,n){var l=t.pingCache;l!==null&&l.delete(e),t.pingedLanes|=t.suspendedLanes&n,t.warmLanes&=~n,Nt===t&&(pt&n)===n&&(Vt===4||Vt===3&&(pt&62914560)===pt&&300>Et()-Du?(Rt&2)===0&&dl(t,0):hc|=n,ol===pt&&(ol=0)),Fe(t)}function Jh(t,e){e===0&&(e=Yo()),t=oa(t,e),t!==null&&(Dl(t,e),Fe(t))}function fv(t){var e=t.memoizedState,n=0;e!==null&&(n=e.retryLane),Jh(t,n)}function dv(t,e){var n=0;switch(t.tag){case 31:case 13:var l=t.stateNode,s=t.memoizedState;s!==null&&(n=s.retryLane);break;case 19:l=t.stateNode;break;case 22:l=t.stateNode._retryCache;break;default:throw Error(c(314))}l!==null&&l.delete(e),Jh(t,n)}function hv(t,e){return An(t,e)}var qu=null,ml=null,_c=!1,Yu=!1,Ec=!1,Qn=0;function Fe(t){t!==ml&&t.next===null&&(ml===null?qu=ml=t:ml=ml.next=t),Yu=!0,_c||(_c=!0,yv())}function di(t,e){if(!Ec&&Yu){Ec=!0;do for(var n=!1,l=qu;l!==null;){if(t!==0){var s=l.pendingLanes;if(s===0)var r=0;else{var f=l.suspendedLanes,g=l.pingedLanes;r=(1<<31-_e(42|t)+1)-1,r&=s&~(f&~g),r=r&201326741?r&201326741|1:r?r|2:0}r!==0&&(n=!0,$h(l,r))}else r=pt,r=Qi(l,l===Nt?r:0,l.cancelPendingCommit!==null||l.timeoutHandle!==-1),(r&3)===0||Cl(l,r)||(n=!0,$h(l,r));l=l.next}while(n);Ec=!1}}function mv(){kh()}function kh(){Yu=_c=!1;var t=0;Qn!==0&&Av()&&(t=Qn);for(var e=Et(),n=null,l=qu;l!==null;){var s=l.next,r=Fh(l,e);r===0?(l.next=null,n===null?qu=s:n.next=s,s===null&&(ml=n)):(n=l,(t!==0||(r&3)!==0)&&(Yu=!0)),l=s}$t!==0&&$t!==5||di(t),Qn!==0&&(Qn=0)}function Fh(t,e){for(var n=t.suspendedLanes,l=t.pingedLanes,s=t.expirationTimes,r=t.pendingLanes&-62914561;0g)break;var B=E.transferSize,H=E.initiatorType;B&&im(H)&&(E=E.responseEnd,f+=B*(E"u"?null:document;function gm(t,e,n){var l=yl;if(l&&typeof e=="string"&&e){var s=Ce(e);s='link[rel="'+t+'"][href="'+s+'"]',typeof n=="string"&&(s+='[crossorigin="'+n+'"]'),pm.has(s)||(pm.add(s),t={rel:t,crossOrigin:n,href:e},l.querySelector(s)===null&&(e=l.createElement("link"),ue(e,"link",t),Wt(e),l.head.appendChild(e)))}}function Nv(t){vn.D(t),gm("dns-prefetch",t,null)}function Uv(t,e){vn.C(t,e),gm("preconnect",t,e)}function Bv(t,e,n){vn.L(t,e,n);var l=yl;if(l&&t&&e){var s='link[rel="preload"][as="'+Ce(e)+'"]';e==="image"&&n&&n.imageSrcSet?(s+='[imagesrcset="'+Ce(n.imageSrcSet)+'"]',typeof n.imageSizes=="string"&&(s+='[imagesizes="'+Ce(n.imageSizes)+'"]')):s+='[href="'+Ce(t)+'"]';var r=s;switch(e){case"style":r=pl(t);break;case"script":r=gl(t)}qe.has(r)||(t=S({rel:"preload",href:e==="image"&&n&&n.imageSrcSet?void 0:t,as:e},n),qe.set(r,t),l.querySelector(s)!==null||e==="style"&&l.querySelector(pi(r))||e==="script"&&l.querySelector(gi(r))||(e=l.createElement("link"),ue(e,"link",t),Wt(e),l.head.appendChild(e)))}}function jv(t,e){vn.m(t,e);var n=yl;if(n&&t){var l=e&&typeof e.as=="string"?e.as:"script",s='link[rel="modulepreload"][as="'+Ce(l)+'"][href="'+Ce(t)+'"]',r=s;switch(l){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":r=gl(t)}if(!qe.has(r)&&(t=S({rel:"modulepreload",href:t},e),qe.set(r,t),n.querySelector(s)===null)){switch(l){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(n.querySelector(gi(r)))return}l=n.createElement("link"),ue(l,"link",t),Wt(l),n.head.appendChild(l)}}}function Hv(t,e,n){vn.S(t,e,n);var l=yl;if(l&&t){var s=Ha(l).hoistableStyles,r=pl(t);e=e||"default";var f=s.get(r);if(!f){var g={loading:0,preload:null};if(f=l.querySelector(pi(r)))g.loading=5;else{t=S({rel:"stylesheet",href:t,"data-precedence":e},n),(n=qe.get(r))&&Hc(t,n);var E=f=l.createElement("link");Wt(E),ue(E,"link",t),E._p=new Promise(function(w,B){E.onload=w,E.onerror=B}),E.addEventListener("load",function(){g.loading|=1}),E.addEventListener("error",function(){g.loading|=2}),g.loading|=4,Zu(f,e,l)}f={type:"stylesheet",instance:f,count:1,state:g},s.set(r,f)}}}function qv(t,e){vn.X(t,e);var n=yl;if(n&&t){var l=Ha(n).hoistableScripts,s=gl(t),r=l.get(s);r||(r=n.querySelector(gi(s)),r||(t=S({src:t,async:!0},e),(e=qe.get(s))&&qc(t,e),r=n.createElement("script"),Wt(r),ue(r,"link",t),n.head.appendChild(r)),r={type:"script",instance:r,count:1,state:null},l.set(s,r))}}function Yv(t,e){vn.M(t,e);var n=yl;if(n&&t){var l=Ha(n).hoistableScripts,s=gl(t),r=l.get(s);r||(r=n.querySelector(gi(s)),r||(t=S({src:t,async:!0,type:"module"},e),(e=qe.get(s))&&qc(t,e),r=n.createElement("script"),Wt(r),ue(r,"link",t),n.head.appendChild(r)),r={type:"script",instance:r,count:1,state:null},l.set(s,r))}}function vm(t,e,n,l){var s=(s=rt.current)?Qu(s):null;if(!s)throw Error(c(446));switch(t){case"meta":case"title":return null;case"style":return typeof n.precedence=="string"&&typeof n.href=="string"?(e=pl(n.href),n=Ha(s).hoistableStyles,l=n.get(e),l||(l={type:"style",instance:null,count:0,state:null},n.set(e,l)),l):{type:"void",instance:null,count:0,state:null};case"link":if(n.rel==="stylesheet"&&typeof n.href=="string"&&typeof n.precedence=="string"){t=pl(n.href);var r=Ha(s).hoistableStyles,f=r.get(t);if(f||(s=s.ownerDocument||s,f={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},r.set(t,f),(r=s.querySelector(pi(t)))&&!r._p&&(f.instance=r,f.state.loading=5),qe.has(t)||(n={rel:"preload",as:"style",href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},qe.set(t,n),r||Gv(s,t,n,f.state))),e&&l===null)throw Error(c(528,""));return f}if(e&&l!==null)throw Error(c(529,""));return null;case"script":return e=n.async,n=n.src,typeof n=="string"&&e&&typeof e!="function"&&typeof e!="symbol"?(e=gl(n),n=Ha(s).hoistableScripts,l=n.get(e),l||(l={type:"script",instance:null,count:0,state:null},n.set(e,l)),l):{type:"void",instance:null,count:0,state:null};default:throw Error(c(444,t))}}function pl(t){return'href="'+Ce(t)+'"'}function pi(t){return'link[rel="stylesheet"]['+t+"]"}function Sm(t){return S({},t,{"data-precedence":t.precedence,precedence:null})}function Gv(t,e,n,l){t.querySelector('link[rel="preload"][as="style"]['+e+"]")?l.loading=1:(e=t.createElement("link"),l.preload=e,e.addEventListener("load",function(){return l.loading|=1}),e.addEventListener("error",function(){return l.loading|=2}),ue(e,"link",n),Wt(e),t.head.appendChild(e))}function gl(t){return'[src="'+Ce(t)+'"]'}function gi(t){return"script[async]"+t}function bm(t,e,n){if(e.count++,e.instance===null)switch(e.type){case"style":var l=t.querySelector('style[data-href~="'+Ce(n.href)+'"]');if(l)return e.instance=l,Wt(l),l;var s=S({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return l=(t.ownerDocument||t).createElement("style"),Wt(l),ue(l,"style",s),Zu(l,n.precedence,t),e.instance=l;case"stylesheet":s=pl(n.href);var r=t.querySelector(pi(s));if(r)return e.state.loading|=4,e.instance=r,Wt(r),r;l=Sm(n),(s=qe.get(s))&&Hc(l,s),r=(t.ownerDocument||t).createElement("link"),Wt(r);var f=r;return f._p=new Promise(function(g,E){f.onload=g,f.onerror=E}),ue(r,"link",l),e.state.loading|=4,Zu(r,n.precedence,t),e.instance=r;case"script":return r=gl(n.src),(s=t.querySelector(gi(r)))?(e.instance=s,Wt(s),s):(l=n,(s=qe.get(r))&&(l=S({},n),qc(l,s)),t=t.ownerDocument||t,s=t.createElement("script"),Wt(s),ue(s,"link",l),t.head.appendChild(s),e.instance=s);case"void":return null;default:throw Error(c(443,e.type))}else e.type==="stylesheet"&&(e.state.loading&4)===0&&(l=e.instance,e.state.loading|=4,Zu(l,n.precedence,t));return e.instance}function Zu(t,e,n){for(var l=n.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),s=l.length?l[l.length-1]:null,r=s,f=0;f title"):null)}function Vv(t,e,n){if(n===1||e.itemProp!=null)return!1;switch(t){case"meta":case"title":return!0;case"style":if(typeof e.precedence!="string"||typeof e.href!="string"||e.href==="")break;return!0;case"link":if(typeof e.rel!="string"||typeof e.href!="string"||e.href===""||e.onLoad||e.onError)break;return e.rel==="stylesheet"?(t=e.disabled,typeof e.precedence=="string"&&t==null):!0;case"script":if(e.async&&typeof e.async!="function"&&typeof e.async!="symbol"&&!e.onLoad&&!e.onError&&e.src&&typeof e.src=="string")return!0}return!1}function Rm(t){return!(t.type==="stylesheet"&&(t.state.loading&3)===0)}function Xv(t,e,n,l){if(n.type==="stylesheet"&&(typeof l.media!="string"||matchMedia(l.media).matches!==!1)&&(n.state.loading&4)===0){if(n.instance===null){var s=pl(l.href),r=e.querySelector(pi(s));if(r){e=r._p,e!==null&&typeof e=="object"&&typeof e.then=="function"&&(t.count++,t=Ju.bind(t),e.then(t,t)),n.state.loading|=4,n.instance=r,Wt(r);return}r=e.ownerDocument||e,l=Sm(l),(s=qe.get(s))&&Hc(l,s),r=r.createElement("link"),Wt(r);var f=r;f._p=new Promise(function(g,E){f.onload=g,f.onerror=E}),ue(r,"link",l),n.instance=r}t.stylesheets===null&&(t.stylesheets=new Map),t.stylesheets.set(n,e),(e=n.state.preload)&&(n.state.loading&3)===0&&(t.count++,n=Ju.bind(t),e.addEventListener("load",n),e.addEventListener("error",n))}}var Yc=0;function Qv(t,e){return t.stylesheets&&t.count===0&&Fu(t,t.stylesheets),0Yc?50:800)+e);return t.unsuspend=n,function(){t.unsuspend=null,clearTimeout(l),clearTimeout(s)}}:null}function Ju(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Fu(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var ku=null;function Fu(t,e){t.stylesheets=null,t.unsuspend!==null&&(t.count++,ku=new Map,e.forEach(Zv,t),ku=null,Ju.call(t))}function Zv(t,e){if(!(e.state.loading&4)){var n=ku.get(t);if(n)var l=n.get(null);else{n=new Map,ku.set(t,n);for(var s=t.querySelectorAll("link[data-precedence],style[data-precedence]"),r=0;r"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(a)}catch(u){console.error(u)}}return a(),Fc.exports=s0(),Fc.exports}var c0=r0();const go=new WeakMap,o0=new WeakMap,fs={current:[]};let Ic=!1,zi=0;const xi=new Set,as=new Map;function zy(a){for(const u of a){if(fs.current.includes(u))continue;fs.current.push(u),u.recompute();const i=o0.get(u);if(i)for(const c of i){const o=go.get(c);o?.length&&zy(o)}}}function f0(a){const u={prevVal:a.prevState,currentVal:a.state};for(const i of a.listeners)i(u)}function d0(a){const u={prevVal:a.prevState,currentVal:a.state};for(const i of a.listeners)i(u)}function wy(a){if(zi>0&&!as.has(a)&&as.set(a,a.prevState),xi.add(a),!(zi>0)&&!Ic)try{for(Ic=!0;xi.size>0;){const u=Array.from(xi);xi.clear();for(const i of u){const c=as.get(i)??i.prevState;i.prevState=c,f0(i)}for(const i of u){const c=go.get(i);c&&(fs.current.push(i),zy(c))}for(const i of u){const c=go.get(i);if(c)for(const o of c)d0(o)}}}finally{Ic=!1,fs.current=[],as.clear()}}function Rl(a){zi++;try{a()}finally{if(zi--,zi===0){const u=xi.values().next().value;u&&wy(u)}}}function h0(a){return typeof a=="function"}class m0{constructor(u,i){this.listeners=new Set,this.subscribe=c=>{var o,d;this.listeners.add(c);const p=(d=(o=this.options)==null?void 0:o.onSubscribe)==null?void 0:d.call(o,c,this);return()=>{this.listeners.delete(c),p?.()}},this.prevState=u,this.state=u,this.options=i}setState(u){var i,c,o;this.prevState=this.state,(i=this.options)!=null&&i.updateFn?this.state=this.options.updateFn(this.prevState)(u):h0(u)?this.state=u(this.prevState):this.state=u,(o=(c=this.options)==null?void 0:c.onUpdate)==null||o.call(c),wy(this)}}const ta="__TSR_index",Jm="popstate",km="beforeunload";function y0(a){let u=a.getLocation();const i=new Set,c=p=>{u=a.getLocation(),i.forEach(m=>m({location:u,action:p}))},o=p=>{a.notifyOnIndexChange??!0?c(p):u=a.getLocation()},d=async({task:p,navigateOpts:m,...h})=>{if(m?.ignoreBlocker??!1){p();return}const _=a.getBlockers?.()??[],S=h.type==="PUSH"||h.type==="REPLACE";if(typeof document<"u"&&_.length&&S)for(const v of _){const A=ds(h.path,h.state);if(await v.blockerFn({currentLocation:u,nextLocation:A,action:h.type})){a.onBlocked?.();return}}p()};return{get location(){return u},get length(){return a.getLength()},subscribers:i,subscribe:p=>(i.add(p),()=>{i.delete(p)}),push:(p,m,h)=>{const y=u.state[ta];m=Fm(y+1,m),d({task:()=>{a.pushState(p,m),c({type:"PUSH"})},navigateOpts:h,type:"PUSH",path:p,state:m})},replace:(p,m,h)=>{const y=u.state[ta];m=Fm(y,m),d({task:()=>{a.replaceState(p,m),c({type:"REPLACE"})},navigateOpts:h,type:"REPLACE",path:p,state:m})},go:(p,m)=>{d({task:()=>{a.go(p),o({type:"GO",index:p})},navigateOpts:m,type:"GO"})},back:p=>{d({task:()=>{a.back(p?.ignoreBlocker??!1),o({type:"BACK"})},navigateOpts:p,type:"BACK"})},forward:p=>{d({task:()=>{a.forward(p?.ignoreBlocker??!1),o({type:"FORWARD"})},navigateOpts:p,type:"FORWARD"})},canGoBack:()=>u.state[ta]!==0,createHref:p=>a.createHref(p),block:p=>{if(!a.setBlockers)return()=>{};const m=a.getBlockers?.()??[];return a.setBlockers([...m,p]),()=>{const h=a.getBlockers?.()??[];a.setBlockers?.(h.filter(y=>y!==p))}},flush:()=>a.flush?.(),destroy:()=>a.destroy?.(),notify:c}}function Fm(a,u){u||(u={});const i=Ro();return{...u,key:i,__TSR_key:i,[ta]:a}}function p0(a){const u=typeof document<"u"?window:void 0,i=u.history.pushState,c=u.history.replaceState;let o=[];const d=()=>o,p=G=>o=G,m=(G=>G),h=(()=>ds(`${u.location.pathname}${u.location.search}${u.location.hash}`,u.history.state));if(!u.history.state?.__TSR_key&&!u.history.state?.key){const G=Ro();u.history.replaceState({[ta]:0,key:G,__TSR_key:G},"")}let y=h(),_,S=!1,v=!1,A=!1,L=!1;const x=()=>y;let D,V;const Z=()=>{D&&(X._ignoreSubscribers=!0,(D.isPush?u.history.pushState:u.history.replaceState)(D.state,"",D.href),X._ignoreSubscribers=!1,D=void 0,V=void 0,_=void 0)},Q=(G,F,st)=>{const lt=m(F);V||(_=y),y=ds(F,st),D={href:lt,state:st,isPush:D?.isPush||G==="push"},V||(V=Promise.resolve().then(()=>Z()))},Y=G=>{y=h(),X.notify({type:G})},$=async()=>{if(v){v=!1;return}const G=h(),F=G.state[ta]-y.state[ta],st=F===1,lt=F===-1,ct=!st&&!lt||S;S=!1;const qt=ct?"GO":lt?"BACK":"FORWARD",St=ct?{type:"GO",index:F}:{type:lt?"BACK":"FORWARD"};if(A)A=!1;else{const Ut=d();if(typeof document<"u"&&Ut.length){for(const U of Ut)if(await U.blockerFn({currentLocation:y,nextLocation:G,action:qt})){v=!0,u.history.go(1),X.notify(St);return}}}y=h(),X.notify(St)},k=G=>{if(L){L=!1;return}let F=!1;const st=d();if(typeof document<"u"&&st.length)for(const lt of st){const ct=lt.enableBeforeUnload??!0;if(ct===!0){F=!0;break}if(typeof ct=="function"&&ct()===!0){F=!0;break}}if(F)return G.preventDefault(),G.returnValue=""},X=y0({getLocation:x,getLength:()=>u.history.length,pushState:(G,F)=>Q("push",G,F),replaceState:(G,F)=>Q("replace",G,F),back:G=>(G&&(A=!0),L=!0,u.history.back()),forward:G=>{G&&(A=!0),L=!0,u.history.forward()},go:G=>{S=!0,u.history.go(G)},createHref:G=>m(G),flush:Z,destroy:()=>{u.history.pushState=i,u.history.replaceState=c,u.removeEventListener(km,k,{capture:!0}),u.removeEventListener(Jm,$)},onBlocked:()=>{_&&y!==_&&(y=_)},getBlockers:d,setBlockers:p,notifyOnIndexChange:!1});return u.addEventListener(km,k,{capture:!0}),u.addEventListener(Jm,$),u.history.pushState=function(...G){const F=i.apply(u.history,G);return X._ignoreSubscribers||Y("PUSH"),F},u.history.replaceState=function(...G){const F=c.apply(u.history,G);return X._ignoreSubscribers||Y("REPLACE"),F},X}function g0(a){let u=a.replace(/[\x00-\x1f\x7f]/g,"");return u.startsWith("//")&&(u="/"+u.replace(/^\/+/,"")),u}function ds(a,u){const i=g0(a),c=i.indexOf("#"),o=i.indexOf("?"),d=Ro();return{href:i,pathname:i.substring(0,c>0?o>0?Math.min(c,o):c:o>0?o:i.length),hash:c>-1?i.substring(c):"",search:o>-1?i.slice(o,c===-1?void 0:c):"",state:u||{[ta]:0,key:d,__TSR_key:d}}}function Ro(){return(Math.random()+1).toString(36).substring(7)}function hs(a){return a[a.length-1]}function v0(a){return typeof a=="function"}function Wn(a,u){return v0(a)?a(u):a}const S0=Object.prototype.hasOwnProperty;function Ye(a,u){if(a===u)return a;const i=u,c=Wm(a)&&Wm(i);if(!c&&!(ms(a)&&ms(i)))return i;const o=c?a:Pm(a);if(!o)return i;const d=c?i:Pm(i);if(!d)return i;const p=o.length,m=d.length,h=c?new Array(m):{};let y=0;for(let _=0;_"u")return!0;const i=u.prototype;return!(!$m(i)||!i.hasOwnProperty("isPrototypeOf"))}function $m(a){return Object.prototype.toString.call(a)==="[object Object]"}function Wm(a){return Array.isArray(a)&&a.length===Object.keys(a).length}function Aa(a,u,i){if(a===u)return!0;if(typeof a!=typeof u)return!1;if(Array.isArray(a)&&Array.isArray(u)){if(a.length!==u.length)return!1;for(let c=0,o=a.length;co||!Aa(a[p],u[p],i)))return!1;return o===d}return!1}function xa(a){let u,i;const c=new Promise((o,d)=>{u=o,i=d});return c.status="pending",c.resolve=o=>{c.status="resolved",c.value=o,u(o),a?.(o)},c.reject=o=>{c.status="rejected",i(o)},c}function b0(a){return typeof a?.message!="string"?!1:a.message.startsWith("Failed to fetch dynamically imported module")||a.message.startsWith("error loading dynamically imported module")||a.message.startsWith("Importing a module script failed")}function ea(a){return!!(a&&typeof a=="object"&&typeof a.then=="function")}function _0(a){return a.replace(/[\x00-\x1f\x7f]/g,"")}function Im(a){let u;try{u=decodeURI(a)}catch{u=a.replaceAll(/%[0-9A-F]{2}/gi,i=>{try{return decodeURI(i)}catch{return i}})}return _0(u)}const Cy=["http:","https:","mailto:","tel:"];function ys(a){if(!a)return!1;try{const u=new URL(a);return!Cy.includes(u.protocol)}catch{return!1}}const E0={"&":"\\u0026",">":"\\u003e","<":"\\u003c","\u2028":"\\u2028","\u2029":"\\u2029"},R0=/[&><\u2028\u2029]/g;function Dy(a){return a.replace(R0,u=>E0[u])}function ty(a,u){if(!a)return a;const i=/%25|%5C/gi;let c=0,o="",d;for(;(d=i.exec(a))!==null;)o+=Im(a.slice(c,d.index))+d[0],c=i.lastIndex;return o=o+Im(c?a.slice(c):a),o.startsWith("//")&&(o="/"+o.replace(/^\/+/,"")),o}var T0="Invariant failed";function ve(a,u){if(!a)throw new Error(T0)}function ps(a){const u=new Map;let i,c;const o=d=>{d.next&&(d.prev?(d.prev.next=d.next,d.next.prev=d.prev,d.next=void 0,c&&(c.next=d,d.prev=c)):(d.next.prev=void 0,i=d.next,d.next=void 0,c&&(d.prev=c,c.next=d)),c=d)};return{get(d){const p=u.get(d);if(p)return o(p),p.value},set(d,p){if(u.size>=a&&i){const h=i;u.delete(h.key),h.next&&(i=h.next,h.next.prev=void 0),h===c&&(c=void 0)}const m=u.get(d);if(m)m.value=p,o(m);else{const h={key:d,value:p,prev:c};c&&(c.next=h),c=h,i||(i=h),u.set(d,h)}},clear(){u.clear(),i=void 0,c=void 0}}}const Ol=0,za=1,wa=2,Ni=3,_l=4,A0=5,M0=/^([^{]*)\{\$([a-zA-Z_$][a-zA-Z0-9_$]*)\}([^}]*)$/,O0=/^([^{]*)\{-\$([a-zA-Z_$][a-zA-Z0-9_$]*)\}([^}]*)$/,x0=/^([^{]*)\{\$\}([^}]*)$/;function To(a,u,i=new Uint16Array(6)){const c=a.indexOf("/",u),o=c===-1?a.length:c,d=a.substring(u,o);if(!d||!d.includes("$"))return i[0]=Ol,i[1]=u,i[2]=u,i[3]=o,i[4]=o,i[5]=o,i;if(d==="$"){const y=a.length;return i[0]=wa,i[1]=u,i[2]=u,i[3]=y,i[4]=y,i[5]=y,i}if(d.charCodeAt(0)===36)return i[0]=za,i[1]=u,i[2]=u+1,i[3]=o,i[4]=o,i[5]=o,i;const p=d.match(x0);if(p){const _=p[1].length;return i[0]=wa,i[1]=u+_,i[2]=u+_+1,i[3]=u+_+2,i[4]=u+_+3,i[5]=a.length,i}const m=d.match(O0);if(m){const y=m[1],_=m[2],S=m[3],v=y.length;return i[0]=Ni,i[1]=u+v,i[2]=u+v+3,i[3]=u+v+3+_.length,i[4]=o-S.length,i[5]=o,i}const h=d.match(M0);if(h){const y=h[1],_=h[2],S=h[3],v=y.length;return i[0]=za,i[1]=u+v,i[2]=u+v+2,i[3]=u+v+2+_.length,i[4]=o-S.length,i[5]=o,i}return i[0]=Ol,i[1]=u,i[2]=u,i[3]=o,i[4]=o,i[5]=o,i}function As(a,u,i,c,o,d,p){p?.(i);let m=c;{const h=i.fullPath??i.from,y=h.length,_=i.options?.caseSensitive??a,S=!!(i.options?.params?.parse&&i.options?.skipRouteOnParseError?.params);for(;m!G.skipOnParamError&&G.caseSensitive===Y&&G.prefix===$&&G.suffix===k);if(X)L=X;else{const G=eo(za,i.fullPath??i.from,Y,$,k);L=G,G.depth=d,G.parent=o,o.dynamic??=[],o.dynamic.push(G)}break}case Ni:{const Z=h.substring(x,A[1]),Q=h.substring(A[4],D),Y=_&&!!(Z||Q),$=Z?Y?Z:Z.toLowerCase():void 0,k=Q?Y?Q:Q.toLowerCase():void 0,X=!S&&o.optional?.find(G=>!G.skipOnParamError&&G.caseSensitive===Y&&G.prefix===$&&G.suffix===k);if(X)L=X;else{const G=eo(Ni,i.fullPath??i.from,Y,$,k);L=G,G.parent=o,G.depth=d,o.optional??=[],o.optional.push(G)}break}case wa:{const Z=h.substring(x,A[1]),Q=h.substring(A[4],D),Y=_&&!!(Z||Q),$=Z?Y?Z:Z.toLowerCase():void 0,k=Q?Y?Q:Q.toLowerCase():void 0,X=eo(wa,i.fullPath??i.from,Y,$,k);L=X,X.parent=o,X.depth=d,o.wildcard??=[],o.wildcard.push(X)}}o=L}if(S&&i.children&&!i.isRoot&&i.id&&i.id.charCodeAt(i.id.lastIndexOf("/")+1)===95){const A=Ra(i.fullPath??i.from);A.kind=A0,A.parent=o,d++,A.depth=d,o.pathless??=[],o.pathless.push(A),o=A}const v=(i.path||!i.children)&&!i.isRoot;if(v&&h.endsWith("/")){const A=Ra(i.fullPath??i.from);A.kind=_l,A.parent=o,d++,A.depth=d,o.index=A,o=A}o.parse=i.options?.params?.parse??null,o.skipOnParamError=S,o.parsingPriority=i.options?.skipRouteOnParseError?.priority??0,v&&!o.route&&(o.route=i,o.fullPath=i.fullPath??i.from)}if(i.children)for(const h of i.children)As(a,u,h,m,o,d,p)}function to(a,u){if(a.skipOnParamError&&!u.skipOnParamError)return-1;if(!a.skipOnParamError&&u.skipOnParamError)return 1;if(a.skipOnParamError&&u.skipOnParamError&&(a.parsingPriority||u.parsingPriority))return u.parsingPriority-a.parsingPriority;if(a.prefix&&u.prefix&&a.prefix!==u.prefix){if(a.prefix.startsWith(u.prefix))return-1;if(u.prefix.startsWith(a.prefix))return 1}if(a.suffix&&u.suffix&&a.suffix!==u.suffix){if(a.suffix.endsWith(u.suffix))return-1;if(u.suffix.endsWith(a.suffix))return 1}return a.prefix&&!u.prefix?-1:!a.prefix&&u.prefix?1:a.suffix&&!u.suffix?-1:!a.suffix&&u.suffix?1:a.caseSensitive&&!u.caseSensitive?-1:!a.caseSensitive&&u.caseSensitive?1:0}function $n(a){if(a.pathless)for(const u of a.pathless)$n(u);if(a.static)for(const u of a.static.values())$n(u);if(a.staticInsensitive)for(const u of a.staticInsensitive.values())$n(u);if(a.dynamic?.length){a.dynamic.sort(to);for(const u of a.dynamic)$n(u)}if(a.optional?.length){a.optional.sort(to);for(const u of a.optional)$n(u)}if(a.wildcard?.length){a.wildcard.sort(to);for(const u of a.wildcard)$n(u)}}function Ra(a){return{kind:Ol,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:a,parent:null,parse:null,skipOnParamError:!1,parsingPriority:0}}function eo(a,u,i,c,o){return{kind:a,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:u,parent:null,parse:null,skipOnParamError:!1,parsingPriority:0,caseSensitive:i,prefix:c,suffix:o}}function z0(a,u){const i=Ra("/"),c=new Uint16Array(6);for(const o of a)As(!1,c,o,1,i,0);$n(i),u.masksTree=i,u.flatCache=ps(1e3)}function w0(a,u){a||="/";const i=u.flatCache.get(a);if(i)return i;const c=Ao(a,u.masksTree);return u.flatCache.set(a,c),c}function C0(a,u,i,c,o){a||="/",c||="/";const d=u?`case\0${a}`:a;let p=o.singleCache.get(d);if(!p){p=Ra("/");const m=new Uint16Array(6);As(u,m,{from:a},1,p,0),o.singleCache.set(d,p)}return Ao(c,p,i)}function D0(a,u,i=!1){const c=i?a:`nofuzz\0${a}`,o=u.matchCache.get(c);if(o!==void 0)return o;a||="/";const d=Ao(a,u.segmentTree,i);return d&&(d.branch=U0(d.route)),u.matchCache.set(c,d),d}function L0(a){return a==="/"?a:a.replace(/\/{1,}$/,"")}function N0(a,u=!1,i){const c=Ra(a.fullPath),o=new Uint16Array(6),d={},p={};let m=0;return As(u,o,a,1,c,0,y=>{if(i?.(y,m),ve(!(y.id in d),`Duplicate routes found with id: ${String(y.id)}`),d[y.id]=y,m!==0&&y.path){const _=L0(y.fullPath);(!p[_]||y.fullPath.endsWith("/"))&&(p[_]=y)}m++}),$n(c),{processedTree:{segmentTree:c,singleCache:ps(1e3),matchCache:ps(1e3),flatCache:null,masksTree:null},routesById:d,routesByPath:p}}function Ao(a,u,i=!1){const c=a.split("/"),o=j0(a,c,u,i);if(!o)return null;const[d]=Ly(a,c,o);return{route:o.node.route,rawParams:d,parsedParams:o.parsedParams}}function Ly(a,u,i){const c=B0(i.node);let o=null;const d={};let p=i.extract?.part??0,m=i.extract?.node??0,h=i.extract?.path??0;for(;m=0;lt--){const ct=v.optional[lt];m.push({node:ct,index:A,skipped:F,depth:st,statics:D,dynamics:V,optionals:Z,extract:Q,rawParams:Y,parsedParams:$})}if(!k)for(let lt=v.optional.length-1;lt>=0;lt--){const ct=v.optional[lt],{prefix:qt,suffix:St}=ct;if(qt||St){const Ut=ct.caseSensitive?X:G??=X.toLowerCase();if(qt&&!Ut.startsWith(qt)||St&&!Ut.endsWith(St))continue}m.push({node:ct,index:A+1,skipped:L,depth:st,statics:D,dynamics:V,optionals:Z+1,extract:Q,rawParams:Y,parsedParams:$})}}if(!k&&v.dynamic&&X)for(let F=v.dynamic.length-1;F>=0;F--){const st=v.dynamic[F],{prefix:lt,suffix:ct}=st;if(lt||ct){const qt=st.caseSensitive?X:G??=X.toLowerCase();if(lt&&!qt.startsWith(lt)||ct&&!qt.endsWith(ct))continue}m.push({node:st,index:A+1,skipped:L,depth:x+1,statics:D,dynamics:V+1,optionals:Z,extract:Q,rawParams:Y,parsedParams:$})}if(!k&&v.staticInsensitive){const F=v.staticInsensitive.get(G??=X.toLowerCase());F&&m.push({node:F,index:A+1,skipped:L,depth:x+1,statics:D+1,dynamics:V,optionals:Z,extract:Q,rawParams:Y,parsedParams:$})}if(!k&&v.static){const F=v.static.get(X);F&&m.push({node:F,index:A+1,skipped:L,depth:x+1,statics:D+1,dynamics:V,optionals:Z,extract:Q,rawParams:Y,parsedParams:$})}if(v.pathless){const F=x+1;for(let st=v.pathless.length-1;st>=0;st--){const lt=v.pathless[st];m.push({node:lt,index:A,skipped:L,depth:F,statics:D,dynamics:V,optionals:Z,extract:Q,rawParams:Y,parsedParams:$})}}}if(_&&h)return Ti(h,_)?_:h;if(_)return _;if(h)return h;if(c&&y){let S=y.index;for(let A=0;Aa.statics||u.statics===a.statics&&(u.dynamics>a.dynamics||u.dynamics===a.dynamics&&(u.optionals>a.optionals||u.optionals===a.optionals&&((u.node.kind===_l)>(a.node.kind===_l)||u.node.kind===_l==(a.node.kind===_l)&&u.depth>a.depth))):!0}function rs(a){return Mo(a.filter(u=>u!==void 0).join("/"))}function Mo(a){return a.replace(/\/{2,}/g,"/")}function Ny(a){return a==="/"?a:a.replace(/^\/{1,}/,"")}function Ma(a){const u=a.length;return u>1&&a[u-1]==="/"?a.replace(/\/{1,}$/,""):a}function Uy(a){return Ma(Ny(a))}function gs(a,u){return a?.endsWith("/")&&a!=="/"&&a!==`${u}/`?a.slice(0,-1):a}function H0(a,u,i){return gs(a,i)===gs(u,i)}function q0({base:a,to:u,trailingSlash:i="never",cache:c}){const o=u.startsWith("/"),d=!o&&u===".";let p;if(c){p=o?u:d?a:a+"\0"+u;const S=c.get(p);if(S)return S}let m;if(d)m=a.split("/");else if(o)m=u.split("/");else{for(m=a.split("/");m.length>1&&hs(m)==="";)m.pop();const S=u.split("/");for(let v=0,A=S.length;v1&&(hs(m)===""?i==="never"&&m.pop():i==="always"&&m.push(""));let h,y="";for(let S=0;S0&&(y+="/");const v=m[S];if(!v)continue;h=To(v,0,h);const A=h[0];if(A===Ol){y+=v;continue}const L=h[5],x=v.substring(0,h[1]),D=v.substring(h[4],L),V=v.substring(h[2],h[3]);A===za?y+=x||D?`${x}{$${V}}${D}`:`$${V}`:A===wa?y+=x||D?`${x}{$}${D}`:"$":y+=`${x}{-$${V}}${D}`}y=Mo(y);const _=y||"/";return p&&c&&c.set(p,_),_}function ao(a,u,i){const c=u[a];return typeof c!="string"?c:a==="_splat"?encodeURI(c):Y0(c,i)}function lo({path:a,params:u,decodeCharMap:i}){let c=!1;const o={};if(!a||a==="/")return{interpolatedPath:"/",usedParams:o,isMissingParams:c};if(!a.includes("$"))return{interpolatedPath:a,usedParams:o,isMissingParams:c};const d=a.length;let p=0,m,h="";for(;p{let i;return(...c)=>{i||(i=setTimeout(()=>{a(...c),i=null},u))}};function X0(){const a=G0();if(!a)return null;const u=a.getItem(vs);let i=u?JSON.parse(u):{};return{state:i,set:c=>{i=Wn(c,i)||i;try{a.setItem(vs,JSON.stringify(i))}catch{console.warn("[ts-router] Could not persist scroll restoration state to sessionStorage.")}}}}const ls=X0(),vo=a=>a.state.__TSR_key||a.href;function Q0(a){const u=[];let i;for(;i=a.parentNode;)u.push(`${a.tagName}:nth-child(${Array.prototype.indexOf.call(i.children,a)+1})`),a=i;return`${u.reverse().join(" > ")}`.toLowerCase()}let Ss=!1;function By({storageKey:a,key:u,behavior:i,shouldScrollRestoration:c,scrollToTopSelectors:o,location:d}){let p;try{p=JSON.parse(sessionStorage.getItem(a)||"{}")}catch(y){console.error(y);return}const m=u||window.history.state?.__TSR_key,h=p[m];Ss=!0;t:{if(c&&h&&Object.keys(h).length>0){for(const S in h){const v=h[S];if(S==="window")window.scrollTo({top:v.scrollY,left:v.scrollX,behavior:i});else if(S){const A=document.querySelector(S);A&&(A.scrollLeft=v.scrollX,A.scrollTop=v.scrollY)}}break t}const y=(d??window.location).hash.split("#",2)[1];if(y){const S=window.history.state?.__hashScrollIntoViewOptions??!0;if(S){const v=document.getElementById(y);v&&v.scrollIntoView(S)}break t}const _={top:0,left:0,behavior:i};if(window.scrollTo(_),o)for(const S of o){if(S==="window")continue;const v=typeof S=="function"?S():document.querySelector(S);v&&v.scrollTo(_)}}Ss=!1}function Z0(a,u){if(!ls&&!a.isServer||((a.options.scrollRestoration??!1)&&(a.isScrollRestoring=!0),a.isServer||a.isScrollRestorationSetup||!ls))return;a.isScrollRestorationSetup=!0,Ss=!1;const c=a.options.getScrollRestorationKey||vo;window.history.scrollRestoration="manual";const o=d=>{if(Ss||!a.isScrollRestoring)return;let p="";if(d.target===document||d.target===window)p="window";else{const h=d.target.getAttribute("data-scroll-restoration-id");h?p=`[data-scroll-restoration-id="${h}"]`:p=Q0(d.target)}const m=c(a.state.location);ls.set(h=>{const y=h[m]||={},_=y[p]||={};if(p==="window")_.scrollX=window.scrollX||0,_.scrollY=window.scrollY||0;else if(p){const S=document.querySelector(p);S&&(_.scrollX=S.scrollLeft||0,_.scrollY=S.scrollTop||0)}return h})};typeof document<"u"&&document.addEventListener("scroll",V0(o,100),!0),a.subscribe("onRendered",d=>{const p=c(d.toLocation);if(!a.resetNextScroll){a.resetNextScroll=!0;return}typeof a.options.scrollRestoration=="function"&&!a.options.scrollRestoration({location:a.latestLocation})||(By({storageKey:vs,key:p,behavior:a.options.scrollRestorationBehavior,shouldScrollRestoration:a.isScrollRestoring,scrollToTopSelectors:a.options.scrollToTopSelectors,location:a.history.location}),a.isScrollRestoring&&ls.set(m=>(m[p]||={},m)))})}function K0(a){if(typeof document<"u"&&document.querySelector){const u=a.state.location.state.__hashScrollIntoViewOptions??!0;if(u&&a.state.location.hash!==""){const i=document.getElementById(a.state.location.hash);i&&i.scrollIntoView(u)}}}function jy(a,u=String){const i=new URLSearchParams;for(const c in a){const o=a[c];o!==void 0&&i.set(c,u(o))}return i.toString()}function io(a){return a?a==="false"?!1:a==="true"?!0:+a*0===0&&+a+""===a?+a:a:""}function J0(a){const u=new URLSearchParams(a),i={};for(const[c,o]of u.entries()){const d=i[c];d==null?i[c]=io(o):Array.isArray(d)?d.push(io(o)):i[c]=[d,io(o)]}return i}const k0=P0(JSON.parse),F0=$0(JSON.stringify,JSON.parse);function P0(a){return u=>{u[0]==="?"&&(u=u.substring(1));const i=J0(u);for(const c in i){const o=i[c];if(typeof o=="string")try{i[c]=a(o)}catch{}}return i}}function $0(a,u){const i=typeof u=="function";function c(o){if(typeof o=="object"&&o!==null)try{return a(o)}catch{}else if(i&&typeof o=="string")try{return u(o),a(o)}catch{}return o}return o=>{const d=jy(o,c);return d?`?${d}`:""}}const ze="__root__";function Hy(a){if(a.statusCode=a.statusCode||a.code||307,typeof a.href=="string"&&ys(a.href))throw new Error(`Redirect blocked: unsafe protocol in href "${a.href}". Only ${Cy.join(", ")} protocols are allowed.`);if(!a.reloadDocument&&typeof a.href=="string")try{new URL(a.href),a.reloadDocument=!0}catch{}const u=new Headers(a.headers);a.href&&u.get("Location")===null&&u.set("Location",a.href);const i=new Response(null,{status:a.statusCode,headers:u});if(i.options=a,a.throw)throw i;return i}function Ge(a){return a instanceof Response&&!!a.options}function W0(a){if(a!==null&&typeof a=="object"&&a.isSerializedRedirect)return Hy(a)}const cs=a=>{if(!a.rendered)return a.rendered=!0,a.onReady?.()},Ms=(a,u)=>!!(a.preload&&!a.router.state.matches.some(i=>i.id===u)),Oo=(a,u,i=!0)=>{const c={...a.router.options.context??{}},o=i?u:u-1;for(let d=0;d<=o;d++){const p=a.matches[d];if(!p)continue;const m=a.router.getMatch(p.id);m&&Object.assign(c,m.__routeContext,m.__beforeLoadContext)}return c},qy=(a,u)=>{const i=a.router.routesById[u.routeId??""]??a.router.routeTree;!i.options.notFoundComponent&&a.router.options?.defaultNotFoundComponent&&(i.options.notFoundComponent=a.router.options.defaultNotFoundComponent),ve(i.options.notFoundComponent);const c=a.matches.find(o=>o.routeId===i.id);ve(c,"Could not find match for route: "+i.id),a.updateMatch(c.id,o=>({...o,status:"notFound",error:u,isFetching:!1})),u.routerCode==="BEFORE_LOAD"&&i.parentRoute&&(u.routeId=i.parentRoute.id,qy(a,u))},In=(a,u,i)=>{if(!(!Ge(i)&&!oe(i))){if(Ge(i)&&i.redirectHandled&&!i.options.reloadDocument)throw i;if(u){u._nonReactive.beforeLoadPromise?.resolve(),u._nonReactive.loaderPromise?.resolve(),u._nonReactive.beforeLoadPromise=void 0,u._nonReactive.loaderPromise=void 0;const c=Ge(i)?"redirected":"notFound";u._nonReactive.error=i,a.updateMatch(u.id,o=>({...o,status:c,isFetching:!1,error:i})),oe(i)&&!i.routeId&&(i.routeId=u.routeId),u._nonReactive.loadPromise?.resolve()}throw Ge(i)?(a.rendered=!0,i.options._fromLocation=a.location,i.redirectHandled=!0,i=a.router.resolveRedirect(i),i):(qy(a,i),i)}},Yy=(a,u)=>{const i=a.router.getMatch(u);return!!(!a.router.isServer&&i._nonReactive.dehydrated||a.router.isServer&&i.ssr===!1)},Ai=(a,u,i,c)=>{const{id:o,routeId:d}=a.matches[u],p=a.router.looseRoutesById[d];if(i instanceof Promise)throw i;i.routerCode=c,a.firstBadMatchIndex??=u,In(a,a.router.getMatch(o),i);try{p.options.onError?.(i)}catch(m){i=m,In(a,a.router.getMatch(o),i)}a.updateMatch(o,m=>(m._nonReactive.beforeLoadPromise?.resolve(),m._nonReactive.beforeLoadPromise=void 0,m._nonReactive.loadPromise?.resolve(),{...m,error:i,status:"error",isFetching:!1,updatedAt:Date.now(),abortController:new AbortController}))},I0=(a,u,i,c)=>{const o=a.router.getMatch(u),d=a.matches[i-1]?.id,p=d?a.router.getMatch(d):void 0;if(a.router.isShell()){o.ssr=c.id===ze;return}if(p?.ssr===!1){o.ssr=!1;return}const m=A=>A===!0&&p?.ssr==="data-only"?"data-only":A,h=a.router.options.defaultSsr??!0;if(c.options.ssr===void 0){o.ssr=m(h);return}if(typeof c.options.ssr!="function"){o.ssr=m(c.options.ssr);return}const{search:y,params:_}=o,S={search:is(y,o.searchError),params:is(_,o.paramsError),location:a.location,matches:a.matches.map(A=>({index:A.index,pathname:A.pathname,fullPath:A.fullPath,staticData:A.staticData,id:A.id,routeId:A.routeId,search:is(A.search,A.searchError),params:is(A.params,A.paramsError),ssr:A.ssr}))},v=c.options.ssr(S);if(ea(v))return v.then(A=>{o.ssr=m(A??h)});o.ssr=m(v??h)},Gy=(a,u,i,c)=>{if(c._nonReactive.pendingTimeout!==void 0)return;const o=i.options.pendingMs??a.router.options.defaultPendingMs;if(!!(a.onReady&&!a.router.isServer&&!Ms(a,u)&&(i.options.loader||i.options.beforeLoad||Qy(i))&&typeof o=="number"&&o!==1/0&&(i.options.pendingComponent??a.router.options?.defaultPendingComponent))){const p=setTimeout(()=>{cs(a)},o);c._nonReactive.pendingTimeout=p}},tS=(a,u,i)=>{const c=a.router.getMatch(u);if(!c._nonReactive.beforeLoadPromise&&!c._nonReactive.loaderPromise)return;Gy(a,u,i,c);const o=()=>{const d=a.router.getMatch(u);d.preload&&(d.status==="redirected"||d.status==="notFound")&&In(a,d,d.error)};return c._nonReactive.beforeLoadPromise?c._nonReactive.beforeLoadPromise.then(o):o()},eS=(a,u,i,c)=>{const o=a.router.getMatch(u),d=o._nonReactive.loadPromise;o._nonReactive.loadPromise=xa(()=>{d?.resolve()});const{paramsError:p,searchError:m}=o;p&&Ai(a,i,p,"PARSE_PARAMS"),m&&Ai(a,i,m,"VALIDATE_SEARCH"),Gy(a,u,c,o);const h=new AbortController,y=a.matches[i-1]?.id;(y?a.router.getMatch(y):void 0)?.context??a.router.options.context;let S=!1;const v=()=>{S||(S=!0,a.updateMatch(u,k=>({...k,isFetching:"beforeLoad",fetchCount:k.fetchCount+1,abortController:h})))},A=()=>{o._nonReactive.beforeLoadPromise?.resolve(),o._nonReactive.beforeLoadPromise=void 0,a.updateMatch(u,k=>({...k,isFetching:!1}))};if(!c.options.beforeLoad){Rl(()=>{v(),A()});return}o._nonReactive.beforeLoadPromise=xa();const L={...Oo(a,i,!1),...o.__routeContext},{search:x,params:D,cause:V}=o,Z=Ms(a,u),Q={search:x,abortController:h,params:D,preload:Z,context:L,location:a.location,navigate:k=>a.router.navigate({...k,_fromLocation:a.location}),buildLocation:a.router.buildLocation,cause:Z?"preload":V,matches:a.matches,...a.router.options.additionalContext},Y=k=>{if(k===void 0){Rl(()=>{v(),A()});return}(Ge(k)||oe(k))&&(v(),Ai(a,i,k,"BEFORE_LOAD")),Rl(()=>{v(),a.updateMatch(u,X=>({...X,__beforeLoadContext:k})),A()})};let $;try{if($=c.options.beforeLoad(Q),ea($))return v(),$.catch(k=>{Ai(a,i,k,"BEFORE_LOAD")}).then(Y)}catch(k){v(),Ai(a,i,k,"BEFORE_LOAD")}Y($)},nS=(a,u)=>{const{id:i,routeId:c}=a.matches[u],o=a.router.looseRoutesById[c],d=()=>{if(a.router.isServer){const h=I0(a,i,u,o);if(ea(h))return h.then(m)}return m()},p=()=>eS(a,i,u,o),m=()=>{if(Yy(a,i))return;const h=tS(a,i,o);return ea(h)?h.then(p):p()};return d()},aS=(a,u,i)=>{const c=a.router.getMatch(u);if(!c||!i.options.head&&!i.options.scripts&&!i.options.headers)return;const o={matches:a.matches,match:c,params:c.params,loaderData:c.loaderData};return Promise.all([i.options.head?.(o),i.options.scripts?.(o),i.options.headers?.(o)]).then(([d,p,m])=>{const h=d?.meta,y=d?.links,_=d?.scripts,S=d?.styles;return{meta:h,links:y,headScripts:_,headers:m,scripts:p,styles:S}})},Vy=(a,u,i,c)=>{const o=a.matchPromises[i-1],{params:d,loaderDeps:p,abortController:m,cause:h}=a.router.getMatch(u),y=Oo(a,i),_=Ms(a,u);return{params:d,deps:p,preload:!!_,parentMatchPromise:o,abortController:m,context:y,location:a.location,navigate:S=>a.router.navigate({...S,_fromLocation:a.location}),cause:_?"preload":h,route:c,...a.router.options.additionalContext}},ey=async(a,u,i,c)=>{try{const o=a.router.getMatch(u);try{(!a.router.isServer||o.ssr===!0)&&Xy(c);const d=c.options.loader?.(Vy(a,u,i,c)),p=c.options.loader&&ea(d);if((p||c._lazyPromise||c._componentsPromise||c.options.head||c.options.scripts||c.options.headers||o._nonReactive.minPendingPromise)&&a.updateMatch(u,y=>({...y,isFetching:"loader"})),c.options.loader){const y=p?await d:d;In(a,a.router.getMatch(u),y),y!==void 0&&a.updateMatch(u,_=>({..._,loaderData:y}))}c._lazyPromise&&await c._lazyPromise;const h=o._nonReactive.minPendingPromise;h&&await h,c._componentsPromise&&await c._componentsPromise,a.updateMatch(u,y=>({...y,error:void 0,status:"success",isFetching:!1,updatedAt:Date.now()}))}catch(d){let p=d;if(p?.name==="AbortError"){a.updateMatch(u,h=>({...h,status:h.status==="pending"?"success":h.status,isFetching:!1}));return}const m=o._nonReactive.minPendingPromise;m&&await m,oe(d)&&await c.options.notFoundComponent?.preload?.(),In(a,a.router.getMatch(u),d);try{c.options.onError?.(d)}catch(h){p=h,In(a,a.router.getMatch(u),h)}a.updateMatch(u,h=>({...h,error:p,status:"error",isFetching:!1}))}}catch(o){const d=a.router.getMatch(u);d&&(d._nonReactive.loaderPromise=void 0),In(a,d,o)}},lS=async(a,u)=>{const{id:i,routeId:c}=a.matches[u];let o=!1,d=!1;const p=a.router.looseRoutesById[c],m=()=>{a.updateMatch(i,_=>({..._,context:Oo(a,u)}))};if(Yy(a,i)){if(a.router.isServer)return a.router.getMatch(i)}else{const _=a.router.getMatch(i);if(_._nonReactive.loaderPromise){if(_.status==="success"&&!a.sync&&!_.preload)return _;await _._nonReactive.loaderPromise;const S=a.router.getMatch(i),v=S._nonReactive.error||S.error;v&&In(a,S,v)}else{const S=Date.now()-_.updatedAt,v=Ms(a,i),A=v?p.options.preloadStaleTime??a.router.options.defaultPreloadStaleTime??3e4:p.options.staleTime??a.router.options.defaultStaleTime??0,L=p.options.shouldReload,x=typeof L=="function"?L(Vy(a,i,u,p)):L,D=!!v&&!a.router.state.matches.some(Y=>Y.id===i),V=a.router.getMatch(i);V._nonReactive.loaderPromise=xa(),D!==V.preload&&a.updateMatch(i,Y=>({...Y,preload:D}));const{status:Z,invalid:Q}=V;o=Z==="success"&&(Q||(x??S>A)),v&&p.options.preload===!1||(o&&!a.sync?(d=!0,(async()=>{try{await ey(a,i,u,p),m();const Y=a.router.getMatch(i);Y._nonReactive.loaderPromise?.resolve(),Y._nonReactive.loadPromise?.resolve(),Y._nonReactive.loaderPromise=void 0}catch(Y){Ge(Y)&&await a.router.navigate(Y.options)}})()):(Z!=="success"||o&&a.sync)&&await ey(a,i,u,p))}}const h=a.router.getMatch(i);d||(h._nonReactive.loaderPromise?.resolve(),h._nonReactive.loadPromise?.resolve()),clearTimeout(h._nonReactive.pendingTimeout),h._nonReactive.pendingTimeout=void 0,d||(h._nonReactive.loaderPromise=void 0),h._nonReactive.dehydrated=void 0,d||m();const y=d?h.isFetching:!1;return y!==h.isFetching||h.invalid!==!1?(a.updateMatch(i,_=>({..._,isFetching:y,invalid:!1})),a.router.getMatch(i)):h};async function ny(a){const u=Object.assign(a,{matchPromises:[]});!u.router.isServer&&u.router.state.matches.some(i=>i._forcePending)&&cs(u);try{for(let m=0;mm.status==="rejected").map(m=>m.reason);let d;for(const m of o){if(Ge(m))throw m;!d&&oe(m)&&(d=m)}for(const m of u.matches){const{id:h,routeId:y}=m,_=u.router.looseRoutesById[y];try{const S=aS(u,h,_);if(S){const v=await S;u.updateMatch(h,A=>({...A,...v}))}}catch(S){console.error(`Error executing head for route ${y}:`,S)}}if(d)throw d;const p=cs(u);ea(p)&&await p}catch(i){if(oe(i)&&!u.preload){const c=cs(u);throw ea(c)&&await c,i}if(Ge(i))throw i}return u.matches}async function Xy(a){if(!a._lazyLoaded&&a._lazyPromise===void 0&&(a.lazyFn?a._lazyPromise=a.lazyFn().then(u=>{const{id:i,...c}=u.options;Object.assign(a.options,c),a._lazyLoaded=!0,a._lazyPromise=void 0}):a._lazyLoaded=!0),!a._componentsLoaded&&a._componentsPromise===void 0){const u=()=>{const i=[];for(const c of Zy){const o=a.options[c]?.preload;o&&i.push(o())}if(i.length)return Promise.all(i).then(()=>{a._componentsLoaded=!0,a._componentsPromise=void 0});a._componentsLoaded=!0,a._componentsPromise=void 0};a._componentsPromise=a._lazyPromise?a._lazyPromise.then(u):u()}return a._componentsPromise}function is(a,u){return u?{status:"error",error:u}:{status:"success",value:a}}function Qy(a){for(const u of Zy)if(a.options[u]?.preload)return!0;return!1}const Zy=["component","errorComponent","pendingComponent","notFoundComponent"];function iS(a){return{input:({url:u})=>{for(const i of a)u=So(i,u);return u},output:({url:u})=>{for(let i=a.length-1;i>=0;i--)u=Ky(a[i],u);return u}}}function uS(a){const u=Uy(a.basepath),i=`/${u}`,c=`${i}/`,o=a.caseSensitive?i:i.toLowerCase(),d=a.caseSensitive?c:c.toLowerCase();return{input:({url:p})=>{const m=a.caseSensitive?p.pathname:p.pathname.toLowerCase();return m===o?p.pathname="/":m.startsWith(d)&&(p.pathname=p.pathname.slice(i.length)),p},output:({url:p})=>(p.pathname=rs(["/",u,p.pathname]),p)}}function So(a,u){const i=a?.input?.({url:u});if(i){if(typeof i=="string")return new URL(i);if(i instanceof URL)return i}return u}function Ky(a,u){const i=a?.output?.({url:u});if(i){if(typeof i=="string")return new URL(i);if(i instanceof URL)return i}return u}function sS(a){return a instanceof Error?{name:a.name,message:a.message}:{data:a}}function Oa(a){const u=a.resolvedLocation,i=a.location,c=u?.pathname!==i.pathname,o=u?.href!==i.href,d=u?.hash!==i.hash;return{fromLocation:u,toLocation:i,pathChanged:c,hrefChanged:o,hashChanged:d}}class rS{constructor(u){this.tempLocationKey=`${Math.round(Math.random()*1e7)}`,this.resetNextScroll=!0,this.shouldViewTransition=void 0,this.isViewTransitionTypesSupported=void 0,this.subscribers=new Set,this.isScrollRestoring=!1,this.isScrollRestorationSetup=!1,this.startTransition=i=>i(),this.update=i=>{i.notFoundRoute&&console.warn("The notFoundRoute API is deprecated and will be removed in the next major version. See https://tanstack.com/router/v1/docs/framework/react/guide/not-found-errors#migrating-from-notfoundroute for more info.");const c=this.options,o=this.basepath??c?.basepath??"/",d=this.basepath===void 0,p=c?.rewrite;this.options={...c,...i},this.isServer=this.options.isServer??typeof document>"u",this.pathParamsDecodeCharMap=this.options.pathParamsAllowedCharacters?new Map(this.options.pathParamsAllowedCharacters.map(v=>[encodeURIComponent(v),v])):void 0,(!this.history||this.options.history&&this.options.history!==this.history)&&(this.options.history?this.history=this.options.history:this.isServer||(this.history=p0())),this.origin=this.options.origin,this.origin||(!this.isServer&&window?.origin&&window.origin!=="null"?this.origin=window.origin:this.origin="http://localhost"),this.history&&this.updateLatestLocation(),this.options.routeTree!==this.routeTree&&(this.routeTree=this.options.routeTree,this.buildRouteTree()),!this.__store&&this.latestLocation&&(this.__store=new m0(oS(this.latestLocation),{onUpdate:()=>{this.__store.state={...this.state,cachedMatches:this.state.cachedMatches.filter(v=>!["redirected"].includes(v.status))}}}),Z0(this));let m=!1;const h=this.options.basepath??"/",y=this.options.rewrite;if(d||o!==h||p!==y){this.basepath=h;const v=[];Uy(h)!==""&&v.push(uS({basepath:h})),y&&v.push(y),this.rewrite=v.length===0?void 0:v.length===1?v[0]:iS(v),this.history&&this.updateLatestLocation(),m=!0}m&&this.__store&&(this.__store.state={...this.state,location:this.latestLocation}),typeof window<"u"&&"CSS"in window&&typeof window.CSS?.supports=="function"&&(this.isViewTransitionTypesSupported=window.CSS.supports("selector(:active-view-transition-type(a)"))},this.updateLatestLocation=()=>{this.latestLocation=this.parseLocation(this.history.location,this.latestLocation)},this.buildRouteTree=()=>{const{routesById:i,routesByPath:c,processedTree:o}=N0(this.routeTree,this.options.caseSensitive,(p,m)=>{p.init({originalIndex:m})});this.options.routeMasks&&z0(this.options.routeMasks,o),this.routesById=i,this.routesByPath=c,this.processedTree=o;const d=this.options.notFoundRoute;d&&(d.init({originalIndex:99999999999}),this.routesById[d.id]=d)},this.subscribe=(i,c)=>{const o={eventType:i,fn:c};return this.subscribers.add(o),()=>{this.subscribers.delete(o)}},this.emit=i=>{this.subscribers.forEach(c=>{c.eventType===i.type&&c.fn(i)})},this.parseLocation=(i,c)=>{const o=({href:h,state:y})=>{const _=new URL(h,this.origin),S=So(this.rewrite,_),v=this.options.parseSearch(S.search),A=this.options.stringifySearch(v);return S.search=A,{href:S.href.replace(S.origin,""),publicHref:h,url:S,pathname:ty(S.pathname),searchStr:A,search:Ye(c?.search,v),hash:S.hash.split("#").reverse()[0]??"",state:Ye(c?.state,y)}},d=o(i),{__tempLocation:p,__tempKey:m}=d.state;if(p&&(!m||m===this.tempLocationKey)){const h=o(p);return h.state.key=d.state.key,h.state.__TSR_key=d.state.__TSR_key,delete h.state.__tempLocation,{...h,maskedLocation:d}}return d},this.resolvePathCache=ps(1e3),this.resolvePathWithBase=(i,c)=>q0({base:i,to:Mo(c),trailingSlash:this.options.trailingSlash,cache:this.resolvePathCache}),this.matchRoutes=(i,c,o)=>typeof i=="string"?this.matchRoutesInternal({pathname:i,search:c},o):this.matchRoutesInternal(i,c),this.getMatchedRoutes=i=>fS({pathname:i,routesById:this.routesById,processedTree:this.processedTree}),this.cancelMatch=i=>{const c=this.getMatch(i);c&&(c.abortController.abort(),clearTimeout(c._nonReactive.pendingTimeout),c._nonReactive.pendingTimeout=void 0)},this.cancelMatches=()=>{const i=this.state.matches.filter(d=>d.status==="pending"),c=this.state.matches.filter(d=>d.isFetching==="loader");new Set([...this.state.pendingMatches??[],...i,...c]).forEach(d=>{this.cancelMatch(d.id)})},this.buildLocation=i=>{const c=(d={})=>{const p=d._fromLocation||this.pendingBuiltLocation||this.latestLocation,m=this.matchRoutes(p,{_buildLocation:!0}),h=hs(m);d.from;const y=d.unsafeRelative==="path"?p.pathname:d.from??h.fullPath,_=this.resolvePathWithBase(y,"."),S=h.search,v={...h.params},A=d.to?this.resolvePathWithBase(_,`${d.to}`):this.resolvePathWithBase(_,"."),L=d.params===!1||d.params===null?{}:(d.params??!0)===!0?v:Object.assign(v,Wn(d.params,v)),x=lo({path:A,params:L}).interpolatedPath,D=this.matchRoutes(x,void 0,{_buildLocation:!0}).map(st=>this.looseRoutesById[st.routeId]);if(Object.keys(L).length>0)for(const st of D){const lt=st.options.params?.stringify??st.options.stringifyParams;lt&&Object.assign(L,lt(L))}const V=i.leaveParams?A:ty(lo({path:A,params:L,decodeCharMap:this.pathParamsDecodeCharMap}).interpolatedPath);let Z=S;if(i._includeValidateSearch&&this.options.search?.strict){const st={};D.forEach(lt=>{if(lt.options.validateSearch)try{Object.assign(st,bo(lt.options.validateSearch,{...st,...Z}))}catch{}}),Z=st}Z=dS({search:Z,dest:d,destRoutes:D,_includeValidateSearch:i._includeValidateSearch}),Z=Ye(S,Z);const Q=this.options.stringifySearch(Z),Y=d.hash===!0?p.hash:d.hash?Wn(d.hash,p.hash):void 0,$=Y?`#${Y}`:"";let k=d.state===!0?p.state:d.state?Wn(d.state,p.state):{};k=Ye(p.state,k);const X=`${V}${Q}${$}`,G=new URL(X,this.origin),F=Ky(this.rewrite,G);return{publicHref:F.pathname+F.search+F.hash,href:X,url:F,pathname:V,search:Z,searchStr:Q,state:k,hash:Y??"",unmaskOnReload:d.unmaskOnReload}},o=(d={},p)=>{const m=c(d);let h=p?c(p):void 0;if(!h){const y={};if(this.options.routeMasks){const _=w0(m.pathname,this.processedTree);if(_){Object.assign(y,_.rawParams);const{from:S,params:v,...A}=_.route,L=v===!1||v===null?{}:(v??!0)===!0?y:Object.assign(y,Wn(v,y));p={from:i.from,...A,params:L},h=c(p)}}}return h&&(m.maskedLocation=h),m};return i.mask?o(i,{from:i.from,...i.mask}):o(i)},this.commitLocation=({viewTransition:i,ignoreBlocker:c,...o})=>{const d=()=>{const h=["key","__TSR_key","__TSR_index","__hashScrollIntoViewOptions"];h.forEach(_=>{o.state[_]=this.latestLocation.state[_]});const y=Aa(o.state,this.latestLocation.state);return h.forEach(_=>{delete o.state[_]}),y},p=Ma(this.latestLocation.href)===Ma(o.href),m=this.commitLocationPromise;if(this.commitLocationPromise=xa(()=>{m?.resolve()}),p&&d())this.load();else{let{maskedLocation:h,hashScrollIntoView:y,url:_,...S}=o;h&&(S={...h,state:{...h.state,__tempKey:void 0,__tempLocation:{...S,search:S.searchStr,state:{...S.state,__tempKey:void 0,__tempLocation:void 0,__TSR_key:void 0,key:void 0}}}},(S.unmaskOnReload??this.options.unmaskOnReload??!1)&&(S.state.__tempKey=this.tempLocationKey)),S.state.__hashScrollIntoViewOptions=y??this.options.defaultHashScrollIntoView??!0,this.shouldViewTransition=i,this.history[o.replace?"replace":"push"](S.publicHref,S.state,{ignoreBlocker:c})}return this.resetNextScroll=o.resetScroll??!0,this.history.subscribers.size||this.load(),this.commitLocationPromise},this.buildAndCommitLocation=({replace:i,resetScroll:c,hashScrollIntoView:o,viewTransition:d,ignoreBlocker:p,href:m,...h}={})=>{if(m){const S=this.history.location.state.__TSR_index,v=ds(m,{__TSR_index:i?S:S+1}),A=new URL(v.pathname,this.origin),L=So(this.rewrite,A);h.to=L.pathname,h.search=this.options.parseSearch(v.search),h.hash=v.hash.slice(1)}const y=this.buildLocation({...h,_includeValidateSearch:!0});this.pendingBuiltLocation=y;const _=this.commitLocation({...y,viewTransition:d,replace:i,resetScroll:c,hashScrollIntoView:o,ignoreBlocker:p});return Promise.resolve().then(()=>{this.pendingBuiltLocation===y&&(this.pendingBuiltLocation=void 0)}),_},this.navigate=async({to:i,reloadDocument:c,href:o,publicHref:d,...p})=>{let m=!1;if(o)try{new URL(`${o}`),m=!0}catch{}if(m&&!c&&(c=!0),c){if(i!==void 0||!o){const y=this.buildLocation({to:i,...p});o=o??y.url.href,d=d??y.url.href}const h=!m&&d?d:o;if(ys(h))return Promise.resolve();if(!p.ignoreBlocker){const _=this.history.getBlockers?.()??[];for(const S of _)if(S?.blockerFn&&await S.blockerFn({currentLocation:this.latestLocation,nextLocation:this.latestLocation,action:"PUSH"}))return Promise.resolve()}return p.replace?window.location.replace(h):window.location.href=h,Promise.resolve()}return this.buildAndCommitLocation({...p,href:o,to:i,_isNavigate:!0})},this.beforeLoad=()=>{if(this.cancelMatches(),this.updateLatestLocation(),this.isServer){const c=this.buildLocation({to:this.latestLocation.pathname,search:!0,params:!0,hash:!0,state:!0,_includeValidateSearch:!0});if(this.latestLocation.publicHref!==c.publicHref||c.url.origin!==this.origin){const o=this.getParsedLocationHref(c);throw Hy({href:o})}}const i=this.matchRoutes(this.latestLocation);this.__store.setState(c=>({...c,status:"pending",statusCode:200,isLoading:!0,location:this.latestLocation,pendingMatches:i,cachedMatches:c.cachedMatches.filter(o=>!i.some(d=>d.id===o.id))}))},this.load=async i=>{let c,o,d;for(d=new Promise(m=>{this.startTransition(async()=>{try{this.beforeLoad();const h=this.latestLocation,y=this.state.resolvedLocation;this.state.redirect||this.emit({type:"onBeforeNavigate",...Oa({resolvedLocation:y,location:h})}),this.emit({type:"onBeforeLoad",...Oa({resolvedLocation:y,location:h})}),await ny({router:this,sync:i?.sync,matches:this.state.pendingMatches,location:h,updateMatch:this.updateMatch,onReady:async()=>{this.startTransition(()=>{this.startViewTransition(async()=>{let _=[],S=[],v=[];Rl(()=>{this.__store.setState(A=>{const L=A.matches,x=A.pendingMatches||A.matches;return _=L.filter(D=>!x.some(V=>V.id===D.id)),S=x.filter(D=>!L.some(V=>V.id===D.id)),v=x.filter(D=>L.some(V=>V.id===D.id)),{...A,isLoading:!1,loadedAt:Date.now(),matches:x,pendingMatches:void 0,cachedMatches:[...A.cachedMatches,..._.filter(D=>D.status!=="error"&&D.status!=="notFound")]}}),this.clearExpiredCache()}),[[_,"onLeave"],[S,"onEnter"],[v,"onStay"]].forEach(([A,L])=>{A.forEach(x=>{this.looseRoutesById[x.routeId].options[L]?.(x)})})})})}})}catch(h){Ge(h)?(c=h,this.isServer||this.navigate({...c.options,replace:!0,ignoreBlocker:!0})):oe(h)&&(o=h),this.__store.setState(y=>({...y,statusCode:c?c.status:o?404:y.matches.some(_=>_.status==="error")?500:200,redirect:c}))}this.latestLoadPromise===d&&(this.commitLocationPromise?.resolve(),this.latestLoadPromise=void 0,this.commitLocationPromise=void 0),m()})}),this.latestLoadPromise=d,await d;this.latestLoadPromise&&d!==this.latestLoadPromise;)await this.latestLoadPromise;let p;this.hasNotFoundMatch()?p=404:this.__store.state.matches.some(m=>m.status==="error")&&(p=500),p!==void 0&&this.__store.setState(m=>({...m,statusCode:p}))},this.startViewTransition=i=>{const c=this.shouldViewTransition??this.options.defaultViewTransition;if(delete this.shouldViewTransition,c&&typeof document<"u"&&"startViewTransition"in document&&typeof document.startViewTransition=="function"){let o;if(typeof c=="object"&&this.isViewTransitionTypesSupported){const d=this.latestLocation,p=this.state.resolvedLocation,m=typeof c.types=="function"?c.types(Oa({resolvedLocation:p,location:d})):c.types;if(m===!1){i();return}o={update:i,types:m}}else o=i;document.startViewTransition(o)}else i()},this.updateMatch=(i,c)=>{this.startTransition(()=>{const o=this.state.pendingMatches?.some(d=>d.id===i)?"pendingMatches":this.state.matches.some(d=>d.id===i)?"matches":this.state.cachedMatches.some(d=>d.id===i)?"cachedMatches":"";o&&this.__store.setState(d=>({...d,[o]:d[o]?.map(p=>p.id===i?c(p):p)}))})},this.getMatch=i=>{const c=o=>o.id===i;return this.state.cachedMatches.find(c)??this.state.pendingMatches?.find(c)??this.state.matches.find(c)},this.invalidate=i=>{const c=o=>i?.filter?.(o)??!0?{...o,invalid:!0,...i?.forcePending||o.status==="error"||o.status==="notFound"?{status:"pending",error:void 0}:void 0}:o;return this.__store.setState(o=>({...o,matches:o.matches.map(c),cachedMatches:o.cachedMatches.map(c),pendingMatches:o.pendingMatches?.map(c)})),this.shouldViewTransition=!1,this.load({sync:i?.sync})},this.getParsedLocationHref=i=>{let c=i.url.href;return this.origin&&i.url.origin===this.origin&&(c=c.replace(this.origin,"")||"/"),c},this.resolveRedirect=i=>{const c=i.headers.get("Location");if(i.options.href){if(c)try{const o=new URL(c);if(this.origin&&o.origin===this.origin){const d=o.pathname+o.search+o.hash;i.options.href=d,i.headers.set("Location",d)}}catch{}}else{const o=this.buildLocation(i.options),d=this.getParsedLocationHref(o);i.options.href=d,i.headers.set("Location",d)}return i.headers.get("Location")||i.headers.set("Location",i.options.href),i},this.clearCache=i=>{const c=i?.filter;c!==void 0?this.__store.setState(o=>({...o,cachedMatches:o.cachedMatches.filter(d=>!c(d))})):this.__store.setState(o=>({...o,cachedMatches:[]}))},this.clearExpiredCache=()=>{const i=c=>{const o=this.looseRoutesById[c.routeId];if(!o.options.loader)return!0;const d=(c.preload?o.options.preloadGcTime??this.options.defaultPreloadGcTime:o.options.gcTime??this.options.defaultGcTime)??300*1e3;return c.status==="error"?!0:Date.now()-c.updatedAt>=d};this.clearCache({filter:i})},this.loadRouteChunk=Xy,this.preloadRoute=async i=>{const c=this.buildLocation(i);let o=this.matchRoutes(c,{throwOnError:!0,preload:!0,dest:i});const d=new Set([...this.state.matches,...this.state.pendingMatches??[]].map(m=>m.id)),p=new Set([...d,...this.state.cachedMatches.map(m=>m.id)]);Rl(()=>{o.forEach(m=>{p.has(m.id)||this.__store.setState(h=>({...h,cachedMatches:[...h.cachedMatches,m]}))})});try{return o=await ny({router:this,matches:o,location:c,preload:!0,updateMatch:(m,h)=>{d.has(m)?o=o.map(y=>y.id===m?h(y):y):this.updateMatch(m,h)}}),o}catch(m){if(Ge(m))return m.options.reloadDocument?void 0:await this.preloadRoute({...m.options,_fromLocation:c});oe(m)||console.error(m);return}},this.matchRoute=(i,c)=>{const o={...i,to:i.to?this.resolvePathWithBase(i.from||"",i.to):void 0,params:i.params||{},leaveParams:!0},d=this.buildLocation(o);if(c?.pending&&this.state.status!=="pending")return!1;const m=(c?.pending===void 0?!this.state.isLoading:c.pending)?this.latestLocation:this.state.resolvedLocation||this.state.location,h=C0(d.pathname,c?.caseSensitive??!1,c?.fuzzy??!1,m.pathname,this.processedTree);return!h||i.params&&!Aa(h.rawParams,i.params,{partial:!0})?!1:c?.includeSearch??!0?Aa(m.search,d.search,{partial:!0})?h.rawParams:!1:h.rawParams},this.hasNotFoundMatch=()=>this.__store.state.matches.some(i=>i.status==="notFound"||i.globalNotFound),this.update({defaultPreloadDelay:50,defaultPendingMs:1e3,defaultPendingMinMs:500,context:void 0,...u,caseSensitive:u.caseSensitive??!1,notFoundMode:u.notFoundMode??"fuzzy",stringifySearch:u.stringifySearch??F0,parseSearch:u.parseSearch??k0}),typeof document<"u"&&(self.__TSR_ROUTER__=this)}isShell(){return!!this.options.isShell}isPrerendering(){return!!this.options.isPrerendering}get state(){return this.__store.state}get looseRoutesById(){return this.routesById}matchRoutesInternal(u,i){const c=this.getMatchedRoutes(u.pathname),{foundRoute:o,routeParams:d,parsedParams:p}=c;let{matchedRoutes:m}=c,h=!1;(o?o.path!=="/"&&d["**"]:Ma(u.pathname))&&(this.options.notFoundRoute?m=[...m,this.options.notFoundRoute]:h=!0);const y=(()=>{if(h){if(this.options.notFoundMode!=="root")for(let v=m.length-1;v>=0;v--){const A=m[v];if(A.children)return A.id}return ze}})(),_=[],S=v=>v?.id?v.context??this.options.context??void 0:this.options.context??void 0;return m.forEach((v,A)=>{const L=_[A-1],[x,D,V]=(()=>{const St=L?.search??u.search,Ut=L?._strictSearch??void 0;try{const U=bo(v.options.validateSearch,{...St})??void 0;return[{...St,...U},{...Ut,...U},void 0]}catch(U){let J=U;if(U instanceof bs||(J=new bs(U.message,{cause:U})),i?.throwOnError)throw J;return[St,{},J]}})(),Z=v.options.loaderDeps?.({search:x})??"",Q=Z?JSON.stringify(Z):"",{interpolatedPath:Y,usedParams:$}=lo({path:v.fullPath,params:d,decodeCharMap:this.pathParamsDecodeCharMap}),k=v.id+Y+Q,X=this.getMatch(k),G=this.state.matches.find(St=>St.routeId===v.id),F=X?._strictParams??$;let st;if(!X)if(v.options.skipRouteOnParseError)for(const St in $)St in p&&(F[St]=p[St]);else{const St=v.options.params?.parse??v.options.parseParams;if(St)try{Object.assign(F,St(F))}catch(Ut){if(oe(Ut)||Ge(Ut)?st=Ut:st=new cS(Ut.message,{cause:Ut}),i?.throwOnError)throw st}}Object.assign(d,F);const lt=G?"stay":"enter";let ct;if(X)ct={...X,cause:lt,params:G?Ye(G.params,d):d,_strictParams:F,search:Ye(G?G.search:X.search,x),_strictSearch:D};else{const St=v.options.loader||v.options.beforeLoad||v.lazyFn||Qy(v)?"pending":"success";ct={id:k,ssr:this.isServer?void 0:v.options.ssr,index:A,routeId:v.id,params:G?Ye(G.params,d):d,_strictParams:F,pathname:Y,updatedAt:Date.now(),search:G?Ye(G.search,x):x,_strictSearch:D,searchError:void 0,status:St,isFetching:!1,error:void 0,paramsError:st,__routeContext:void 0,_nonReactive:{loadPromise:xa()},__beforeLoadContext:void 0,context:{},abortController:new AbortController,fetchCount:0,cause:lt,loaderDeps:G?Ye(G.loaderDeps,Z):Z,invalid:!1,preload:!1,links:void 0,scripts:void 0,headScripts:void 0,meta:void 0,staticData:v.options.staticData||{},fullPath:v.fullPath}}i?.preload||(ct.globalNotFound=y===v.id),ct.searchError=V;const qt=S(L);ct.context={...qt,...ct.__routeContext,...ct.__beforeLoadContext},_.push(ct)}),_.forEach((v,A)=>{const L=this.looseRoutesById[v.routeId];if(!this.getMatch(v.id)&&i?._buildLocation!==!0){const D=_[A-1],V=S(D);if(L.options.context){const Z={deps:v.loaderDeps,params:v.params,context:V??{},location:u,navigate:Q=>this.navigate({...Q,_fromLocation:u}),buildLocation:this.buildLocation,cause:v.cause,abortController:v.abortController,preload:!!v.preload,matches:_};v.__routeContext=L.options.context(Z)??void 0}v.context={...V,...v.__routeContext,...v.__beforeLoadContext}}}),_}}class bs extends Error{}class cS extends Error{}function oS(a){return{loadedAt:0,isLoading:!1,isTransitioning:!1,status:"idle",resolvedLocation:void 0,location:a,matches:[],pendingMatches:[],cachedMatches:[],statusCode:200}}function bo(a,u){if(a==null)return{};if("~standard"in a){const i=a["~standard"].validate(u);if(i instanceof Promise)throw new bs("Async validation not supported");if(i.issues)throw new bs(JSON.stringify(i.issues,void 0,2),{cause:i});return i.value}return"parse"in a?a.parse(u):typeof a=="function"?a(u):{}}function fS({pathname:a,routesById:u,processedTree:i}){const c={},o=Ma(a);let d,p;const m=D0(o,i,!0);return m&&(d=m.route,Object.assign(c,m.rawParams),p=Object.assign({},m.parsedParams)),{matchedRoutes:m?.branch||[u[ze]],routeParams:c,foundRoute:d,parsedParams:p}}function dS({search:a,dest:u,destRoutes:i,_includeValidateSearch:c}){const o=i.reduce((m,h)=>{const y=[];if("search"in h.options)h.options.search?.middlewares&&y.push(...h.options.search.middlewares);else if(h.options.preSearchFilters||h.options.postSearchFilters){const _=({search:S,next:v})=>{let A=S;"preSearchFilters"in h.options&&h.options.preSearchFilters&&(A=h.options.preSearchFilters.reduce((x,D)=>D(x),S));const L=v(A);return"postSearchFilters"in h.options&&h.options.postSearchFilters?h.options.postSearchFilters.reduce((x,D)=>D(x),L):L};y.push(_)}if(c&&h.options.validateSearch){const _=({search:S,next:v})=>{const A=v(S);try{return{...A,...bo(h.options.validateSearch,A)??void 0}}catch{return A}};y.push(_)}return m.concat(y)},[])??[],d=({search:m})=>u.search?u.search===!0?m:Wn(u.search,m):{};o.push(d);const p=(m,h)=>{if(m>=o.length)return h;const y=o[m];return y({search:h,next:S=>p(m+1,S)})};return p(0,a)}const Pe=Symbol.for("TSR_DEFERRED_PROMISE");function hS(a,u){const i=a;return i[Pe]||(i[Pe]={status:"pending"},i.then(c=>{i[Pe].status="success",i[Pe].data=c}).catch(c=>{i[Pe].status="error",i[Pe].error={data:sS(c),__isServerError:!0}})),i}const mS="Error preloading route! ☝️";class Jy{constructor(u){if(this.init=i=>{this.originalIndex=i.originalIndex;const c=this.options,o=!c?.path&&!c?.id;this.parentRoute=this.options.getParentRoute?.(),o?this._path=ze:this.parentRoute||ve(!1);let d=o?ze:c?.path;d&&d!=="/"&&(d=Ny(d));const p=c?.id||d;let m=o?ze:rs([this.parentRoute.id===ze?"":this.parentRoute.id,p]);d===ze&&(d="/"),m!==ze&&(m=rs(["/",m]));const h=m===ze?"/":rs([this.parentRoute.fullPath,d]);this._path=d,this._id=m,this._fullPath=h,this._to=h},this.addChildren=i=>this._addFileChildren(i),this._addFileChildren=i=>(Array.isArray(i)&&(this.children=i),typeof i=="object"&&i!==null&&(this.children=Object.values(i)),this),this._addFileTypes=()=>this,this.updateLoader=i=>(Object.assign(this.options,i),this),this.update=i=>(Object.assign(this.options,i),this),this.lazy=i=>(this.lazyFn=i,this),this.options=u||{},this.isRoot=!u?.getParentRoute,u?.id&&u?.path)throw new Error("Route cannot have both an 'id' and a 'path' option.")}get to(){return this._to}get id(){return this._id}get path(){return this._path}get fullPath(){return this._fullPath}}class yS extends Jy{constructor(u){super(u)}}var pS=(a=>(a[a.AggregateError=1]="AggregateError",a[a.ArrowFunction=2]="ArrowFunction",a[a.ErrorPrototypeStack=4]="ErrorPrototypeStack",a[a.ObjectAssign=8]="ObjectAssign",a[a.BigIntTypedArray=16]="BigIntTypedArray",a[a.RegExp=32]="RegExp",a))(pS||{}),bn=Symbol.asyncIterator,ky=Symbol.hasInstance,Tl=Symbol.isConcatSpreadable,_n=Symbol.iterator,Fy=Symbol.match,Py=Symbol.matchAll,$y=Symbol.replace,Wy=Symbol.search,Iy=Symbol.species,tp=Symbol.split,ep=Symbol.toPrimitive,Al=Symbol.toStringTag,np=Symbol.unscopables,ap={[bn]:0,[ky]:1,[Tl]:2,[_n]:3,[Fy]:4,[Py]:5,[$y]:6,[Wy]:7,[Iy]:8,[tp]:9,[ep]:10,[Al]:11,[np]:12},gS={0:bn,1:ky,2:Tl,3:_n,4:Fy,5:Py,6:$y,7:Wy,8:Iy,9:tp,10:ep,11:Al,12:np},b=void 0,vS={2:!0,3:!1,1:b,0:null,4:-0,5:Number.POSITIVE_INFINITY,6:Number.NEGATIVE_INFINITY,7:Number.NaN},SS={0:"Error",1:"EvalError",2:"RangeError",3:"ReferenceError",4:"SyntaxError",5:"TypeError",6:"URIError"},bS={0:Error,1:EvalError,2:RangeError,3:ReferenceError,4:SyntaxError,5:TypeError,6:URIError};function Tt(a,u,i,c,o,d,p,m,h,y,_,S){return{t:a,i:u,s:i,c,m:o,p:d,e:p,a:m,f:h,b:y,o:_,l:S}}function na(a){return Tt(2,b,a,b,b,b,b,b,b,b,b,b)}var lp=na(2),ip=na(3),_S=na(1),ES=na(0),RS=na(4),TS=na(5),AS=na(6),MS=na(7);function OS(a){switch(a){case'"':return'\\"';case"\\":return"\\\\";case` -`:return"\\n";case"\r":return"\\r";case"\b":return"\\b";case" ":return"\\t";case"\f":return"\\f";case"<":return"\\x3C";case"\u2028":return"\\u2028";case"\u2029":return"\\u2029";default:return b}}function aa(a){let u="",i=0,c;for(let o=0,d=a.length;on1(a),fp=class extends Error{constructor(a,u){super(a1(a)),this.cause=u}},ay=class extends fp{constructor(a){super("parsing",a)}},l1=class extends fp{constructor(a){super("deserialization",a)}};function En(a){return`Seroval Error (specific: ${a})`}var Os=class extends Error{constructor(a){super(En(1)),this.value=a}},dp=class extends Error{constructor(a){super(En(2))}},i1=class extends Error{constructor(a){super(En(3))}},Bi=class extends Error{constructor(a){super(En(4))}},u1=class extends Error{constructor(a){super(En(5)),this.value=a}},s1=class extends Error{constructor(a){super(En(6))}},r1=class extends Error{constructor(a){super(En(7))}},la=class extends Error{constructor(u){super(En(8))}},c1=class extends Error{constructor(u){super(En(9))}},o1=class{constructor(a,u){this.value=a,this.replacement=u}},xs=()=>{let a={p:0,s:0,f:0};return a.p=new Promise((u,i)=>{a.s=u,a.f=i}),a},f1=(a,u)=>{a.s(u),a.p.s=1,a.p.v=u},d1=(a,u)=>{a.f(u),a.p.s=2,a.p.v=u};xs.toString();f1.toString();d1.toString();var h1=()=>{let a=[],u=[],i=!0,c=!1,o=0,d=(h,y,_)=>{for(_=0;_{for(y=0,_=a.length;y<_;y++)S=a[y],!i&&y===_-1?h[c?"return":"throw"](S):h.next(S)},m=(h,y)=>(i&&(y=o++,u[y]=h),p(h),()=>{i&&(u[y]=u[o],u[o--]=void 0)});return{__SEROVAL_STREAM__:!0,on:h=>m(h),next:h=>{i&&(a.push(h),d(h,"next"))},throw:h=>{i&&(a.push(h),d(h,"throw"),i=!1,c=!1,u.length=0)},return:h=>{i&&(a.push(h),d(h,"return"),i=!1,c=!0,u.length=0)}}},m1=a=>u=>()=>{let i=0,c={[a]:()=>c,next:()=>{if(i>u.d)return{done:!0,value:void 0};let o=i++,d=u.v[o];if(o===u.t)throw d;return{done:o===u.d,value:d}}};return c},y1=(a,u)=>i=>()=>{let c=0,o=-1,d=!1,p=[],m=[],h=(_=0,S=m.length)=>{for(;_{let S=m.shift();S&&S.s({done:!1,value:_}),p.push(_)},throw:_=>{let S=m.shift();S&&S.f(_),h(),o=p.length,d=!0,p.push(_)},return:_=>{let S=m.shift();S&&S.s({done:!0,value:_}),h(),o=p.length,p.push(_)}});let y={[a]:()=>y,next:()=>{if(o===-1){let v=c++;if(v>=p.length){let A=u();return m.push(A),A.p}return{done:!1,value:p[v]}}if(c>o)return{done:!0,value:void 0};let _=c++,S=p[_];if(_!==o)return{done:!1,value:S};if(d)throw S;return{done:!0,value:S}}};return y},hp=a=>{let u=atob(a),i=u.length,c=new Uint8Array(i);for(let o=0;o{}),u}var b1=y1(bn,xs);function _1(a){return b1(a)}function E1(a){let u=[],i=-1,c=-1,o=a[_n]();for(;;)try{let d=o.next();if(u.push(d.value),d.done){c=u.length-1;break}}catch(d){i=u.length,u.push(d)}return{v:u,t:i,d:c}}var R1=m1(_n);function T1(a){return R1(a)}async function A1(a){try{return[1,await a]}catch(u){return[0,u]}}function M1(a,u){return{plugins:u.plugins,mode:a,marked:new Set,features:63^(u.disabledFeatures||0),refs:u.refs||new Map,depthLimit:u.depthLimit||1e3}}function os(a,u){a.marked.add(u)}function O1(a,u){let i=a.refs.size;return a.refs.set(u,i),i}function ws(a,u){let i=a.refs.get(u);return i!=null?(os(a,i),{type:1,value:US(i)}):{type:0,value:O1(a,u)}}function zo(a,u){let i=ws(a,u);return i.type===1?i:sp(u)?{type:2,value:qS(i.value,u)}:i}function Ta(a,u){let i=zo(a,u);if(i.type!==0)return i.value;if(u in ap)return HS(i.value,u);throw new Os(u)}function Cs(a,u){let i=ws(a,v1[u]);return i.type===1?i.value:Tt(26,i.value,u,b,b,b,b,b,b,b,b,b)}function x1(a){let u=ws(a,p1);return u.type===1?u.value:Tt(27,u.value,b,b,b,b,b,b,Ta(a,_n),b,b,b)}function z1(a){let u=ws(a,g1);return u.type===1?u.value:Tt(29,u.value,b,b,b,b,b,[Cs(a,1),Ta(a,bn)],b,b,b,b)}function w1(a,u,i,c){return Tt(i?11:10,a,b,b,b,c,b,b,b,b,cp(u),b)}function C1(a,u,i,c){return Tt(8,u,b,b,b,b,{k:i,v:c},b,Cs(a,0),b,b,b)}function D1(a,u,i){let c=new Uint8Array(i),o="";for(let d=0,p=c.length;d{os(this.base,u),te(this,a,m).then(h=>{d.push(WS(u,h))},h=>{o(h),p()})},throw:m=>{os(this.base,u),te(this,a,m).then(h=>{d.push(IS(u,h)),c(d),p()},h=>{o(h),p()})},return:m=>{os(this.base,u),te(this,a,m).then(h=>{d.push(t1(u,h)),c(d),p()},h=>{o(h),p()})}})}async function K1(a,u,i,c){return $S(i,Cs(a.base,4),await new Promise(Z1.bind(a,u,i,c)))}async function J1(a,u,i,c){if(Array.isArray(c))return B1(a,u,i,c);if(zs(c))return K1(a,u,i,c);let o=c.constructor;if(o===o1)return te(a,u,c.replacement);let d=await mp(a,u,i,c);if(d)return d;switch(o){case Object:return uo(a,u,i,c,!1);case b:return uo(a,u,i,c,!0);case Date:return BS(i,c);case Error:case EvalError:case RangeError:case ReferenceError:case SyntaxError:case TypeError:case URIError:return ly(a,u,i,c);case Number:case Boolean:case String:case BigInt:return j1(a,u,i,c);case ArrayBuffer:return D1(a.base,i,c);case Int8Array:case Int16Array:case Int32Array:case Uint8Array:case Uint16Array:case Uint32Array:case Uint8ClampedArray:case Float32Array:case Float64Array:return H1(a,u,i,c);case DataView:return Y1(a,u,i,c);case Map:return V1(a,u,i,c);case Set:return X1(a,u,i,c)}if(o===Promise||c instanceof Promise)return Q1(a,u,i,c);let p=a.base.features;if(p&32&&o===RegExp)return jS(i,c);if(p&16)switch(o){case BigInt64Array:case BigUint64Array:return q1(a,u,i,c)}if(p&1&&typeof AggregateError<"u"&&(o===AggregateError||c instanceof AggregateError))return G1(a,u,i,c);if(c instanceof Error)return ly(a,u,i,c);if(_n in c||bn in c)return uo(a,u,i,c,!!o);throw new Os(c)}async function k1(a,u,i){let c=zo(a.base,i);if(c.type!==0)return c.value;let o=await mp(a,u,c.value,i);if(o)return o;throw new Os(i)}async function te(a,u,i){switch(typeof i){case"boolean":return i?lp:ip;case"undefined":return _S;case"string":return op(i);case"number":return LS(i);case"bigint":return NS(i);case"object":{if(i){let c=zo(a.base,i);return c.type===0?await J1(a,u+1,c.value,i):c.value}return ES}case"symbol":return Ta(a.base,i);case"function":return k1(a,u,i);default:throw new Os(i)}}async function F1(a,u){try{return await te(a,0,u)}catch(i){throw i instanceof ay?i:new ay(i)}}var P1=(a=>(a[a.Vanilla=1]="Vanilla",a[a.Cross=2]="Cross",a))(P1||{});function yp(a,u){for(let i=0,c=u.length;i0)for(let d=0,p=i.v,m=o.length;dtb)throw new la(u);return ee(a,u.i,new RegExp(i,u.m))}throw new dp(u)}function yb(a,u,i){let c=ee(a,i.i,new Set);for(let o=0,d=i.a,p=d.length;oW1)throw new la(u);return ee(a,u.i,hp(Ca(u.s)))}function vb(a,u,i){var c;let o=$1(i.c),d=Bt(a,u,i.f),p=(c=i.b)!=null?c:0;if(p<0||p>d.byteLength)throw new la(i);return ee(a,i.i,new o(d,p,i.l))}function Sb(a,u,i){var c;let o=Bt(a,u,i.f),d=(c=i.b)!=null?c:0;if(d<0||d>o.byteLength)throw new la(i);return ee(a,i.i,new DataView(o,d,i.l))}function bp(a,u,i,c){if(i.p){let o=Sp(a,u,i.p,{});Object.defineProperties(c,Object.getOwnPropertyDescriptors(o))}return c}function bb(a,u,i){let c=ee(a,i.i,new AggregateError([],Ca(i.m)));return bp(a,u,i,c)}function _b(a,u,i){let c=_o(i,bS,i.s),o=ee(a,i.i,new c(Ca(i.m)));return bp(a,u,i,o)}function Eb(a,u,i){let c=xs(),o=ee(a,i.i,c.p),d=Bt(a,u,i.f);return i.s?c.s(d):c.f(d),o}function Rb(a,u,i){return ee(a,i.i,Object(Bt(a,u,i.f)))}function Tb(a,u,i){let c=a.base.plugins;if(c){let o=Ca(i.c);for(let d=0,p=c.length;da.base.depthLimit)throw new c1(a.base.depthLimit);switch(u+=1,i.t){case 2:return _o(i,vS,i.s);case 0:return Number(i.s);case 1:return Ca(String(i.s));case 3:if(String(i.s).length>I1)throw new la(i);return BigInt(i.s);case 4:return a.base.refs.get(i.i);case 18:return sb(a,i);case 9:return rb(a,u,i);case 10:case 11:return db(a,u,i);case 5:return hb(a,i);case 6:return mb(a,i);case 7:return yb(a,u,i);case 8:return pb(a,u,i);case 19:return gb(a,i);case 16:case 15:return vb(a,u,i);case 20:return Sb(a,u,i);case 14:return bb(a,u,i);case 13:return _b(a,u,i);case 12:return Eb(a,u,i);case 17:return _o(i,gS,i.s);case 21:return Rb(a,u,i);case 25:return Tb(a,u,i);case 22:return Ab(a,i);case 23:return Mb(a,u,i);case 24:return Ob(a,u,i);case 28:return xb(a,u,i);case 30:return zb(a,u,i);case 31:return wb(a,u,i);case 32:return Cb(a,u,i);case 33:return Db(a,u,i);case 34:return Lb(a,u,i);case 27:return Nb(a,u,i);case 29:return Ub(a,u,i);default:throw new dp(i)}}function Bb(a,u){try{return Bt(a,0,u)}catch(i){throw new l1(i)}}var jb=()=>T;jb.toString();function so(a,u){let i=pp(u.plugins),c=ab({plugins:i,refs:u.refs,features:u.features,disabledFeatures:u.disabledFeatures});return Bb(c,a)}async function Hb(a,u={}){let i=pp(u.plugins),c=L1(1,{plugins:i,disabledFeatures:u.disabledFeatures});return{t:await F1(c,a),f:c.base.features,m:Array.from(c.base.marked)}}function qb(a){return{tag:"$TSR/t/"+a.key,test:a.test,parse:{sync(u,i){return i.parse(a.toSerializable(u))},async async(u,i){return await i.parse(a.toSerializable(u))},stream(u,i){return i.parse(a.toSerializable(u))}},serialize:void 0,deserialize(u,i){return a.fromSerializable(i.deserialize(u))}}}var wi={},_p=a=>new ReadableStream({start:u=>{a.on({next:i=>{try{u.enqueue(i)}catch{}},throw:i=>{u.error(i)},return:()=>{try{u.close()}catch{}}})}}),Yb={tag:"seroval-plugins/web/ReadableStreamFactory",test(a){return a===wi},parse:{sync(){},async async(){return await Promise.resolve(void 0)},stream(){}},serialize(){return _p.toString()},deserialize(){return wi}};function uy(a){let u=Da(),i=a.getReader();async function c(){try{let o=await i.read();o.done?u.return(o.value):(u.next(o.value),await c())}catch(o){u.throw(o)}}return c().catch(()=>{}),u}var Gb={tag:"seroval/plugins/web/ReadableStream",extends:[Yb],test(a){return typeof ReadableStream>"u"?!1:a instanceof ReadableStream},parse:{sync(a,u){return{factory:u.parse(wi),stream:u.parse(Da())}},async async(a,u){return{factory:await u.parse(wi),stream:await u.parse(uy(a))}},stream(a,u){return{factory:u.parse(wi),stream:u.parse(uy(a))}}},serialize(a,u){return"("+u.serialize(a.factory)+")("+u.serialize(a.stream)+")"},deserialize(a,u){let i=u.deserialize(a.stream);return _p(i)}},Vb=Gb;const Xb={tag:"$TSR/Error",test(a){return a instanceof Error},parse:{sync(a,u){return{message:u.parse(a.message)}},async async(a,u){return{message:await u.parse(a.message)}},stream(a,u){return{message:u.parse(a.message)}}},serialize(a,u){return"new Error("+u.serialize(a.message)+")"},deserialize(a,u){return new Error(u.deserialize(a.message))}};class Qb{constructor(u,i){this.stream=u,this.hint=i?.hint??"binary"}}const _s=globalThis.Buffer,Ep=!!_s&&typeof _s.from=="function";function Rp(a){if(a.length===0)return"";if(Ep)return _s.from(a).toString("base64");const u=32768,i=[];for(let c=0;cnew ReadableStream({start(u){a.on({next(i){try{u.enqueue(Tp(i))}catch{}},throw(i){u.error(i)},return(){try{u.close()}catch{}}})}}),Kb=new TextEncoder,Jb=a=>new ReadableStream({start(u){a.on({next(i){try{typeof i=="string"?u.enqueue(Kb.encode(i)):u.enqueue(Tp(i.$b64))}catch{}},throw(i){u.error(i)},return(){try{u.close()}catch{}}})}}),kb="(s=>new ReadableStream({start(c){s.on({next(b){try{const d=atob(b),a=new Uint8Array(d.length);for(let i=0;i{const e=new TextEncoder();return new ReadableStream({start(c){s.on({next(v){try{if(typeof v==='string'){c.enqueue(e.encode(v))}else{const d=atob(v.$b64),a=new Uint8Array(d.length);for(let i=0;i{try{for(;;){const{done:c,value:o}=await i.read();if(c){u.return(void 0);break}u.next(Rp(o))}}catch(c){u.throw(c)}finally{i.releaseLock()}})(),u}function ry(a){const u=Da(),i=a.getReader(),c=new TextDecoder("utf-8",{fatal:!0});return(async()=>{try{for(;;){const{done:o,value:d}=await i.read();if(o){try{const p=c.decode();p.length>0&&u.next(p)}catch{}u.return(void 0);break}try{const p=c.decode(d,{stream:!0});p.length>0&&u.next(p)}catch{u.next({$b64:Rp(d)})}}}catch(o){u.throw(o)}finally{i.releaseLock()}})(),u}const Pb={tag:"tss/RawStreamFactory",test(a){return a===Ci},parse:{sync(){},async(){return Promise.resolve(void 0)},stream(){}},serialize(){return kb},deserialize(){return Ci}},$b={tag:"tss/RawStreamFactoryText",test(a){return a===Di},parse:{sync(){},async(){return Promise.resolve(void 0)},stream(){}},serialize(){return Fb},deserialize(){return Di}},Wb={tag:"tss/RawStream",extends:[Pb,$b],test(a){return a instanceof Qb},parse:{sync(a,u){const i=a.hint==="text"?Di:Ci;return{hint:a.hint,factory:u.parse(i),stream:u.parse(Da())}},async async(a,u){const i=a.hint==="text"?Di:Ci,c=a.hint==="text"?ry(a.stream):sy(a.stream);return{hint:a.hint,factory:await u.parse(i),stream:await u.parse(c)}},stream(a,u){const i=a.hint==="text"?Di:Ci,c=a.hint==="text"?ry(a.stream):sy(a.stream);return{hint:a.hint,factory:u.parse(i),stream:u.parse(c)}}},serialize(a,u){return"("+u.serialize(a.factory)+")("+u.serialize(a.stream)+")"},deserialize(a,u){const i=u.deserialize(a.stream);return a.hint==="text"?Jb(i):Zb(i)}};function Ib(a){return{tag:"tss/RawStream",test:()=>!1,parse:{},serialize(){throw new Error("RawStreamDeserializePlugin.serialize should not be called. Client only deserializes.")},deserialize(u){return a(u.streamId)}}}const t_=[Xb,Wb,Vb];function e_({promise:a}){if(et.use)return et.use(a);const u=hS(a);if(u[Pe].status==="pending")throw u;if(u[Pe].status==="error")throw u[Pe].error;return u[Pe].data}function n_(a){const u=K.jsx(a_,{...a});return a.fallback?K.jsx(et.Suspense,{fallback:a.fallback,children:u}):u}function a_(a){const u=e_(a);return a.children(u)}function Co(a){const u=a.errorComponent??Ds;return K.jsx(l_,{getResetKey:a.getResetKey,onCatch:a.onCatch,children:({error:i,reset:c})=>i?et.createElement(u,{error:i,reset:c}):a.children})}class l_ extends et.Component{constructor(){super(...arguments),this.state={error:null}}static getDerivedStateFromProps(u){return{resetKey:u.getResetKey()}}static getDerivedStateFromError(u){return{error:u}}reset(){this.setState({error:null})}componentDidUpdate(u,i){i.error&&i.resetKey!==this.state.resetKey&&this.reset()}componentDidCatch(u,i){this.props.onCatch&&this.props.onCatch(u,i)}render(){return this.props.children({error:this.state.resetKey!==this.props.getResetKey()?null:this.state.error,reset:()=>{this.reset()}})}}function Ds({error:a}){const[u,i]=et.useState(!1);return K.jsxs("div",{style:{padding:".5rem",maxWidth:"100%"},children:[K.jsxs("div",{style:{display:"flex",alignItems:"center",gap:".5rem"},children:[K.jsx("strong",{style:{fontSize:"1rem"},children:"Something went wrong!"}),K.jsx("button",{style:{appearance:"none",fontSize:".6em",border:"1px solid currentColor",padding:".1rem .2rem",fontWeight:"bold",borderRadius:".25rem"},onClick:()=>i(c=>!c),children:u?"Hide Error":"Show Error"})]}),K.jsx("div",{style:{height:".25rem"}}),u?K.jsx("div",{children:K.jsx("pre",{style:{fontSize:".7em",border:"1px solid red",borderRadius:".25rem",padding:".3rem",color:"red",overflow:"auto"},children:a.message?K.jsx("code",{children:a.message}):null})}):null]})}function i_({children:a,fallback:u=null}){return u_()?K.jsx(Li.Fragment,{children:a}):K.jsx(Li.Fragment,{children:u})}function u_(){return Li.useSyncExternalStore(s_,()=>!0,()=>!1)}function s_(){return()=>{}}var ro={exports:{}},co={},oo={exports:{}},fo={};var cy;function r_(){if(cy)return fo;cy=1;var a=Ui();function u(S,v){return S===v&&(S!==0||1/S===1/v)||S!==S&&v!==v}var i=typeof Object.is=="function"?Object.is:u,c=a.useState,o=a.useEffect,d=a.useLayoutEffect,p=a.useDebugValue;function m(S,v){var A=v(),L=c({inst:{value:A,getSnapshot:v}}),x=L[0].inst,D=L[1];return d(function(){x.value=A,x.getSnapshot=v,h(x)&&D({inst:x})},[S,A,v]),o(function(){return h(x)&&D({inst:x}),S(function(){h(x)&&D({inst:x})})},[S]),p(A),A}function h(S){var v=S.getSnapshot;S=S.value;try{var A=v();return!i(S,A)}catch{return!0}}function y(S,v){return v()}var _=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?y:m;return fo.useSyncExternalStore=a.useSyncExternalStore!==void 0?a.useSyncExternalStore:_,fo}var oy;function c_(){return oy||(oy=1,oo.exports=r_()),oo.exports}var fy;function o_(){if(fy)return co;fy=1;var a=Ui(),u=c_();function i(y,_){return y===_&&(y!==0||1/y===1/_)||y!==y&&_!==_}var c=typeof Object.is=="function"?Object.is:i,o=u.useSyncExternalStore,d=a.useRef,p=a.useEffect,m=a.useMemo,h=a.useDebugValue;return co.useSyncExternalStoreWithSelector=function(y,_,S,v,A){var L=d(null);if(L.current===null){var x={hasValue:!1,value:null};L.current=x}else x=L.current;L=m(function(){function V(k){if(!Z){if(Z=!0,Q=k,k=v(k),A!==void 0&&x.hasValue){var X=x.value;if(A(X,k))return Y=X}return Y=k}if(X=Y,c(Q,k))return X;var G=v(k);return A!==void 0&&A(X,G)?(Q=k,X):(Q=k,Y=G)}var Z=!1,Q,Y,$=S===void 0?null:S;return[function(){return V(_())},$===null?void 0:function(){return V($())}]},[_,S,v,A]);var D=o(y,L[0],L[1]);return p(function(){x.hasValue=!0,x.value=D},[D]),h(D),D},co}var dy;function f_(){return dy||(dy=1,ro.exports=o_()),ro.exports}var d_=f_();function h_(a,u=c=>c,i={}){const c=i.equal??m_;return d_.useSyncExternalStoreWithSelector(a.subscribe,()=>a.state,()=>a.state,u,c)}function m_(a,u){if(Object.is(a,u))return!0;if(typeof a!="object"||a===null||typeof u!="object"||u===null)return!1;if(a instanceof Map&&u instanceof Map){if(a.size!==u.size)return!1;for(const[c,o]of a)if(!u.has(c)||!Object.is(o,u.get(c)))return!1;return!0}if(a instanceof Set&&u instanceof Set){if(a.size!==u.size)return!1;for(const c of a)if(!u.has(c))return!1;return!0}if(a instanceof Date&&u instanceof Date)return a.getTime()===u.getTime();const i=hy(a);if(i.length!==hy(u).length)return!1;for(let c=0;c"u"?ho:window.__TSR_ROUTER_CONTEXT__?window.__TSR_ROUTER_CONTEXT__:(window.__TSR_ROUTER_CONTEXT__=ho,ho)}function se(a){const u=et.useContext(Ap());return a?.warn,u}function Zt(a){const u=se({warn:a?.router===void 0}),i=a?.router||u,c=et.useRef(void 0);return h_(i.__store,o=>{if(a?.select){if(a.structuralSharing??i.options.defaultStructuralSharing){const d=Ye(c.current,a.select(o));return c.current=d,d}return a.select(o)}return o})}const Ls=et.createContext(void 0),y_=et.createContext(void 0);function $e(a){const u=et.useContext(a.from?y_:Ls);return Zt({select:c=>{const o=c.matches.find(d=>a.from?a.from===d.routeId:d.id===u);if(ve(!((a.shouldThrow??!0)&&!o),`Could not find ${a.from?`an active match from "${a.from}"`:"a nearest match!"}`),o!==void 0)return a.select?a.select(o):o},structuralSharing:a.structuralSharing})}function Do(a){return $e({from:a.from,strict:a.strict,structuralSharing:a.structuralSharing,select:u=>a.select?a.select(u.loaderData):u.loaderData})}function Lo(a){const{select:u,...i}=a;return $e({...i,select:c=>u?u(c.loaderDeps):c.loaderDeps})}function No(a){return $e({from:a.from,shouldThrow:a.shouldThrow,structuralSharing:a.structuralSharing,strict:a.strict,select:u=>{const i=a.strict===!1?u.params:u._strictParams;return a.select?a.select(i):i}})}function Uo(a){return $e({from:a.from,strict:a.strict,shouldThrow:a.shouldThrow,structuralSharing:a.structuralSharing,select:u=>a.select?a.select(u.search):u.search})}const ss=typeof window<"u"?et.useLayoutEffect:et.useEffect;function mo(a){const u=et.useRef({value:a,prev:null}),i=u.current.value;return a!==i&&(u.current={value:a,prev:i}),u.current.prev}function p_(a,u,i={},c={}){et.useEffect(()=>{if(!a.current||c.disabled||typeof IntersectionObserver!="function")return;const o=new IntersectionObserver(([d])=>{u(d)},i);return o.observe(a.current),()=>{o.disconnect()}},[u,i,c.disabled,a])}function g_(a){const u=et.useRef(null);return et.useImperativeHandle(a,()=>u.current,[]),u}function Bo(a){const u=se();return et.useCallback(i=>u.navigate({...i,from:i.from??a?.from}),[a?.from,u])}var v_=xy();function S_(a,u){const i=se(),[c,o]=et.useState(!1),d=et.useRef(!1),p=g_(u),{activeProps:m,inactiveProps:h,activeOptions:y,to:_,preload:S,preloadDelay:v,hashScrollIntoView:A,replace:L,startTransition:x,resetScroll:D,viewTransition:V,children:Z,target:Q,disabled:Y,style:$,className:k,onClick:X,onFocus:G,onMouseEnter:F,onMouseLeave:st,onTouchStart:lt,ignoreBlocker:ct,params:qt,search:St,hash:Ut,state:U,mask:J,reloadDocument:it,unsafeRelative:At,from:xt,_fromLocation:M,...q}=a,P=Zt({select:wt=>wt.location.search,structuralSharing:!0}),W=a.from,ut=et.useMemo(()=>({...a,from:W}),[i,P,W,a._fromLocation,a.hash,a.to,a.search,a.params,a.state,a.mask,a.unsafeRelative]),rt=et.useMemo(()=>i.buildLocation({...ut}),[i,ut]),yt=et.useMemo(()=>{if(Y)return;let wt=rt.maskedLocation?rt.maskedLocation.url.href:rt.url.href,Et=!1;return i.origin&&(wt.startsWith(i.origin)?wt=i.history.createHref(wt.replace(i.origin,""))||"/":Et=!0),{href:wt,external:Et}},[Y,rt.maskedLocation,rt.url,i.origin,i.history]),Kt=et.useMemo(()=>{if(yt?.external)return ys(yt.href)?void 0:yt.href;try{return new URL(_),ys(_)?void 0:_}catch{}},[_,yt]),zt=a.reloadDocument||Kt?!1:S??i.options.defaultPreload,We=v??i.options.defaultPreloadDelay??0,Rn=Zt({select:wt=>{if(Kt)return!1;if(y?.exact){if(!H0(wt.location.pathname,rt.pathname,i.basepath))return!1}else{const Et=gs(wt.location.pathname,i.basepath),Se=gs(rt.pathname,i.basepath);if(!(Et.startsWith(Se)&&(Et.length===Se.length||Et[Se.length]==="/")))return!1}return(y?.includeSearch??!0)&&!Aa(wt.location.search,rt.search,{partial:!y?.exact,ignoreUndefined:!y?.explicitUndefined})?!1:y?.includeHash?wt.location.hash===rt.hash:!0}}),Ve=et.useCallback(()=>{i.preloadRoute({...ut}).catch(wt=>{console.warn(wt),console.warn(mS)})},[i,ut]),ji=et.useCallback(wt=>{wt?.isIntersecting&&Ve()},[Ve]);p_(p,ji,T_,{disabled:!!Y||zt!=="viewport"}),et.useEffect(()=>{d.current||!Y&&zt==="render"&&(Ve(),d.current=!0)},[Y,Ve,zt]);const Ie=wt=>{const Et=wt.currentTarget.getAttribute("target"),Se=Q!==void 0?Q:Et;if(!Y&&!A_(wt)&&!wt.defaultPrevented&&(!Se||Se==="_self")&&wt.button===0){wt.preventDefault(),v_.flushSync(()=>{o(!0)});const zl=i.subscribe("onResolved",()=>{zl(),o(!1)});i.navigate({...ut,replace:L,resetScroll:D,hashScrollIntoView:A,startTransition:x,viewTransition:V,ignoreBlocker:ct})}};if(Kt)return{...q,ref:p,href:Kt,...Z&&{children:Z},...Q&&{target:Q},...Y&&{disabled:Y},...$&&{style:$},...k&&{className:k},...X&&{onClick:X},...G&&{onFocus:G},...F&&{onMouseEnter:F},...st&&{onMouseLeave:st},...lt&&{onTouchStart:lt}};const La=wt=>{Y||zt&&Ve()},xl=La,Ns=wt=>{if(!(Y||!zt))if(!We)Ve();else{const Et=wt.target;if(Mi.has(Et))return;const Se=setTimeout(()=>{Mi.delete(Et),Ve()},We);Mi.set(Et,Se)}},Hi=wt=>{if(Y||!zt||!We)return;const Et=wt.target,Se=Mi.get(Et);Se&&(clearTimeout(Se),Mi.delete(Et))},Tn=Rn?Wn(m,{})??b_:yo,An=Rn?yo:Wn(h,{})??yo,Na=[k,Tn.className,An.className].filter(Boolean).join(" "),qi=($||Tn.style||An.style)&&{...$,...Tn.style,...An.style};return{...q,...Tn,...An,href:yt?.href,ref:p,onClick:Oi([X,Ie]),onFocus:Oi([G,La]),onMouseEnter:Oi([F,Ns]),onMouseLeave:Oi([st,Hi]),onTouchStart:Oi([lt,xl]),disabled:!!Y,target:Q,...qi&&{style:qi},...Na&&{className:Na},...Y&&__,...Rn&&E_,...c&&R_}}const yo={},b_={className:"active"},__={role:"link","aria-disabled":!0},E_={"data-status":"active","aria-current":"page"},R_={"data-transitioning":"transitioning"},Mi=new WeakMap,T_={rootMargin:"100px"},Oi=a=>u=>{for(const i of a)if(i){if(u.defaultPrevented)return;i(u)}},Es=et.forwardRef((a,u)=>{const{_asChild:i,...c}=a,{type:o,ref:d,...p}=S_(c,u),m=typeof c.children=="function"?c.children({isActive:p["data-status"]==="active"}):c.children;return i===void 0&&delete p.disabled,et.createElement(i||"a",{...p,ref:d},m)});function A_(a){return!!(a.metaKey||a.altKey||a.ctrlKey||a.shiftKey)}let M_=class extends Jy{constructor(u){super(u),this.useMatch=i=>$e({select:i?.select,from:this.id,structuralSharing:i?.structuralSharing}),this.useRouteContext=i=>$e({...i,from:this.id,select:c=>i?.select?i.select(c.context):c.context}),this.useSearch=i=>Uo({select:i?.select,structuralSharing:i?.structuralSharing,from:this.id}),this.useParams=i=>No({select:i?.select,structuralSharing:i?.structuralSharing,from:this.id}),this.useLoaderDeps=i=>Lo({...i,from:this.id}),this.useLoaderData=i=>Do({...i,from:this.id}),this.useNavigate=()=>Bo({from:this.fullPath}),this.Link=Li.forwardRef((i,c)=>K.jsx(Es,{ref:c,from:this.fullPath,...i})),this.$$typeof=Symbol.for("react.memo")}};function O_(a){return new M_(a)}class x_ extends yS{constructor(u){super(u),this.useMatch=i=>$e({select:i?.select,from:this.id,structuralSharing:i?.structuralSharing}),this.useRouteContext=i=>$e({...i,from:this.id,select:c=>i?.select?i.select(c.context):c.context}),this.useSearch=i=>Uo({select:i?.select,structuralSharing:i?.structuralSharing,from:this.id}),this.useParams=i=>No({select:i?.select,structuralSharing:i?.structuralSharing,from:this.id}),this.useLoaderDeps=i=>Lo({...i,from:this.id}),this.useLoaderData=i=>Do({...i,from:this.id}),this.useNavigate=()=>Bo({from:this.fullPath}),this.Link=Li.forwardRef((i,c)=>K.jsx(Es,{ref:c,from:this.fullPath,...i})),this.$$typeof=Symbol.for("react.memo")}}function z_(a){return new x_(a)}function Rs(a){return typeof a=="object"?new my(a,{silent:!0}).createRoute(a):new my(a,{silent:!0}).createRoute}class my{constructor(u,i){this.path=u,this.createRoute=c=>{this.silent;const o=O_(c);return o.isRoot=!1,o},this.silent=i?.silent}}class yy{constructor(u){this.useMatch=i=>$e({select:i?.select,from:this.options.id,structuralSharing:i?.structuralSharing}),this.useRouteContext=i=>$e({from:this.options.id,select:c=>i?.select?i.select(c.context):c.context}),this.useSearch=i=>Uo({select:i?.select,structuralSharing:i?.structuralSharing,from:this.options.id}),this.useParams=i=>No({select:i?.select,structuralSharing:i?.structuralSharing,from:this.options.id}),this.useLoaderDeps=i=>Lo({...i,from:this.options.id}),this.useLoaderData=i=>Do({...i,from:this.options.id}),this.useNavigate=()=>{const i=se();return Bo({from:i.routesById[this.options.id].fullPath})},this.options=u,this.$$typeof=Symbol.for("react.memo")}}function py(a){return typeof a=="object"?new yy(a):u=>new yy({id:a,...u})}function Mp(a,u){let i,c,o,d;const p=()=>(i||(i=a().then(h=>{i=void 0,c=h[u]}).catch(h=>{if(o=h,b0(o)&&o instanceof Error&&typeof window<"u"&&typeof sessionStorage<"u"){const y=`tanstack_router_reload:${o.message}`;sessionStorage.getItem(y)||(sessionStorage.setItem(y,"1"),d=!0)}})),i),m=function(y){if(d)throw window.location.reload(),new Promise(()=>{});if(o)throw o;if(!c)if(et.use)et.use(p());else throw p();return et.createElement(c,y)};return m.preload=p,m}function w_(){const a=se(),u=et.useRef({router:a,mounted:!1}),[i,c]=et.useState(!1),{hasPendingMatches:o,isLoading:d}=Zt({select:S=>({isLoading:S.isLoading,hasPendingMatches:S.matches.some(v=>v.status==="pending")}),structuralSharing:!0}),p=mo(d),m=d||i||o,h=mo(m),y=d||o,_=mo(y);return a.startTransition=S=>{c(!0),et.startTransition(()=>{S(),c(!1)})},et.useEffect(()=>{const S=a.history.subscribe(a.load),v=a.buildLocation({to:a.latestLocation.pathname,search:!0,params:!0,hash:!0,state:!0,_includeValidateSearch:!0});return Ma(a.latestLocation.publicHref)!==Ma(v.publicHref)&&a.commitLocation({...v,replace:!0}),()=>{S()}},[a,a.history]),ss(()=>{if(typeof window<"u"&&a.ssr||u.current.router===a&&u.current.mounted)return;u.current={router:a,mounted:!0},(async()=>{try{await a.load()}catch(v){console.error(v)}})()},[a]),ss(()=>{p&&!d&&a.emit({type:"onLoad",...Oa(a.state)})},[p,a,d]),ss(()=>{_&&!y&&a.emit({type:"onBeforeRouteMount",...Oa(a.state)})},[y,_,a]),ss(()=>{if(h&&!m){const S=Oa(a.state);a.emit({type:"onResolved",...S}),a.__store.setState(v=>({...v,status:"idle",resolvedLocation:v.location})),S.hrefChanged&&K0(a)}},[m,h,a]),null}function C_(a){const u=Zt({select:i=>`not-found-${i.location.pathname}-${i.status}`});return K.jsx(Co,{getResetKey:()=>u,onCatch:(i,c)=>{if(oe(i))a.onCatch?.(i,c);else throw i},errorComponent:({error:i})=>{if(oe(i))return a.fallback?.(i);throw i},children:a.children})}function D_(){return K.jsx("p",{children:"Not Found"})}function bl(a){return K.jsx(K.Fragment,{children:a.children})}function Op(a,u,i){return u.options.notFoundComponent?K.jsx(u.options.notFoundComponent,{...i}):a.options.defaultNotFoundComponent?K.jsx(a.options.defaultNotFoundComponent,{...i}):K.jsx(D_,{})}function L_({children:a}){const u=se();return u.isServer?K.jsx("script",{nonce:u.options.ssr?.nonce,dangerouslySetInnerHTML:{__html:a+";document.currentScript.remove()"}}):null}function N_(){const a=se();if(!a.isScrollRestoring||!a.isServer||typeof a.options.scrollRestoration=="function"&&!a.options.scrollRestoration({location:a.latestLocation}))return null;const i=(a.options.getScrollRestorationKey||vo)(a.latestLocation),c=i!==vo(a.latestLocation)?i:void 0,o={storageKey:vs,shouldScrollRestoration:!0};return c&&(o.key=c),K.jsx(L_,{children:`(${By.toString()})(${Dy(JSON.stringify(o))})`})}const xp=et.memo(function({matchId:u}){const i=se(),c=Zt({select:V=>{const Z=V.matches.find(Q=>Q.id===u);return ve(Z),{routeId:Z.routeId,ssr:Z.ssr,_displayPending:Z._displayPending}},structuralSharing:!0}),o=i.routesById[c.routeId],d=o.options.pendingComponent??i.options.defaultPendingComponent,p=d?K.jsx(d,{}):null,m=o.options.errorComponent??i.options.defaultErrorComponent,h=o.options.onCatch??i.options.defaultOnCatch,y=o.isRoot?o.options.notFoundComponent??i.options.notFoundRoute?.options.component:o.options.notFoundComponent,_=c.ssr===!1||c.ssr==="data-only",S=(!o.isRoot||o.options.wrapInSuspense||_)&&(o.options.wrapInSuspense??d??(o.options.errorComponent?.preload||_))?et.Suspense:bl,v=m?Co:bl,A=y?C_:bl,L=Zt({select:V=>V.loadedAt}),x=Zt({select:V=>{const Z=V.matches.findIndex(Q=>Q.id===u);return V.matches[Z-1]?.routeId}}),D=o.isRoot?o.options.shellComponent??bl:bl;return K.jsxs(D,{children:[K.jsx(Ls.Provider,{value:u,children:K.jsx(S,{fallback:p,children:K.jsx(v,{getResetKey:()=>L,errorComponent:m||Ds,onCatch:(V,Z)=>{if(oe(V))throw V;h?.(V,Z)},children:K.jsx(A,{fallback:V=>{if(!y||V.routeId&&V.routeId!==c.routeId||!V.routeId&&!o.isRoot)throw V;return et.createElement(y,V)},children:_||c._displayPending?K.jsx(i_,{fallback:p,children:K.jsx(gy,{matchId:u})}):K.jsx(gy,{matchId:u})})})})}),x===ze&&i.options.scrollRestoration?K.jsxs(K.Fragment,{children:[K.jsx(U_,{}),K.jsx(N_,{})]}):null]})});function U_(){const a=se(),u=et.useRef(void 0);return K.jsx("script",{suppressHydrationWarning:!0,ref:i=>{i&&(u.current===void 0||u.current.href!==a.latestLocation.href)&&(a.emit({type:"onRendered",...Oa(a.state)}),u.current=a.latestLocation)}},a.latestLocation.state.__TSR_key)}const gy=et.memo(function({matchId:u}){const i=se(),{match:c,key:o,routeId:d}=Zt({select:h=>{const y=h.matches.find(L=>L.id===u),_=y.routeId,v=(i.routesById[_].options.remountDeps??i.options.defaultRemountDeps)?.({routeId:_,loaderDeps:y.loaderDeps,params:y._strictParams,search:y._strictSearch});return{key:v?JSON.stringify(v):void 0,routeId:_,match:{id:y.id,status:y.status,error:y.error,invalid:y.invalid,_forcePending:y._forcePending,_displayPending:y._displayPending}}},structuralSharing:!0}),p=i.routesById[d],m=et.useMemo(()=>{const h=p.options.component??i.options.defaultComponent;return h?K.jsx(h,{},o):K.jsx(B_,{})},[o,p.options.component,i.options.defaultComponent]);if(c._displayPending)throw i.getMatch(c.id)?._nonReactive.displayPendingPromise;if(c._forcePending)throw i.getMatch(c.id)?._nonReactive.minPendingPromise;if(c.status==="pending"){const h=p.options.pendingMinMs??i.options.defaultPendingMinMs;if(h){const y=i.getMatch(c.id);if(y&&!y._nonReactive.minPendingPromise&&!i.isServer){const _=xa();y._nonReactive.minPendingPromise=_,setTimeout(()=>{_.resolve(),y._nonReactive.minPendingPromise=void 0},h)}}throw i.getMatch(c.id)?._nonReactive.loadPromise}if(c.status==="notFound")return ve(oe(c.error)),Op(i,p,c.error);if(c.status==="redirected")throw ve(Ge(c.error)),i.getMatch(c.id)?._nonReactive.loadPromise;if(c.status==="error"){if(i.isServer){const h=(p.options.errorComponent??i.options.defaultErrorComponent)||Ds;return K.jsx(h,{error:c.error,reset:void 0,info:{componentStack:""}})}throw c.error}return m}),B_=et.memo(function(){const u=se(),i=et.useContext(Ls),c=Zt({select:y=>y.matches.find(_=>_.id===i)?.routeId}),o=u.routesById[c],d=Zt({select:y=>{const S=y.matches.find(v=>v.id===i);return ve(S),S.globalNotFound}}),p=Zt({select:y=>{const _=y.matches,S=_.findIndex(v=>v.id===i);return _[S+1]?.id}}),m=u.options.defaultPendingComponent?K.jsx(u.options.defaultPendingComponent,{}):null;if(d)return Op(u,o,void 0);if(!p)return null;const h=K.jsx(xp,{matchId:p});return c===ze?K.jsx(et.Suspense,{fallback:m,children:h}):h});function j_(){const a=se(),i=a.routesById[ze].options.pendingComponent??a.options.defaultPendingComponent,c=i?K.jsx(i,{}):null,o=a.isServer||typeof document<"u"&&a.ssr?bl:et.Suspense,d=K.jsxs(o,{fallback:c,children:[!a.isServer&&K.jsx(w_,{}),K.jsx(H_,{})]});return a.options.InnerWrap?K.jsx(a.options.InnerWrap,{children:d}):d}function H_(){const a=se(),u=Zt({select:o=>o.matches[0]?.id}),i=Zt({select:o=>o.loadedAt}),c=u?K.jsx(xp,{matchId:u}):null;return K.jsx(Ls.Provider,{value:u,children:a.options.disableGlobalCatchBoundary?c:K.jsx(Co,{getResetKey:()=>i,errorComponent:Ds,onCatch:o=>{o.message||o.toString()},children:c})})}const q_=a=>new Y_(a);class Y_ extends rS{constructor(u){super(u)}}typeof globalThis<"u"?(globalThis.createFileRoute=Rs,globalThis.createLazyFileRoute=py):typeof window<"u"&&(window.createFileRoute=Rs,window.createLazyFileRoute=py);function G_({router:a,children:u,...i}){Object.keys(i).length>0&&a.update({...a.options,...i,context:{...a.options.context,...i.context}});const c=Ap(),o=K.jsx(c.Provider,{value:a,children:u});return a.options.Wrap?K.jsx(a.options.Wrap,{children:o}):o}function V_({router:a,...u}){return K.jsx(G_,{router:a,...u,children:K.jsx(j_,{})})}function zp({tag:a,attrs:u,children:i,nonce:c}){switch(a){case"title":return K.jsx("title",{...u,suppressHydrationWarning:!0,children:i});case"meta":return K.jsx("meta",{...u,suppressHydrationWarning:!0});case"link":return K.jsx("link",{...u,nonce:c,suppressHydrationWarning:!0});case"style":return K.jsx("style",{...u,dangerouslySetInnerHTML:{__html:i},nonce:c});case"script":return K.jsx(X_,{attrs:u,children:i});default:return null}}function X_({attrs:a,children:u}){const i=se();if(et.useEffect(()=>{if(a?.src){const c=(()=>{try{const p=document.baseURI||window.location.href;return new URL(a.src,p).href}catch{return a.src}})();if(Array.from(document.querySelectorAll("script[src]")).find(p=>p.src===c))return;const d=document.createElement("script");for(const[p,m]of Object.entries(a))p!=="suppressHydrationWarning"&&m!==void 0&&m!==!1&&d.setAttribute(p,typeof m=="boolean"?"":String(m));return document.head.appendChild(d),()=>{d.parentNode&&d.parentNode.removeChild(d)}}if(typeof u=="string"){const c=typeof a?.type=="string"?a.type:"text/javascript",o=typeof a?.nonce=="string"?a.nonce:void 0;if(Array.from(document.querySelectorAll("script:not([src])")).find(m=>{if(!(m instanceof HTMLScriptElement))return!1;const h=m.getAttribute("type")??"text/javascript",y=m.getAttribute("nonce")??void 0;return m.textContent===u&&h===c&&y===o}))return;const p=document.createElement("script");if(p.textContent=u,a)for(const[m,h]of Object.entries(a))m!=="suppressHydrationWarning"&&h!==void 0&&h!==!1&&p.setAttribute(m,typeof h=="boolean"?"":String(h));return document.head.appendChild(p),()=>{p.parentNode&&p.parentNode.removeChild(p)}}},[a,u]),!i.isServer){const{src:c,...o}=a||{};return K.jsx("script",{suppressHydrationWarning:!0,dangerouslySetInnerHTML:{__html:""},...o})}return a?.src&&typeof a.src=="string"?K.jsx("script",{...a,suppressHydrationWarning:!0}):typeof u=="string"?K.jsx("script",{...a,dangerouslySetInnerHTML:{__html:u},suppressHydrationWarning:!0}):null}const Q_=()=>{const a=se(),u=a.options.ssr?.nonce,i=Zt({select:h=>h.matches.map(y=>y.meta).filter(Boolean)}),c=et.useMemo(()=>{const h=[],y={};let _;for(let S=i.length-1;S>=0;S--){const v=i[S];for(let A=v.length-1;A>=0;A--){const L=v[A];if(L)if(L.title)_||(_={tag:"title",children:L.title});else if("script:ld+json"in L)try{const x=JSON.stringify(L["script:ld+json"]);h.push({tag:"script",attrs:{type:"application/ld+json"},children:Dy(x)})}catch{}else{const x=L.name??L.property;if(x){if(y[x])continue;y[x]=!0}h.push({tag:"meta",attrs:{...L,nonce:u}})}}}return _&&h.push(_),u&&h.push({tag:"meta",attrs:{property:"csp-nonce",content:u}}),h.reverse(),h},[i,u]),o=Zt({select:h=>{const y=h.matches.map(v=>v.links).filter(Boolean).flat(1).map(v=>({tag:"link",attrs:{...v,nonce:u}})),_=a.ssr?.manifest,S=h.matches.map(v=>_?.routes[v.routeId]?.assets??[]).filter(Boolean).flat(1).filter(v=>v.tag==="link").map(v=>({tag:"link",attrs:{...v.attrs,suppressHydrationWarning:!0,nonce:u}}));return[...y,...S]},structuralSharing:!0}),d=Zt({select:h=>{const y=[];return h.matches.map(_=>a.looseRoutesById[_.routeId]).forEach(_=>a.ssr?.manifest?.routes[_.id]?.preloads?.filter(Boolean).forEach(S=>{y.push({tag:"link",attrs:{rel:"modulepreload",href:S,nonce:u}})})),y},structuralSharing:!0}),p=Zt({select:h=>h.matches.map(y=>y.styles).flat(1).filter(Boolean).map(({children:y,..._})=>({tag:"style",attrs:_,children:y,nonce:u})),structuralSharing:!0}),m=Zt({select:h=>h.matches.map(y=>y.headScripts).flat(1).filter(Boolean).map(({children:y,..._})=>({tag:"script",attrs:{..._,nonce:u},children:y})),structuralSharing:!0});return K_([...c,...d,...o,...p,...m],h=>JSON.stringify(h))};function Z_(){const a=Q_(),i=se().options.ssr?.nonce;return K.jsxs(K.Fragment,{children:[!1,a.map(c=>et.createElement(zp,{...c,key:`tsr-meta-${JSON.stringify(c)}`,nonce:i}))]})}function K_(a,u){const i=new Set;return a.filter(c=>{const o=u(c);return i.has(o)?!1:(i.add(o),!0)})}const J_=()=>{const a=se(),u=a.options.ssr?.nonce,i=Zt({select:p=>{const m=[],h=a.ssr?.manifest;return h?(p.matches.map(y=>a.looseRoutesById[y.routeId]).forEach(y=>h.routes[y.id]?.assets?.filter(_=>_.tag==="script").forEach(_=>{m.push({tag:"script",attrs:{..._.attrs,nonce:u},children:_.children})})),m):[]},structuralSharing:!0}),{scripts:c}=Zt({select:p=>({scripts:p.matches.map(m=>m.scripts).flat(1).filter(Boolean).map(({children:m,...h})=>({tag:"script",attrs:{...h,suppressHydrationWarning:!0,nonce:u},children:m}))}),structuralSharing:!0});let o;a.serverSsr&&(o=a.serverSsr.takeBufferedScripts());const d=[...c,...i];return o&&d.unshift(o),K.jsx(K.Fragment,{children:d.map((p,m)=>et.createElement(zp,{...p,key:`tsr-scripts-${p.tag}-${m}`}))})};function k_(a,u){a.id=u.i,a.__beforeLoadContext=u.b,a.loaderData=u.l,a.status=u.s,a.ssr=u.ssr,a.updatedAt=u.u,a.error=u.e}async function F_(a){ve(window.$_TSR);const u=a.options.serializationAdapters;if(u?.length){const x=new Map;u.forEach(D=>{x.set(D.key,D.fromSerializable)}),window.$_TSR.t=x,window.$_TSR.buffer.forEach(D=>D())}window.$_TSR.initialized=!0,ve(window.$_TSR.router);const{manifest:i,dehydratedData:c,lastMatchId:o}=window.$_TSR.router;a.ssr={manifest:i};const p=document.querySelector('meta[property="csp-nonce"]')?.content;a.options.ssr={nonce:p};const m=a.matchRoutes(a.state.location),h=Promise.all(m.map(x=>{const D=a.looseRoutesById[x.routeId];return a.loadRouteChunk(D)}));function y(x){const V=a.looseRoutesById[x.routeId].options.pendingMinMs??a.options.defaultPendingMinMs;if(V){const Z=xa();x._nonReactive.minPendingPromise=Z,x._forcePending=!0,setTimeout(()=>{Z.resolve(),a.updateMatch(x.id,Q=>(Q._nonReactive.minPendingPromise=void 0,{...Q,_forcePending:void 0}))},V)}}function _(x){const D=a.looseRoutesById[x.routeId];D&&(D.options.ssr=x.ssr)}let S;m.forEach(x=>{const D=window.$_TSR.router.matches.find(V=>V.i===x.id);if(!D){x._nonReactive.dehydrated=!1,x.ssr=!1,_(x);return}k_(x,D),_(x),x._nonReactive.dehydrated=x.ssr!==!1,(x.ssr==="data-only"||x.ssr===!1)&&S===void 0&&(S=x.index,y(x))}),a.__store.setState(x=>({...x,matches:m})),await a.options.hydrate?.(c),await Promise.all(a.state.matches.map(async x=>{try{const D=a.looseRoutesById[x.routeId],Z=a.state.matches[x.index-1]?.context??a.options.context;if(D.options.context){const k={deps:x.loaderDeps,params:x.params,context:Z??{},location:a.state.location,navigate:X=>a.navigate({...X,_fromLocation:a.state.location}),buildLocation:a.buildLocation,cause:x.cause,abortController:x.abortController,preload:!1,matches:m};x.__routeContext=D.options.context(k)??void 0}x.context={...Z,...x.__routeContext,...x.__beforeLoadContext};const Q={matches:a.state.matches,match:x,params:x.params,loaderData:x.loaderData},Y=await D.options.head?.(Q),$=await D.options.scripts?.(Q);x.meta=Y?.meta,x.links=Y?.links,x.headScripts=Y?.scripts,x.styles=Y?.styles,x.scripts=$}catch(D){if(oe(D))x.error={isNotFound:!0},console.error(`NotFound error during hydration for routeId: ${x.routeId}`,D);else throw x.error=D,console.error(`Error during hydration for route ${x.routeId}:`,D),D}}));const v=m[m.length-1].id!==o;if(!m.some(x=>x.ssr===!1)&&!v)return m.forEach(x=>{x._nonReactive.dehydrated=void 0}),h;const L=Promise.resolve().then(()=>a.load()).catch(x=>{console.error("Error during router hydration:",x)});if(v){const x=m[1];ve(x),y(x),x._displayPending=!0,x._nonReactive.displayPendingPromise=L,L.then(()=>{Rl(()=>{a.__store.state.status==="pending"&&a.__store.setState(D=>({...D,status:"idle",resolvedLocation:D.location})),a.updateMatch(x.id,D=>({...D,_displayPending:void 0,displayPendingPromise:void 0}))})})}return h}const P_="__TSS_CONTEXT",Eo=Symbol.for("TSS_SERVER_FUNCTION"),HE=Symbol.for("TSS_SERVER_FUNCTION_FACTORY"),$_="x-tss-serialized",W_="x-tss-raw",wp="application/x-tss-framed",Sn={JSON:0,CHUNK:1,END:2,ERROR:3},Sl=9,vy=1,I_=/;\s*v=(\d+)/;function tE(a){const u=a.match(I_);return u?parseInt(u[1],10):void 0}function eE(a){const u=tE(a);if(u!==void 0&&u!==vy)throw new Error(`Incompatible framed protocol version: server=${u}, client=${vy}. Please ensure client and server are using compatible versions.`)}const nE=()=>window.__TSS_START_OPTIONS__;function aE(){return[...nE()?.serializationAdapters?.map(qb)??[],...t_]}const Sy=new TextDecoder,lE=new Uint8Array(0),by=16*1024*1024,_y=32*1024*1024,Ey=1024,Ry=1e5;function iE(a){const u=new Map,i=new Map,c=new Set;let o=!1,d=null,p=0,m;const h=new ReadableStream({start(S){m=S},cancel(){o=!0;try{d?.cancel()}catch{}u.forEach(S=>{try{S.error(new Error("Framed response cancelled"))}catch{}}),u.clear(),i.clear(),c.clear()}});function y(S){const v=i.get(S);if(v)return v;if(c.has(S))return new ReadableStream({start(L){L.close()}});if(i.size>=Ey)throw new Error(`Too many raw streams in framed response (max ${Ey})`);const A=new ReadableStream({start(L){u.set(S,L)},cancel(){c.add(S),u.delete(S),i.delete(S)}});return i.set(S,A),A}function _(S){return y(S),u.get(S)}return(async()=>{const S=a.getReader();d=S;const v=[];let A=0;function L(){if(A=Sl){const X=D[0],G=(D[1]<<24|D[2]<<16|D[3]<<8|D[4])>>>0,F=(D[5]<<24|D[6]<<16|D[7]<<8|D[8])>>>0;return{type:X,streamId:G,length:F}}const V=new Uint8Array(Sl);let Z=0,Q=Sl;for(let X=0;X0;X++){const G=v[X],F=Math.min(G.length,Q);V.set(G.subarray(0,F),Z),Z+=F,Q-=F}const Y=V[0],$=(V[1]<<24|V[2]<<16|V[3]<<8|V[4])>>>0,k=(V[5]<<24|V[6]<<16|V[7]<<8|V[8])>>>0;return{type:Y,streamId:$,length:k}}function x(D){if(D===0)return lE;const V=new Uint8Array(D);let Z=0,Q=D;for(;Q>0&&v.length>0;){const Y=v[0];if(!Y)break;const $=Math.min(Y.length,Q);V.set(Y.subarray(0,$),Z),Z+=$,Q-=$,$===Y.length?v.shift():v[0]=Y.subarray($)}return A-=D,V}try{for(;;){const{done:D,value:V}=await S.read();if(o||D)break;if(V){if(A+V.length>_y)throw new Error(`Framed response buffer exceeded ${_y} bytes`);for(v.push(V),A+=V.length;;){const Z=L();if(!Z)break;const{type:Q,streamId:Y,length:$}=Z;if(Q!==Sn.JSON&&Q!==Sn.CHUNK&&Q!==Sn.END&&Q!==Sn.ERROR)throw new Error(`Unknown frame type: ${Q}`);if(Q===Sn.JSON){if(Y!==0)throw new Error("Invalid JSON frame streamId (expected 0)")}else if(Y===0)throw new Error("Invalid raw frame streamId (expected non-zero)");if($>by)throw new Error(`Frame payload too large: ${$} bytes (max ${by})`);const k=Sl+$;if(ARy)throw new Error(`Too many frames in framed response (max ${Ry})`);x(Sl);const X=x($);switch(Q){case Sn.JSON:{try{m.enqueue(Sy.decode(X))}catch{}break}case Sn.CHUNK:{const G=_(Y);G&&G.enqueue(X);break}case Sn.END:{const G=_(Y);if(c.add(Y),G){try{G.close()}catch{}u.delete(Y)}break}case Sn.ERROR:{const G=_(Y);if(c.add(Y),G){const F=Sy.decode(X);G.error(new Error(F)),u.delete(Y)}break}}}}}if(A!==0)throw new Error("Incomplete frame at end of framed response");try{m.close()}catch{}u.forEach(D=>{try{D.close()}catch{}}),u.clear()}catch(D){try{m.error(D)}catch{}u.forEach(V=>{try{V.error(D)}catch{}}),u.clear()}finally{try{S.releaseLock()}catch{}d=null}})(),{getOrCreateStream:y,jsonChunks:h}}let Ml=null;const uE=Object.prototype.hasOwnProperty;function Cp(a){for(const u in a)if(uE.call(a,u))return!0;return!1}async function sE(a,u,i){Ml||(Ml=aE());const o=u[0],d=o.data instanceof FormData?"formData":"payload",p=o.headers?new Headers(o.headers):new Headers;if(p.set("x-tsr-serverFn","true"),d==="payload"&&p.set("accept",`${wp}, application/x-ndjson, application/json`),o.method==="GET"){if(d==="formData")throw new Error("FormData is not supported with GET requests");const h=await Dp(o);if(h!==void 0){const y=jy({payload:h});a.includes("?")?a+=`&${y}`:a+=`?${y}`}}let m;if(o.method==="POST"){const h=await rE(o);h?.contentType&&p.set("content-type",h.contentType),m=h?.body}return await cE(async()=>i(a,{method:o.method,headers:p,signal:o.signal,body:m}))}async function Dp(a){let u=!1;const i={};if(a.data!==void 0&&(u=!0,i.data=a.data),a.context&&Cp(a.context)&&(u=!0,i.context=a.context),u)return Lp(i)}async function Lp(a){return JSON.stringify(await Promise.resolve(Hb(a,{plugins:Ml})))}async function rE(a){if(a.data instanceof FormData){let i;return a.context&&Cp(a.context)&&(i=await Lp(a.context)),i!==void 0&&a.data.set(P_,i),{body:a.data}}const u=await Dp(a);if(u)return{body:u,contentType:"application/json"}}async function cE(a){let u;try{u=await a()}catch(o){if(o instanceof Response)u=o;else throw console.log(o),o}if(u.headers.get(W_)==="true")return u;const i=u.headers.get("content-type");if(ve(i),!!u.headers.get($_)){let o;if(i.includes(wp)){if(eE(i),!u.body)throw new Error("No response body for framed response");const{getOrCreateStream:d,jsonChunks:p}=iE(u.body),h=[Ib(d),...Ml||[]],y=new Map;o=await fE({jsonStream:p,onMessage:_=>so(_,{refs:y,plugins:h}),onError(_,S){console.error(_,S)}})}else if(i.includes("application/x-ndjson")){const d=new Map;o=await oE({response:u,onMessage:p=>so(p,{refs:d,plugins:Ml}),onError(p,m){console.error(p,m)}})}else if(i.includes("application/json")){const d=await u.json();o=so(d,{plugins:Ml})}if(ve(o),o instanceof Error)throw o;return o}if(i.includes("application/json")){const o=await u.json(),d=W0(o);if(d)throw d;if(oe(o))throw o;return o}if(!u.ok)throw new Error(await u.text());return u}async function oE({response:a,onMessage:u,onError:i}){if(!a.body)throw new Error("No response body");const c=a.body.pipeThrough(new TextDecoderStream).getReader();let o="",d=!1,p;for(;!d;){const{value:m,done:h}=await c.read();if(m&&(o+=m),o.length===0&&h)throw new Error("Stream ended before first object");if(o.endsWith(` -`)){const y=o.split(` -`).filter(Boolean),_=y[0];if(!_)throw new Error("No JSON line in the first chunk");p=JSON.parse(_),d=!0,o=y.slice(1).join(` -`)}else{const y=o.indexOf(` -`);if(y>=0){const _=o.slice(0,y).trim();o=o.slice(y+1),_.length>0&&(p=JSON.parse(_),d=!0)}}}return(async()=>{try{for(;;){const{value:m,done:h}=await c.read();m&&(o+=m);const y=o.lastIndexOf(` -`);if(y>=0){const _=o.slice(0,y);o=o.slice(y+1);const S=_.split(` -`).filter(Boolean);for(const v of S)try{u(JSON.parse(v))}catch(A){i?.(`Invalid JSON line: ${v}`,A)}}if(h)break}}catch(m){i?.("Stream processing error:",m)}})(),u(p)}async function fE({jsonStream:a,onMessage:u,onError:i}){const c=a.getReader(),{value:o,done:d}=await c.read();if(d||!o)throw new Error("Stream ended before first object");const p=JSON.parse(o);return(async()=>{try{for(;;){const{value:m,done:h}=await c.read();if(h)break;if(m)try{u(JSON.parse(m))}catch(y){i?.(`Invalid JSON: ${m}`,y)}}}catch(m){i?.("Stream processing error:",m)}})(),u(p)}function dE(a){const u="/_serverFn/"+a;return Object.assign((...c)=>sE(u,c,fetch),{url:u,functionId:a,[Eo]:!0})}const hE={key:"$TSS/serverfn",test:a=>typeof a!="function"||!(Eo in a)?!1:!!a[Eo],toSerializable:({functionId:a})=>({functionId:a}),fromSerializable:({functionId:a})=>dE(a)},mE="/assets/styles-DnGqQgkE.css",yE="modulepreload",pE=function(a){return"/"+a},Ty={},Ts=function(u,i,c){let o=Promise.resolve();if(i&&i.length>0){let h=function(y){return Promise.all(y.map(_=>Promise.resolve(_).then(S=>({status:"fulfilled",value:S}),S=>({status:"rejected",reason:S}))))};document.getElementsByTagName("link");const p=document.querySelector("meta[property=csp-nonce]"),m=p?.nonce||p?.getAttribute("nonce");o=h(i.map(y=>{if(y=pE(y),y in Ty)return;Ty[y]=!0;const _=y.endsWith(".css"),S=_?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${y}"]${S}`))return;const v=document.createElement("link");if(v.rel=_?"stylesheet":yE,_||(v.as="script"),v.crossOrigin="",v.href=y,m&&v.setAttribute("nonce",m),document.head.appendChild(v),_)return new Promise((A,L)=>{v.addEventListener("load",A),v.addEventListener("error",()=>L(new Error(`Unable to preload CSS for ${y}`)))})}))}function d(p){const m=new Event("vite:preloadError",{cancelable:!0});if(m.payload=p,window.dispatchEvent(m),!m.defaultPrevented)throw p}return o.then(p=>{for(const m of p||[])m.status==="rejected"&&d(m.reason);return u().catch(d)})},Ay="syncable-agent-settings",My={provider:"openai",model:"gpt-5.2",apiKey:"",awsRegion:"us-east-1"},Oy={openai:[{id:"gpt-5.2",name:"GPT-5.2 - Latest reasoning model (Dec 2025)"},{id:"gpt-5.2-mini",name:"GPT-5.2 Mini - Fast and affordable"},{id:"gpt-4o",name:"GPT-4o - Multimodal workhorse"},{id:"o1-preview",name:"o1-preview - Advanced reasoning"}],anthropic:[{id:"claude-opus-4-5-20251101",name:"Claude Opus 4.5 - Most capable (Nov 2025)"},{id:"claude-sonnet-4-5-20250929",name:"Claude Sonnet 4.5 - Balanced (Sep 2025)"},{id:"claude-haiku-4-5-20251001",name:"Claude Haiku 4.5 - Fast (Oct 2025)"},{id:"claude-sonnet-4-20250514",name:"Claude Sonnet 4 - Previous gen"}],bedrock:[{id:"global.anthropic.claude-opus-4-5-20251101-v1:0",name:"Claude Opus 4.5 - Most capable (Nov 2025)"},{id:"global.anthropic.claude-sonnet-4-5-20250929-v1:0",name:"Claude Sonnet 4.5 - Balanced (Sep 2025)"},{id:"global.anthropic.claude-haiku-4-5-20251001-v1:0",name:"Claude Haiku 4.5 - Fast (Oct 2025)"},{id:"global.anthropic.claude-sonnet-4-20250514-v1:0",name:"Claude Sonnet 4 - Previous gen"}]},Np=et.createContext(null);function gE({children:a}){const[u,i]=et.useState(My);et.useEffect(()=>{try{const h=localStorage.getItem(Ay);if(h){const y=JSON.parse(h);i({...My,...y})}}catch(h){console.error("Failed to load agent settings:",h)}},[]),et.useEffect(()=>{try{localStorage.setItem(Ay,JSON.stringify(u))}catch(h){console.error("Failed to save agent settings:",h)}},[u]);const c=h=>{i(y=>({...y,provider:h,model:Oy[h][0].id}))},o=h=>{i(y=>({...y,model:h}))},d=h=>{i(y=>({...y,apiKey:h}))},p=h=>{i(y=>({...y,awsRegion:h}))},m=Oy[u.provider];return K.jsx(Np.Provider,{value:{settings:u,setProvider:c,setModel:o,setApiKey:d,setAwsRegion:p,availableModels:m},children:a})}function vE(){const a=et.useContext(Np);if(!a)throw new Error("useAgentSettings must be used within AgentSettingsProvider");return a}const SE="http://localhost:9090";function bE({children:a}){const[u,i]=et.useState(null),{settings:c}=vE();if(et.useEffect(()=>{Promise.all([Ts(()=>import("@copilotkit/react-core"),[]),Ts(()=>import("@copilotkit/react-ui/styles.css"),[])]).then(([d])=>{i(()=>d.CopilotKit)})},[]),!u)return K.jsx(K.Fragment,{children:a});const o={provider:c.provider,model:c.model,apiKey:c.apiKey,awsRegion:c.awsRegion};return K.jsx(u,{runtimeUrl:SE,properties:o,agent:"syncable",children:a})}function _E({children:a}){return K.jsx(gE,{children:K.jsx(bE,{children:a})})}const jo=z_({head:()=>({meta:[{charSet:"utf-8"},{name:"viewport",content:"width=device-width, initial-scale=1"},{title:"Smart Reply Generator"},{name:"description",content:"AI-powered reply suggestions for your messages"}],links:[{rel:"stylesheet",href:mE},{rel:"icon",href:"/favicon.ico"}]}),shellComponent:EE});function EE({children:a}){return K.jsxs("html",{lang:"en",className:"dark",children:[K.jsx("head",{children:K.jsx(Z_,{})}),K.jsxs("body",{className:"bg-slate-950 antialiased",children:[K.jsxs("nav",{className:"fixed top-4 right-4 z-50 flex gap-2",children:[K.jsx(Es,{to:"/",className:"px-3 py-1.5 text-xs font-medium rounded-lg bg-slate-800/80 text-slate-300 hover:bg-slate-700 hover:text-white border border-slate-700 backdrop-blur-sm transition-all",activeProps:{className:"bg-cyan-600/20 text-cyan-400 border-cyan-500/30"},children:"Smart Reply"}),K.jsx(Es,{to:"/agent",className:"px-3 py-1.5 text-xs font-medium rounded-lg bg-slate-800/80 text-slate-300 hover:bg-slate-700 hover:text-white border border-slate-700 backdrop-blur-sm transition-all",activeProps:{className:"bg-emerald-600/20 text-emerald-400 border-emerald-500/30"},children:"Agent Chat"})]}),K.jsx(_E,{children:a}),K.jsx(J_,{})]})]})}const RE=()=>Ts(()=>import("./agent-Bh38wHiA.js"),__vite__mapDeps([0,1])),TE=Rs("/agent")({component:Mp(RE,"component")}),AE=()=>Ts(()=>import("./index-CfGsyQoR.js"),__vite__mapDeps([2,1])),ME=Rs("/")({component:Mp(AE,"component")}),OE=TE.update({id:"/agent",path:"/agent",getParentRoute:()=>jo}),xE=ME.update({id:"/",path:"/",getParentRoute:()=>jo}),zE={IndexRoute:xE,AgentRoute:OE},wE=jo._addFileChildren(zE),CE=()=>q_({routeTree:wE,context:{},scrollRestoration:!0,defaultPreloadStaleTime:0});async function DE(){const a=await CE();let u;return u=[],window.__TSS_START_OPTIONS__={serializationAdapters:u},u.push(hE),a.options.serializationAdapters&&u.push(...a.options.serializationAdapters),a.update({basepath:"",serializationAdapters:u}),a.state.matches.length||await F_(a),a}async function LE(){const a=await DE();return window.$_TSR?.h(),a}let po;function NE(){return po||(po=LE()),K.jsx(n_,{promise:po,children:a=>K.jsx(V_,{router:a})})}et.startTransition(()=>{c0.hydrateRoot(document,K.jsx(et.StrictMode,{children:K.jsx(NE,{})}))});export{HE as T,Ts as _,dE as c,nE as g,Ge as i,K as j,W0 as p,et as r,vE as u}; diff --git a/tests/ag-ui-app/frontend/dist/client/assets/styles-DnGqQgkE.css b/tests/ag-ui-app/frontend/dist/client/assets/styles-DnGqQgkE.css deleted file mode 100644 index 3662fbac..00000000 --- a/tests/ag-ui-app/frontend/dist/client/assets/styles-DnGqQgkE.css +++ /dev/null @@ -1 +0,0 @@ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-green-300:oklch(87.1% .15 154.449);--color-green-500:oklch(72.3% .219 149.579);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-teal-300:oklch(85.5% .138 181.071);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-600:oklch(60.9% .126 221.723);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-500:oklch(62.7% .265 303.9);--color-fuchsia-500:oklch(66.7% .295 322.15);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-slate-950:oklch(12.9% .042 264.695);--color-zinc-700:oklch(37% .013 285.805);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--text-5xl:3rem;--text-5xl--line-height:1;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-sm:8px;--blur-lg:16px;--blur-xl:24px;--blur-3xl:64px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.-top-24{top:calc(var(--spacing)*-24)}.-top-48{top:calc(var(--spacing)*-48)}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing)*4)}.-right-48{right:calc(var(--spacing)*-48)}.right-0{right:calc(var(--spacing)*0)}.right-1\/4{right:25%}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.-bottom-48{bottom:calc(var(--spacing)*-48)}.-left-24{left:calc(var(--spacing)*-24)}.-left-48{left:calc(var(--spacing)*-48)}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-16{margin-top:calc(var(--spacing)*16)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-5{margin-bottom:calc(var(--spacing)*5)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.mb-10{margin-bottom:calc(var(--spacing)*10)}.ml-1{margin-left:calc(var(--spacing)*1)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2{height:calc(var(--spacing)*2)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-10{height:calc(var(--spacing)*10)}.h-16{height:calc(var(--spacing)*16)}.h-20{height:calc(var(--spacing)*20)}.h-48{height:calc(var(--spacing)*48)}.h-64{height:calc(var(--spacing)*64)}.h-96{height:calc(var(--spacing)*96)}.h-\[calc\(100vh-280px\)\]{height:calc(100vh - 280px)}.h-full{height:100%}.max-h-\[120px\]{max-height:120px}.max-h-\[150px\]{max-height:150px}.max-h-\[200px\]{max-height:200px}.max-h-\[300px\]{max-height:300px}.min-h-\[500px\]{min-height:500px}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing)*2)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-4\/5{width:80%}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-10{width:calc(var(--spacing)*10)}.w-11\/12{width:91.6667%}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-28{width:calc(var(--spacing)*28)}.w-48{width:calc(var(--spacing)*48)}.w-64{width:calc(var(--spacing)*64)}.w-96{width:calc(var(--spacing)*96)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-\[200px\]{max-width:200px}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-0{--tw-translate-y:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-4{--tw-translate-y:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-95{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.appearance-none{appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-amber-500\/20{border-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/20{border-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.border-blue-500\/20{border-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/20{border-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.border-cyan-400\/50{border-color:#00d2ef80}@supports (color:color-mix(in lab,red,red)){.border-cyan-400\/50{border-color:color-mix(in oklab,var(--color-cyan-400)50%,transparent)}}.border-cyan-500\/20{border-color:#00b7d733}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/20{border-color:color-mix(in oklab,var(--color-cyan-500)20%,transparent)}}.border-cyan-500\/25{border-color:#00b7d740}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/25{border-color:color-mix(in oklab,var(--color-cyan-500)25%,transparent)}}.border-cyan-500\/30{border-color:#00b7d74d}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/30{border-color:color-mix(in oklab,var(--color-cyan-500)30%,transparent)}}.border-cyan-500\/40{border-color:#00b7d766}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/40{border-color:color-mix(in oklab,var(--color-cyan-500)40%,transparent)}}.border-emerald-500\/20{border-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/20{border-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.border-emerald-500\/30{border-color:#00bb7f4d}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/30{border-color:color-mix(in oklab,var(--color-emerald-500)30%,transparent)}}.border-emerald-500\/40{border-color:#00bb7f66}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/40{border-color:color-mix(in oklab,var(--color-emerald-500)40%,transparent)}}.border-indigo-500\/20{border-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.border-indigo-500\/20{border-color:color-mix(in oklab,var(--color-indigo-500)20%,transparent)}}.border-indigo-500\/40{border-color:#625fff66}@supports (color:color-mix(in lab,red,red)){.border-indigo-500\/40{border-color:color-mix(in oklab,var(--color-indigo-500)40%,transparent)}}.border-orange-500\/30{border-color:#fe6e004d}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/30{border-color:color-mix(in oklab,var(--color-orange-500)30%,transparent)}}.border-purple-500\/30{border-color:#ac4bff4d}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/30{border-color:color-mix(in oklab,var(--color-purple-500)30%,transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.border-rose-500\/20{border-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.border-rose-500\/20{border-color:color-mix(in oklab,var(--color-rose-500)20%,transparent)}}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-slate-500\/20{border-color:#62748e33}@supports (color:color-mix(in lab,red,red)){.border-slate-500\/20{border-color:color-mix(in oklab,var(--color-slate-500)20%,transparent)}}.border-slate-600{border-color:var(--color-slate-600)}.border-slate-600\/30{border-color:#45556c4d}@supports (color:color-mix(in lab,red,red)){.border-slate-600\/30{border-color:color-mix(in oklab,var(--color-slate-600)30%,transparent)}}.border-slate-600\/50{border-color:#45556c80}@supports (color:color-mix(in lab,red,red)){.border-slate-600\/50{border-color:color-mix(in oklab,var(--color-slate-600)50%,transparent)}}.border-slate-700{border-color:var(--color-slate-700)}.border-slate-700\/30{border-color:#3141584d}@supports (color:color-mix(in lab,red,red)){.border-slate-700\/30{border-color:color-mix(in oklab,var(--color-slate-700)30%,transparent)}}.border-slate-700\/50{border-color:#31415880}@supports (color:color-mix(in lab,red,red)){.border-slate-700\/50{border-color:color-mix(in oklab,var(--color-slate-700)50%,transparent)}}.border-slate-800{border-color:var(--color-slate-800)}.border-slate-800\/50{border-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.border-slate-800\/50{border-color:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.border-violet-500\/20{border-color:#8d54ff33}@supports (color:color-mix(in lab,red,red)){.border-violet-500\/20{border-color:color-mix(in oklab,var(--color-violet-500)20%,transparent)}}.border-violet-500\/30{border-color:#8d54ff4d}@supports (color:color-mix(in lab,red,red)){.border-violet-500\/30{border-color:color-mix(in oklab,var(--color-violet-500)30%,transparent)}}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.border-white\/20{border-color:#fff3}@supports (color:color-mix(in lab,red,red)){.border-white\/20{border-color:color-mix(in oklab,var(--color-white)20%,transparent)}}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/30{border-color:color-mix(in oklab,var(--color-yellow-500)30%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/20{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black)70%,transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.bg-blue-500\/15{background-color:#3080ff26}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/15{background-color:color-mix(in oklab,var(--color-blue-500)15%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.bg-cyan-400{background-color:var(--color-cyan-400)}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/10{background-color:color-mix(in oklab,var(--color-cyan-500)10%,transparent)}}.bg-cyan-600\/20{background-color:#0092b533}@supports (color:color-mix(in lab,red,red)){.bg-cyan-600\/20{background-color:color-mix(in oklab,var(--color-cyan-600)20%,transparent)}}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/10{background-color:color-mix(in oklab,var(--color-emerald-500)10%,transparent)}}.bg-emerald-500\/20{background-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/20{background-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.bg-emerald-600{background-color:var(--color-emerald-600)}.bg-emerald-600\/20{background-color:#00976733}@supports (color:color-mix(in lab,red,red)){.bg-emerald-600\/20{background-color:color-mix(in oklab,var(--color-emerald-600)20%,transparent)}}.bg-fuchsia-500\/5{background-color:#e12afb0d}@supports (color:color-mix(in lab,red,red)){.bg-fuchsia-500\/5{background-color:color-mix(in oklab,var(--color-fuchsia-500)5%,transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.bg-indigo-500\/10{background-color:#625fff1a}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/10{background-color:color-mix(in oklab,var(--color-indigo-500)10%,transparent)}}.bg-indigo-500\/20{background-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/20{background-color:color-mix(in oklab,var(--color-indigo-500)20%,transparent)}}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/10{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.bg-orange-500\/20{background-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/20{background-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.bg-rose-500\/10{background-color:#ff23571a}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/10{background-color:color-mix(in oklab,var(--color-rose-500)10%,transparent)}}.bg-rose-500\/20{background-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/20{background-color:color-mix(in oklab,var(--color-rose-500)20%,transparent)}}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-500\/10{background-color:#62748e1a}@supports (color:color-mix(in lab,red,red)){.bg-slate-500\/10{background-color:color-mix(in oklab,var(--color-slate-500)10%,transparent)}}.bg-slate-500\/20{background-color:#62748e33}@supports (color:color-mix(in lab,red,red)){.bg-slate-500\/20{background-color:color-mix(in oklab,var(--color-slate-500)20%,transparent)}}.bg-slate-700{background-color:var(--color-slate-700)}.bg-slate-700\/30{background-color:#3141584d}@supports (color:color-mix(in lab,red,red)){.bg-slate-700\/30{background-color:color-mix(in oklab,var(--color-slate-700)30%,transparent)}}.bg-slate-700\/50{background-color:#31415880}@supports (color:color-mix(in lab,red,red)){.bg-slate-700\/50{background-color:color-mix(in oklab,var(--color-slate-700)50%,transparent)}}.bg-slate-800{background-color:var(--color-slate-800)}.bg-slate-800\/40{background-color:#1d293d66}@supports (color:color-mix(in lab,red,red)){.bg-slate-800\/40{background-color:color-mix(in oklab,var(--color-slate-800)40%,transparent)}}.bg-slate-800\/50{background-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.bg-slate-800\/50{background-color:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.bg-slate-800\/60{background-color:#1d293d99}@supports (color:color-mix(in lab,red,red)){.bg-slate-800\/60{background-color:color-mix(in oklab,var(--color-slate-800)60%,transparent)}}.bg-slate-800\/80{background-color:#1d293dcc}@supports (color:color-mix(in lab,red,red)){.bg-slate-800\/80{background-color:color-mix(in oklab,var(--color-slate-800)80%,transparent)}}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-900\/5{background-color:#0f172b0d}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/5{background-color:color-mix(in oklab,var(--color-slate-900)5%,transparent)}}.bg-slate-900\/30{background-color:#0f172b4d}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/30{background-color:color-mix(in oklab,var(--color-slate-900)30%,transparent)}}.bg-slate-900\/50{background-color:#0f172b80}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/50{background-color:color-mix(in oklab,var(--color-slate-900)50%,transparent)}}.bg-slate-900\/80{background-color:#0f172bcc}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/80{background-color:color-mix(in oklab,var(--color-slate-900)80%,transparent)}}.bg-slate-900\/95{background-color:#0f172bf2}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/95{background-color:color-mix(in oklab,var(--color-slate-900)95%,transparent)}}.bg-slate-900\/\[0\.03\]{background-color:#0f172b08}@supports (color:color-mix(in lab,red,red)){.bg-slate-900\/\[0\.03\]{background-color:color-mix(in oklab,var(--color-slate-900)3%,transparent)}}.bg-slate-950{background-color:var(--color-slate-950)}.bg-slate-950\/90{background-color:#020618e6}@supports (color:color-mix(in lab,red,red)){.bg-slate-950\/90{background-color:color-mix(in oklab,var(--color-slate-950)90%,transparent)}}.bg-teal-500\/5{background-color:#00baa70d}@supports (color:color-mix(in lab,red,red)){.bg-teal-500\/5{background-color:color-mix(in oklab,var(--color-teal-500)5%,transparent)}}.bg-transparent{background-color:#0000}.bg-violet-500\/10{background-color:#8d54ff1a}@supports (color:color-mix(in lab,red,red)){.bg-violet-500\/10{background-color:color-mix(in oklab,var(--color-violet-500)10%,transparent)}}.bg-white\/5{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/5{background-color:color-mix(in oklab,var(--color-white)5%,transparent)}}.bg-white\/10{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.bg-white\/10{background-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.bg-white\/\[0\.03\]{background-color:#ffffff08}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.03\]{background-color:color-mix(in oklab,var(--color-white)3%,transparent)}}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-linear-to-br{--tw-gradient-position:to bottom right}@supports (background-image:linear-gradient(in lab,red,red)){.bg-linear-to-br{--tw-gradient-position:to bottom right in oklab}}.bg-linear-to-br{background-image:linear-gradient(var(--tw-gradient-stops))}.bg-linear-to-r{--tw-gradient-position:to right}@supports (background-image:linear-gradient(in lab,red,red)){.bg-linear-to-r{--tw-gradient-position:to right in oklab}}.bg-linear-to-r{background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-\[linear-gradient\(rgba\(255\,255\,255\,0\.02\)_1px\,transparent_1px\)\,linear-gradient\(90deg\,rgba\(255\,255\,255\,0\.02\)_1px\,transparent_1px\)\]{background-image:linear-gradient(#ffffff05 1px,#0000 1px),linear-gradient(90deg,#ffffff05 1px,#0000 1px)}.bg-\[radial-gradient\(ellipse_at_top\,rgba\(34\,211\,238\,0\.1\)\,transparent_50\%\)\]{background-image:radial-gradient(at top,#22d3ee1a,#0000 50%)}.from-cyan-300{--tw-gradient-from:var(--color-cyan-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-cyan-400{--tw-gradient-from:var(--color-cyan-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-cyan-500{--tw-gradient-from:var(--color-cyan-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-cyan-500\/20{--tw-gradient-from:#00b7d733}@supports (color:color-mix(in lab,red,red)){.from-cyan-500\/20{--tw-gradient-from:color-mix(in oklab,var(--color-cyan-500)20%,transparent)}}.from-cyan-500\/20{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-emerald-400{--tw-gradient-from:var(--color-emerald-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-emerald-500{--tw-gradient-from:var(--color-emerald-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-emerald-500\/20{--tw-gradient-from:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.from-emerald-500\/20{--tw-gradient-from:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.from-emerald-500\/20{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-200{--tw-gradient-from:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-600{--tw-gradient-from:var(--color-slate-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-800{--tw-gradient-from:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-800\/60{--tw-gradient-from:#1d293d99}@supports (color:color-mix(in lab,red,red)){.from-slate-800\/60{--tw-gradient-from:color-mix(in oklab,var(--color-slate-800)60%,transparent)}}.from-slate-800\/60{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-800\/80{--tw-gradient-from:#1d293dcc}@supports (color:color-mix(in lab,red,red)){.from-slate-800\/80{--tw-gradient-from:color-mix(in oklab,var(--color-slate-800)80%,transparent)}}.from-slate-800\/80{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-800\/90{--tw-gradient-from:#1d293de6}@supports (color:color-mix(in lab,red,red)){.from-slate-800\/90{--tw-gradient-from:color-mix(in oklab,var(--color-slate-800)90%,transparent)}}.from-slate-800\/90{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-900\/90{--tw-gradient-from:#0f172be6}@supports (color:color-mix(in lab,red,red)){.from-slate-900\/90{--tw-gradient-from:color-mix(in oklab,var(--color-slate-900)90%,transparent)}}.from-slate-900\/90{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-slate-950{--tw-gradient-from:var(--color-slate-950);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-teal-300{--tw-gradient-from:var(--color-teal-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-teal-400{--tw-gradient-from:var(--color-teal-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-transparent{--tw-gradient-from:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-violet-500{--tw-gradient-from:var(--color-violet-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-white\/5{--tw-gradient-from:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.from-white\/5{--tw-gradient-from:color-mix(in oklab,var(--color-white)5%,transparent)}}.from-white\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.via-blue-400{--tw-gradient-via:var(--color-blue-400);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-500{--tw-gradient-via:var(--color-blue-500);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-cyan-400{--tw-gradient-via:var(--color-cyan-400);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-700{--tw-gradient-via:var(--color-slate-700);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-900{--tw-gradient-via:var(--color-slate-900);--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-900\/70{--tw-gradient-via:#0f172bb3}@supports (color:color-mix(in lab,red,red)){.via-slate-900\/70{--tw-gradient-via:color-mix(in oklab,var(--color-slate-900)70%,transparent)}}.via-slate-900\/70{--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-950\/50{--tw-gradient-via:#02061880}@supports (color:color-mix(in lab,red,red)){.via-slate-950\/50{--tw-gradient-via:color-mix(in oklab,var(--color-slate-950)50%,transparent)}}.via-slate-950\/50{--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-white\/20{--tw-gradient-via:#fff3}@supports (color:color-mix(in lab,red,red)){.via-white\/20{--tw-gradient-via:color-mix(in oklab,var(--color-white)20%,transparent)}}.via-white\/20{--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-blue-400{--tw-gradient-to:var(--color-blue-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-cyan-300{--tw-gradient-to:var(--color-cyan-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-cyan-400{--tw-gradient-to:var(--color-cyan-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-cyan-500{--tw-gradient-to:var(--color-cyan-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-cyan-600\/20{--tw-gradient-to:#0092b533}@supports (color:color-mix(in lab,red,red)){.to-cyan-600\/20{--tw-gradient-to:color-mix(in oklab,var(--color-cyan-600)20%,transparent)}}.to-cyan-600\/20{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-fuchsia-500{--tw-gradient-to:var(--color-fuchsia-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-400{--tw-gradient-to:var(--color-slate-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-800{--tw-gradient-to:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-800\/50{--tw-gradient-to:#1d293d80}@supports (color:color-mix(in lab,red,red)){.to-slate-800\/50{--tw-gradient-to:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.to-slate-800\/50{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-900\/60{--tw-gradient-to:#0f172b99}@supports (color:color-mix(in lab,red,red)){.to-slate-900\/60{--tw-gradient-to:color-mix(in oklab,var(--color-slate-900)60%,transparent)}}.to-slate-900\/60{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-900\/80{--tw-gradient-to:#0f172bcc}@supports (color:color-mix(in lab,red,red)){.to-slate-900\/80{--tw-gradient-to:color-mix(in oklab,var(--color-slate-900)80%,transparent)}}.to-slate-900\/80{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-900\/90{--tw-gradient-to:#0f172be6}@supports (color:color-mix(in lab,red,red)){.to-slate-900\/90{--tw-gradient-to:color-mix(in oklab,var(--color-slate-900)90%,transparent)}}.to-slate-900\/90{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-slate-950{--tw-gradient-to:var(--color-slate-950);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-violet-400{--tw-gradient-to:var(--color-violet-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-violet-500{--tw-gradient-to:var(--color-violet-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-violet-500\/20{--tw-gradient-to:#8d54ff33}@supports (color:color-mix(in lab,red,red)){.to-violet-500\/20{--tw-gradient-to:color-mix(in oklab,var(--color-violet-500)20%,transparent)}}.to-violet-500\/20{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-violet-600{--tw-gradient-to:var(--color-violet-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-violet-600\/20{--tw-gradient-to:#7f22fe33}@supports (color:color-mix(in lab,red,red)){.to-violet-600\/20{--tw-gradient-to:color-mix(in oklab,var(--color-violet-600)20%,transparent)}}.to-violet-600\/20{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-white\/\[0\.02\]{--tw-gradient-to:#ffffff05}@supports (color:color-mix(in lab,red,red)){.to-white\/\[0\.02\]{--tw-gradient-to:color-mix(in oklab,var(--color-white)2%,transparent)}}.to-white\/\[0\.02\]{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-zinc-700{--tw-gradient-to:var(--color-zinc-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-\[size\:60px_60px\]{background-size:60px 60px}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-2\.5{padding:calc(var(--spacing)*2.5)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.py-16{padding-block:calc(var(--spacing)*16)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[15px\]{font-size:15px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-300{color:var(--color-amber-300)}.text-amber-400{color:var(--color-amber-400)}.text-blue-300{color:var(--color-blue-300)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-emerald-300{color:var(--color-emerald-300)}.text-emerald-400{color:var(--color-emerald-400)}.text-green-300{color:var(--color-green-300)}.text-indigo-400{color:var(--color-indigo-400)}.text-orange-300{color:var(--color-orange-300)}.text-orange-400{color:var(--color-orange-400)}.text-purple-300{color:var(--color-purple-300)}.text-red-400{color:var(--color-red-400)}.text-rose-300{color:var(--color-rose-300)}.text-rose-400{color:var(--color-rose-400)}.text-slate-100{color:var(--color-slate-100)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-transparent{color:#0000}.text-violet-400{color:var(--color-violet-400)}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.uppercase{text-transform:uppercase}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-slate-500::placeholder{color:var(--color-slate-500)}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-70{opacity:.7}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_20px_rgba\(34\,211\,238\,0\.15\)\]{--tw-shadow:0 0 20px var(--tw-shadow-color,#22d3ee26);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_20px_rgba\(52\,211\,153\,0\.2\)\]{--tw-shadow:0 0 20px var(--tw-shadow-color,#34d39933);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_30px_rgba\(16\,185\,129\,0\.15\)\]{--tw-shadow:0 0 30px var(--tw-shadow-color,#10b98126);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_30px_rgba\(34\,211\,238\,0\.3\)\]{--tw-shadow:0 0 30px var(--tw-shadow-color,#22d3ee4d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_30px_rgba\(34\,211\,238\,0\.15\)\]{--tw-shadow:0 0 30px var(--tw-shadow-color,#22d3ee26);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_30px_rgba\(139\,92\,246\,0\.3\)\]{--tw-shadow:0 0 30px var(--tw-shadow-color,#8b5cf64d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_60px_rgba\(34\,211\,238\,0\.15\)\]{--tw-shadow:0 0 60px var(--tw-shadow-color,#22d3ee26);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_8px_32px_rgba\(0\,0\,0\,0\.3\)\]{--tw-shadow:0 8px 32px var(--tw-shadow-color,#0000004d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-cyan-500\/25{--tw-shadow-color:#00b7d740}@supports (color:color-mix(in lab,red,red)){.shadow-cyan-500\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-cyan-500)25%,transparent)var(--tw-shadow-alpha),transparent)}}.blur-3xl{--tw-blur:blur(var(--blur-3xl));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-lg{--tw-backdrop-blur:blur(var(--blur-lg));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-none{--tw-backdrop-blur: ;-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.delay-300{transition-delay:.3s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}@media(hover:hover){.group-hover\:translate-x-0\.5:is(:where(.group):hover *){--tw-translate-x:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.group-hover\:translate-x-full:is(:where(.group):hover *){--tw-translate-x:100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.group-hover\:scale-110:is(:where(.group):hover *){--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.group-hover\:text-slate-300:is(:where(.group):hover *){color:var(--color-slate-300)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\/btn\:translate-x-1:is(:where(.group\/btn):hover *){--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}}.placeholder\:text-slate-500::placeholder{color:var(--color-slate-500)}.focus-within\:border-cyan-500\/50:focus-within{border-color:#00b7d780}@supports (color:color-mix(in lab,red,red)){.focus-within\:border-cyan-500\/50:focus-within{border-color:color-mix(in oklab,var(--color-cyan-500)50%,transparent)}}.focus-within\:shadow-\[0_0_20px_rgba\(34\,211\,238\,0\.15\)\]:focus-within{--tw-shadow:0 0 20px var(--tw-shadow-color,#22d3ee26);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hover\:scale-\[1\.02\]:hover{scale:1.02}.hover\:scale-\[1\.03\]:hover{scale:1.03}.hover\:border-cyan-500\/30:hover{border-color:#00b7d74d}@supports (color:color-mix(in lab,red,red)){.hover\:border-cyan-500\/30:hover{border-color:color-mix(in oklab,var(--color-cyan-500)30%,transparent)}}.hover\:border-cyan-500\/40:hover{border-color:#00b7d766}@supports (color:color-mix(in lab,red,red)){.hover\:border-cyan-500\/40:hover{border-color:color-mix(in oklab,var(--color-cyan-500)40%,transparent)}}.hover\:border-white\/15:hover{border-color:#ffffff26}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/15:hover{border-color:color-mix(in oklab,var(--color-white)15%,transparent)}}.hover\:border-white\/20:hover{border-color:#fff3}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/20:hover{border-color:color-mix(in oklab,var(--color-white)20%,transparent)}}.hover\:border-white\/30:hover{border-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/30:hover{border-color:color-mix(in oklab,var(--color-white)30%,transparent)}}.hover\:bg-emerald-500:hover{background-color:var(--color-emerald-500)}.hover\:bg-rose-500\/20:hover{background-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.hover\:bg-rose-500\/20:hover{background-color:color-mix(in oklab,var(--color-rose-500)20%,transparent)}}.hover\:bg-slate-700:hover{background-color:var(--color-slate-700)}.hover\:bg-slate-700\/30:hover{background-color:#3141584d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-slate-700\/30:hover{background-color:color-mix(in oklab,var(--color-slate-700)30%,transparent)}}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-white\/5:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/5:hover{background-color:color-mix(in oklab,var(--color-white)5%,transparent)}}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.hover\:bg-white\/\[0\.07\]:hover{background-color:#ffffff12}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.07\]:hover{background-color:color-mix(in oklab,var(--color-white)7%,transparent)}}.hover\:bg-white\/\[0\.08\]:hover{background-color:#ffffff14}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/\[0\.08\]:hover{background-color:color-mix(in oklab,var(--color-white)8%,transparent)}}.hover\:from-cyan-400:hover{--tw-gradient-from:var(--color-cyan-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.hover\:to-violet-500:hover{--tw-gradient-to:var(--color-violet-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.hover\:text-cyan-300:hover{color:var(--color-cyan-300)}.hover\:text-rose-400:hover{color:var(--color-rose-400)}.hover\:text-slate-100:hover{color:var(--color-slate-100)}.hover\:text-slate-200:hover{color:var(--color-slate-200)}.hover\:text-white:hover{color:var(--color-white)}.hover\:shadow-\[0_0_30px_rgba\(34\,211\,238\,0\.25\)\]:hover{--tw-shadow:0 0 30px var(--tw-shadow-color,#22d3ee40);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-\[0_0_40px_rgba\(34\,211\,238\,0\.1\)\]:hover{--tw-shadow:0 0 40px var(--tw-shadow-color,#22d3ee1a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-\[0_0_40px_rgba\(34\,211\,238\,0\.4\)\]:hover{--tw-shadow:0 0 40px var(--tw-shadow-color,#22d3ee66);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-cyan-500\/40:hover{--tw-shadow-color:#00b7d766}@supports (color:color-mix(in lab,red,red)){.hover\:shadow-cyan-500\/40:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-cyan-500)40%,transparent)var(--tw-shadow-alpha),transparent)}}}.focus\:border-amber-500\/50:focus{border-color:#f99c0080}@supports (color:color-mix(in lab,red,red)){.focus\:border-amber-500\/50:focus{border-color:color-mix(in oklab,var(--color-amber-500)50%,transparent)}}.focus\:border-cyan-500\/50:focus{border-color:#00b7d780}@supports (color:color-mix(in lab,red,red)){.focus\:border-cyan-500\/50:focus{border-color:color-mix(in oklab,var(--color-cyan-500)50%,transparent)}}.focus\:border-transparent:focus{border-color:#0000}.focus\:border-violet-500\/50:focus{border-color:#8d54ff80}@supports (color:color-mix(in lab,red,red)){.focus\:border-violet-500\/50:focus{border-color:color-mix(in oklab,var(--color-violet-500)50%,transparent)}}.focus\:opacity-100:focus{opacity:1}.focus\:shadow-\[0_0_20px_rgba\(34\,211\,238\,0\.1\)\]:focus{--tw-shadow:0 0 20px var(--tw-shadow-color,#22d3ee1a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-\[0_0_30px_rgba\(34\,211\,238\,0\.1\)\]:focus{--tw-shadow:0 0 30px var(--tw-shadow-color,#22d3ee1a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-\[0_0_30px_rgba\(139\,92\,246\,0\.1\)\]:focus{--tw-shadow:0 0 30px var(--tw-shadow-color,#8b5cf61a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-\[0_0_30px_rgba\(245\,158\,11\,0\.1\)\]:focus{--tw-shadow:0 0 30px var(--tw-shadow-color,#f59e0b1a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-cyan-500\/50:focus{--tw-ring-color:#00b7d780}@supports (color:color-mix(in lab,red,red)){.focus\:ring-cyan-500\/50:focus{--tw-ring-color:color-mix(in oklab,var(--color-cyan-500)50%,transparent)}}.focus\:ring-emerald-500:focus{--tw-ring-color:var(--color-emerald-500)}.focus\:ring-rose-500\/50:focus{--tw-ring-color:#ff235780}@supports (color:color-mix(in lab,red,red)){.focus\:ring-rose-500\/50:focus{--tw-ring-color:color-mix(in oklab,var(--color-rose-500)50%,transparent)}}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-slate-900:focus{--tw-ring-offset-color:var(--color-slate-900)}.focus\:ring-offset-slate-950:focus{--tw-ring-offset-color:var(--color-slate-950)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:scale-\[0\.98\]:active{scale:.98}.disabled\:transform-none:disabled{transform:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:shadow-none:disabled{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(min-width:40rem){.sm\:w-\[400px\]{width:400px}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:p-6{padding:calc(var(--spacing)*6)}.sm\:px-6{padding-inline:calc(var(--spacing)*6)}.sm\:py-16{padding-block:calc(var(--spacing)*16)}.sm\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.sm\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.sm\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media(min-width:64rem){.lg\:px-8{padding-inline:calc(var(--spacing)*8)}}}body{margin:calc(var(--spacing)*0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}@keyframes fade-in-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in-up{animation:.5s ease-out forwards fade-in-up}@keyframes glass-reveal{0%{opacity:0;-webkit-backdrop-filter:blur();backdrop-filter:blur();transform:scale(.95)}to{opacity:1;-webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px);transform:scale(1)}}.animate-glass-reveal{animation:.7s ease-out forwards glass-reveal}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.animate-shimmer{background:linear-gradient(90deg,#1e293b80,#33415580,#1e293b80) 0 0/200% 100%;animation:1.5s ease-in-out infinite shimmer}@keyframes glow-pulse{0%,to{box-shadow:0 0 20px #22d3ee33}50%{box-shadow:0 0 40px #22d3ee66}}.animate-glow-pulse{animation:2s ease-in-out infinite glow-pulse}@keyframes slide-in-right{0%{transform:translate(100%)}to{transform:translate(0)}}.animate-slide-in-right{animation:.3s ease-out forwards slide-in-right}@keyframes slide-out-right{0%{transform:translate(0)}to{transform:translate(100%)}}.animate-slide-out-right{animation:.3s ease-in forwards slide-out-right}@keyframes backdrop-fade-in{0%{opacity:0}to{opacity:1}}.animate-backdrop-fade-in{animation:.3s ease-out forwards backdrop-fade-in}@keyframes slow-pulse{0%,to{opacity:.1;transform:scale(1)}50%{opacity:.15;transform:scale(1.05)}}.animate-slow-pulse{animation:8s ease-in-out infinite slow-pulse}@keyframes typing-dot{0%,60%,to{opacity:.4;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}}.animate-typing-dot{animation:1.4s ease-in-out infinite typing-dot}.animate-typing-dot-delay-1{animation-delay:.2s}.animate-typing-dot-delay-2{animation-delay:.4s}.hover-scale{transition:transform .2s ease-out}.hover-scale:hover{transform:scale(1.02)}.stagger-delay-1{animation-delay:75ms}.stagger-delay-2{animation-delay:.15s}.stagger-delay-3{animation-delay:.225s}.stagger-delay-4{animation-delay:.3s}.custom-scrollbar::-webkit-scrollbar{width:6px}.custom-scrollbar::-webkit-scrollbar-track{background:#ffffff0d;border-radius:3px}.custom-scrollbar::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:3px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:#fff3}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}} diff --git a/tests/ag-ui-app/frontend/dist/client/assets/x-DtBV5VmR.js b/tests/ag-ui-app/frontend/dist/client/assets/x-DtBV5VmR.js deleted file mode 100644 index 20ea79c2..00000000 --- a/tests/ag-ui-app/frontend/dist/client/assets/x-DtBV5VmR.js +++ /dev/null @@ -1 +0,0 @@ -import{r as s}from"./main-DbgxqtWz.js";const C=t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),w=t=>t.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,r,o)=>o?o.toUpperCase():r.toLowerCase()),l=t=>{const e=w(t);return e.charAt(0).toUpperCase()+e.slice(1)},d=(...t)=>t.filter((e,r,o)=>!!e&&e.trim()!==""&&o.indexOf(e)===r).join(" ").trim(),f=t=>{for(const e in t)if(e.startsWith("aria-")||e==="role"||e==="title")return!0};var g={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const k=s.forwardRef(({color:t="currentColor",size:e=24,strokeWidth:r=2,absoluteStrokeWidth:o,className:n="",children:a,iconNode:h,...i},p)=>s.createElement("svg",{ref:p,...g,width:e,height:e,stroke:t,strokeWidth:o?Number(r)*24/Number(e):r,className:d("lucide",n),...!a&&!f(i)&&{"aria-hidden":"true"},...i},[...h.map(([m,u])=>s.createElement(m,u)),...Array.isArray(a)?a:[a]]));const c=(t,e)=>{const r=s.forwardRef(({className:o,...n},a)=>s.createElement(k,{ref:a,iconNode:e,className:d(`lucide-${C(l(t))}`,`lucide-${t}`,o),...n}));return r.displayName=l(t),r};const y=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],L=c("chevron-right",y);const b=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],_=c("loader-circle",b);const v=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],x=c("x",v);export{L as C,_ as L,x as X,c}; diff --git a/tests/ag-ui-app/frontend/dist/client/favicon.ico b/tests/ag-ui-app/frontend/dist/client/favicon.ico deleted file mode 100644 index a11777cc471a4344702741ab1c8a588998b1311a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3870 zcma);c{J4h9>;%nil|2-o+rCuEF-(I%-F}ijC~o(k~HKAkr0)!FCj~d>`RtpD?8b; zXOC1OD!V*IsqUwzbMF1)-gEDD=A573Z-&G7^LoAC9|WO7Xc0Cx1g^Zu0u_SjAPB3vGa^W|sj)80f#V0@M_CAZTIO(t--xg= z!sii`1giyH7EKL_+Wi0ab<)&E_0KD!3Rp2^HNB*K2@PHCs4PWSA32*-^7d{9nH2_E zmC{C*N*)(vEF1_aMamw2A{ZH5aIDqiabnFdJ|y0%aS|64E$`s2ccV~3lR!u<){eS` z#^Mx6o(iP1Ix%4dv`t@!&Za-K@mTm#vadc{0aWDV*_%EiGK7qMC_(`exc>-$Gb9~W!w_^{*pYRm~G zBN{nA;cm^w$VWg1O^^<6vY`1XCD|s_zv*g*5&V#wv&s#h$xlUilPe4U@I&UXZbL z0)%9Uj&@yd03n;!7do+bfixH^FeZ-Ema}s;DQX2gY+7g0s(9;`8GyvPY1*vxiF&|w z>!vA~GA<~JUqH}d;DfBSi^IT*#lrzXl$fNpq0_T1tA+`A$1?(gLb?e#0>UELvljtQ zK+*74m0jn&)5yk8mLBv;=@}c{t0ztT<v;Avck$S6D`Z)^c0(jiwKhQsn|LDRY&w(Fmi91I7H6S;b0XM{e zXp0~(T@k_r-!jkLwd1_Vre^v$G4|kh4}=Gi?$AaJ)3I+^m|Zyj#*?Kp@w(lQdJZf4 z#|IJW5z+S^e9@(6hW6N~{pj8|NO*>1)E=%?nNUAkmv~OY&ZV;m-%?pQ_11)hAr0oAwILrlsGawpxx4D43J&K=n+p3WLnlDsQ$b(9+4 z?mO^hmV^F8MV{4Lx>(Q=aHhQ1){0d*(e&s%G=i5rq3;t{JC zmgbn5Nkl)t@fPH$v;af26lyhH!k+#}_&aBK4baYPbZy$5aFx4}ka&qxl z$=Rh$W;U)>-=S-0=?7FH9dUAd2(q#4TCAHky!$^~;Dz^j|8_wuKc*YzfdAht@Q&ror?91Dm!N03=4=O!a)I*0q~p0g$Fm$pmr$ zb;wD;STDIi$@M%y1>p&_>%?UP($15gou_ue1u0!4(%81;qcIW8NyxFEvXpiJ|H4wz z*mFT(qVx1FKufG11hByuX%lPk4t#WZ{>8ka2efjY`~;AL6vWyQKpJun2nRiZYDij$ zP>4jQXPaP$UC$yIVgGa)jDV;F0l^n(V=HMRB5)20V7&r$jmk{UUIe zVjKroK}JAbD>B`2cwNQ&GDLx8{pg`7hbA~grk|W6LgiZ`8y`{Iq0i>t!3p2}MS6S+ zO_ruKyAElt)rdS>CtF7j{&6rP-#c=7evGMt7B6`7HG|-(WL`bDUAjyn+k$mx$CH;q2Dz4x;cPP$hW=`pFfLO)!jaCL@V2+F)So3}vg|%O*^T1j>C2lx zsURO-zIJC$^$g2byVbRIo^w>UxK}74^TqUiRR#7s_X$e)$6iYG1(PcW7un-va-S&u zHk9-6Zn&>T==A)lM^D~bk{&rFzCi35>UR!ZjQkdSiNX*-;l4z9j*7|q`TBl~Au`5& z+c)*8?#-tgUR$Zd%Q3bs96w6k7q@#tUn`5rj+r@_sAVVLqco|6O{ILX&U-&-cbVa3 zY?ngHR@%l{;`ri%H*0EhBWrGjv!LE4db?HEWb5mu*t@{kv|XwK8?npOshmzf=vZA@ zVSN9sL~!sn?r(AK)Q7Jk2(|M67Uy3I{eRy z_l&Y@A>;vjkWN5I2xvFFTLX0i+`{qz7C_@bo`ZUzDugfq4+>a3?1v%)O+YTd6@Ul7 zAfLfm=nhZ`)P~&v90$&UcF+yXm9sq!qCx3^9gzIcO|Y(js^Fj)Rvq>nQAHI92ap=P z10A4@prk+AGWCb`2)dQYFuR$|H6iDE8p}9a?#nV2}LBCoCf(Xi2@szia7#gY>b|l!-U`c}@ zLdhvQjc!BdLJvYvzzzngnw51yRYCqh4}$oRCy-z|v3Hc*d|?^Wj=l~18*E~*cR_kU z{XsxM1i{V*4GujHQ3DBpl2w4FgFR48Nma@HPgnyKoIEY-MqmMeY=I<%oG~l!f<+FN z1ZY^;10j4M4#HYXP zw5eJpA_y(>uLQ~OucgxDLuf}fVs272FaMxhn4xnDGIyLXnw>Xsd^J8XhcWIwIoQ9} z%FoSJTAGW(SRGwJwb=@pY7r$uQRK3Zd~XbxU)ts!4XsJrCycrWSI?e!IqwqIR8+Jh zlRjZ`UO1I!BtJR_2~7AbkbSm%XQqxEPkz6BTGWx8e}nQ=w7bZ|eVP4?*Tb!$(R)iC z9)&%bS*u(lXqzitAN)Oo=&Ytn>%Hzjc<5liuPi>zC_nw;Z0AE3Y$Jao_Q90R-gl~5 z_xAb2J%eArrC1CN4G$}-zVvCqF1;H;abAu6G*+PDHSYFx@Tdbfox*uEd3}BUyYY-l zTfEsOqsi#f9^FoLO;ChK<554qkri&Av~SIM*{fEYRE?vH7pTAOmu2pz3X?Wn*!ROX ztd54huAk&mFBemMooL33RV-*1f0Q3_(7hl$<#*|WF9P!;r;4_+X~k~uKEqdzZ$5Al zV63XN@)j$FN#cCD;ek1R#l zv%pGrhB~KWgoCj%GT?%{@@o(AJGt*PG#l3i>lhmb_twKH^EYvacVY-6bsCl5*^~L0 zonm@lk2UvvTKr2RS%}T>^~EYqdL1q4nD%0n&Xqr^cK^`J5W;lRRB^R-O8b&HENO||mo0xaD+S=I8RTlIfVgqN@SXDr2&-)we--K7w= zJVU8?Z+7k9dy;s;^gDkQa`0nz6N{T?(A&Iz)2!DEecLyRa&FI!id#5Z7B*O2=PsR0 zEvc|8{NS^)!d)MDX(97Xw}m&kEO@5jqRaDZ!+%`wYOI<23q|&js`&o4xvjP7D_xv@ z5hEwpsp{HezI9!~6O{~)lLR@oF7?J7i>1|5a~UuoN=q&6N}EJPV_GD`&M*v8Y`^2j zKII*d_@Fi$+i*YEW+Hbzn{iQk~yP z>7N{S4)r*!NwQ`(qcN#8SRQsNK6>{)X12nbF`*7#ecO7I)Q$uZsV+xS4E7aUn+U(K baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/tests/ag-ui-app/frontend/dist/client/logo192.png b/tests/ag-ui-app/frontend/dist/client/logo192.png deleted file mode 100644 index fc44b0a3796c0e0a64c3d858ca038bd4570465d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN diff --git a/tests/ag-ui-app/frontend/dist/client/manifest.json b/tests/ag-ui-app/frontend/dist/client/manifest.json deleted file mode 100644 index 078ef501..00000000 --- a/tests/ag-ui-app/frontend/dist/client/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "TanStack App", - "name": "Create TanStack App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/tests/ag-ui-app/frontend/dist/client/robots.txt b/tests/ag-ui-app/frontend/dist/client/robots.txt deleted file mode 100644 index e9e57dc4..00000000 --- a/tests/ag-ui-app/frontend/dist/client/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/tests/ag-ui-app/frontend/dist/client/tanstack-circle-logo.png b/tests/ag-ui-app/frontend/dist/client/tanstack-circle-logo.png deleted file mode 100644 index 9db3e67bade15d9ced50bdfc2393359c5f6f5712..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265387 zcmXVWby!s2_ch%q5|RP}N=QkUv`C4-07DEgbR$SJbV{g*bT`A$T|*5vzPq_>6{zq5t28hx2r&-70zc>45I` zSy>LPe3bs+=>p&7gMk|w8X?7h8#-EQIt>~Y(I*uJSsky%;}!MDbWimDi?Pe$Z;lj~ z<`gOkmHc&^RrSRm4ivSfdYkYA*YP@p_Y8EAWJYai+qd62wdv1 zkawV4`tI&&eeIpU{pNCS!%QyKy(I(Xzdj`R0wc)G^EzoSC8I<^fmbu`=JJ%8o{zmLzRwFSwK_$CHn40VOZZ&;T&OT=(NmBt$#~Y93I=xi&Y~Gzm zucsTXN-;Rrd(`%t&{_a+raP`{X zP_3JO6C8a79DqXE?^5bv9#4*I%9XG@{q8sPyh_>&^E{0O{GUmB4R2^o-iFUvwnn;% zE-~O z`hF>6j&n$QU)AOVKaH?*Na>$-SD{+eR>z4Jb$9tD%d9Ko*oL>!KJbiI0K3-a|DHGB z!9*~@Zlh-E3~6Mt7Y)yA?EcHIYyIJbWas5!FhA#&aVJ~Zme@UhK&RmRd)svC zF=Y!=#h&v!wZz%;s&ZUc`UqDnf8u&KfL+*I5t8_F*k%LmW30@&tz?+qr&7V%|L_PV z-87R7jo*BI4Q4{tGt1i*)U{pc<|vhPQX2Id+%sx!dg_nK(~y%x_xif`UbhElZP1sS zg?nV5lovwPeXAc@#v3plHwhl1&bAWf$0~`gA|t(u+FrN4>ub4S_QTXPcUvHxj45$X z$b%YBTHa%TdTp1(X#KzT0TdLD3$=vg)|gvKv-;yNRROm6xrO{>mb=uA9 zMf6BRqa)~Gty5sE6C!4X395kiid;kmRS9*9CXutUY}Ut=mQS|bvM_doCa6X#!@QMr zeQ;x8>JB%3v(D!{ezeM;Qmy`b=SVSM-}3^BTLDO5QG}B2s7?VTgQ4Czmmh9cuQVa6 z8vmfSvVsCp34h<8E@nS`nI7%=eH3YcpmphF^sH(4NYI=EiZ*^zyD9Vuzo9XG9-Uiy zNU+Q%Wo`49f4nE1_54S6-`=j{OGZa<>(E%*C@O4g6Rk`8Q3rHe)+%1cl>WL3aLwas zj^sEOq93M9QBpW|1WN|@yW%@)C1@p1PS*1?iFW%^$@E5CKBpkBYLG79ecm2xeJAts zuN>l+(~`uX^ZJYQ&Ky5AO@$si5@^UR5hUuxVs%U@Ls==+OoblE`AR8ow$Waoebv3% zMxY6@=GA)Zn>PnF{A!^4VCcQ-pSuOb6iA&LOat9cAX48~84px2`u38Jq$ z@srn`nX&%98HnKi?i@rDB61!sa*h$=80zQ%Hk|*>J@i|N)qpi>!mBTD;z~dYRbSfN zzduuO_vOu;uext#%iY7qC{L-=N~yLGYV|C`?eIAbI{$Y(8cY=dyUpLSTDq@!im{=( zAWnV5y6UoeX8^{lD2Z}(un`5>99>8x`t!kOJZ)PBN2$Th;lX`|euP(2)N=RYE-zje z|5``mG082))7R31Gkh%8`FwzOPPOhF2)B?I4riNhrBDA}IR5bmfY8J~N|qt{J2fB8 z`-U;VtBt?MM8j&V86>rarY{(jeU0@QI=G8JC(Q@&KI%b5y#CHswj)~B)>3FSq0V!pE0__^vG zc{P5*Y9r8R`jZpOCf!VY*B1VYL~nCpR0E?CHO6G&`Mu!`n;qsD!sjnq;a2? zDC_>p8Mx^H>sy~?K4N^cbdu91T|cLc8=_Sc!n`e3|5|B1ypHuX7ZvrJidH$-3xOT5 zOs8FtT-)`Sd`n0^K9O7TZpH5*Jma9}uY=)JH67GJ%7IBGztAoJKlTlkFKy1hT|?H# zf+B*n$;%E+Ic3FJ=Uw?M#~;wj+kuapy&|jfN1ky%8Ko6``elc&-kD?B*72PP3#_oG zWXhU^Ks9_Zf9P}~O2rSWqKnO8iqp*}=y=|r(F0)X=s&gjqK@eD8^v0^joXuxu@r*C zG5N8vvN`(R*wcjim8av*pDYP(c(#%Q^Dai<6El$|73?nA~`~8x? zdaUTe-la@O&Nn$+Sk=fEvTgpq3iCo#I>NYnL-@%%uW^epc^QZ%e&Ck*!I*;P)>HOq zBp+V&swREHU|9ZU@!}U15=G#LE2iH9mF2pXkjC{RzI}C$`FQy<7@&hyR$M&7<>%Le z1?*VE2-xWgyli1CiwOb%!;nB2L7|fe{y;5ieaUvsH}!geBLA`>h~|~5GUFRT2}}b* zbR9|~ZQM_GJkphsc$_bF+BDyn(#8}3_v%ZQvJhY2c@=imJ$so@4rV~37^92Rz1Lzt zPb~?2{hHuGzd?rO$lDyvd|3S4);ZAl2*6ljM)JCQsDjVy+B;mvKYW8UEBn=W7yZw| zw?6{V6!A-2$ZH2*GOSy!YpsWnG@^aZeuYHK`!DR(xzT%6Ls81>Oo4*$aXan`>^&^S zu)wCtTU=>r1()Nf+OcKzy>0%VQi6dmZG$Zq@QQ5;OesH2bN(v8Wc`HB1SVrYKUZUf z<=N8WvlFZsdo3xZj&aa1HVy~-hkRY-r(@f=crMb7W7GVToCU`klxVuf^xh=fG6?v( zoL9i>8Ut07RdDl1e0bnFA)0&f;Cs2(*#mP`U z<_w^EG3`Y%@@dY&9#7w}K?sZ4^=3AV2EDe{CNobFz4X@#W60&$l<%>MPy9pVMxA}E znVG4`uh>diSPSDh!}?l~@>aj)e{=r2DEw7Q4_o;ooy%Hzv6&?nJLL=A`Mm-R>ab z{c(JV@VNvAshIo&oNPP|0q^+_`7d1hrqo3>M~Dcbhn#68oc(Du>n`rQAxFZ3KVa=x zyznx}YN2=P*jg+zku8w0FXnGEa;%N-?-vBGNp1jwwwFRP&+E|QgM@s_i+2)%f?m>ML za+f0R7G2_dsw94b7>_SSt_vLEC$v#&cFv>WVF-=1gnc(e6X~4a_3n22?5y8Czpfut zGIIGK?$(E8`pi&1?&tbq1lV%xF@Z)W+o)OghGTDE=8*+;^zACkC+=@Sl7U`J*zky) zN%qP^v2r3q87bN$X*%u~PPU7Z?*Dj8!r;v(FH=NBhi?!(`*TyC$vx+1sSObk!r9yk z?%+qjZyDdf>kawZn={M9EBwX&IGuCdU#6Lyg)NB02Ej;XQ8G;MWKLkGf zBfJ8&{y9fJTOs6~c)Y&3V2dDmua(UuVwQY1t#_fn{uroYxDQod$F4{76 z6&_1qco@EiEJAx9p+Wu!?;>62hbjb|j7^ zcP=cnIq)*9L)S;K3aw4KocY|<+Bum_ePmXds5bRFAV5*S{aLY^f3vALEA9LHSziGdS6W_#FCY`4 z=2^8FnrFQIh;P8rcRZIyDEe6#BK6}({d#TJ(l?OPc!ah( zR`1O_^go4ip%40<2@M(|sGvzS@B>h*wok)!1<v1+&#aL1JXHH0fV&An6_}9BTA_7@_ zYW@>Ij{TlEyH5YwM!^UxPU0Y{mk!!09CY5$X*{2Qv}icfD218B33;B`dPUO1Nenxmlj zqi5!$HXFQMp{lm)9Ld79hD28d`Cz3FA2})Cg>Z%@Q#*jeWDeU8-AMDhAP@jRZ;Gxp2MP&C#_o%EW{_A1gHb(6uDXrgEn#A#-K6UGbA&D^S{!tZAN zveJf>-_6rH{2rP9OP~iWulNNJw@amF9j^{SZ%cYx<$g_9)`;a@?Y<40nGvF)asX4B zeC&F5Zn+K*v_``uYctRyugrxQ+5r6nPU6lC#O~$w?A14K2MOt##ey`i{snB_?he^d9l1i6=|XoJTWWzFehJA`^eVn zfnfEt5_WiE6~by<=bO0Qsnu?Hrr1Qjc_aR9(;KbN9x^>c95DH)r}0Y50X^hH9Ukz(@7^DLeqPJ?|Vy4d7cx!D&ZqwE7^bkvE8CnTE|eATCm&c zv#OAZRPSx*^bPTyq{f!e&O6}Lho3%6A;wdsV_Wrx zy=^Bz@$;~4VL%hNB6^~o@xT)0n>v-x$g+8;ztFpDE{86u{_KX6T|+_T^u;RLyB!~m z5SrXfdpo`x0A@qTFXb=vrl4ojS+uLp@+aLGC+rGVP@`^1?%KC12Ij~fA zP8UUo{b;TJlc2yugH@(_ba_^CbrprRMGr0e#%rRPlIFGhA$G1KUY4k^-UI|84d;I+ z$LZXb*QCkizI!0Wx!n5kK$}B63SgG5@ANJz3ZVMuY}0@8-J;9!6gMH^H?MDD9xnJD zNw1`<%v0WpX?u6|6psh3k!i>@tJZW9ramGwcN;jj6{Wpr8g$D4>BRZHZ3_?}C|>>@ zu+Z}NB^Q)y%y)&DM-{W$u1BaGylno9L4+&_jUZC%Jzw#kTw?Q|RXsG|>BelOf`dd8 zeWRNFSAA-^80u3Jn>M8am;KXfmY)*_r^{5-e@11B`LAxQXme!kA$I&X{E2S>s6)>+ z)Trm_KmWbfJ16+*gEU4Qq1E|3^{ZP+tKcieOG|x3VjFok^>@YAML_WVAQ&kLO9YRn z)@c+l%US;d_)7N+NW-QkOTn{ZiJ$71$5@Npjok795;*-Ng#gPlEjXSb^s2jgwfkl; z)A;tmoARx5pK~22W4Nn*Qr9??!(z?)fI>;mXZzEB(y2BbEM75yaFfV83w}O92DHi9 znTqb6+vH$rkGsul@dmBU8x`REdOx9(m$OzWyHG{(n>VnAaRm?L1*KxI4WRDZ!tLI< zm6hH9S2^FNjoHsoqn>}_WkumTPc;VTzD!L<2EVX-Yeg~mdu&fEgUXy(K8tr-GCxu5 zZ)%g+%zdHs0$dFQB4u#3-4T27Lrmz(0GJ3m>Ax)DN*O)!LYHJDMYu}-ySwqqq z{XM*NK^6+}{Y4%)<%OMfUnOrH7;Z~nsn2FLKE_8{Mh3OA{|*Jwg;N1-+IhnEc}>QY zkCg-GjbtZ-M6kkgUBTIF-zSIddm3AkI=#na@RHh$NfRX6Bbdl;nJ-Q~XyWOh7cIV( zQx}X8_;|Enz<52aMR)pAMJ>m^H?bjYLK>CyXmc{_J>QU*RIRy;`09R@WCiaL%Wt4T zQOIz~9zwUjEPWAz;5UR`=hYHOBo4t{Py#oD&SRu+ZZlIW&)AZuAH?mQ~>;mPP9z;Gb?;-N&VUH`f@0_x3a5b)X_+`$R}yZSzr~<5vxGEmtn4 z&s=VPVlzO>;q>T#d5(2BEs!X;-K+Du_J}m@WgB&e)v&Xsvo1@SvhXooX`(H_$}7lM zQH*G^lJ<+(*({oln^9!+tuQp;cByP!j&Q`THy8fw;>QF2+kuX(@PndFNCToN40Lf~{#`}23BVs=v#Yiqv?+r{eE-`uIo*$RI*vy`1a=oWmdxlEcK3Rg5@4tP(@#F`UEmFi@>JH}$7e7}kk z=liz?EQlZ-fPjUry4*JIqbs^CW#%ywE-=kL9&!C?9EZ>4OAmW=SajmiuaOKBF|yKy zwZ$3BOvcj+zL!o$q!+?YwC#6XbK><61ca19f-mTtZCHeE`YPgg+v0Y-#idJTWQvG( zrXuxv9d3mjnwvzhkWemcabZLR5}RV99&f zwqraZJpv@p^~FMi!S9Q>7(D&U+Ej%#s(ROAhPOZROa&6(9*km7KJWf0dbUKDvp?>8 z*Z2$>-yIr|hlm2QN|0rd{tsE;N2%RH6K<+3vY*W*oNqA1gtwOk*Ms+%0te2fyQkZh z%oZP#B8yIKF)VsWI+v|8gH3m(Yr;>IuZ5N>_clm5js>?wYl0aH44(=i2!n_vIe*eBt_+^LH_3lV(4sQm*F;Z1zX!SPw3!;R&=j(rgh@k>$ito z4Q{u*J>lmU&ww^&ZZlKttdKY~xnL2%8^r6hYQoX*%dC04DG!mZcSf}pHVgQA$18C* zgD!43Q`h3N-}@au_?}AqM%lo986<9zGySz^w{?rIcoOniKM0q2Yy3xu=7A@oW&8!I zwunovjeGhHq-7iw;^;rTXiahLIqvI4d(E+>bTmvnl(QZB<5X%5p6t{#sm$1P$-dch zUYs#5Cn22LPUwoi3Aw>d8&I_SGOLYc#7iakxF1vd4RZzp4U{{ev$9ZVgO#fioGhHC2jO0EY1{Rm|Gj)uA|( z#haXHJTwY4EsMa_C{Ebm?DU{oq(srjnndl0WiiU8HapPRoezhaHlKXeTmC4X=Fl?^6)ndsy0t_N?!m zfwj2EIt4>h8|>FHR(@H|SsM~xb;dkA-`5YXVE3(OgTEUsll2ggro?6DWOrp0>^HTc z9Iiwmdyim=Ul-;wbFO|cb`l5*^w>+zz7!Jvc6qh6yK~yjD>xbit9zrK-3H&6i z-&IG49{44lYLQ*W>o=_IVag|=RErZ+lbdN!*U0GnwYgH8E4~l$j2}_V)EUHk(t&hP z5tBfB2~d#z^+s?@Q4kgQSWx7d;52_Q&1KME)o)VHhmG;lQAJadcJ2Gg^Ruy0-VXD? zl&zV;vQ3fQ^GaPXWAnQ`^wEOBH*Oj*r{zB-Nd`8Kf6G8(jJ~Lk`|}5FGHqWu zhHaLxDe7fNvW(p*^1W7GOIs4@+)Q~`I%UTwr7`)RpSN1xK;Za6ElUmPg?tT|0e;p0y?+76;J_V787f^e z025^vw&EX7y5D8lakJK!zcff3$&L6$Z){iTiV;P#1WKTd|IHap znEAo*;Z@?h=UJi{eCCJohLRNQ(=r%Sp3WgDwUM*kS8c@ioogTbc!C#V187s(1aIjj zv-+b79P>d8n2XFJ=6;7p*CX7>#iAH=Ste|XHQ@SsOas-}(Z1vH%#W~C8(tD9kCC{Z*zI>XBn zkF;c`2cU$#XL!r;{}8(v-*CC?#B;4rQa{j!`%7Qicjr>XMdXrOMzv;y%-f(as*>mS z-EUi-*!qYL7V1r8ZvE4lHkHwcpqC>yAF^Pr88*Zv(mB(pKY9vzQSR8O=;16CU?W5( zXlg~;APVTmVojyw8g}^MKY*iVm}%H~C67#21l^g94CBrAIt)|fwOhHylIn4Nao5Pu zDA4WiK#2-1HC@fAoL7J{rj`*y+;^GvBmC36f^v6NN7O%*QIoX%ZjTlS5OfbEr1Pd|@ zNiUJ*?j}kbaL6Bm+a`b`^TT2(l& z!=>S6Rtl4Wkn+@8)c?AT{wLf6kB*(;P`M-RgcPt%%h>N=H2(EXGZTIr?Y8LI!EL05 z&qGA7T)4Gahdv}c$vdVzjJh|k+%jiZK>qfaXeTlRkCp+f&`IHpVE$9%bNbM|0fEkN^%(`RXhF@(U_J%9+1#O7ctnslut%2(@J1< z2J7yvB&Y7|JI=lDMd1y+Ga((uGq)WpV~)2s-0*~3`Iqxv{qrCqf(ZiczeND73N&pK zJ-qj0;yQVYmPNkU?X$*B4?@60B@7|6oSTsqki>Scqa>kT1Nh)m*Ki>f0X=qHr z$fI5_lb~*{!xu3qdFYK=vfyv$aoYJ#KnEL_<67pSDP(?-whgdPJNNx%s{gi0UKE+VGRczCsQV%(~cH8w%h2SD@p6pR= z-D3Z%T3F7^)mD}^wqTJYM|FmFJ@a`}Js^D6)&>z~44*6)5wmpkKponf?fp8TshZ>M zE(K-4^$+zfTSpoAY==;Mc7weOU-9RP^C4*noXzQTM}SQZkZk)%wvi=M6J?6whXueb6d@u%@I5F6VW;yVANN{wld0no!`0HON5dwE zAq6ljiE-gD`7q0ws3HHDu)^vz+GLMzbQdz>B0ooxyjK0BVsVBqd;I##o#$WdmC~re zumAld7L|bjD;@=9meMnni3&5$glcxr1FzlMlr`GS$E$p|r)#I`+Y$EL)6=&7c6&No zkx%GxP(O&()t!sG%im}u15s${&0gUxDhE;h9Igonc%(!(>`5G-{8ER6B z)Uy2Kx9M`2_0Ia4n$NX~{`e}Mi2J)v#9jS_IhU(EbRWuZek!PJng+(bD!8xzNqphw zIMn~cRwGkS@P>6SI+bGY#|0M*t6*VPw3UHisy8!2f``=1M@eF5$kLJ z`F=8!O?17Mf8jw9WQ^ms>O_p~vjgME{q$AaxHwIdc$^#HQvJTy^Cckm-R|^w zk)J*6S(Dr0cZnjLYPXm7M*tJ$Cuv2P8uqhO z&G#R>EwPx-JKLPVtY(Aw+@Gn!9GpA!>e@4Vn z-{Eqy6?Wp&s2g~IMX(oU3&2XZLlFF8@7F98o2TvybK`y`k?-K(3ysg{o7%#}Okt|e z2Rf6&24^ov<*#bdH&_K~$4M#*de4^hSRh;hA|RXR@iaeAK!58D%~mx*p}mb&Km|fQ z9`3HN*m2-qV_n48tchLyB_0RZ*9v0a`YP1l;8T6(LFHQ5bdjUfW}=4&kq9T#@3pYw z5QFY$S$o~f+y0?(F`>Q4SxXzl;G3mIF-1H>*jPE}V5%6@tW>YLbxU3h;wpMT(1A2! zY19nc*qg+FcI$oDI9=%?!rcZ9!^356;D`yP4i1L|1i880Ejw53_+L12H~YVcc>I67 zvYXY~8leZVq@7*6c8w)aa;lSVMFMJWxg}-?R+Hs!7@qj(GJEpfa97+IWjh`$PRPq> zi58N`Bfmg$sB~C{3k|TC6kQ)7k0T`9=0a+iXcL4PrEu zYT9}FG5LJn?#2=IeKOy%8WO7?xNqCLAxx4Jw%gD|8(I8ea`)pZ@9uG0MHn6A8e@4{ zE53tt5mo0x!2=xVQjgoZWV;)fqlT|#|NFB~h%h9Bi(HpkW)L@Cl!h%-$dIt|N=`}r zU`hNq89?*E=&IMK_mpw}IhOamT>a3~o%*jZ=PzQ~dT^u9U)RM=50iKHSzVkWXvtrt z8s9PAG;7!=bD6)khPNMbu~g5w+FDE7JWNw7Z3u-;)eBvDcZ@#K)8t`d5yo5vY%H#m zwJ03W>pEMJGbT|~?R?Ak6*=R~#pt|-13D~=P;6=F+5_;C#V@K%S#imZ@!#XHFbEsu zg#CW{$IO~l9FgHq80KzZTQB3$^^fl?mSbag)X~Ka_pNm6`bv!H?E5sJY25n^(`xJb zvBWKlAxN@1+#wGYfJ|Q&ajynx!g}g8lyrt4AawwdLU*5?>B&@0ji(}qx8`_B%(BM> zKG-*XxHVUbzhr%yE7yHLeM`Oz*fh+@&5B3>Bxh(%Nb8@8D{}~E; zfsse6MfKkxcT*vPY6|x?zL(Fh4WXOlAthNFl$`}PD@5OJoWj_Q5smxfT`I%={R9o` z7`;z4Pr(gu5cTl)9bLYxQ|3~g!gb03sP zU^~kYS`TL(J}g*olWm6jr6H&`1vkpia>B0n(x6R|YwTujx;g-Y5?QusCjWsH7h@E= z{-Ob{s<_J!Tlrchw(KxC`;_u7H#kSoo9{U)s}B+{+hcuk%#jSHi$H}X-nYq!)w$DlsoA!)B8mq`Yb zOWG!s4K`C*+xA;)OFuWHpGGy^_|z@WK>}+DQVugoAYtRI zA*p_}p&-#Q&P4;t1DEFKTLPXS6#<@I|s>d|X?{;+TjLq+a&E}lHI-_=@ zJI8%O=Uy>_AlZz&?e}~!Er&mcNelW(Lvxu6R2D2yn47sSb2sC0Gr!p_76}?k%-@nB zv-r4|;G0o1l@@CN{Z(U9RYRz$fih1ndx%G;@V=zg7R?Rz62!g$L~IT2%Vq}iT`g4B z9g5M^YZPcP-}rsumMgN>a8?xc6^s8Wb+Tv%2WSu*Zi#Of=q@aXA)sr^K!&}T&dCzf zo#{>p>4Eog`gJoT4?GUpDgm{a7_AK+OrjE9mT+S$Z#l*~6z!ey{FVR{XLAWIiJTj(oC! zw~9eV92d*LMpzBWu2TRR4hfGNocKduTQwx4@vTF(9HFPROm+621F`6V*P*7*I-qf< zXsqi5_sC4qkZn1f5HF{O9b@LndODr^w1%(P6v(YPGo?R9<9P_mg?kJa1VEtg${_KGw1&#D2WW{;nOCKos+oR1F?;X?59q;bEfFOIM0hBi?;4FK#D836lZ8bOU*$G(D_`Gd$T*$ZJ`q zXtIHHJH3!8V5!vaaBlo~^N&c;@=XI`%$5=am0dSn61`xxy4f*dRcGn>c<{{`;+ub>K!)PFzvkW$r zWOjnZU0GQ-oHPe29{pk_nA|w74%T-PC-eum=Gd_R{=WEMqO>Y>y2Q~jqaH9K*8lGK zawNU9Jw;otOswv!ex+OKcfq1(9vY}ExwtAwhDJP6vBv=ZyS?$tz#n6HjNEhutSQ92 zhsntP0(aiI<6OJ%FRMp1LEXe;me~K+L!PCZ5Z=V6edV?1#4o;|fc~8m=r8G_nFtZ{kx<~cn4%@vZsSaE`PON0Chb*4{esy1+>ky)Mr!niCo&%ZcX-LQV5Y|HX` za?sRQwI(iOVY1X5QJ)C*XvJwgWcJjMvirGiNf&@xtmiDkikBppe3Bgu&c_I@AHM1W zM|1UcbFW$;tSBW=UJJ===jVIb>tIX4TL8&Hu#~y7|8*I0TG=?#PW9W!%n){4zb+?s zZ+F~KdnT$&suF3+)Bcakj1og^sy{5q0P_UPEz2O&`u0bCbRKHH{mKaIa4(LR6b88v zn+4)C&J8bcW&oiLpx#{}*3k^@xiOBSdVu3mhIa3v-&DLYJQ)9JmMh(uFHP-q={->& z{p*>E-gs(+72eLhid&d1z3=%9XGp9>z9(L8KT}Np^7!l~G9#w|`>C=;(_(z$V#Lgd zZpr;_>8D3FeF1YaL_D4J^YXQ8UHzL0Vf~pZ%G1MA1s(pUL0;>r?6wo1=;d)nkMPHJ zCa;}44(It<-wk`)2$@$NAJaJS)SvAe`D{-Ik|$~Dc@Qx+4_DE+9Wr5d=Rrmms}Pj+ zkgr6M(#G&MV`^VLc!pt3g|(z>Lle8Bi8XY?fwo_ON1sl{!=Ov)G985qTu@bvm-cT0J**wwcs!;~VA9>A?$DTn@8@ zuZCV8W;QC$ww-B&^-Uiwz1ADIqIiwI5Oe!pYBlb4i|emwq1!ienemv%DkWjW!)A`- z$d~31@86Wof^Fz1DFhbnWW+V1U{1sG93blH{<8SHqiI*MxY?4E*4pwq_roVa60YIu znC$;$aVJIlZV_Pu6>k^Lg``jt!P^l_Utb;R+3TKZ8$w4*h+80z?!Mc#OT_^tQ*P=& z!+D{o*>zt?J2928A90)vN&%Rs+E21m*TP4~y=&OzUoCe;=xOu#`|UbwEVFeCduFW} z^YX*#l*;zyedo9;5io0EUL!Qkt`DRHdc0X4GvqvLcIlBb5$77_pyLo`{f*4G27 zt!II3_iKUz@e@tkw1Re@7k0JaM7nT{C&wL_)i0zvni_ik=S|S;CPJnn#L&bSPg>$g z00_m+kj(v*Ru-S~7^wgJ^p+9$GmEFOkp2j;<1dU@1~__Dwm81MUkot*k}2I!ui zdMx!mVB;iO3DbF|PuzQw+4pwYup{Ut=~ZROi;5(TT1)>wxlEMK1=Acza}7i2lhx%< zPkIw|kc@2z70+^?NFF_q!X;ZFz}@CQy-e>RS^(|k=+P zhJr$sQPnt)>D5YlaM#?SQQK1^JD6*k$^%06I>XQu6;bE1WEkIB0LoOc_gHuVCa4=g zQd~a1+znR;*r?dPQ4q%;qf)!!4G$`c$$nTqD+~W(q(4Zy8DXXu_JCF(;@#w{UO(&= zb9b5?TrK3%a0T{8&gTPX+DnPNVhjZb^@SdmCP-Z&)(>bFT z1aD@XpPhBEw~TXBKfH#4_U6h#A?&%%74y1p zx>XLP^{%*ea1Y)1ZwIgLI`sZiMWu0m>9@P(uL5=!{*iyoQa;(9EES1A?k)xiQcH_j z-u*FJ6tmnOEeTOZrJnIR(&?e>MmS9>kTXqW7n~#fFqNGP*wcJ*T@9qR7ugd!i0b2e zdbZ*TGPT6co_7|-a$m6@%|+TqW&-G?59K8$dDxI#1kFvGcO@~RM_BsRN09kjEn){3 zCZB0#Du44(Q=!863eRx}o*kp*(6Q-oNwzszRk61*+=xN2)-Eo!{~y4iL)l)M|NBVW zytUFx>pMfTL??*Rut+<6W}%H2d>{r01OIWSzq5Iyq+jx?eAbVDEBij}-IZ?V-uW_# z+>;ZzGg%J(@|D3SQM(1gAh=l$ab zV~0irLfvIedFI}%neDDqxeVEJCh^oq4L*o4iyiQuPR2d<%Dej4XPQy5P&;+;uS+_B zbtI$=Nl)SE^PPcY(;hhA#O@=kv?5g;c<0mCoEh>rZp20NPTR$EiK4Rn(LX`POsBD!(;APH66h z($2WRn~o(RmKMCJwmISKSBddY{}}Wd|xd9 zx!J0Z^}B#*_{?MyBeM<|p=Ff&IJIXI5_l@J?FQc*nW+{SIb`xb|HJ&-ozI!5bHCHN z)NkZ>^iNKYwYcO^4>99;*LFGDvX^;zdgpH2Rt`GL+TxIm>(7FiODfL7$yxCFuJ!j_ znHKxLAneeoH5T27SiqCHr5bGnaKE#0oHFgVJf?{(qG_@1A)uS2#D2 zNi2YTRn)vU+O@xR87}?s9gS`5{#Lcw@r-*-gi!Tn{(N9{K;M;*9$(oZQs(fbhv^8u z#Lv`7O^M3#-l%CR>PcYUk5$@`t9t$XuWE`)60WN61jM_(ue9TZfo3~amfrJ)Qhk$+ z)6LSB9xWSt6Th%OS8)X$gI-BZB2Ucr5B0vFA%@B-Ktk5Yr}^L-nsqUoSOz% z_BD>FutGt*8%H8X?WF(KPCEO21-JENGc))yS&kStD&$zWYGcD1eG<;kwh)=nA`>qq zm)lMKGAU44Yfwgri1X=Ku53;G5@&ww`N;4IPG>31uu>roqh1hP06U3ioMit5w<{au zZ-WukPa!}{91Q`I>hFW2MM06A!OTvl361PI3BDCn-XbC_1ya~i%3GC(>yq3n2QT9k zx(-~*n9J;=6h5Y59!cc>SfC6cdZuoBqFvQ46dn4D@F3%@#aFS7Q;(eWWgR8%Xz8r* zNkVNcZ!xMgr{NB3PwNi<3#LylC3vS5tt?j0fXip^`!bK-M5g2D!6%D+8`A|IY7djh zy-)(UMsQ^MA-|K`XAKvyOf+KXVL$Wj%O7=DroxbmG;5R$>~a|tQ}oAuB-$&v>T@vQ7W%0k~)SR7tiP{f=pI9l1=#<*yfqc zia|J8dkMpH;NSk=uZP7eV^!c-H!fL;x5LOaE znq%emH4azIpj&kQIzpOLM&3>i2Pt>S8z9?fL%}MaH34qNn7+~kK#z!K*WquxXF6{! z>NONKe7V0Et|@5@=^*LVg9pP9JRW7vHOy@*;lm_cUqW6ZDJ+#)|=;2U)T4#zwCWmT(#zPRCeDXru-Brp-K;_z0Slt5Gp6HkMe4b zm%b6o-G_=>7%vC@x+Z#+t{;92b|Vk4z}Y0#Q2eVx{TWD)alr7_!n3DqW#xqH2-Ed|-YnUIVV!)rm83K|ql+dpac?;q4I2!-~oHmxpuQ8V80< z(8)bzBls}8KTR}lo)`yzcveCro5fop6tSs>Kt;q8$i^sIj2Ok)Qf=&8i6#37rqjnE z&_#`zkpe(=A3>bs_zph)Alh)$uWEz}UmKO?v1F`va#Yj`2=PLrzWTUL(IahzaR#O; zrZ$?Ndnxk`KXC~n9Hc4R)JDH_MDxynuo2UOg!RlMzIJFM4`FCXpZDulte^NGjv$!S;8B`D0lkVdqp=Rfa4ypa z>)DIq%OEKT#LyfINjBs~Gcpl^2?ce%p5v{F5h_M9NEbb09eu-`EEYxHlm4z00nJEI zLzVtHyU4v=GS*+&>^z$4x_?^!lNJ3Y7AidvCJMBRN?_l~Z4MU*jjAi+J}rR9%W79{ zdv!HD7hT@S`yaC;mj7^22#4svAb+okkzw_KtM}UlK4`sS;hY(OOreZ+jlRZ?V`~bz ze)~(5E&7)}A&$X|2-)9`njk4cUS3I3x=-f!@g0Q!505}_zZFPsOTnt+A7lBhw-L8z zGFF`oN8(8*l26AVwJDYmh4sym$ZWSEr^60+rycpcIz=m0B&;k*CxMl9#)=%VVr56s zd7B}u1Uv*oSd|#KXhYd$JF2cau#H4j-Cbh^i)_wCAhmT8mhX8PCAX?k+}A*;7k&3I z6EtHsDmhhzoZ?55(-GA#v%Y?1o(lq~-yoo^U$qbV`eo?qm&cJXlqBw zsi~cIPAVGzIcfO%eLy1Xn?|hdEWyP0?EV*${_U8HtRI;Fi!h&HY!gBw8g*?t+6!t0h?1Nd_s{8l>rMaf5Uk@__ zh0LB_AAz~gfIQD5`z!3FFVi>`XwK#RO3cbWK2KoSSk2>8A^V^}jzN(ziT2j}ZTss{ z(%XoH6kf`di ziPfpB_6V$R@gt)n0@)<4^3Rj7GVr+_9^yg*iz|W^#h2_TyJAPh6&p5Rx1;Wk3%AxpGWd=;D>E=f>;%SpPwAg;ugcTQDnE^9g?(j~9tP$KSbnH)|n@tL-RxXgtK~ z2woKTd5IZ)KFNJUnU50}DYnJT{Fxx_i-@HkY0J!Q$--tRdWSHdXK;zZ5+ojo{r;Jy zO$md24PKx9F}SV9s*H&mD}!U@xR}*g1x#X9$5;9saz25XVIFH~KgZk8@sW=*;|s2r zE3meC9pd)R$49#+V)>y+Bp#iLHOFGH=J;C#JJueLAaNCmv?dEOTCK?LunA$6f5rhf zab`y(lAFSj(j0*-YP-)^kWZqk=$sH&1fxwjZyQ{>@?5kSe-;!gSD)Q*p!RkY>c4c7 z&~lRh2qZMUfuvJ2;rXJ9`d&|HFhDWXb9_qu5SV2yWla)BTE9LphOb}7$m*A%%c(qH z5yeV>m00sR(eTffp)KtCr9^`YkU0UaliRrW23tnM;QoNeLnWc_lyUPRhMcq8{O%{raRr(lQN?Q=eWPF zLE()mB%e&elDfCCsNoH)JQ72~>TM(*b0OiVo!Sm0owOkNlm)3xkw|ZjL{_^MZXvF$ z$RlL7M`B&`6cTCS$Y`@5x0{aB{=&01c*LItiG{$jiq9n^xJoYC;kjVJ1`=1**B#h$ z(+ThGX!!0qP)^5^Pfx_^=GT$Yu?)puZ)b5Oa!0W+t2HSRWc_*{QT-|pWybKe!wAZj zuc@hDjct%IGXlv|khUJpoMdXN$%Q7qp>55W1zMBA*DrH$_1h~TL{>^?30{9|l6Y23 zc>Za@V}u`uJQL3nUJSkMJ+zHvdz|?VLeH3rD}IUSgcqL??by|`koU!Als**VNa*4rxk$UiefUekcwCz%D5^v|Cm_ZjRPjHSH4UWh9pHhR4*wip8j z3p?UEaW2_O-&cnlH_O-1~XXskMBA%r7=UAlhZ|ePGqTeeaJ~OM3 z$IYPHFq``=7j0$&Gox;h3G$p?!;V=Efp8>KJY#b*xP|tStsGv645;q7g z@B67e`|>mUS7R+ViA5Ky8mAg7$H#5<#msHV9Jj)X4`%K^fnp*y1m#!Tk$5m3^S9Wr zq|t%61JPK1I0h>YM`7h*I|(c+5{^V5@n|GgpNK?C6N#%<5?37&B%C6VPsr_vMAE6r zh(9(FYfpz^eM=a!JHmxc?wJVWpN&KTA^#i+t#f{n<+)%ZSdBjq5-g}RzIS!aA^yjp z_l^TKx2-6<;6ifq>ol3$;klem{jK5tdj&W@KNoAz^~?JMtY2b%{hH@epHro+jZ+VO ztlF5le*x4lLsP$Me9WVjQ$hf95+S%bX~a1-iIw@$)NfF8k{WAiUo9&C&`9^Z8!x`< z|Gw^%&ptI?B5MQT&qD9X3T^797Q_xNnEx5el`Fn6U|$l0+$)u+_{IV}j<~fS~%JYcP{YsvuMj2%*5AeW-X?k4hmNYEkx}24y`pC?~FXP>Tv$r763& z1!Z@(p#1JuRNUEyN-)3L91prVJyz&M}S@KZtYRXuDF*AOg3 zmzVqH#fCnD>~j#YGLOob$u{Rlj8mo2_xrUdx>}A;>K0($j>%YhAO>-JqY<~qfo1!w zB(khndB`Hf)#_tYkaRp8DW@aE_pUNqjg>1BOm*?xD^CuZZiiCdHw)Fvbn8*m)f<&(q0Ih0WS+3gAL)c9UFRfp#1(;lp3Px9tox| zs*!g-1DS2f$Z1`N{I*o&wX8wL@g-P$XdaRdzK7&Pv#^$sbm$%8nMgiJIPwlsj?NUw zJhcD?Z7WdFxfc0dsmSU|M8)+YRDZpVgb7{q0SPb~r%LLJz<`g~$Ne=vdm&>Ddd$RF zFf+Jqc8)ouAfbX_j-@+Y_++;g%l2FG=>fw2NUS;(j>Kc( zesOgwoWvE0rdF|HMItK#>rRGY)!|8qKSJV)+9^$8NN){CdfOBsuCh9fRW8EW2(fxa zD_Qh8oHzo7=Pg2Hc}QfHUbdk6x*glES!o5!fsL2_uX>0-`2sS!7NhdsW@Ef5|Gtsb z;HcJbg|2=B0iP%R)G`9hsXkUl(ESUheq|pVIiO!l88e1JO@EF{UDm`9BF!A%PXz%vP_a5&oOi_?U~UiSJH9*4Ywl{LXmz z)}WFneP(VeJU|dP8FRm%#_RKhe+7F5J@DW5|7VB;^}vg)HDNYnx6m8qzlj)8bpz z__T2eX4gdEgGMJl-W`jNcRBGsQ8QQoq``6gHWQ z7-l(DW0m&ibCQ}+CsyrC z70;8$o_QRPJ@MFh{LjMsgue-itk5>J0A411Kh|$*f0CDSUva*U%q|Z${h=O}eKjNq zNZjxZNX(ykpJek%QGMt!vkz{wZ-SiAq^R0xT(e(ZQ}DxDRFg2MqSd3y+nZ2y$%EXp zxyU}d9?9)Xk=(KjNhe5L9sK}njxHoIFdu7=&qZ?6`$%bik9aoLo}P_V1I=@g(f&RO zEaH|qB&ud3yZ!gb?tGU(!m49BaykV_XiYIpTe0{`v_#z8h!^*PTel z+-*^q?{#6(&M17?7=@2^S_u&(wj%M#-YJAhh}%CI@kgd0k;K*7V_`@kfkl5Bq;ZR+ z@eaeMhkl3UgjGi;Vl@e@&6kb{6T=IslP1opPyF9K zU&WW3`>v{9Yi8)Ij*h&Uk&)pt;(zCy_|HH)4Aqs_`}4``I-J%*b}a{K!{e9v_SBWn z9eGZ&prC+pU-|ic*t|5DcDPSj$D#6C89(mEF9Yjp&-|tL{B8N`5B~%ChJXC6X*c@q z&pUlX!}$9FxEBD{3k~2Iei*=)0Eqm3>_->{NCBL^^Bmst*S;RNf70FP>ITj^fTPq2 zAXyKkYZhDLEFB!|hCpB)E&@0QyT({xH-&ll2uR)FaBm;&VjqW#T}U%WvzVf7Yxv+G zWsN9_AJw}O9s!V6GxVPCM+77yAVOS(b6(5f5fRZL1P7;;620!c(>VBm00$2Tc!Bc~ z-giLRqLmiiJi^BbinX(FxD%tvc@%3Kh^2=_frp3iADlzpWP+m{{b{X*9b34+KiIH( zEzUYze(VW+=&`q9ZEb{56p*+8Vu!L2I4^(`ZHwSK6douFk5D)S7ZE~0;T@*)7CW;> z@0H#oI6+YaghKm4+s(5LHF&_WTpSy0-xNmPqpAWb?=<1ny@*TuduW?=+;#gGVEwlH z5o4$McCpiODk51!WWz8UtNr7*|8QB0OO~&WU&aIdd6Rt#zJwPCzD0oN1K;fMcWQ@N z{q(T9W?3Bb_+>oYZ}~mFaI`rId0j1imh$Ef%Y${bK0S`{%X(t<@q1H!%HL5?PAYuh zXWoZ@`pw^ilP@}r>yKQu#K(WMq&oWFY4{!hbT0s`|MrfOi2JVt_*2g(xB?uu?>m9r z$FAesf8bB!p4Z-wcG1|^HuymaY(gABSav<1N%*{7fj9*~;vIquD8!@QpJI3S3Jzy` z=oWL-hX)WhM;)e!E+V)Jg)364A;7zc7#C=o79tViTX;`^B?8Z4I1dG7It@93GA{H$ zgoq$`2Oj`H7xDxiLdwTI7koe{Mko;ATn8x+5nYRJTEMw;5Puqz$^Dp|y&W|ca1okM zbOI+1F1-o)ByJZt`3-$24v3NlOw8_QtD(MAykN0A!==X`!y`}s21XkN#<4=(?gPF< za6kxA+swJBK(bKqlfYG3I1~ZeKGRu;X)W+hFsgh4Pk;if!l#0J=MvbwG^C-Lf$=OjMI zcwp@0ba*V&q-RkP?mWAPreKP{M+7&GcP@dD-S;V{DCaf1ib~o z4}c)+1%UP67`UR-%C~0Y`}hvN<;+XZ;>v@U@lF5ikKv_nei@p&>-*T5zHjVuQp&!- zqkgf@!E#f;;!#A8cDBIb!7g_9uAx5ILtXD-fkOpfl^?;m0$rCtP?Qc)T6EGNA}$vO zPpOnR2k#uO$5cEAJrBVv2!lj@#sz(bl9mLp9GI3;7!|B&o?f31MTB>e=}L@>HNekN z%QZAj4Tt+tR4>NXsh1;GB_g^6Dm2$LbvJ{?NhFJj4(^k!@2V9WR;j2&L;D{fC|k5X42D~#VGwhxfi zuXj#5Dgy0qhk-sFuHW#yXnlIb@yj?LzwFc1#_w>xjB#t`B|0&^qj0eHPcAeHzy;J%g)PpTxE4 z(>Uz*02ffW5rBZYZBf??)Xf5ItkFrU07)WLy%nPnGk800N9q=b_8!4vi5dtV*`tJPakE>V0j|KNqQ#EYdrkH_u}H!kDv%16$&i6{ZtQh z3M#!9fFRQs9w8*@Qt(dWE(#82EvB>7&XSy$JW1bY=samlp zl5}fYdtBVTf*>bx_S_q=aq>O|q@Ha8Cu}R}Z!<1Z)@BxS4Dy}_mT8YLel6eZomswY z2giWHv_0>277wLG_AL^~nzV5F53FlehJj%9>#4JizpSnd{qO0SOR~CJIKsTc=UC@p z_W-x@oRrtEzn^>hv?rhbc5qq$_NKKuTblvHm{__}b zPw?LV^iFKwa}tZ)Igb4>7`FxBwUlBl0Js+^unNQdHA^DujtsdsGAz`u*KzPAH}t6&!FvExH6!elnVS} zEpQRZ)+x4t7k=z}D?Au-j&zBncp0fBGWc@t&W9puu=NLfuZ`T&-;dwD2L-xA2nA3y+}ED?t$gflrI+ ztVP!f$}*rV9lT>Z0Lk9Ur??0%Eqb16L8360l4TT4J1RX!B`_|$_RZC4#M4)w#+mg` z!Pz_BfXRv5(RPj1X`qc(9>1&4M5|lq$SUP^)~7dFR~t_&mSI?8F;Kso9KU%UIL` z-v{6u00geL@&ds6#06L2E{C`9w>#!`P$0E-ZXJgg5AY>_`pfY<-tt+f#-&PdT!yZO z1;E2|S$@yMAri=VctD_Fs9ChA52v{D>{GaQSZ?Kfy7sw%Gx9w9h_ zs)*q9T-!5*02dqxBW=R~D81s44?dt!+b+SRfEu@V?FdgEUdQfkiX^lHoob&UW>780gcRAHTzW%0uU!D^Fa;5C7HwfDiuk`!PAQhS`gY*Kx`ouaMLj>@h0RAEXIrcW-WHmlL#_Y-rpY`>hgU|cMFTncg&7>N1 z|G%(lz{qkSEzh{vQ#BOP&Kn$F-NUmNp1|JpI@-`Eu#)J|wXLe*L|Ur-BE%&yBwZvh z>ZoE8F4Ou$;{+)pyr<+zB78EOf)6yj<}(qZ`6+1P;;oV|RwsCu)^$5KLvB@_arc|^ z@H&Bb1tLzj)^Gb{xMBoH1N;sG&o;fOKgQwgS&3S= zcfajd@R6&(hLfA;^bKuXz`F)zV7qzNB_vf{Svakq_eu5Ab`IC~Yt(JTsPqI~UfZMa zD*dAb&uM^z#vV7S4_h%cA4IR|yT+x*NI_k|{uFrh$_H@oiLb%wdtZfOvH{1+6O87g zbqFrp#Q41_tCalyP4wwN8S>oocV$N0!)XVOTL#>Q4_N^|@@y3&_toqC{Pk&nuEXPZ zxKD4XRn^Vc6;V)3Dm?YR$MM#0_#W(C-^cjW81rj$9Q$D!qU!+uplXf?vR(jKpLljx zUrOoK#`C2b9Gx0tacz!Qe(tOA6@TulaOU2#Xcui-+~Klq*nNM6tiF`E6zMw@9JLYi z-5DNz=p)#jU4bh*6uJvXQO9Z1A@51wBGY6Ma>XhTjS}6AHn-S z{L3guBS>uF&;hb2ai>OKmuQpk%ucA%`VJ6LT zF#%BTX}EuUU0l9!5f^u#0!9%<906@qRbYcA?FOWQx!RyFclUu-o>B9QQ8a-M z&WzHR#_R-I5EH~p0xKR4&YMwfl0I>FT%d%ibQ%L4Bs~b?0E+ZAJ%NX}-E}6lLE(T( ze|y>?c}lsWKI7mKe5E!``_pI9EMAO_Gp`56+wesItPO3S<_Zgd?XCm5ro*)hm+`Cb zcpGB5k8zkNkWI7`QA6+%RjHtpI{*o=Qom}SOc$LRwHLvw`Vb1nOyX5|O01UlSAtT< z7CFYegE(kk70#iHKx7@OB49ECCS`y~z<$%<>fr_4cj{}fdB;oOLjh;D9GP6^x3+lp zqA{^+1J4iXO$Oe@ODmM?)&Qa5vJD=?bCHvvj- z(fG~p-P-ZX;^iZ9bxik(x(LJ;KgB_` zlOf=NcWCz(c=Dr<;_~cS)V@X$1JFg(bq%59N%Ua>qDqHsM@k^yOpJQcNlSp#X>8{XCo zQ>j@L(em$IxsLb$#yjx%{)5=A)^$TcL<_h!Ntqm!mLU{QZJ!h)mR@b53JAI=m^BfH zvkox=ld1p^;j})QE=dEqVIUA}I)~+W2#&uUja?N8J9JJ|S~l_pMxzena*RbCac!}K z!@UZxd+j&CudTt5i7i;-K-I1rTu+D9Jo)&w`@GbvIhUjASR(9sFi%T7v{3g07M96t zK6Z$#K>m<{_kp!HTT}-z0i~~&&p=|PvHIohV9)1s(m<}geVWlW7+&bj#C{Irpu8`v z=Z5Ve^L`c#kkqa+e)B%PmE+g*_ff_#Zyj~~#wco&^&9``*YGd@)W3i$oYpC}$BuJ^ zf{ua!>z}*;us(5)KLFrg7>Yjrqg*f09Jcu8w|)y={kgAEaOH#VNy!g3;|A(GB%>B~ z8IztkAi(3`${wD4_6h9nUPaS2=v3k)3YcPSX}3d(3*Q~l>HEFNa@>n3pi+PZM_wu< zkfLk5fM&1*|=t$|%0WKDL&%(VA zycxbMbiBYa7?56Z+#2A_7hXQqPj_yqvs>AhVJZ6rO-dlk=d(;%E&~hwl<2TqM*=LY zYd^rsGnZk~OqYM<;?OUYFs7z{v>*m{trElIH&13QZ%H%q)BZUL{Y0*AnB)tU8_DEP&H{fqda zzx;#nrAKU@7o5{kqjZ7%SnmNBn->7q$J=2e>XQKc41n97FT1O1vqHUJ8Bag`1a`Y!zy}mD>HfMH5k;1u z2$BGoKJ5faZIVoN zk=L6D@e$*yK&4>Ism;--3@EDrNYpwi$tf3eUx1P+k0KN*#VQlXpeoT(D&|loz9<^L zdj+8(R4QSc%?}X#r(yGs*Px|3%_eI`Hgj>=;WeYb(E1c*f$Ps)#c%xjFX3|gG}inE zx@M+itigAbxQPbu0mm{cu%gj<*9mqHYAhO2yQxYgTSy?v!wlC$pbAu|4ut-%ApHUY z$Xf4A#p`^Mo&_jq+bAl`+6AsHYM|S~D_{DRD7UwhM9z4#I2g0B%L%_%IstHTdJzw&hU|^ z?g!ikIsN4pw&Gyy((fQHm;`aMq zr95a{$ZdhoR5A9~GFkBjIZsw5X)&XBj`cpjkUZGlJ+dqNj)v7;1IO@!X?>l)hw*@s z^{wHyx?$;;nc9uQSN8PY`U9;!&67-e90K-c2q8o&J!+i?iCh9uyhr^Is zHE(0U65jOq?cdRvj~x-wx$xT4SMh)RuD^)!*)e99XH5S5fGqCl|4T2Z2tR(0|CJqh z{NRdwSEExSY;SMl+rIBxapL^8E^Iwwo)P}d!rMMT{+Ist5~1&k{wz#5GLipm4hp>8{s zh>5rapeQw~3fpbxN$XG+LEr10-q$h#9!{L{M){(}{$2~2d=@rN-i4wZkv_uITg0EN zt7ChSYfK`FP~y^K7w}(x=7+GhHA3is+BHeCg%mXXs!22%1SjA~t=V^Ntqonuwplz^TR;c3wd((Yvt-l`k-v0(v zlMP#RNbwOPCuejJhg{yIK$p+h9R~vn;t-eZ7s>r*sht=jdDL@2tnKySxwZR%;a89S z(-HWTtX7tokqmsZL%u$(T(pbpx518mZd9@o57sr)2@TiP7T=ZTq})YU4hV+`cXNa7 zVMmZh#Hd4ZJn&tEb!A>hn1Ai1R*5{hvv%m|roOt~!trZm>r*}V8#&r$sIn^Y#QUGX zcYVcQ$BDbQvGe3Eo~I)+d>(+GdjVj5{2iYG;HQa{pO@q0y(e(}@g4lHKlwD6IPR*6xg9plxXc~;qs?c)OF=Ae~ zYJXJMz+xVMGc;(Lh)_E9-#1wUMxz32lTz!_v=L1g3Fti4fKI`ON~tLQq1}?k)sm<) zB!H#BWrW)AV|Q-@Cr*9_#vAA1%F=ew_3VlklD9^>IcjD#KKN_z!!JJapK!fxF{)6#Q7ptL*x9X7w}O%&(JglidbX`-TsXa-0GENM^Oek{^mb@ObkdxAT}MOiwdCIc8=-d zI@)%jk~*iTPDpMG+RQ=0XH|?*78|I-1aKn-eFzS&)Hb|P^`oGTSHvm67Y@0;tyQ;% z?bHA>7w6mt=jOy62ufEc?vf+aZ&0Oaz@PEW60Y$$ zqLsT{UZGD{AkE6_*HebR!xtX!|Je`VAAIva!uDO;*nMjE`Q*oX5WpJ&?7RT5K7Ni3 z0KZ22D~_<|2V8x}KmS&|?9DGj*LDMcgv$S?NPc*J@^noT)qwt~$DhJGAASdJJ9C>7 zm)3Qy_gbkDI{+#%APoZyU?s_d%CisRE3eLJ?KJTVpJw6*=*6OG^=h!vzQ$&aDBH%6sOXw@d&>e3T&>I7*#5N zQv>?8OVT!~zzAX})p8L*9YmW^d{qPm8BQEL1U>}aS-7{qhf(=@Otv3Dv9<{av>m{{ z>oxA>XS9E12fy@lKab1xQ<#)%Xu3J2HZ6)E@I{BD^t{r9~N#bix^ zUT_L{Qq{VcTq?D7A};MZH1io;m-xlHxR1Tr1=P(HT{i`!)geTPrR6~(7?m3s7aORG z(-@bVaBhq)I-scFMq{){edM3}jY=zI7=X*5Si27&nYq|aus;m(9u0r1EA7g^iQ|^B zKje3gJSSlVHu>YI>&^Eme#>>W{BC`pTJdgSU9D62EH8Vf<|IG={lADG{Hs5V&D%Gz ze_{W516kaF_6J`8SRX&M4FBx;>@bQ}@s_{zwRqE)zX3%R5Id^XR;H)3)cNimj|3{_ zI}1GV_*3}6;~&JsX!&tK(|Zxg=!rufl7X& zG<06f;LY_7oDH7fh1X45<>3(uxj{AH1Lz7zjCI@vlqFD=L4k!tV9_)>MqhKJi;6My8@@Oq!i7m1dE2{9wYA)cmWN_%LzIh zV0Wj)`sSx%>-5VZCEznVAWq0^TTebPho?XK6#m`Mz7=<$egKR43{o`k5+JTcK&z@v z??gi=97a{3npV(6;NYM^-2z2PKZ4vTNvRx(S4DxN*T#Y*GW z3Lg}xPKpt(FP_D0dOKeCz!&1wU3b8F0<+jDk5^EF+7`3fF0So9p~R`q&Cs;h;G|V) zn1aTULxJbc*`Ge5Oo)%F!2}{>3&hT=8XD?3K;YdvisB?%0Hg6381*Dr5}720gD z&I2@~x0I4J|214LYloX{v*RAWgXI$um0s^%-p9ZG3;!1H`HA;o{p=bJFHiA29YSA) z{}f!qUjSGiyKL=$2*5w)Z#nKdZr{0u-KY2P8E^S?eCc<53ARpepl#ahuc1{OJ@oU) zfvS>vr^W|A{9%0b!lT&QSchvpX6fo@g+Z!Tnp*JJzq{SX2v^cl)VXnX8Gdw}=;>U;67tG|fzYjhJ2ib3xa&#tGjIJOsVCL$ieroYI3ATK zNfi6c)raunJO3bF`AM%sLvmT)MqImg0S7ymF`ZvR)9oNs4JKQHP}Xn}%KjfhiGUI= zs(O?FHc-w<=iDJAU?*7Gh6OEPhp3?i(7=ZVuJDLWiM9diL!b5ZlbQoG=7KsR8Wp8Tzupr{`}`9yYtDZF=of@PR9y#8NlxV@DK>HUI17h zdu;752JkijcRe5AYV-U$_Aedc+&yRTZQuJXxa~#fQO|2Q?~fwS=HfT3KM;|n(`N5I zKJ<lKeP-2Bh36~jSGu8y;$fEiQ%)@r66+zGWK9%l}8_Q!<7U&|de>lgisUcEf zF%w+4w1?z8FmrouBR-P;I8llE##9b9B6d!lf+GTB9@r7 zJBaN)IC=aU=>-PIzm!J;PD3^!%zRhe@3xY!8bs33y63ibSQnKQM#!py`HD+ zX7f&s+r1($?gA7?-Ykz0*sdY~+mYZA&Rg4RZ${eK!RQqfCsXY>DpRLjn|%b6=8gEw zPySM1)8oREk7GXD!S#bj;n(Mwtj*w2p^yp6FoH)=pakD)omEn(LA1<{;|XvG1lK`X zP-1|ElWQD+XnkI%XhF(Z<0;k%G*DFV?;SB@z6UyiofwS-;Hvs zgo{9Z&^#|7i*!n#OMoQ|AJ<3tgaC;|ykv)cICX`W?Qd) zBv+MI$_&%VowZm*b%i6sygj*^2);RqYfSDkQARJEXDN)#W*0y!iVLEGp zxQ5ePXR(+*M@aiCn1+YYhAS9XB_`!M3cm>#E4b(&R6D~p5NRL)cVyC(-Eq8g+<>)X zj{{Myx3~}oL?87oH%uT|0@jI0RidT|GJ$uS2yTo~S)rQLD7T(O7e9jewQE2<1=<=_ zQL@fPrka4Bxma4^TRg&dngxNyIN~+e6JX)y$~%z$md#0wag;h|VX(YzsJu6Sjqz|@ zEgo)%JnjoaWpgW(cc9;HRiBELOS*`Yx1GTG7oEd9e&k(PJ2gQwYXNy)*75rQECBoh zH>Y(kNU(nUkBYn+-w5FE0vH`P!19HMA9?lR^v!?o594)T@LF_DXNKPclG^;Q6kvE` zjk#C4aPyXv||*Ul9zwbG&OI&?H@cOnWL=qHdMhYO(-yeG;ogUtq3l+Bqj|@Hgk|R!GTVk^@pRR zi(F5I*YheUwSqo(aL{3QHU(UTgZ+rBSLSHZ;>?*5*2e+slTx3n>m~uT00bz(C>=qI zg4V)?^nO)Wg3^?P(}NqKYZqu2TR4CFXJh@$SwKXer@h+J-{ZsY{V3k`fp_9+dl~C~ z4YkbRfy9De2x%fHK~rbfNu}2{w7ZIluPZoE^~$y;a0*_Ls9tvx=`zR}Tf5`9azZc$ z0%HT2h=QuBFsVvZqcO}04LlNl2p)=c3e;M1pK0m-TuGf!fjZV_pUV(tucz|$yP=wV_qPl#WTiFnN zU``UC&%k?lP8xQrlht)t^O)Dw4VM>oZ`f^7X4l=^L!b+KLy~60lW`1`*}fv_1kw8D2EIbzk#Eo>1;|Yv_x)IFlh$zmcIX1TE6~{s1L<9431l&OItcOICTv2+}C}V!7 zAZBf{j>%+Dty3lzo_^m!6U6lC)z4tp6taA&XdQAS0cv_0HznMi-7lGlYbPG zf<~rI|5$ATD`9;=AQZvpI6l4W5PVBtmTSouW4VXLVi#yU3Rl4??LF5iIDV7XXK*GF z%%vs(cuivs;5qVi{9&<4H(mBE1N^oa1B`m44O(A&FN6MITCubmn-MjWu^b#P7KS7# ze7=89%Ilh^wK5CeZFSA(KugQ~t*%*lv$Q>RWw5gK)Rng_WPOUPPy6Oy_9>qio<$}! zUMXgom)~L@S)a14VbACFTd7Zn$1mgl@yoQa6O1=Txb6P4c>1A>cci%^ zg*_dB69CQv`1b(tf{O6BU-~8h{xVT-3!pe&)nKKDe=m8%{rJ6q>PxY4aviZXS2*@H z47*0fz@%=$fK_rm5_OSW>>ps~>a%LFT+ps(RF;X1b5bD$l@e%B;3MW9L8Jqg98NK* z6Qk}rqxvufP_0KpY7j_~8w;6g3hkQ^qjjY}LJbl9JD@WE-iap_TppoF9yfd@p4x*&F74VUUj5r?kn^cis;om|3v zI>!3=Ji@qSy*x`9as8=XeE5kE;?nFQ*2;C%?SeoxE+vM6I|V~QV|0BtR5duONgis$ zhG03B%>0yz;V(%##%OjRteb;GhuOSU;1+y|_3;`uCu>++o9OuPMbPwZ>`3Dg(s@kk z_np-YOlRemK@O@RnUEWvlX6g+0}HDw z^Udp**V6*)r_b2)-r3t+U!J^^@&5PsDHYacTVlXztXxOwQ`VJfdmQ`p zGxGQK>0n*iwkc-)`s(RjqHQ>Fb_?h3J%eBS(RX9EGgB`m$Lti^F}#fa3f}>$3SR(N z&;L=f;lGjqi*-3()nGGib%1`;-~L0m=acS0J*$;*95y*{klrvk*uSihesHsor3XLK zwe<iehbSYO^^YYta}_BL>%lVuBhlDMx#Q{o0fJ?R3T9g18^Z1N;^9B*%-7P8ddpj zD$i5fY~l3jvk0L;@IlK_g$f^j=Y#m@#Rsv#93hs71g|z>3sqqWTzU3$-3s;(J4oyF zK9UxeQW~%QLs~KdH7lccUK2-c8_+|(QwIE5-Kby2?Tt-rZEj$)Rw0z0K-fUEwxa^A z7G50lk6P~}sEl+7+a)_b04)KMFz!-^u+9X)P6)ybaA`8IoUCYcE~zgG&UDOyeN0KK zXuhyz&;<}2CqsB;$@eH^3std>vfM!^9tGl6%%=xBZr00fNGSA~Kyb{35N6+IV1nOj zitXFuy%kpp{}i%lsW*09eo85!exTQZ;xlfabWR zR}w|zxBTy4g-`jy*P^W(gg|hWiE3f-fEvP77oHD03j`JL7aQ|SYkGxV{djHi*AN-<*`;&%Hmywuv{3s*y{Ve z{&Q3}IZ7cC6F4Os`r$>r&xyinaZKGnsaPj~mm4Hp!byR4+AxV1mf)g; zN=NiwOUKn^;V~JRkHWRJQYCVG2Q46pqiQrN72HsI0=^BD_-NhIRZ;0x+W~DWNxkSs zShV|)b{!|z&ZDdzd=(glO&t@#KMQHDpsnZdvB0R@glH#-1{OB^tG@S}H2{)*pL#wNz;KiW z)dCm7qZ!|$58OBdQhjA`eamW|w>#pT!~n54%Xe6n2MYsr?gtvFzL{!ru+8%}He7t{SzLJN z62izIw^L|Oo9f$m{$D6aaW5FTKYwKwp8t6Oe}NrR9k-uBiGo*t*30m%fA1SmjtlOb zBiP7Ta#x_T8QiklZ}6Jy^u0=)Vw>_=%oo_-J5Z3Yf4GPJ-5s>^4p*;S!F+az!)}Tu z)+mY*JWAAEjd?Stgd&x%Nze<#P3FY8blj#diIrV{3kVjBZb3PV>`zzEE&R@V1s*ie zrlGlpbj@tMXcTP|8tB|vjq|-IqxncxfgaO&hl53p*-UWldV~Fg7VB#TPHYFPt(SmA z-9;DSQ2M|bBvOoSGh183oIH0yS@DZ_3iqCSE$;iIm!POB1p${Hx`H47^&iLH;SMH# zf~H+SywI3Cl0HFD`efW*6l(mfW!T$~=wc#bW%IJ#BB19mLwBXeAXw$*1d@v)c2Mc* zWK?2ORVYV={_ZGgqUs7D=w%z)_!Ojc7ur~*_vn>+2@*v z#**HzX@hqPmW)2aBzQi3V>EiF9i84tpglPCzo_jJpqKSoFOH8K`54h zkN{TcQ28V=J6v?wKP06<;RxQ%R!a!5sOCvvjUs!tixPFZHYr6m)+b7~S``I>kNFU^ z9ZNvxB3!p5{WOj!i3C_Tst3Fms+EkR%#+lVaw}!g7jldV@0UzVV}1zRI21sMfR0xn zRxfyP$0S>TpssS>z&YO?yk}cQ1Yc-1eUZRf5VGTogA0}Jh@3CxXcht0=ykx_y$IDB z%#O#iz2Y%^tE+Rw^ZX{?DT8?*e?5oEEr-i>6NIbBKmXn;r-8>XcI@79)Kx?f%7FL$ z^at?W-}H}l-ioc@SP!ZSX@~V?0GMlZ`cuiVE-i)%WA^ohOu=%tsIL5g-Wkl;Alr zcQ*)5!QE^wsN2YXGMOZKL^XD7JTbCE+BVUMyh*eYr%!BReWNn9txNqLg=#w|OBHW~ zsp}#HlyXtx%6Ex@T{Bz;ZK~3XaK~q4bz_S_wZTmF3g`fzhV4_vY zb_ixhN-$A zjem>tZ(iR1I=iFv=}p#^^&A?%H{GXv1ZVC(jm30>BnjPw|D{@Y(o%fAULERH2VFcaW(W!^fbgma*Bl*tH=? z90WTz=4X2Q^n7pycAwqH6OUZL6Hh&XE4vplm4oEd4wd>hteaNto76`gsABTq4y`10 zC_-A)LJ?I`R22?^(u<-h5Im(%iZn>LNp+uhR2foCrg=11vD z0ad}NjahI3^SZ;qtihFQ4K7}uV=^hRx#6+7re#$r65Y$tRxB(Hyt@_(V%n}l-AGas zwjC<0zGR3}fVlS;8THp<`?5Rc0eRq0R=bUE5cb)c;r zgmrKzTzVFs5-T7v|N8z@igIR){-R3V6~EIax4 z*?a9s^6k~;gQ2uT-iUAEoHVF9Tvfi3KY!G;$1?viUZEXU=u@Mg$r<8@(+=15w^q+# zmiqpF2H=9nY;TJH<179)E7_ukw+*%|9?);>2nZf zSA_(Y0^mew_oI2z4#=@uk>D%P)&~X_3Bb5;V_X&qM9o{w+K9`So&#D7jK>})Pm~x> zLQ;V?EkaNNQi6t#yVweq%rwe~@6axsI-mA!iL=H1_}nl199+4689(t8KZ^!+B6w{Z z6b0Mgi$(>(OjV>hFIrK5gNs@aXk$fCWzLM9&;fV2p^cg@k&LdbkFd2l!DJMc4El%> z9jF>KR&yGwla9lsYu=nlBRH{f$8n(2O6Qi(>lyPagCnu5U#d4npkSI~i@>de5N&xb z0G-68k#h91-09>Amd8~{WU{5ls0bL33X~)yg;U^21Ahl|?d0{HB>`;_g68Luq=()G zXbSI=8dCy7m>{~V*gY6yRDCka$^D2$u~hX(i5UrSW{V`a*<;v+33$GO%fR8ZD*%fu zG4nXiI^TqQ!PV-V146MreZJ=3V{$Jz*r&%ae)W5L;zQ5i&wttfjnns?z}3fgo-YE@ zUjpz&0QO%{5gy+W=yCfM&j&y}efKsFug~$PzVDlG{@ydXFguUFW7uWEu*Y`NB}j@L zjvm0^vNH6=#;KL7Mf9F8Jl40?aOb_Z;okf1!gjTdZk85D@e!lZ+ENlB5fDxqEp4`- zJMw_2oKjG!PmGZ-Hs5$Y#U`0B5>(9>3apq$ta8#BxmE{qEy3KzbR8E-L^KlBkD;_J zs!@Tqt1(|hG_@cEL0JU?3Ia@+TbHg?^_k|^bb{%;RiAiu6SD!}FyEOl&+vkTjNb8H;(V#{8#FZ(wtCtU#`% zw5^R1E$aYn1AT@p?!tOe0+slvH15)KT0g4w6jUaDEP)3>aquW8X$oafe}?6Au}YkP zstmNjbClG1-GEROPUEWb+Wtl9QB|cH%j*3=P)l`S+eR#&3mtISL{&E~+9uTvD&3;Q zPf`%CZ4|ty)X+=nci6C@iz%wC1^hijg%c=89lH1=q@JVjrx1NX>6bTYF(fm9&%V!9 z0~im?7ro!^3Td-runfbPL{^@YEDX#^E4l&dPdlW>8fb3@$@}zH%)c0@>p=a6<0ID9 z()RahK7Mf%eag1lII)IPcbvel{NOur{>7(pczt&41f*mNxC-D`UQiJppY7FG1Ne8( zhc5QEmz>7Mhpyu5|I+WnXMWYEsncK~V8~u?zj6_--55v<2n9l7QW*$8`XPt1xu_Sz zJBpYU(^?sCaCxCSy|~csU!UQTk34|~AN?q<@9$u3bF2_%?_i2WU88ARH0U4-JRMY) zLbai;p}y6Hbu@A)3em;5D1%Bvj7=K|f#Af!oELjCu6vk|KB~<$EM4^o0g2bL6(t$p z)oFBv{pl13Q^Cb&YaGs6oIX9m)_SQDp+(zj-dtD{XsHuWX*HR9V>&B_bO7zVTu^XD6;@I^9vrl2qLp0;2!gEwprf~?JyFxN z7!@VXpI=8c@{lM?HELAAmh;*V2G~ilfHr}=7IYXcU`a^S7__VrsoXIpAXpIaS3aPq9I67? zs7j1Srqb&~5JX6Y;g^z9M<7N}rg4FghPzK?Moc?F=j{#)A582KiMO|z4!|?nYwNI^u2J4!)#eiB>E0MuL*DKs(`G)J4x6cvBuk{JXzm-+j z!s-4D-~X5YBYy7R{wlU_-@xvL103@qw8eTAfX80|ShwEbZ35t30A7M)KDKUK$KJ(5 zeAd^z0l)vxekrz2Z7AuQ^Sz*HCHXQZjo#;Ze?U^7`DS0CV~5ik#RLHEO!VlQh`sBF z*xA{`rDv|n zYA{-W8*Hn1o%i_BYsHy1@I5wdBsVH)H=`1bF6tK3dBpB+gDcl-?ZdODMkq@pR`Jdj zo~TCQ4s8<^6tyidn|7$10*i&)+zu})!C_2W71402q7>{QYb-y!V{CXm?RJeNJWI4SbCjQ z2wvN-P%xO%g^=2{EEPy4@I}B_DZmP}Z4#|V(6)|n$$*PH4vlsK&L|n^lw8|DC7fNS z($6TYfsn*D0H6d;{Z>*kHYoxo<0MThy{9^oI{?{+!Fg?WkJSI(^O_?etrG?FP6|2; zXi>K%T=-NJ>vz(IHG%hL|m_2xJJ9YLEo%zZD`4n2J>F`^umc>3Xs__Lq?T{wC7 z2KFva(Y28YkM;O|0AEK;5MKaTH+vZW#s3N5e>-+07aUI9v4Lw(?&JUX!SBEWufGrV zyx|t|{!hla!Dc1$AJp4tpZ-y&E6IKh+a=h}DW7dk&m77!z374z^xDC}4A-CC!($(L z8t;4J{n%-)V7=TxDI;9pJ3yl@X*xt{A$$i4qZknq-(hS29nJc`aBkORQ~;uzNUD5b9Yb#ojI^kiv!C`Kb2}XEhd$@+(*$ zcu!R!n_H5!Yr1Gqg#uff6}Gl3jK*p+X3Y6OIv0n?2Xkk#F?LL&$>_R0wGK9c3M^yD39cWX%LU5`1M+8mp`95Ro_o5w2iB;MuxTrf! zn-+BhTF#oKbnuQ2t2-la9akQcDqvg&1?%1%EE!OzWRSaTm(HaNlZL{n>T!bTrkKsg zsH!)j+_(+3)+J=hBwPGehA)=-=(qST<4%%ZnC9SaVon-ZezUsb*3L;hhUX;d<53@~ zYd(~UmNhw?_Ao23JZzi+{un0t-)mR2K)=1!!nD(qi7#KmW>;Hf7r;lqzVh{yIm zg42_`;M;(M#Z39NB3Hx+wUOZhCmM<>NlL2HV>A*~`xK=TM4HNv(y5XQjQC^>?ak1^ zXetty66qGqJ&lV(mpUZ?m*@i;!S(CU8NJVN=F}Kf72ldlPL(gn5QEZgMu=?%YnN+9Xx#^ma0Mp?WmF}4J!3v=WD-# zpv3{pw$(S&699L3IuE~8`BD-Wp#ZrIfoez6F;I;f0@aRA`;PWwf_c|sx@a+5v{-cN zCsF}h6#x;V1u2yOtW=<-ekXk(kc;FJBgg^}xTqCA0Z2%fz-BDq+z6r|YYWBbm8drE zhj?=VWZy4f{WfH;-Txim;8-PpcKBNzo)3oSq`~QcY4SN~&|W)AUl|5*;;M(W*`Uq1 zl@+Vczl^iG4)og*=H^@2r>pX9SzRNd)vpiO@i8~lr^D@_M~sPpbnn_Be(3N0SNzxS z`^D!=RS4Q)eL5Kg+7|%UZ~Z8VSpN-xuf;JR8)qgsxH7|M{=QGaTmIDV!O7dUsT%Y^ zAB!9a8MH6F9W1$M9QDaFIDz3+Y4 zahEZ6>q=gFFq>jgFA$wdQ%E|Br5Xx8qEG@(s2XfM@oHmK6x@wQ^`fVWD|g@ENYg+N z1Ash8EPkMNO2J*>l?$nh9o3qGXD=_5_F!|XLRAILyH$cwt#dK}Z&H}g1a<8&pG7R{ zh;cDOHTGy+L;&Bt+$fOP?VU55lKH&*%@839LacKHu)d7YU5y*-70#bqM^Os-$Bfzn z!i7S_#t#97L?)p-A?IR}2snpA!3~sgzbpy`R+LUrHJCuH;wpWnMW9rQ5-Gli)CdGs z8dT~gN^`-oXg)4je-)k}$5HJYXkV9Jm-x#HulIuk$F|43ZLw$~W_62copyveAteY^ zj}jH=sYGkN@^q79YD!XJr1$FYfWm{ooarRMhY?!YL*1;whu2}W@gfCT`62eDXDXe1 zDa$@1rdjDb&F|;=0{v{<$22GLe0WZ>w8t?gW%I8cvO-;lRgu{fF3TsctJ#gMbTE(p zGW4;&4`8%j+qvOBWx~Ne&6)E1(&`*Fo>chIuRVgl@YR18vxNYKl4xi2Ykxsy%LLAqmwg+`AG6O9}D@<;ekCQ z85^GXhTfT{9mr>>o;K+@D(n`A3q1A2MSSGpNAdWDXRzPyt4b@h9tVrLs@%vQ$!A3u z;F2n;;00yn)V^jkN{b|^9C#nxYA;-{0|1z$i$!N0%x4#0>O9B*?sP&}v>gr)1y`@m zRf;yA6e`K;THBEqqeIt<0;=ikxfp2X5sO7skW!2sxVw$ukCGg!G>uCNI6Q38b=Gd| zC;msna}jK=mpFBLjG|2C6tJCz%ArYB{V;)S)IWsEtg}E^*d{7l&nGD%Akkk{L z21$)!2KNa72|=z|rUU{ceg9H22_rFto>LNO06l;p4i4ab?@|aJq-4;03bIEbzz5O% zy9m^sU{-h7pEeqI(Kx~qv8z{5TKb@_lO~l1C1)B`O~`V4NCH~q_0hUZK+sJrEf%#$ zQN0;{?RJ8DI?D!n)L#RV1<9@r&JT9v^TUe1a)!a&@SHRRg7U;GfU!){G&m<&(&2jK z*$%g_r9Hy@J8W4sG=Hu(=dV8h-u#>otTcXy%SRcRL^UJ%iGT5n_@{s2hjH@G4P1Zv z;Q63!_%MLa1aJi`_zN@*zct|Z56OLXtnP5uPmPrG>TCYw@5b-^>Nlz@9KT#Z``C8# zt;o(|fWgk02V=3g*WoGeWpVcWP+s{vv-f9Z&dQ(H)c_175w^Poqe;Nl)&|P9!gMjm zQ~?(Ky*Ysa0dqJkLVh9uAfOR)5Q()Z7wOCas`o&QX1~BXMIoTl!e$&}nvQBjEr&xr zi)dR(ZefB-Y?ueMd@(`+SKA_mCK;Z4?s`l}#EjIbnly$p+Jx0jgothb7Y(UhI<33t z;(z+&I>r;10BLjtq|7T4c1H#lluns>%>iVPFAD`#RR4KBPc~tkB!%q|0cr+GaB2&M zII52f6M{Y@*+B+y4Pa57m;qnC??EXXOy$PBWa(5or85pMZR-S@s7CIz&M9-VW%1~s z?LhF2)UOZ&yY^?fm04pdZZRU@s}|Wc$M}GBjteqO6nRUmF56(~s^Aq$#+`Ry>j(D)aT8aVx zXof#-{mV8A%yw$L`mfkl#RT?aF!Iq@@Ieps}?!V_QoFAVD4jnEXUO`txR0z5#cA}tyV8M)>T}*Fj zsosi_E$NJ(ifcVv)@;KNslM_6B7u>Bs!Y-M>f)&!T|&?zOB=EIT=cr)3$MyA-goH4 zspP3{It5qK0bQG3;7Ic?vi7)D^Y7~U-h7|3j(u^jI~;ejeTuA4vwXaB zD*fL)xu!ttm;U3s6kLVMLpnJwa1|Nf4d536JV~$>Ur-Sqan~Rl?}q`rp1;GfG(d36 zf$$xF=MUgBzT)*sH5eQqtGckbdBWdeP2LKO#@Wy8J;P)$R@VXd3HAAu@_uwOYABCX9x~wUg<$N~N1(Q4m+#!gh77RcrysER-#y~ap3K&9>(uF{tF40|T zOZPeflza%KSt_>9Y$~FVQQo1Y!TaH1gZ=#$MO9*40w_YVeNumfP!-`~7L$r_0W>p# zuM$U;hZ<^=XDZn}dAbb}mw?-RrX-#Orsl~$B4W{W*qDrP`t%rOg#`Kp1Su(qq_obc zCM8X@BNC-6wGXIfC~xXuW8xp4xa+1g$3!u;>JP2Q{u9)OsFR zE|o`>0aX<=9+SH(h(koHWl0nhEF&o=^q;G=?uYZ|c;9|6+-M!5brwW1qwwMHc=c}; z{U%#^+GCoNhAj!ue{J={4bDl!?CisCb#B@GYkA(<`Bzr(O+D<==H?v+2yn~CuS7vL zF7faF_J6?-{LP<0xn7_-Y;k-CF@e!G1OhC1L4q}OSVR438n$-^=5PJsi$vh~MSu7+ z@TK4Q*=XyI;3^Z7azHWW!HKfNVObk$mJm5l0CS)Np=nRN&nCiGsr!?ZYu|PQ52!KKQi-c!|5**85Y^hgo1Yoo|CK$d0l#(6Ei-_+y@K8bCO;isB>OdD+_>IIRCo-cDT9um(TUq zFQ0#{e8~FrChMB7CHeT}{iCtI=j($;g4^yrg9qRF7%o3?-K1E@D##SSp^Gid!%6C8O0#Sm zzw&1}j7Pv^Tp*Orh%N(Icfc==nA80Z-_bKq`Ttz3Vdw0;;tDXGZR* zJ5j^-dLbI-3&E(WNM*oxlEGX72zNeqG38q?BHJxM6c{BJLPZG5&R&iMu}vk_yIwS% zkd57@J9S4y{wif7C&Rhd_d}##^+TW%Jg ze7%D=gK%Yccfs3EJ&sMa-?BVWILAD|=UsA`EtIMW49HVLmIRI5U_feyk(Sx3o1NWR zgS~kJCyu*N!Hp9hli*aMRR&5F0}wz$Re~q4#``6TavdlxBF;Y(etZwYXq>rOfu!mvO=HUTzkUS zoSr^ChPocvgNR6?Zg~5DelPyUxBN3a4@W~#^WZXPso^~fsH^7)Y#JI)0vk!g% zZw=&Z`2nEexs(?0c0;gNjZRgOm6P+O%k(0O!Wy9RGq^qom^y!U{ZbTG1wJkOy#+nsG=Dn{>VgGR7bPxyYbdI>u0g-tZrH(O) zb1Y`(G2VDBs`c9trPF@zq=l^DpExJ-%@?czY%`Gakc*gnPGVZir-u`Y#}VeF;gnWa z&pBy;*<#>0+Wc$SafLR^`oiuH*AEz;e`5xsk2L=>wzEvlDA`8TLEx43a%2J^*CU$eG0!S3DxKJ<}C@xG@Y zQNEPeMa-L8*}NT@+F7XMbb*|VJjyC0kmV@(;_O3kk-4Mc1p{6ta*s~I z)6VV;p)3-CrX+*=MkQFvg{45NQGit#yf95E@L&I748=fGKZJt80H5dfUkIUA<2)KYb0~`q_UKj2&g;4LQpJD%g@9qYLM%pv~9kN$Do`$=~oce}Ob25hmQ7p=k2G+>^9AZzhyC5D0*k%FeO$SA9bO6? z98PidU>Aqe1zZaS1dRxUi%-`s0OvK2u4%D9J;1a+#H;RnG48+fUOe>VlX%~Sk0Lgm z)}d)@bX}*QCUyisJ{gn4M>-jHdR~^I?qf!k$EZ@^LGnp*4zO(q@+jRQ00RLa1f+FX z8fYHuH<&Gel42DRplj5Pi-N4WhVBR)jY>GzrCpSP07pQ&he*;;Q57S7|DlV?cz%7o zz~+epMbLVh8c&QP3_OqQqp~0gYY?NckjVN_RSAmZ=+=f{GnGw)rXXh(*uM%DKtGRm zjc$B`69OOuZXqn2I9-P`t}-*sKtV|poG^{oHUkV4v0N-anff&kdoJ*0uyM)sW*6eg zucZd-yjB%yOdKp)T%WbrpLIZtC<9QC*tGQejK<0l29lko%AkNp5yo)E6wUkueECXD zw(mw1fngWPAjs;B<~GmtYMut?Bn)#I4TvT3G7ry5P6jBbZrPkX7bUJ^y)w(Cfk>ERV{MR@KuEQ@5^Jsx>wf-fs)t~KC5>5X}dH$ta` zV}z{e&I4UCWZ&JN(;;)vw6Sw&ny7ROi#gD=P-Ak3hAmbME-_1WSaG2Cf{_>xt0N zoFIw7r=n(+0F1y3WMZepiG-sL1ZYejG;d0Sgn8&mVEII2Fs);K6bPpBP#s7q9-r}^ z(h_k73Q57>Wa$f0c%;$S-1*U7ERcX;}YZc@Ep;Wx4Pq18@C2vJhnMZ?f_Y9yyb|`>l;% z&?a}fH^(1&{dZyQoM89ToOL}`^3g|#As*rH_yWMPUfKZgF9Ccxb*Kf7={SG?HZDE7 zi@*O1|1-|tbxNftuJ_NYVTHV1$iU4}((--w+yL9|>ifNUHFwS6cy0@9tOG7Sdlfso zhbpbwJ2=FZ`3|0$UP9q3c=g-Xp+G^eyOKf#5RTw6!X z=dFUPu8D}9N8L6{J}LyqMc6~SE;%ly^aUQ=Ehh+`i+v%eDqwxBP-zwon;`@@G)3Zd ze}k$}bCw8clhoqyaDjS(C0SxlKW zQq_V!C`-F>dMJ|i%9NS=8RuxTBH;4I}M z4lK6=z;rMPPQg4ha)KcGUM=@1Bo$&EBd+Y#IGi;oLQ=OD0T`9)N{K4R2oIq2MXH}t zag5+YqZA!Q@g}UFz8`JZ-mu`p4O+GEb07Fl^R$-aCg-FqkAbvt$vb?TGa8`T7@tg6kefXV^;eY*o--EOFY~k_~ zd(Vdy>rVpsn=ja4Wd}WE|KxE?ug>1HjmuB$;!pqm{~mApf>++)#a#g7E6Uk)^QIaW zSq~T+X1|72kHfopQq|br%nhBmQM`a-oKS>!v>_9F3TlIV46(B7dVDXBz9 zvQg-cz_x=Ln|lHV1X5PIWglrhniiliO7lH;ZVg32(oBK58e%`jNJIJ3(Vre5`W7=_ z_e2%miSg_e+8hKYEC=;~j?XzV&qvzPX*xu5z?sSpd%BEpX6u@B(lI#t7_Zpx$^nQ%Fu zBl;yNOffi@@HpC>bW{WAetvy-uf4Q2ygrM@5I`P3e%nX zxcwAf0`RE-p5kwLfd>m1bLZfTj?J(gP+jo;&J_2*^bUO0|M)wxu{}|zzZ^t{ya~4a zFf*@R8w_^%L6qD79_K9fdd@gc$21n_%)@|0Q4-aIgE=l-zK%zqxrmQmcoNg*5Uz>{ z1yGd*Jd$yG>>6~jK_d$|*U`==!c%&LkQV!(yZt=3(}TVQDHZXiE`sZTpdTOa3COEt zA!SL^VqhSzryq}*VLDf~f6o5m6~2ZEfSfJ$w_1Ze?{jREjwnmk!f<7)cUK%XGeVV1sIK{G<{czlUI$YY#;?o!l%&(+wF-CKcN+isU;GNr z+_{CroyBnqi(2|Dz6#(MULYF1QF`^A$Hv&sXaGD+ zvf%}xH0~^~pv;+MC^`QnVCl4Mu@g;LmL=ds)w)73BjT2Ttc!_>ohm6aB9B7gLg|%6 zL_QlepQKug>LMort0~O6FOyvH!@eVO<~#W8zoTW9j^gi()(G4 zNPk0XBabo^=%iCRjTo7|*_NopuM@B=lz;}0wq2mWHb$fE1Y}uW41J%#D&IJtADELY z-A&C&=%15pJm1PWX%M)veyhxjtGO4tF;5J?2P*Za~2B`f!-3q45`uPZ2RKvGAkrAT^2)d5#3WThP$g931(H6|Zw?7ENv|L`l8TzqxB(Xs zj0AWE${nSOfjbgCvnSOB2|hi%k&32%UH;ZspHI&SsSWfe0kqa3g#fxWQV2>37o|m7 zF1O^Q0%$-lj<#GuGc`4@Gaeo<`iS=i2=u9R7ll!c0LIy+<7TZ0%Cx^bTVQY2z`KBo zu7*ml{90)|aR+2jus2cy(kOG3lhV6L9ly^Wa|74EGs{xsN&EBdA=n&t z`*Kvy%%jdpygWQ74eh|olMd&b)iu+?-op9UN-;eD4)$sOp1g8H`409eZlbOOeVTQ} zV4n`U;K`Fp6pSV%o_y$8e9Pzj4UD%vqy-j-?Qt8t{}llL%YVB7i`_u(iXS{y1Xp^T zI5)x7rw{SBe)NywlU{!>riTmKY}iOy88um&RUA8IX$L>HBRVbw(;Kf+y=Zay;sG9g z>JlEmehCL|hIKyyqF~leQO7xe4xWDPT6FL}GV7SO=`&!tSc^4UoE@f!=S`CQFUV74SrYuGm41#G5t%F(P;&>pOX~O2Cys zptP(Q3D!p*#kfQf5=(j{2q0N14+VRAE$aIP^&(vZlyX7KRyXAL3JMM=Au}GKwvKA6 zCC*`ey~23S17Mp*BPA6lsEVMHDox#-3n^z2gkG`fKeo9y|tbSU_2<9 zGKO||F+QuP#!3V(S`Gs&8tZ$soP4d`*SsMt2T=F`A1G}htwwO3V2WTc1QZZ>v(M7} z%Azn}>0xaAT0d`f1)qTjSTDzP5TttW+!-L#Gy%rh6F62UmDl%OpSHNZw@8feRdSMD zsic$=+u#$omsA*41r~81Bll@Ix&2baAkain%io6}Zpr)r8OS?7UI_+(MPI(d z*9JBb_N6@%Q*vI1TQmP=&&g5x!cfx+?_2>j-GsB+75-Qs%J=5lS8t~|bcoC2q=iK?cFEs{j5jtOkF zf<`585%q#9yOtm;0#PMk4$?grVq!<9RLd7YD8XHBNKTL|wS!6vre`w+`pcuGod^;q z7g^W44hifnqNY%Z+5%cGfkRQzPUwcM^jcnhZn9osZKDKY15sNoju}xfnAD>Qbr}eX z21XQAL~JLyC0&6Lc&0_(8%sNk_lh(J6!9|;EkDUesjC&|&Dk>uAidD| z+@LXgLN*k|xd>6Zh`8(1F47N>;Ok>Wa# z&E9NBOtPpyscIBweWf;oE+k2)19lE;>`YsDmn4d#%3)lpDv?I{0f-LcPy%5NC%0i^ z{0fxoXVoUpaeNO@imosp{MP3rz49?JCt14TeDW#|sq6S>-}RsIwjX{sw$Dwl`|J$IeEdBC-);r`#J9o9 zd9VHufN#Mu9+RySJC~+-trSltBZ;ewR3091UDS&~_0QFC5}+?|Tof&v!8@D^+_f<}=LNIYc^$Yc*D$ZC#u@ zAapcDrf@*7a}lMfxDNsWDG5BdKx!~bfIyopPqh#iI!j~rM{pSlqch@5J$J^}MYR&d zn~`?P&#Wqz9DF(WU9h8PJ1g#nIyTtMv7Jz+Uxo33>Ph~5QQf9l(4-{Vw* z70RIC%K1oDU@*5m!ExuFPtVyzs@DX}7iQ?s$}H`ege>O(_&Y*)Pydnb!lj3~@H1VE z%k&aGqL_(oj6T&n^7$w%3k#^(IAi-4+Bn6?G$zRuf;)#oIOnU}?bbCOBEvvM49PRjGe@LY%C zx(?TqxAXbe^%`R5b+vK~xBakje|Dbbd+Yi%Ga&aqyEf1td7oODn9mAzbr!OGSFfw{ zPS@O1=eE@7{bzpgo!C4%#_XUuZrJll0KOl<4gmMx8o=^2L_2?+wpZJyCz$Ow_yd39 z^YJO4^KvX^H9Wsi$C1D|o1hFFSr|58!~ihjL`Yy@0by zGCum$MNHc{AOUq#D@%IQwQ9f(bdcClZ51_EsRi@A(s_Uf2(mbF(4d{5 zN`I%gi&RSr)97_??E#a%#7;DfCnFRE+roPgKEMa6)~rqtupT@oP%@ARB5(+D)@xhL zXaFuYS~Z)LM>d)~@3T&NLOP0Q?s^~|lG>SRZN-f-1xhA0A22V{Qt+CpWSP$1PAqLT zS@t!{L+KoHl~M%35v)eS_7O`<-mk3YT24v>`7Q!R@4yAyr~VcKO!`W6!NFQKaj;2j z;IM^BaSqN0N_?3D=%p6$-eTwMJU?ts(jPZ9CjrBAlFQ1(0OW?s8DPohB*wjUwZioq zVZ-L%421OqPMq0*4-UWnQxD?QZR-lMj`0`)*aq+)iFM=?Uqxun z7{3j`-v$tl3t+9EsBpMb<9Geu*W&B{%$MNIZQE+!W6Z#2FzGXOvR&+DXJ=Y5F}+a< z=SG4((o}Z@kMf2uvWdjl%3UJ#ym(=LBe=i=ZU(I(U$E+w5Lg|AYz{p`+A57qc#^#4W1YPf*ilt7#&- zCUH*HEp%62(?wMib}jUt#!)nu4&1e;3%Z_zy9h(&Xn?LeAC+fnTzOOz4|tQL*{(RR z08}Mb(ItcXnrg}>k%$6L^U``c>$b(BQ((2eS()n4CnOWNLr@4jY&>b(67V{RHWa}T z0Se()lEU$s$i9m<4uHt}-l3H)rHe>@_0oPkRnNgkRV!9ShtY_>7agTt9hvPBs8Zxa zEnqv^!lYRRZ@gASP(CrJ^Lf!U$!WKvs~RM=pLfiMJ6$=(0V6XrLVi?6tDG_OGc--Zq_5Bfaw%9>SmdeQ!lXhobaon|PeOSl0l2CV&s}w|pW4 zEXPm)csqdCnbrR-JlLV*%>uK%7JvM2{674iKkz0T?#~elKcHI9Ccb!+n8*z8bQfs0;qD2f0lkrWqR z1MR4idXJqDyA~okxv8C7qircz)PiRZ7dTinD2fr*ssf`*uvvAOj7AV?mm~oYN3cbD zj$o3gpkv1)Ea^G~1?f5*?YD|VTB_x)N(aaKR3)WEg}S(5I-k^z!Bl(vzBH{5CSfRo z=eQ)mH=m0)8owjCgerZ|HukoaiZCL|g?B?bqO~Us`1c6B(_`8MnTEor2_w|C1RbR&p z$j+Y4zuphHzqvK`T7%6nyd(VxecFG`>gt$x281#|c;FpH-ppmoIf)h0<9oT3$?#UK% zO<@VR99jiNq-ThNrWG^`L96FL*C0u|TBu~Jp)+NP2~0%=WJpph<{>c=N=ie!Cyx{g z-Cd|Y468~dDN8=8*d*zdI3Nm{=aJ|c+FA&(m=|T{pi#U^$JR?gOw|hSth*ItM*4t+ z#txA?`xKBQmn~&bX-^1H5T)wIBI@r%O%V~4^*tcyI|#5mjm!yd&DBjL+qz4@%NK~T zR0@pwyhAf@bj$S4{sO!E4X*Ch*xNtEXtIvUBw%ggP?b=?6{8u^t8_|)QY=Z=5~w0` zx`!_kKq|D|x!X{U*hTxHEEFVZ9gCpp)UmZM2(~Jcl)t&) zh^KEuc$NMIIQ|oYFu=pBw90!Zh~J&g0eDKWoPslz!nr~TVG&yRSmEUO^HHs@0lD;Q z8Dm&j60>`5h++F!=cE+?;RPK+h8?rP-QzPZo4f+bQ#1!e;~^5Hs<; zsUD2b4(VM;Hb~xVSU4{x($1#o&=LsgZo#HA;H1}~k`|Rx3GJKc`><^j0E<)&5@4aO zfi|fJi4pa!++|26b3{0*w#q7TWtEjme<)1?A{3;V-Ia!jNEKm>2^7%uiE0;*(KsL! zj{PKN79<@!eG7@V9eYI}6#&6hSq6Bo=gVD&fsFLfx zq@ThHK>dkmEs`R{l(7(956`$_zZtTuW4FS4Vqy(uK~8h(1Y%D%%GhPp%;zAqKR145sO8{;iAKQ zA*iXgYU!$`?GoUjpFkRt>d)maHDd;6@L`GK>HkKrcMuv~Yox%y>?^dKOM51P@pg&W z?m`jTc|qIF`cOeoP!J$6_cR)dur0lovnX5w3uVA)QsUCwTLPt9B_1y_vGo46g zJgfh1_@D`rIp!I|jETVxl8j6eEWf~!N6qW5LJ={)g&`z>>`Hji=}g;reSAA5hE|^6 zs!N;UIcbhqJnt$_htTg`)mv^{Nv-B(Eo7Hor zx{AelT?d{UD(_%jS6fp(Vz6))ooA=%kJE_bI3i!U0pLdf?3m2$6GwvO z2et&vM&Ko_2g<1=TYmiNtal3F%Hj!A6?RE#Qh zxpHNSUwrRFxH>;XRaS^ii{05S>Q;RQCi(^PTai$bfJ*2B1XNokHIom-W1cD=ss=$P zzLIROOhQG&N)0fzOrYw~2S>>XUBgg?w`fGAR`a^WY)&;`$3>Hbk|IDL-~-H2uW1=f z?FI3g2k0wMIw5JPNyO@Ti^Ic+`6B4=Mj7D)ym)&>}mb)F!~BQgpWLg_0haqhxJMvZn_FP7W}M4b13Vr7=N4 z4hc1R?jivrrBix6QCK)n_Ir@BAo!$WL;&F_eCSY>5mgo7ivVDRwyrSWi+K9+>v+$@ z*RgXDaV2`39tqZ~4uNzCLTB9Y#-N@6JV74Y3SPL&ly+CDS`?}|9YP5OcCHXqzD#Kf zL8+S*o{LrTr%t^`MS-$3J}qaCqxCs)tgF6@&Y3j{dAMw$1pl%oajM6tEU@T0%oggV zNhN5Ls?f~=Wf-Hxn%ZPV?BTYpuR>Xk;C$vG00ZpYc5U@J$$-JooHV3*8csVrC-oWY z4_~vLvjcU_?aT(|-@ME#agq+rzbp0yV{VxEt@i1#V{3PloTLC`xKD@sf*IZF@hcIi zMg_j-FZ>98@E`w@O7deX$1NoMGXVbj^C`u0&-2c~&nCdS<2a4mMSxem;a>dpfAdWQ z0|SF>c!nB2R%g`B<6&lD0fdH{*RDF#C8hLA3qL(ELx|g(IM%(7SoV24eBF{*krM7cun}Dt^P#-p^XD#Nl z2D`g6T-%M1g-1kEKlu_EZ#s-eBn1^t0%VVeM1n48ER<-K0Vu~V<>5mL!CcW$jiuK- z0k0WX1HqeX8EAV$ffJnexvB9AfeS_;8mMNAP6!mJGNhB^V3I?!+{5~lD&wMX8Y|ci zLCaP6lEmbWAf-_tGai>}2tS{%;ritUPd~MT4?lPXAH3FKd)?tg*`e^MQe^ID9dGo- zDU?8h4xG)F%C_qlg?2thrAt7I8xzXlF)Dp3w};wqjZ0PYs;yX2COav5*IDk46sInC z(zxZu0TD4NE>94_HWLCeuWHircEM!DxGJ2@V7i zj$`O3?BSLUw|Y)mIW6BkTwXiNgd_uK=P2{9$cjtX3M}=4qcG18gRi`<_S}lV&8^<2 zH}3^twek-4>CLaH)C4^B$Q69U@BCYuo=I-~VZxlB2k>P8cFh*+69`~Y5Beu)=w2R| zTb!k*58_Rq_j1H219pjnW5H1OO0IH_!umcu0O4#)br&P9U7O>l-t_?-#ttK2 z;&66|dA&dzYt-FbRX*uZ0#R-4AOcw$tH-5#lHiQXM4S*X(NLW#H}Kiu&3;GJ_DKD- zabZkSkuE}|Q%x(%#kFXFc>_r6(8dmYXOg%SUPA&TbfJ)`w~}fuvhCOoT&R}@Lq5Qf zF*Hg?w%{cAZliHAbsZg6BP3_eYX9Xh-3yrRE^x3v$8^8JY$ghFs!54zl#H$sREL%7 zArVm=UG=E6N!wzw9uR`5DxJyHO`VEl;O#xhTxk>z1y3L_QAwVj7lC$b1YdM>s47Gd zsOaeulnK0Ww`TzYIiEm6aJ1P9RBg!;6jwedcoBWCCnZ8;e|rF8gqVHFKlr&F~*$d^wtczz6W)ZYYz8deS_M z%Ai0i6#6@rrwpicOsSMfcZZJ0E?p}M2+8koL&GiF|yRAN{Xa&-4M{qv;N z6?r{+?V&9m&ex7XXM{QH``}B59XJk4a%4byw91q}Sl5BRSdsNzWC%3n(ktHU>m=N= zH8li}xBlh-jDP$0e~z>h$126@0Q?d1Z*s%k$3MUd06MzE`2heA@OL~XGj}RJYFX|Q-pFO~@e(0mvM?`^ugZTkwiz%9V zt^iArq$46NNUmwL3mQ6t84s`5h0jp3fwZH`nTf8c5FM6kCa+vTy7-ZdGpgEcT1V_p zTXYRb)Cf3FK*dlpE&`}t^MK>Ktlep^WbFk>z{PeB33d67{py)8f{VDu5N9rKG(>W6 z0d42e@|yY>tbzJKFx~AiJ#1AS=;(5XKKL|al4zo-6PVjrFA)lDLjo65wSpj)606id zAZUc+eW6X9zJu5JP7g|{QcU$B`KAcI0@ZHLK(I0B3qfBhTUi7GK9-AY@&Q8Wp_YLp zvW`aUf?yI#lwNn>k)7RRGO92h3w91Bc;?X>AO7fNJagp$qjH2oRcDFp)=AL1M@q_s zKvi^{a#tzIe4IE$_J527LTnt-aTq}=2W^{@+*+f;p&ADilyFrAu(4KPJStSJSP{@W zp)W>CYvDmNcUy$Xy~Y0VAXrWQF}oI%YJ^enlyH>^B+C}=I{%pp+QigFz2ClDVV-b9 z&+|M!UvtvU&x^xpdA3SBtbPpF)ib^oWf*omW1Lp9LK$wVPgfW_E7du>zVh?V;qucv z__|O3YZ$EuEDk#y%V9#De-Gg60Mu5jPYi(drvd!6VI5)=5vvd5-fA@{5A~evD z@yo67$q%Kxd6jVfM%ooI+0z&I@GBpB81pXiE?t`*;Baw>MLk;*h|&;xsqTTJe>zXq zAGo88Tvgm9xFlrsH#h{AWzh;BQIgxh*^VqUvBSIu7K^C%DRtYy>25v(Di8QFZS60s zfFemmC~b*aFY4lXT>v46k)B(N*k4f|;wWu+E(9~%rOHwNwGlw1TN)p0wk!UQq>vf`0mfC!q0E{fP!Z zxA&(y6RmX;fs`jpp1@Ni2-C;Nr4<6zReXn6u&RJg`^Thv28fH`tba8h<1`C_;LHIf zRbk#hVtOA;y+{&K80ab}Rr6Yp_0d>q3wHJ+9(nW-Z-3`?bh5x*r$>m<>&A-?9aUx? z9TKn=r12@-#1I`cP1kj_qe{l{9fVR%*28PBQ>q6Ak3~po+EFD-N!Qvq(LhW_UR8)y z1#*%lAeV-u@w0i)7(fu+-N|2&Hx%FiTFIyXt`v`DsSJ8vUT%! zM>{E!k)n;z^4H1|pGZs>i)3FDTSvPaP)wbkHPw=kyDKI&!N;L1`vNr8jB~M|E1@y8u0> zO4uY{B=iLPx(KPf1kk)jP~|lyP^0agFU-A%Qwb7*Xo+_2ap~d|@BYv(c6K99Z&f-b zL>+v$5CpWM?5*0bjkJ)qL)5-=i4wyp7nzo&r9?DpEFh!(@|yv9w7mDp2tGjLN#!w_ zc%{A=S5Vu!(Fpt<*iQfwq@~)D{UanUHE>BF>(Rs(Z3`t!RU0w4mi&E&E4XX?l^AWT zYaZr{2W02(A%Gg5la4Hd$?b>oIceo1pOf-^&l$ye_R`@wHTwfO1@O#WAxt za7=R~$MyLxp1cux-BbiNA!KF^xru_eoip&kV=<3-`+J_igI6x2DhnkK-J2fZV73oY zCiV`Io{0q=B}||zTS*J6XMT8^;ap_vdv&RLB`FTVz@63WV!i@40 z$!ftZ|H5m6#R51uh`OV(Rf8>`Y@ezm@euf=I!pyZ;Rv#V2OU5wptydiMm_iNl|IW| znNCB10UaNJf>am;u52fQBZ5=bnbIx5Sng9l0w!g_O%}qo@CgVJ=mvshB{F57mSlu)I+ZLckgG6Km0_$d zYOj;L&UEedM~@E%qsoVY|9p)ThYgT|Iqjw^p#<8|YJ> z=i`?N`o{0dYpSjRs)GN~fAwqlJKy=Auzh-j-K&e^Bp%%X@I~|{Lk-)<8^E$AJqzGp zlUVdvs2N6U1wtu!;N|z=uYBJh#K!iR{*8K=fq^4-{?VndZeZJR-yeJVv83wsNy753 z_g%m*Klvz5u1^x(z+We zsP58*eLAgCw`5aqp+F@Ro@%Bftttyy?rI~6CZ#jxING?g97HmnErO_CXSZBP0s4_+ z14e*=!KRxCA#ax1ZwV6V>AV(9XRQL)`69WD386r-B>7BIp=Q=$-gLlHDn~+0ufbd# zjUr~39WFdMhg4qqqXGZ~fu*Hg70EO~wV_+8@<2e#Hg^O&8V3*vlL4N-7dw-_X@9gG zYIVq%?A%79B)O>)c`Bt>*3K*&^Ny5e1*#Vv)o$F)$Feyu1n`<4=`)}z(!e}|FStl6 zLl(y4pd3;cE;e}j31IKp0*iJI@eQJz0=hFYB2*m;-_eQqCMLxMMJV7S(1}OedX?tY zZH;O3Tv(vd9kV`dfasj6s6{#jdIAILMer(R8jYWey@JE_$#Z2>n=IuY8;=4;1f_tf zvK>GV2I-n7m@GW`z9jW?8466Q3E=iIs$YUTZ+``+3Q$`kbp zA6DOt<}f6b=zAw^ijKfMFP7C!&c6e79%^4XLOpx>!me0d#n5S1j;v~yPj+MFKFy8U zv$Cz$r~PHf`#E>#cI($v5$MLEOHb|KYv1(OaL4^yxbWB>M2=O8^%nvB`K%>AE&(|sJy4zPcCsJl)p zOmw&DvpbitdvFOcdL=!TVu9%9TIO*vMp=vzTc1?BO^v!~v4|ZOQmdN{i7xT0#SU$f zs&R*4M3L;Y)bM?C%_#t_#sTAz`^^yacM+ThM>(*O>KX%ml8}-7RMI$_j3)?D5SlIA zf5)d`Jl;ZFE=CdAaqH$Jrt?RelkCRuoOJU}REFOov*g2Q=TM0$B!!52W^U2$L zFq`2z59c#nhSmGV(jU{B8WX@$Z!16WPkssi_`7}*C(loC{nG3>rC1k88+EMibKEh5 zt91bX1i;rGm-K49UZALe^|ft$&wubfx!zFbV*!{VD;8}QnYaUN3+q&@0E zNlV*$Jau6gKmUP8Q2Rt2b!~r70anv3;DwX~UKB(?VzRj*hytYz0HqZLuN$c%{|ceh zAq3E`9aS!Zu8F#E&uL@j9Mww!X+f=kQ9=l&f+Cnwph(GxEVsl1dWYZ+Y%g%}0wqkG zD2xW2_sxkZdrI%8GQ~(bhXfTN{MSW?d7XH;=Cg>pfgVMHw972GrSy$t?+PgErqgyY zhI_3u{TT8MQ6K>b(Lu@*PrZ8=J5K>|ql7;nG4jd4+Bue40j;NlWKVT%#1^7;3s@`H z;6shOw%&k^@p<@=BzjR|46E#HCi52sz>7+m{?*E9QhTx+U`psWf!@{x=9L%;L8araA3;Higq zAablb3cn4&|MT(aD#tN?0KnfTxC+Ms=x)Dv8&{s0;?IB2*WjhEyaTc0q}J~WA$J>L zY9@2PVKFT33pSWQ`T={M_AG;L9V~aj?p}>YpSg~wukT?puCQ}(fO#`V)77*(67Dhs z2qp+(xFAAc`ggIk*n#zwSh2H$3E&!$Q><+;-vL<3?jO+Y}!uaA6T9oh)pDL9=)%nqXhZC?b8E45Lg zA+$^9Wq?~6??-@YCGV;JQ2@kUcZDP9NT6jBM`*_pv)vl?6HrhfrvyHV=1cXZF^R)i z>di5+*Zjy%Z>m&p?W94gFC1mYw^ga+NsP{cpgE{mtV?!F9niH75)}wjod*pZn*_sP zIoaHB48nf%B<%#x0l*mNJqY6A^*-6^c~21i4z6C=LlrhLuEr{%8jnU;o0J$&ir3|H=B;%~>O+*Hs7NhzEyU%hjZ#k|J8 z7@m`Gq&dm)jPu;koRopFeE!SI;WV~$Se(_hcR}S@KL2L*8=ik*=UF~So_{Ux{y3|v z%j!Dpa;Lv97`rSUs_Sr@rkIExS>8h)-@HdO+szc;#Yq1A)Gol!R)X-PW$jP z0Dg!&3*E<~k^6U2*Lh5~S3#My53T{<|MTCD`(Anui&+C7{84EAS8^AS9XG?iYk5-2 zb9mU{oew^Z_doG0M&mJNvnei5ub^4906KKBr5-%esGas9v4bnjAXTLg0xsMz5VTYa zQIbP0Dfr|lc{+~@O4zP=0$HN4gv#cNsI2kRnM!q(qi8&$jTlu}PC-*pX;3Gk zQm(d%=%~gl$TaSg+ZL0~6{!e>3s`F=IM|ru;Az2ye|L!8c7gKEV?^&UF4YD}L1`zV z#FpMk#@z1UMQCVkk~>GKP*Db?Lnuv)FxNUBAqdKVC5M%F3P4nPvuGEI-k|fC&!bLI z2lHCnVolXzUduNb2l}iOf+DB(PJt4fTt(&x&OE_L;RDK`X|z2{4-`%%J*s|Hz=w!J zMhL|O2i*>yy0pNHPQ3}YpSlwplL;KioUi{P*s~Ds93(*3^Tj;k@rxJmm|lx?hLHFO1O%V zZ9GVQr+}Meumu2NI}}(SZ6K^ϟKoY=Snqfy1)i`l$#Yvv>#w{lM68ok2%j-k9O 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%0A(FZ&@S*K}LKE%X8U=5NC^*)q#N-fWN^U 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

PA1kxM>X}C)4Cw|l0ri~kthJMgZmD5j;BYmD! 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

{Pegpj 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

oR9O3D4_(l&vOI_L+1e*rKJ`%!KeXK-?Z z{`iOKAN?W5>)*_H;wfsbWDGQ6O9U~ga_Se$ScP3l=4=3vVe3*TEy-m^U3`1Q1J-V*vq#Wek?hLWJuZ0_Q!H>rR4&E(JhPXktjo zKa50C{RUt_jsf`ZZh=g%mdBf)uX3)e)kObPM=^sz75-JtTEWHFEQt{Oddd=v+3*X#xV;E&`c&x zTZdNZSX%uAR@8Y20u%S0Vnu(+J?uWNG%B(fI&WP9wEG_-gck)Sva9`^w-4-!D`{ha zb0G>ufy&=1nGIhC`iutu=v!UZRs#VLnz%o+=`2QyO^ z*aOKVnJj%^%Yh{(1Z1ylIk06i&|*$*=m6&sMZh;wR^gyS?+1FXNm|w{MmixxBrupH zBD-J&QO(jV-bw%q9SgA@xOV;mCl?>)=?AJ3vbC-a%O-g6yfwCUSE_TCM|<2&|8X zleE(o1P<};W~8`~O-ljA`4F+LpZ@D8Ere`IjB0WjtoQHp;jjBf-t`~+XK=bB$3oqG zfVS5vdGkLDeAgA{>Iz_eDhZJS{C41vy`~^*cMKP20BpbbH?sMhUuwUl)4TwnL4i3Z z4Ge*t0%-AhI;goitwa}Iy|szK0x*OoXFwb(yTK$3S#uKA)M15rpw~(7_dod&IX^*s zm##J-0%St61x^DtEk2K2hfPCS6N_a*YLAY{()p1ID2H7@uSEzmFmztaniMUv_(CjA zwk%$oBsR%%a>C7LZj;w{EW3qeNm}*YB=azt3LwZFFq^SiXatUQtfJ1`!vv0<Roh|5o@u5lt|O)TgVny}_dL{k;+&;n4^lMIonNhoMabsYj$MakwQ=}Ct4wH77L zT|1bOEIAmnVXh1j&_@sQVr6^r9H-|W;QIOdxOVX@+j>Cs$H^{Y13h+RUL#^b{57@9 zqy?KxyCD+8`ul>s}_2$(&7t+HKHC0Cy}zY=C6xf|x{Ku<$BQ zV*Dl^EXTZ<@8IR#FX7(FXK>hEV_lBfk0Z`=vX5lOhB1bE=|4auYcOpc`_i2HI9#D6*Rvw%}U$I0bknAyB<`vlLjU7*eEo zT0l8r?GmUDsMev*9i0^_t99le(0fHwEV~`&ANvIF{h$5<@BL$6O`PtCT&R1mDY&`| z{4>C}T}d%q0jy6|koD!j9|rn0tMknc4i^Ay{^>7b`^)|T`uNyxHJZj-mnLDXDWnma zXp~{zsXMG4?If`!x0GFB)`1#mWi2!O?6$Hw0X3QX1HBe;xS%gC=ygIDolbU3Dd8>& zJpo&&8i<}g*2%6>cpM#=G*1wlLC+#uKrlnDwM3)5Ib4yDZt2J*$DqfcqRFuPw4&ME zJmU26n`|GuPMmDnYz9jNx-4@xhr`NYt}G)l8D5Eh1EN|2r~$NB`!pi9n-t$Ee#j&+ zs8!8_I6F*%27(+||En6T(}e&Zv;aUiqk~R80|8*rT_UY_NoOfRpX^EW$`X@NleLPD zg_RA(B8h=w$I4MP+g^wc#>MlT-2Wg)XCL6?{5@Pd{5VId|5lSQpf}g3Jb~VT6yh04 z*JOzc#PwoySiwJvh@>dgRTSmd;#eit>n(w^f%0s!EQzhd04ehPD$b%6<}(9p%!z*# z>5)OtPuY`h4GmdODW~eGfn+VnOo+)ODE04RL&p|+L*$n3X3Tpm^)a>$JD?iQ3N9A7 zzxzU7Jo;Sj9)C86)3Ln`?2sKwq(F2HzdKR3pTkYIY0-ML3R)<%t%&--u+1m zOpX>$Ga;N%nw8Wu$d%ntQDilN+KE=wZ6LfwU{`o&yUj6BrX55gl@tP5wW4($5z=vx z0f3i~8vmGdBqNjP23>=IMeg-Om7r{r$2;!5|0npNKlD|6=%)6kLgO z^*62*+OGiCr#i^`t-x0Tyk^ArWdn1CZpP>TgKYlcf0DTKI5CT&46+(05t9odP{C{B z2p4NjrJvLw2V0u7a#gh41i1=B6PL(DBILGH!T`(zJ&Rf=wXSe}PG4LIR7e-P3z6Qj zu9MT^Ktel$E|-Ol!15B%nFc9cXu!nd(E#k4>va+tFox5NL?8e}k3r((xV;P#7`uhz zCvI^1^i4L`jtBv<%~+T{S!YvIE;s~&TCxRg9)5tx)OxN0#1TW9XCRkmyG?XCyI+-G zl0xnyg%G6G%bh`Kr7O@Q={nY3O1Vy)rKlK6gj@5V2th~8$(=>Ez(-F)eM6dOPH_~l z^vb4-g~{kWvnF|d_Q}Y^ll}9Y>_5S^^`o5DPjK94>==yDgj-Mx?6Z|maYMz184Ep{ zj3%ndK9Jq8!00_=7K++a+m|%7N;$>Gp-r1Ab@r|HIX#%%tTY+P+C>o9C}!@}pa2yzngYAq(U749NFYlwa=jNS zo8A?4emB{{3=7favgOrxzlZPo(%;7Z?m2OC1nWxgS04H1x#tCet8cpkS62Y*=L^XC zjlh2g#A~FJ<+dFC|7NOIt-kHPLIY_NmWfWF9ZA)ewk9%wwqyXourCE2 zYKw@F0uh*sY^|!Xq$ZoG2}9PTbx&PP`moY-g~R&E@1b-z@*B9^VwsrhQRI>om^e(q z<&63X7)-kETEK2jyJ%vNnM{fZoja0DsWc7fSws#p2aI4`+j8y6TO8j!Wt{GaT`<-O ztz2bYtaCCCa#MCN49l5S)=COQaQEOJ>A1ussV>V6G}?ozsObchz9UsHO{o45VZW?U zu8DBA>K6oZgJ#M)5wgMrq%5CP6Rx$INP^)WK&>l`B6<*#0m&!`IFM8fX+EV~P?M$4 z+15FmI%Cv5avm7#%4R-fS{_W*v+(kv87 zlXCsoA)fNF22;EoAe`;VNzn@c<&u=7LJJfp(QQys&1N)7(z#I9DkX%3mzaz=uyDqh zdzRRvH3mJQf&GB<0q1#}eSSLo?d!O=d75>5o3rgP565*L<}J>*w>T`P%-AT&akF$Y zMg!-JN?;&f6d6!VGLz^)7F$P9HcXu2NQs$kIW5R>N#wLqZ*$V?R9$4IYTkh@x*tW$ z65R&0q6ajgJ`G@=6&~aCHR#Z_I#p%|83fWQQnf5aSTI{UG87tA@Sy0&({&9(emab4)k47e-$0`-&^ms~vSM*itwm!; zL=nrR4tp?>-MlRS1_ds*R-8f>B#<8DnI)#U6IyGhzv47J*zG3w*%Zi2V&YY7iefFo zK8I%BE6(E{b+NL!b;9Pxj#w5V8|x?yVib);3md0z5r+yaG}_fFf6!oM2ThD_C$M0t zK!w0`b%4XoeR!89Slk|Mu2uqFLFtTe3Lqp{3K*eZmOHRShYllEr~#y=T?wCd5!I4B zm}Cx$Q!e_Hb0zO(LeI&j&OiAX^PH3QRZiD8ay(yTyIwHX3%2o)T^xvcMsh*qIoWr~ z^_=FMP8)QyQC;d&3po<{n3M|))!!J2WGw``Q?4r$xIxP?- zj+HKqIb0Mtk-b3`0d~L2%_mBOX^nUU>$cS@@ZvE1o`{jLDca~wNHETfYKg>F>nbi?g&j=yie!00E*y%1b@1Ufi#_}>BGuA z>|uWn`vXkqw7w7xV1;=`g7`!&{t;uLYZbN%kE}Q{4P7%^KrrPV0lBj*@|h#4fC9#{ z5F<4}TMD8Qnqa%zu)BSo-P70ET;H)-HslzDRPO_r)y!%IArzfSsxHG>h-9U5lqjTx z?-NG|Hc47P3rQ7(Qt-5AjlPVH14R!eNmCvAJtQ_;g!O~0=nv2nZ1vGaMH(&%n>vb4 z@WVhqXj?Ys0ipONa8SZZad$-+wLGkUVkNz_9+R+5FF$-aQx(=nhIf&-EFpd0KFCu3pAy*GO@ zC^D5-kDXNKPn}qp4KudvbIbYIvL7d`%MI=?PjYWO!Ns`A+}&W!-A@U4E(1DPMp8>s zBw0yEvl3Fzm&>&dB!JxBWNlw6M^8eLZc zz1E{!HVgMZ_$=@KeSd^ceA9O@p1Q;Q#ESs0HMsgg;9mvaeHATu1+ad8ZtmFs9Prh^ z?Vl?kYkbz{viqn0CF04a>HVG%sBp5}q|r)GfW;z&fVeX#@+zunJjlv>Bo_zBCq;|2 zx@6it7f?0nD&aW0S0c3zmU}`j6%pQ7=EWW^4zNy`D?KNXc8$^kS&c))$8fn3?c`#EOti=CwFdea_1W3bjvv2u-OiR zK}vF=X0y(Mg0(Hu2#%htE&*LdSk*MBQ*pGHNV`-sgD3$x4BR>(j8(qtFNdZL^|lf=8XcittCUOm|Y;Z&}7g=PqK@o2@2hY*_!y-Kn63C zh(UxvW`z1?D1r7Wpj3=r>0U{tugNZ=E)e`9dotF8Dg%b@UtMIW9Y`{p?v;)pYY;JD zK*Y*upf^+|j6ubsc7YfttT~utVU7(gfw(mnY7BZLhg_&mmTr2ZoP6DES&5p=hLO!S zC!;2j2f5!Dhz*3w6G2mwlqg0>=vDv-TfCqw1tFIlM6$}IM>Rz=Mli6X%MeJjW^O~H z>$mDI5%4EL!M{`P$s_`#)ke~G#UhXv;N;XMe|g=M_!HG)}uG@Mhfoj1>>eHiFU?FcE_#WGK) zP#I|PJPJs0KdKX5$>Zs|Qu_<~>>T#fe3xh}5}E>%mUsY&z+2$0t+!N!)v-NN!w6gs zyCPc_sX&DKkz~u&mrCkfijdkZET>yeZ(ZZ)&UMD=5!<5$R7WF|T8qPBrCQfO=K{+} z8k}cxI)5D@pc=uPQ-H_$>ZT`7!T?*KvJDt5LTechf{_UsBIS!e*Mr$BAx_N1iN!O* znp?6ojAfy^h$<3u=R8r`DhY!Q?JukMcMN7FPz<$u)XEY~)=D!4pcGJ$8;FG&LAQF- zII`M)CzT1&0x1D8r~qOxdc_KGp;Av8^YNmBc4R0742tgNz``6!Bng0PZ$ifBYJ|TPMWvhRyL$C6<=ZB2xgjY33|yHdrN}%n(2gz|W=j z#85d%0ksq{o$x*|xU%pp+K-Qz4JlF8NT(Ds8J==VOGh?#Q8=w+&q?Aq{ve=0BCA-@ ze?kn9z-p8t{4@azxIrKV_(6A2as{c~#rY|^WW7ol(Ns;cAqkQq)RrAc!ebC)5OakF zmDROnK}s_89#I79FRITR4UuO3Wh$9*qqZhu0?pb<=KxZcr(})|#X?65040?h7P$aZ zP)5n2>8Y}+ykJm~DL|oqLqa3&7`Tmq0>qJ0pDD5o`n$mzcu)v_fk57He*x|HSkM_P z0ZlmXBH}4FXkh@du8#mEt0n_2VjxxK5P-^*m~2C#^MF<#GtWWoq8ycLVabzOaB{?h zk9?dD{MoPL-GB5?5>MSB9-dM69`c%PUa)^7@SA`SU0JTK0M;*5koC)fKL@<|H3V4} zC7mm*2bM4VRc!u&FC=e2L0z0fgm_IxSIAOD1T|Olj5JAGFB&@9lZWs~F##AcIMmeS znEwZRZvrgqdY$DxYkhxr?|p`Qudc3UT?uQpuq+uOV?ts`!W2RRWkOP+NCFjx6eJ|6 zR0tsnq!J)NnIRBjMkhc##5UkCc%lqOPI;7N$+qM{vt?W3oz6Yy?7h4HZ>^NheyiT9 zQ(($qtbML}Nqu{F_uv2UegEFyd*1b~cPUlT5|4v>uNCA%FsUl{(px8)L+l9pzQeM;=CIY>Z2T>ayw#)Wh{ zn^{wfr=8Rx33OakQQ0N-aiE(Fs+55qL^p;k0{dv8x4{8QQZki}%m`{EnG0)ABEl)O zYC0BRV)I=wJn|GPrM^bPh+&ma!l(mdBvk=1SOdC?%>Y=Kom2##vW-&X61c0mbz-(I zy4a2eiO)wQ32y&WNdpt8k&Kayxsth%?#oA;rE+nEL}OKw00@+g2E-Z-_gaJ{h*{Db z3XsQ6a8)R?-K&W84Q%B1hozgsAh!IldJBhMZc5f zq8AP=A$@?ZlM39uLhNmk%voHl2Q?gAc;-jn%RB$r|H1k9yo=2%Ud;UDQ`Ff7pTSsx z1^C;*?*u+})Lb0_)@N%hzzVz>_*&q#pK*}2Y+nT zhjUKiVh?9$)LPoN|Iw$$$AHc&gQ#|+-383P2Pm8pEhL;*DiQ0kO6SowK8?jf*&tzA zjQCRf&(I>9jO~WaLnmB+!A+KjPVc?WOhyK&bX^s!Yf&@+hgn)kLt2@j3bBqYhR{`! zrL?KOW9BQsZ7L_y<=EcTixHvgEtkiU1q6&#l^x+*iSoDuv&)tfASOO)h&Zpu`#3_?g4a< zj2twR=%7fMRyIL6Z7p_f45cAUV=Dk3Otd{iCb2yys2^pFB&ujJWf}xAiZawp%v_97zA5%CusPq$Gw9T_ho( zRn5>s+^Q&>;w_wFXw*K$DWV9h!6^iD#4apAt93?rKi#lS>f%73?}_~dbkk!%gFqG$ zV9=IIMYfoNB)|)2YSBTOF0p%sfJua|bqH9jdOsoT5*kbDSO8*@mO(Zq4hhC?VfWB= zP9D9%_K_2on>&{6BCy$HB$<$rTlAqQc_#vUbvfPz#bjAh zOk3L5Oy}-uIOjWu%Jxed9cn_9iVi{|?GjLG5DTInd(L(vL)Hq&1u6$RoE+i8G35^1Gy4K~e)fh`+gtw~oiCmdE-3GSZn zsSXZvrK`ve&evkUt`x;u%`6ze`D}=GMYFV7*-3<(f+xxBN$hs4pZF9{e#PIQzww&@ z$QM6Gee6>JS0OF<=Yih~JbScaI0CF=0JuT)i}VZf>%k!FD(JG7&-(?8U-KKt=RF2y zd2mDK)=R5$Y8Gn5On`lGZ29nyZ6FVP6}uW)?k%QV2~nuA~#7(w445?AEpc z>&l!f+&w4H&uPM~aC$9>QKypQX1&QO?nNFQ$SBZW7HtF3ObXH zlWNYc75A*bSD`4T;99)B}%ZPHCN_PpdA*7(lSjA`qL? zW;M_P<1&1MGTQtkO7+ZcAmxZ%+8;F1nRUAaAI*b6Xm+@P^)fD>(E_e6>X5t002-+Lz(qS9=07#{B%ew!-&g57lM1vcaXHea) zVrDJQGg6tt4L|Sppa0U=w!)YlOX)eCX$STM6UHNJXz(}N3x{XQlU#Z-mjg2zh*5dz7B*;5VCZJ(DkfwtA}ebSu&@Q-op z%l|LdANc^BEc9kee`fVoHtT~w0Q|4Oiq@3l(s~3~#{i5NxDNa=;CBPA!T(IfSU|4j zvE8xzO}~xhb#H_*=;^ZkoKoS}4tK6{0y@rRWtH z0t=#3Ztv2fD4hb!02+zr-48NDm556DMbIPe0j=xLyTR_EQ8uiSBMdQ{ zb=CZ7Ib9g6N?pn7K_EpybqR~jHU3b#9QTnFOGMEqRY$FbW*8SXfSoOxKrY##`IRuf zK>C>ki^3oaCUhhvW)guCF)t8iT4At6(;(pPqKnkfR09IZ>N5zQXP|ZeC5bBKIyApx zC=J|jtIHs46zgAXfuRZ3G9f!Efei_>b>6Zx5%2^&hP2oGJ$AVGUQ(01W6+8TEZkQ0nUV~;4qv0`GK>G zJ?ktksyM$m&=st`QUn*XsFtR$0ac-9Gy@e>6olrB#vWQi-v*Eg-Ks`~q6C5mUD{?O zhl_LefA^cY{nx%40Ir?F`2|#61taTKDZ#%4aCB%t0<2^F(_8EOe&7!STnRQS2ci+! zeethg_sf4RTsx(z>2*alN^ZP5%VKZ977Gb}7YKSvD*}3M`Q0czQi555#o)C6U@FfM zQChgo9&}*sOcm~*XpwHw?v-{}iS+;%d*=RtQeK_~E#v|K)>63#qpwTT79bl2E#O0& zlAd)5ilw&^$Y_biC8p1`<|*XBjV=ZO$C0qz5X&G=2G<_`bQ@8XD=J1pFE;)Ig-twz~8%Ce?RcF{X+-Fi-|fq0>iA5-=o!SH)+J1-dY2 z7K}ya`6Gccp(>D6VeL;%fnT}wV|q?|ba4f3gVqWFG;v}}U8G5M5n~99I28;UqizFn z&GXRq0T`S^fDLA&$PlQ;#TFoj3oOwiDLI}?%=K)(YsBrY zbkFHoY10gWG%;sHkv&L)V6q*l6gg}%m@N^h`KFQgi`%BCp z`BBkr3mjH}t6;esVezd>xbp!AA+tOZs!n88Ye5CjWon)@T}O99&XThHKd>lX-VatyG*nsVF^QK85T zFOUK>jY_fg*BHGI?KVL^HhN*C0q%F0`0enP*xuB~1|kFms=+4?EuC55(t^fmsX&#L zD|MLkVKNV^0FcI$5QET4+n^GAyVgs)BX&b$A&U;3jlOG7Cjy5CU16&M+@LqX;}K>S z@s3O!MbKg16D{=EOwz-M&YK`3^?gtuNc1@*xb=_bRuQBaOmjd)=spS1`4XY@skgs8 zuEv-o^;rNhl97Xqz}MzE$s9z0*)?>D9{BFS#;nF@W2BffooB*`@b3@M;d^d&Gg}pM zsz4OLG6)t!l>{Sw{Ucxza2s9ZXqHUI5{S)|{<1jC;_TwUowEaX&iCBDyXWcK7u-5q zdG^kpv-6eni+N8gxb7#E3M6Kctvz*<4er5??rAgF4a_-_WeKwyRmU>t)az!>rnLXk zHYK~0xfWdwV=&Jy*uVW9oc+N+M1SfD-ygHCLP{`!9|isc*)fbGusSkW$M~6782(!6 zvR)>Tm7no>+ic;m2jKd@|6AF;<*jgfO3#%(6iqWDC<##Qn0wcJ$TS6yF?FkI%ShPW3qZSxLB$Z0ax>h*b(|6860-MeOvSk3D z1A!iKKaw&^pfII|jY>&S#F7H24Wv%7$WnSrBIejE7HXsC5ipP*Dcua0X1RXC$+Z)f z8#{Inov?lAlsw(AjKRnNts7H*PIB0G61SV8z@Sh<_tty{sqGqVf<1|hfyzvfp|;yt z5>SP-V)izxphB-(@3k9^R|^cFo!WQGb09F(rT3x<>_pjwQk5XEu5C2U!eHoL0!0^O zB9;$1+uP}zq_GE;tPkAq9CWQ=3j&SXT9c6}K-M<6bgLn;OpBPqNeZlm7|`I865Y^^&F9Pxkvgch{Buq1Yd$ z>?8(*BopApNuRauN}D4q7x(jAVoaza(7+ zTa@41++7w}x;v%2JD2WG>247z5s9U{TN>%^6j&MrB&0(YK|)$7$(P^%{R-!~o^$4& znR_OxTnRUK;QB7{`~KF;aw%3Og{e>vmZ+{+CY*~>VFJ}nC_UFaPCFMIrpyt&jM0s2 zR;-M0tWyw8AXI{FQPz7mQ_Sh>YWyOqK$qXDg<=*G3?vZJW}LY zCp0J6-Jw4h*tKm4^-S9ZEXc;i*+@Y(qWDd5zon~WW1O&Eo2Z$N!iWCt9zTS(#AWnE zW%!t{&P-SjM%&?$Vtg?CmF%8o6`P{~t(w~~zyrFn3T~c~&r-PnBj9zT(<3={vJ}su z@I~5a-sETU^gZtm#&6ulThBnQzMt$>I1eIU&UtTl31$O{;0B;^;fotUp-5;AJzQ*5 zTC1~n;H9nw>ESm!pqX+e_?5&38le68$6M>aYr^`(lzA_b;C9 z^5xrX@^GT4J#kuEv-!6fs^FA%43flRe0Vo!u0|wDLK{r6K+pT&p6~4UBk=m;T>y@1 zj{+cC(CZ^s#XaUiG$jmPOp22CXW?E|I^&!4E50`AFXkUEn6AiF;!6h=tBMvD6?Y;{ z$fg~bJi2#wN3WaFFN$5=>d&}paD`ckIcftv3EUkR^p+_>EfMTZbjXIPMYOrq012E| zN)P9iMO{~oooh2Ownuk%jV9sVMi2qfup^61UgB<_zU`ZdqUtgl7uP19Y#_#<;0!pL z^YWwSMdUuKYz46$I;{PATmRK2NGICBJhk3)Ed>U@W{A!&nQ;G##B6A|gvWqa#-6I< z%*9Z)%mY(^sy#AJxQT@ocx0!2(wIR61S1}PhW>5Aa4$R&6&G-PSwBH(M9Rb-h3ICI zp_XlLEaN%|O0G}w&)UDObuma$?4c7OfMg@$gOCy=hQUr@gmR)G-U*;jI0|yuo*WBf z(%+sw_GJwj1!CC^(&G%JX#vd`J+3NBXC$Tl|DzyygM4#p$6(Uf4~B5>rl0(4lOC9z zSA<_KEa`syF&t%=iaMFdqzv2O3YTw~n-yq@{ax6S8A z3RS&*g<1K9rp3#|PfmRFg8^NX(D(PlwIj;8aED|YRTeB)l-{0NS&7~ckIjrTS5%V| znHx+wa$8<^%yeuUMB4)zO0u#DO#hgqF&VLAY~ zh};w_J^%3RJJH$^i<{QFex6N$_L#Oe%IBeLf`T9h^kr-}16r$EZ=zFu0I70Y4SSn; zl&&Ry_l*0SERmD7r23~+o&bGm#><|*!yMfPVWax(^4^Z$BT%=nZHG3@ZQVN-#GKPdQqOdm z!WXC+C5fPG-_!PJ^ty=b!}J=8xQ0-&?CGx=0ojyF6;h!&s;mr6 z$=Nklp7lKqMsx9`N`T`dt+F2g2k4gg(CDs$plV9?C#K(v_woHsWQl@j$q8tqE`3Wc zS`*30a~DLo=~v@l2E|!7MV@Bb(62JgQ?HM2jwG$Frs}+;o_5~^{X7(xxIccH=d#Yv zvum1m0Op;(U6SQGP&KGmFyM97MXhWzcCA)OrQD)NPAnBCup(?xR0ylv9exUVT=qEA z7LWyf4|0rMajBN75lw`>O~?vb9gHi-YYmr|koYItyu+tXLmG$; z`nY}wuA)7;$0*zo5~eC%L{N;(9fRjB>z4Yh$c_3(li60YsJU^wX?5NDD+6O~ znYmhfid)^P-te8)?D)~cw$}$M5u=uFa%r5z+6eNZA?$;}OrcsIM_?_Mnmvotlqv>JU}Vc!gk z{v^AAtn=T8MwJx@kSaa?CtENigbUl%tEupq+wj}zsVIS~kBEB5*1O;NCK#7JJQN|8 zWP|lC#o?rpe4q0m6E0;f6z%j z9X+yyLQD~LM<`@YZ1^6#+YrsZF8FJDhaoFgdU3qs{R*C4=nUm`#X3>LR`wRa)2f- zIXlSw9%ha|Y30@2yX@`%cUEw8nrb}_%Vlz?#HH0>Cza34B)_~2c~EtVhW>n=R4M+A_ z(fZf{sJFZ^+VWW`KJR;V2&|ZtwBoT81MJQHEp&>|bV;6=9+lqCDRzZ3bRp;^UOk}S zk@`;o%i~YCnav(l_d+UTR|Ulymrzc`-aQJs1CeChZwHf_^47NbryP`LTGT*iGMqGO zR$9y)awMMIbwF{K>tL@M@Q6jn5hXAOFg{9;VP%+g5*c)MFY@sDIq6R6EmG%#iR2@u z5d%4k6~GesAcbH~MEw2_vF7kH1frcY??%lVxmaXz)_w%|4NM|1o(=r2j*9im%UY*S zFVm+dSZzDtkYspow>`Q@Rm@8DHC-h8a&qopTy#9Z=kDWVsPjF8uJh<>skcs@cS#MR z^a{=6t$2<^^+GLu>uLK20_V~?;tH^jIxa89x2_dF%}oFONsssRtMvCeY)^96ZZu4+kC{Ws4SwWdi4I*NVcA0 zvGAorm%*)Ym1$z)t2H5T9^^Hrx`#E=O4HD{qxJ$-I{2Y(SS6l2QwCmqI2)~uv*Jky zxII*ZVLkm-wG_okNz{}b@;7e7y&%4qQGT-*k+c5s^wh{9<1Sn^oqE=KJUmv%ju2eZ``tE^dRzoqFJ1?Rrve?Y@`J9i7&kGa(#S|1_a}h z8lgKy0OU0Qet$^@O@nwZX8(Hs^h~B65D~RETW>mUcgw7MY++eiUC0CJ*+_f%yb{nP za+ygF2$@$IVIA-)Z?mA9HxrR0qw+>6Ms8@;uKcO3Je-g>piy5%=g9j~h%i#HLIXAg zl}%%Ulo`IZe<({y9!R4)fA`8_RepXJzFzj-jW<-v2DZ;?{>Aa__SK()KU z&akJN6HJ)JpegL;?$#Cb7`}N$^{(00tD8|PH3evctY-*}I`kD1%uS{3HOo`cnk_x> zcZ34J5_1%MPiq0XLPL8_Cx^ZOz1^E@wR=4D`4RTTIVU-yX849b>Y4A5d_ ztmtf2Xh)I_RT+=}3LFc2C+q%Z_;6xJ z8GO6J{k|_NEXmCI#iB(9#>`M2#e-D!Pj3CC+e3?v!Q>?rxyg!N#)QukJpjE#Dr_|D)CEr{@T z2G;+<+u=W&&5X0?io#&@zL|Y)Y}T5ep743JA7r^j*1KZ5Rp{Oj|45JW5d(ZcTQU@xA-~)J~HU#Ev2lj7f6kUvTeL z+|uevCYWVC(aZ|cGzn4>q+R2nfO3Eeb7|X6N8KAg?xuftt#E6|=h5tlXM8m$yC$!( zJzlz0Vq4^*kmK={zKY@6-6L+a2qWwSvzk_-Z|QXg+kvbQrGN169zwVIleFrQW87K+ zdV@RZH~X|&9J&qspXe$LU1rouk4a;vm2sielaaW_l*K1yM(gK}J zx-W~w*1dw7etfKLF?LLAk#RfMUi$)5h^(3Ko@>#UabhRn>D12#C=9-{+l%or?lz-b zQ_9|C|Ij|?$6j$0h>cQh6_^+N9{h7bo?_p1QE0!a;^6H^ipS|KFv~pFdP6qy(0kRX zr&D@YpISPJ(Z$?J8qkx}xdY-|4O2AxpCp0Y^uFh)?Wq1Fzd3fR_p)vs-ZBfN`=vMv zQIC<@{;UKjGHtyP@Ox}b!{gcl|1G@vApln)Di7qHiF)B2SJm0QAT{`hu&SuJRHJEE z71rSwO8j3St7J#;o_9vUFt#kLWAT1P-r&VgQq-8%@gl~G41+~_<;ZC}u*n_b0zBU6 zpnfROsEo^4)7smd*l3uFLyG!~^p{7bNJZ9}4plytOe~6ypNj_Wfk7!muM#?PVh^>I z2ANr9I{ldK{75aJSmrXe@bu;z(OQROjS~a$`<^1+d4rE~Flv2!GCL+UCR>IuhZf9C z2yq#z5}avJS0vE!wo@O~U^?h!Gz*wnT@7jJDRij%_ThR-^!?eXXn)VX2XED5=^leg zM%kuj7jZb$MdOf=;#gx@-ZrvbvVJ=+*Om7xXPv;~imHs&7(-QvF+=NX^Y@Z`us@H) zdyYM5Bn6$11RDFY^6)y-%bpKgc$c! zx*G^;l&|kXVLL(eLnyVV#ksFF%At5p{lwq*&fg=UxlYz2Htlg4dgCSHSy;-zZc4VbVa@G9UJ7T^O?j>M#uq=j=(}5e zzIUl-XO)p&6wx>p2qtwPBQ38rnhkQCGap`o`&vsqV*Z;*(g}W=JTQHa*p5P6Q)Q>I z6DHKXFF<)>IUh@uz5Wjnh8fu3ZULUD&Lxhk@Xc}{BE8s-9l&VrC*eb*FXYrggrl@B z-EGs#nVXpslGew*Zkdg@U7ucDUJn0oe5_?eKotI3lZ6@#Wse79HJTCHUz~ zc_z>)uCAM6Lw?t)QCfg1Yguy}5(!D?P?E3luipd2!o;`62_>)EvR9eomtvqRe(9h* zI`4Z~CvVIL@qqqJ$5xrXjavK{bBHVpAYx9nLFEb$udoHAG& z`kG?iVnip6bIf2(Q7o&FZE-iL=z;qJNzypV@h`^isHG=I!Is#12l>YOY3u%Z*Dc%I z-;JKWz5^p}T-WSTS5Tkie9Sgqa{39k5WcNKfW{g`IE>5S9N3=KNUR;1e=`vjTJ0Hj zoSEbJl|cYbuRF_%>%y-Ev3^Er-09W*HiyVO^LH^2o2P@UAh~eW+ES+nD z613FcqfDjMYkSQ_9{<2*oiHp}99UU6{5Elp7-iW*QkN@_7V`+4vZ7vs%VWN?gq7ho;Hk zLfo?C8Ak)TltJ%Q;K5rb06BWfXchOW7bW{yU||T0GI=)YOi}b(R^JHBhzGlQ4h2ajNjFeBG)Z6fI->jL*mcBfzj@6w35$R%&3~9kPInO- z24_I&99{$1ZAf+Z4Ra`C`qHAeGq;Ww8Q`;Nup>;B0JR8f9fEDKeIbt(}-UaNW35kiEJ-% z7?EGu*Pv&Dz;@Jjqf>9Kx9}0F%FyNB7{8(1?Gf%U*Q~v=1gdr0mRG)}nXb%k!lsy! zvGuafa^&z5uh2}E?u}7K50`}U8aKCW8MO2X)dBfbm=@Ct&s-p2?&1WU97J+Tr6jCF zuv9X0a~XuAxhNC$ZG9>#ATLksRnwTkq|0_+P;>$(CYY^GE)3FQ8A9wR*_c9zo$yR~_ZDR8*z-LV?aNNS5qZ!DB`Y?vg&5U*hY z@+j-@WDu2o6I?o3q9Qm}5B>RP&FO`Bo)PYjR+`}g3U7Y}5RkWvfuaAzC9>Ga~BT^jiTM}cyQ83WL6Imv+ z?KSiA_BVdOP3!9m(T0#L3C*O40~LZsO&f*}Z^riSd`2CTCrgl_oML-+nlV=EZ#)O9 zB)ARR@I^PKI?Q8M_`LOhl}L7ybK2GEmz`8AhSEo~LDyC8=_S#|aoCtHH^ncllhTmg z*Xhe+OAi%~zlRe$k;P200*?RLmGLQWZ%CFtcVs9Y_?KY{ul)8^cr`v#?GZXrs6@nR zil*JolZt7?(w$W*U_hqIsj2JsGjFY|y&??PBhD>an_SfkFvX^d;gR`eR#6+wF)%KJ zRO)ShC?qTyN@_!au?NY-<8qsoTn6S(h+)L6i54pO^6a~edcHBpCVtiN*PbKZsgl(- z1{cyc>YAT+Y6Fp{Cq@rRXPbT>ab@r#Z~jV90IaARXwU1FO*uY_13u~Na z?yS{tVzo=)`%KDN*rFY)-%F6D22fvj_)Ks(?HvfRvF(A5GFp8bXvQ3Mmy^d;<8!z8 zT=*d}?tNcw&(<^7QvZ#H0Q_%Z@M?Bf96NE@7$_j?*t?Gi0jW=X`9?F6$yW|&Y{5i( zGO0Dh>jcrTIDS%$fB_P`!oOmE7d!K5>hioPlaVJ01fy+Kc+P>k1KrY45(AJiA=t^9 z=irca36=Jef&c4k6NDbdqG}zMXtY}e5Z4>_SS6lwX$9#PvUZWMQ zm4;aYx~2@dZM}#@LOh<3Qhp$qgJQ9>MFpi2j*JEUKN<42Fq)fF z>^e!99vW>eugSyP(E@P>H7f*!1j;_K-KX!ussUf!YPr`sLPpJDb)L- zPg)`V(|c^?&1*%T(a7HplC`x*rDtkH^Cv%GHpbb_QMA!1w2JYbSNM2qS}}9zd?VD; zOeJl4O&Duh!ku+d_unGS7S-ZdHnsh039em+_?cZ=Y+pPUm_m|^O@OR#B;g8Mxbk(w zq;}Xb|C&xT(PxX!48uqZS%5Qm+8U~A$Wl!6Y_Brws|EXjF*4?IO6w?4leYMUTLy;M za3D&(&8Q+m7JxfxlRsm8z!uF%J2PxiFH}whFL$CtEgv%h$o5|FIfdbQl6ezq3dgMy z=rNdraIipM=SW*YZ@6mqRCi@(oZ+Qspx+4)s)LPR8yN?rr^?y}b}*?{>~GFeNWKcF z?#*V5;z^?bVK92Wi-5xJeG>9Z7)B|FSRpIdVr!&hOA)(`y^;~vaD<|+)}7$9q=Kh-Vm{10e%CJk zJu~WG_KMgE->JY+d?Xf~fp6G%o6y)+lu#o@*;MhnjY_VEDMNQ0r5t>Wyn}f5hz-n! z42%3mg?{hU^tmW#%DmE zajj-o9ve${K!n~D)14!qjVCJdkJyvkxV@IHPk>3G|#7ETbuAdiJ@H3DUrpIN6 z*vlgxaDBxWAb)xF_WIuF@`uMdRpC#P2OGah5`1B5reVmm?(YNdp}jj++ZM)1;&~VB zBN=!_bhGh4DPa&9k0z(Lf0}OyjYg^Ys4Kcg89Y;k&tqAjnz6RMBXf8Vtr-O80JOvb z45+_-Op-d@Zk%sP&Q!vvB27Y%ZX`*8S~}X4j7%_yL3na2aB2D0Q&s43$wD|;i#%e# z9d^-%d?F~td$F2E#zcWby<(&;S@>T++wD z@PW$yM=Y^8{yotWT1|b~Bue38E6MvNxuL4Ldy`n$I;NmSHvKS*x3z)98rzi=sl9ru zF-Bu}gG_t^DL;rTP;uRzt`XIUfkg~A_va-G$k-c=(m((E@rSiWUyLu7NOp{;5?p*s z9Dt*7ZErNU&}qlXwQ~6#Hwp}30NQr3?9ME$_Q{8G2#SI=iT5zq2lbkv2vh-(&{B{O zeH^-$fnd(WutB@$oNyWiL?AYhveAvWy+_S<L^aexDWqbQ-BS=1ribTK6aK;?yRuwld}9k3y?hj0B3JX zGBkTbTc5t7Bn3Eo;JUjMd-sSE50scf- zrnh)$Q1C|i9Ll_*4SD!zJ3-;VSlTPHqLFMuzlm^lj717P)~d8Y4(;eV4-s&{gV>-y zGraKXkqjq<#&7cWlDPzX+gQmSvT$G^Vl8#qY(#Nf&C1b^;}?UuG_l3 zq%%B|3JDj2l6%jEF?pEyYb|rLji6n)oudcK_T#^C`LvHY1Bi1DHsM1=jKD-mA%86E>M!wLrl_IpGU0 zofc8D(M1*L03?3t`jX?N5_KD<>H7o4&)bH)cA@Y{xv4w_12HaY4Dv8S5`dqMFZ~+` zkiuYg)eD3~5simZ2*j_YP-Y!O46i#S;R?}bJcjr>Tjj|p{%#XA+&%x&-a9KBpPK(| z0Lj`FE9Ok_iqpDxOg@4nwUE_^rJO*$-Fi6y4%_&P8y+99V>jg4ZU6pndfLAE{_ggX z+uOY6F5khGH&*=7%G!4g4nA^j%&mzj)i&b5d#Zvh7@$G#~&i8DyTz9z5gb`_c)ZzNql_ zAD!QhX|$^_VoyUX)xO92{bTsX8{k-8nh$4p+-ZBT0n`@da}6flVBPyE>}(%sftj61 z0=;ipGByNk2LiGVej=)FN@SkWZ*?Xx!l`GcMBh_wAXq|bBIYv`Y>_ptkz=ommL|zs z3c06_tZ!=?mr@sKvoy9{0Mf=G*D}++3d8(pGdGUms;!!0jiR<%ks25_+QLM2_=w(D z{tamc*#Q-3nRUk|^mH85{?(-7l(i9y1zb9j;=Otl<0KXt@n^6itjOD)%3;5h$ScAL z+@>m?Wt}1>GrstR!Zq6ooj;V!!3FJ-v=V|OJLvVAi9^`e?Y*fdrVUx+J9NFxP6H*p z(;+e&%Mi0vSvub$*8IkAfgQ!m6A!n{E5VlpdBIo9O~GU0Km;RqD05+C97esjaZm?9 z3mA-iJH&^(2{^(tquTjTK>EH(Kw;HT7))_)2`t3uR5>(AI+TY)3wa&4NKdVjPu-L1#$e>DI*vRc%Y`)_`L z!8&(@Y3b`nLzrz(t}W$%_7$ckK-rk9Ldf&!{ow^F$mv9Ld$j_95PbXPCVI>Q!X3cd zwJEbCnLn}Ou0tMM{vy7vd46@ztH%KFBA?={l=7|dG@=JUBiki4HEFsC5Xg_>5A{%K)8H?)M1ySIPACa7gT6l;tQ%cJPrYaR4@fjW zf3o)_`v26!p=)fW5D(CW|8986wFDi_1-&%KV=eJMy$0u*KI;5qa>lYi{X*GDy2mzV z_OjBxPL=l~JShD%meW5T5N zbQ(NlES2mWzGz;zv=MnUCx)18+T0RV9;)KMv90}t8b+(BFX7!DVvtN3ZVDSRd0}gD zu(E3-?bK{Un+8CG%+17fZ-WB5y*@llqJvz->)m3>UOEZYptk*!bpG$36N73oFhsY_ z%2qvOoJpJ9XpA7b>fFrRs>KLs&C|1NZJvIqE$JhXCgp97=if0Ske;~MxspI7p=mw4 zmVLBf1fjxl{R6*YAcIIW|Hnmhet0vvx5v6pk)<&o1!E-K>~?QFkW2#)cdqUYcNw5; z-^&;T*{&v6Fb}`@a>Rd3tiBHre*ZLHX!X4LwgPAWJe6dqdyqhD!1~wMsLd6E+hEuh zNKqG%$P`(ym()K${!e8r8Bzxz3tjN8V*o?>H?y7}BqE+*iY}e^<1#vf+nJI)peKWrF;L;^A&q5QJa?_hH|iL>MJV z;{JCFgpnyu9p3&Y;Y&ThWNdD%bmXQFdqE7%d3yFe=WdB+@_+H&>`{3iW};JE+# z$Qd^6$!ZaNN7lLW*)JKss0HlDxiy8q>0MS@`c3*jmA4M!wh1^_sQ&bi(SnPnh!lgv zw6hZ<*6)fUI!j8k?=)eT-EzXAhcT-tWhH>vi?{_|=Zg*`ZX(iFE$lR@n|c}tev#Xn zW=PJahB6g!@p}6d82RL}RfEjtl#4HO1T}R4Qc;QNTedp$kO=V396YGZAp}6W_;U{3 zIUV3UGOxE4@z*RIkodz9U$iom7L2)O5b^36kJ(89O>s+dNH7t%5&|8z$A5fL?p+9d>)vu7Mq2(v#6F-S;dP#8P_mw?l;GieXl}QA8 z{W><69$vk91dz}KNFTlLA9^yt*$Q_oi17IZMAu>p=<$R6L|@E{?o5LgDo8F~G4=3V#Y82#~}oF1#`jZSAN5h+`Uy#c*9_ z0+nG~3wnO4y@j6Sda`e@`ZjJT#G7l@2*SP;u@3g5o6p;>P87@wj}|#n>>_KTp?D+X zDO!d=`Bk!naJ3lE zerf{7^MWf=U-poQ_bLhy$LsxJ;%5&s*k}GEfk#|`yko1Lt_7p%e^w!EV;>DR{w6@F zHdm6~y)6IScJ;j_hmMv8gKrbydqAYu7BDX--dxzvxfgJE^N-{0W7fpJv?9kqG}i2t zXjPZ4eewdQ)#9Jzk}vRg{YAZKA&nMsU(0LAMAa7#g6G^BYH847xn@zjD8U>s@KI%~?*|C1-_!u*=8gyb9|hD6@D_N_Kg{*hYF?TW{lhQPz5 zu~9e+{O^;TTc)Z{529GNmykl2)`KJdgmHY;OuVaAVw_>YPC^k9br>`W&OaY7a_()g zjZ7;f0$iI=&>rW+Ps=8j0$c{gy8O;iyY6rLQXX+0aj%-RK%~Y7L)(B&3EFLOV6w;K z3tS@iyN1*z{&#wvY!@SN8iPBY)3@{&&=5{uJVf-&2dqi%$Bg*G8@3nBu;PBGyw^P+ zl~G-?ghqB#tj*nO&bfUKN@qJ9j1wVP3%tOL<&i7>E$;CNeWMw5atO2AAEcS{YM?_v z=G2t=E^_JJ;=EiMcXvfSDe}xJjXX*5xlSV6^w>3&x}Vc+@i3U=As&9vvkLF3x%T zDft9t*j=fBVbTFrZIDSNqxbWN_l55l=`_(ySrDGMCqD1dH~dy1$miP~K7_q|)PH%81>VAoZ9Cf%p!sh^r~PPoFMru$ zD)Snup{H}o|7;gWJ9wBjA!GUR>}IPi=^?D{nz-)3}J z|B${P<&ERAQBu`D=-g4t@?K}|*KsjS!>xmZnZl;S{$+p>Sxt3ONy|}Yk2E6ydm|mw zCe2`27P4~+^!fwn!&0(7)!FxixbM}iFYwQJUKh~(TJ68i*!Z{AAAfanRx~qX-5E-} zVR8VB?bgz}dl_rk?jtIahKG0)F_(DjZNj1VY#vwl8A1V~R-gM&1?jAM!YDm_QDI=A z{%f`9<>!E(yWyR_`9Dr7eFnFo)8)<;q1vr5{SGpM-L^79-PkwTu!ZfLt6xKha^z|1 z3GAD8ZdJ>t)+G3AAB&L-!`cN{(1s(ev;GLdU->+&%4?Wp#CQ*c*CV~k{EYEYr%iIG zcAh4gvbI$}4|Ac5O@v14aGl9`21_e^*1}jrPDd80AXUhnqM%oFaeL?G z!q;nhoOl*si)ghPgcw|_r1z0!L0x)q$!Ig#Q1k_3Mrmgs>6mXkMh=u&?+6v}M3r+E zN@6sx>M6;x;BR`$YF-xx{ch$)FiVfDzR+s-Z=ddAKR+bFS4GXtpu#v#0*ycWWnRi( zkr6W?HDyV`qyM|K{IuPuexN^O!Taf7J-E^#X!kF>7!#%$Pm|mbFIAq%34*gSxq=p zsw9T9h2jGS@DhOXHlO6~>tjrwtb*A>vRZF4ur=0|0jl%oY}$fMfB*QX@X%nTdrQp+ zzVc-9BoxDUz!+{_Cs8mlcHh@J|0hc!54yyqV5s;Q5uy(c|6WrX$#v{S zghhmu33TQSyg(&Hfg1MdJ$c3%fRZ}3<%rx&YeIZ9>-)U zdy(F3d4wp)D@{AHwIn72?uhr#{Fv_>?qBZf5$;f*2-BItpqVhLr?LD`yupI!%pplM zZ%F=kQ~4#Eo&p|FUVOU!=+HB6=pR}oraX(V%`#WP=0MVh0HU3&2fm-hQPH_L-;;V_ zi83=p*fo?>C#hUe*%z#tTsg0{p+|ZfEy{M_ywqrRGmrM@t49P!W?$GjxX zN!$;GRzbUY^(t+?4YejD|87ZKEWasat43Q17-`*XA7q=PfF9#=HlKDrpc z5goXZvXFYJR`p%E?Y1g11qnr8eDSwB69byHX%p8$D~!6wlLy7q(~@+ZGe7LZ5!vt0 zG`4|v#8EeWg^xx7j3@@&40MMtXSLZgJB>tjiq#4H25SI4(VcGs#-2jP{@Zw#7;i#Q zM+%2*ar6%DA9)F<1kKuVy!6U*Q26~x`<4^*#2r5KDDh`a@a4>6NWMf%bt%@pJ zd>YYZpESGPDAz4$4>JEf(jkyS0eM3Wq>sTYI}ZheD(q|CP*d29l9D$>^5WAe8=(Ab zLN?Or#wo3)Aqa6!O;K^rQftRFmc9c2We$#gJaK#-{O5D+^Nlc6t@i1Fet3d4@aB}! z)L_Jmww&6u_o-(-Do7H=@b9?E|C0noN&pP^$U8Q~7D`Z35Q?!m@ij~d%#o|sD2P!I zxc2ARsPB{CBgM-fIFyhZpY`P-^%uyce!&{KNjXO)V}b#xS+sb8BQd1Sqd)ac=0zq|63}gdtEUB}nx;rtsGLzes zvQ^9978s7a&^k@V@WCY+-zSkva@k08kq1Xh7n4Cv){Sbe&!cdwc^=YSEVqu?k%xn^ zY?>#Yzd5FqQV)20!U0GLHXaQm%p@N>eI^x)68B@j{iXo~liezESkWleE&h)JoM}^* zG{kHTl05$yLh|U{j*at$=V_M^8@2P}It^#of11EdaD2e7&d*;-X-U1&*y&tp#(BO3 z`=Df)i514~4{8}-7)-BdrmgA?-z!fToB{$3e<*3KG@bm;5PjpBBMqi60uLrH>pJ4L z=Z!hRiyFu)h)-;^dVZ(yykV3izw4udb~n8}P$myFdggV_)NjjQ90B9YIB35hRlI{& zMNWJ}Se0nA?P8pOL0Mm=qdRR#Omb-byVvA zF-m5M!007_0ojuhJQzb_*z4teQ znwk1m&O)CPx+g%I5>}r{&B{=&_2#omqnQ%+Cde#;6Jxh&!N6k0`JZ~sxGoD-6cxrG zrfF6o8o2zJ?<`YNA0n^(Ibz)k5RqYC6P*y?rl=$P0^Bq!k zpZ#f$?xo>(F9+~+qeJuX4%+k@F^Xl(oHy~hH+SFTi&EX2J~BUi1dEOI|7Mkzj+4Zl z5%}5Mj#y&Q>1GYEO5X--mWL$bE``Y*#QJY`D4wK0Jm??$HY0b~^wMwgs{A6>b(8NTg-Nqs+FHBCJJ>1Xo^Xc_U z&?GAtniLtO;vJD+RWtl)ipP2k*O*}{DVTJdJ2ROWrw_t>RoFaoPWxAleJ;K!<6V*h z^jJhCSy?^+7=S_D+{KVC~Jcc_j9v-RmCLK?;WCkt(1%W)7rxD-22e8v5=#iJ!IYrYAgb7$;c195 z=$#Y#THQnRy@m*~|5{Oed^vhPx3+?!Y`7blH<@Q@p&%ZCg17dfd^wDoK{dm(>kazD z0X}Qfo2k?rf)#$XLrp5_)&`-q;a)6LZl6$UMH_}THUr1jXuaCz;bf;?2RthINPtyx zjtxRT8DSx{uqnP!K~{a!dq!PZtrZX&^i##7v*{0MW4-p3nSGT>((r|K8R)rrV@0&R zL-c>2nDqJ5kMSHe#1d5^_A_jDr;Ch>{=u1K@`WHFZ3slV__UnH-LKB7DCdZu8!;Z? z-l$l8z*ZfK=|&r?`rH&$1;)2&2NKOLignIg+N0jd!knttxRH468I*U3G->d?vl%gx z2Ps1Bd@&HE?YxtjP;Xu`d2*I{8AY{rBAz&9Gda9lYej6=L0IdC#`4}J3lt79kMd^? zC98*4X`1gakjB9bIqK0~0{o1+nb6LUTWZgOrLRukxAZ;UXYV^c5ZE#Z^eGkeY_|)s zYzY3;0=`cSX8RxB5*d}IsJT+Sg52A~cSOuS+^*u|1tr2nfl7dXAKt#QcvAg&VYKnx z%LkhdKNGl4)wF80sTV>nsw9A7974)cl7Ww;EoZIVF_^=5Bkw^M5{i;F96`!)OwZUi ziO^amR&8Qb*`B4A+0q~j^~DYk-vB9<1k>5+;bo`b*Sxp<%FjbzL0IxV$_n+KZ(A7^ z!r?-Z;G^qe%FmZ$FdU?Hb|Wurjuj<&l_ov#FdKDn)0_3L;AR#v{v{wgW&W?DMmjY{ zyTvUp;%#i3KfZENlH32^&||k`5%&>?SFXQ`uh?P2+aiET$L+`CjG)y)hmaw35(h~) zoD0>T84ZjU7pi}dx#_YZ=(vE(6Nx{LqwR6cXI~{49s*YZ3R!RsHc6&NTrlFm!zGE$ zId9l=uAYPNW5%D%>hMZhkOPTmDs-Pfdl77Wo^Y*Vy(j@T&Skuw`qWaiPWPS%di-42 z+v1#36FqqDrMbEOM$17g>>`YmuG@S6YeGM?*|Wy=In#8r6aN`TA#CG8Pe;l~!!rTR zo}DPY|6#fxu1?imWUUN(^;*5xc$3WT;B-tjFgsht=VQxd)aCn14*LxEB;U z<-bAY13F-G>m4n$+%bmxJQDVQ#JRsAe%jagGl6#e+X{`1G0}mN-Pl}$EIFird^mvM z7VVbW56nUfb)U4;L#iz=U*7-hgLO;X`65s<_-d$emsbbAN0%;LkSRc1pl`s~dRw7V z_muU)p+V=be%wT(uU*YD)*j9LA@X^`E)dHDu=2~py-+Cmu4i;$+gPy0Gh>rF0u%)W{2s~)Ly5FHdAn%z!q5O3BFn2 zRh=HQDG%TBB;GPL|N8^pfkplOQYbZWwlVVD6Y+Q0)zM~z&rg>{_We=~k zy28B`g#T7@nKE47Sk5K45)|yUV4VdbDP)B-giyTW^tJU{OV=r`If_$|-;(>rxaPRg z$PV1StMIt`%)iC(;Re0A!-$sU>Yz4Hx1~?N-%coM;g!37a_kQV4#22(oND?n2%X{Z zk;Z3k%p_h`&9&DLQ6k1GntthXgcXXlpY^Uv4lYb-z z8$+g1g8gIPqS$?15wOa%ZW~NWy0}+bdG6T0g#a9t?(Z8rP$8H|ZcA9@7RTK|IJcr3 z%fUb=tHlLCP1WHEY)PUWs~^+{#AYQAS%4l<9?Vl6fX4NzDr>c7EO|1c#@mv%A8!y= zF=k@WJ2FHkdP(fHWGVF>cFuHc?{I+5j~PehSCt2>`ilBJBYd~9Qn$kG}a(~Yac=)2S zYB?BSK|RiaaD!D1EU?2CrQtZ+)>jk?XtOAW8d!~=KK+nzVzep^dZL3Ki6ht)=y}^U zR@JV(okJI7jOslQ;nt9_g)B2;Z0?~y=n}4HnzFlE1)T+<CKw6UQj_yfo?=LiMRMTv~|0*lN$G^b25bq9tt_0+@q z`qTj&>MvEQ9cgy+lK+Cvbru!yQ#Hk|E`HQ3@7RWq@Cosuf2M!;+|c=Apx&3kIQG$@ z@EfK1KDWm3M>fosjR&`a)eHBrPiyF>^1_1(ahzVZxFYYPaI038h>uRq&W)n`J-;lDK|1^u{8|iBk{q zZnwrTtoz!+1QQth(C_Uz&gayz;C+|ryR?i1GVwybYuWwqI>a+Z?EOdMK=dy-$3Sek z2QkjzvV<0=(vr0?InbKX3SqKgS#4>d0iae%j z<93H5ZmN=WFUVY6%@;S}V7dYNuMQ4jtSru}-74P+l+@hOsj@i74n;+0RqE~6-aING zTr$xMWO(^aHMdu+3{(k|c16lk}z(R79X}N4U7g?!l zCD&)Qy~69jT|hv*$*-B2rD{MEk*&aE8rj-gGOE=AI=&G*t8n^nLFa`s4}1l@3fvEj z5!HdFiwAEgfd_!~L;)BA8lDO?GqeAz z2D*ueTn{_~yant}N47kT=nQJ9Qohxis(XR0h%{ZQ%*^au?-oC#bEFAe2RsbCQ8KdC z0or~V(dn~N)dq?rUji=xcLLiGX?P0I%*xPjOA5ukR^I2gqEy zR$)}6l#J>xP@V-20K0+bRj4NXF)}kVgP_z{>N4OS-8?@BegUS@BlRU|E192K9Jn%- z0WGTkZck|q-(KJo;2Gc+V93*bW@ctRtl=tqLQ%%GIQePd1MRB)D=-5T6=^ALAoE73 zeNm*^z=DoNNir4beZUvMo5171jX)?VQw=XnH|qobB2KdbD_rr|LSz`Y7Pv~;25ba2 z5Yr$PZ|nhLJnC`Qijn~2#1fR62d0#1P5JHDn!G=BBrR0ww6NcLGZ!o~JL|%w#0BxG zt;&`(x;{p^0%G}c0V`EhMj!REZ0&13;H!U~m*&kMX>&R5=bzC8JV@l*7Q3jwe{Et`v*$2$h)&mYMbC zqIsRiVw7qCYl*o87zZu~HY=Nm$942V3OxVtddl%s3=r%RIqmkQEAe-UNZyC z0mq2P<4Ad#iY3Zr%FG4J%mzzH6Q43XqhVl_m=Tq%jT#Y~04}Y_S_aDuFbn(x%qla) z*GGUuDorPWd1BfmDt1OMT&B!iu*_`r3+R}f=SnwqprJHN&WBaDCW)EQ^)x0+k<#&~3R8>tbYGU9_xfH`O*y(qnYmz@ne{TAm$Q7KB6Phb oQY7i&nzDSgv_!c;nOT*80qG>xB!rchc>n+a07*qoM6N<$f_cveRR910 diff --git a/tests/ag-ui-app/frontend/dist/client/tanstack-word-logo-white.svg b/tests/ag-ui-app/frontend/dist/client/tanstack-word-logo-white.svg deleted file mode 100644 index b6ec5086..00000000 --- a/tests/ag-ui-app/frontend/dist/client/tanstack-word-logo-white.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/tests/ag-ui-app/frontend/dist/server/assets/_tanstack-start-manifest_v-Bh9ReuAT.js b/tests/ag-ui-app/frontend/dist/server/assets/_tanstack-start-manifest_v-Bh9ReuAT.js deleted file mode 100644 index 95658f5b..00000000 --- a/tests/ag-ui-app/frontend/dist/server/assets/_tanstack-start-manifest_v-Bh9ReuAT.js +++ /dev/null @@ -1,4 +0,0 @@ -const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "/Users/alexthh/School/Syncable/syncable-cli/tests/ag-ui-app/frontend/src/routes/__root.tsx", "children": ["/", "/agent"], "preloads": ["/assets/main-DbgxqtWz.js"], "assets": [] }, "/": { "filePath": "/Users/alexthh/School/Syncable/syncable-cli/tests/ag-ui-app/frontend/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-CfGsyQoR.js", "/assets/x-DtBV5VmR.js"] }, "/agent": { "filePath": "/Users/alexthh/School/Syncable/syncable-cli/tests/ag-ui-app/frontend/src/routes/agent.tsx", "assets": [], "preloads": ["/assets/agent-Bh38wHiA.js", "/assets/x-DtBV5VmR.js"] } }, "clientEntry": "/assets/main-DbgxqtWz.js" }); -export { - tsrStartManifest -}; diff --git a/tests/ag-ui-app/frontend/dist/server/assets/agent-M-s1KgCM.js b/tests/ag-ui-app/frontend/dist/server/assets/agent-M-s1KgCM.js deleted file mode 100644 index a7446c73..00000000 --- a/tests/ag-ui-app/frontend/dist/server/assets/agent-M-s1KgCM.js +++ /dev/null @@ -1,502 +0,0 @@ -import { jsxs, jsx } from "react/jsx-runtime"; -import { Bot, Settings, Loader2, Terminal, X, ChevronDown, EyeOff, Eye, Wrench, CheckCircle2, Circle, Database, ChevronRight, Shield, AlertTriangle, Server, FolderTree, Package, GitBranch, FileCode, Code2 } from "lucide-react"; -import { useState, useEffect } from "react"; -import { u as useAgentSettings } from "./router-wq7tchx4.js"; -import "@tanstack/react-router"; -function SettingsPanel({ - onClose -}) { - const { - settings, - setProvider, - setModel, - setApiKey, - setAwsRegion, - availableModels - } = useAgentSettings(); - const [showApiKey, setShowApiKey] = useState(false); - return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-slate-950/90 backdrop-blur-sm z-20 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxs("div", { className: "bg-slate-900 border border-slate-700 rounded-2xl p-6 w-full max-w-md shadow-2xl", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [ - /* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-white flex items-center gap-2", children: [ - /* @__PURE__ */ jsx(Settings, { className: "w-5 h-5 text-emerald-400" }), - "Agent Settings" - ] }), - /* @__PURE__ */ jsx("button", { onClick: onClose, className: "p-1 rounded-lg hover:bg-slate-800 text-slate-400 hover:text-white transition-colors", children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" }) }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [ - /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-slate-300 mb-2", children: "Provider" }), - /* @__PURE__ */ jsxs("div", { className: "relative", children: [ - /* @__PURE__ */ jsxs("select", { value: settings.provider, onChange: (e) => setProvider(e.target.value), className: "w-full px-4 py-2.5 bg-slate-800 border border-slate-600 rounded-lg text-white appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent", children: [ - /* @__PURE__ */ jsx("option", { value: "openai", children: "OpenAI" }), - /* @__PURE__ */ jsx("option", { value: "anthropic", children: "Anthropic" }), - /* @__PURE__ */ jsx("option", { value: "bedrock", children: "AWS Bedrock" }) - ] }), - /* @__PURE__ */ jsx(ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" }) - ] }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [ - /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-slate-300 mb-2", children: "Model" }), - /* @__PURE__ */ jsxs("div", { className: "relative", children: [ - /* @__PURE__ */ jsx("select", { value: settings.model, onChange: (e) => setModel(e.target.value), className: "w-full px-4 py-2.5 bg-slate-800 border border-slate-600 rounded-lg text-white appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent", children: availableModels.map((m) => /* @__PURE__ */ jsx("option", { value: m.id, children: m.name }, m.id)) }), - /* @__PURE__ */ jsx(ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" }) - ] }) - ] }), - settings.provider !== "bedrock" && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [ - /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-slate-300 mb-2", children: "API Key" }), - /* @__PURE__ */ jsxs("div", { className: "relative", children: [ - /* @__PURE__ */ jsx("input", { type: showApiKey ? "text" : "password", value: settings.apiKey, onChange: (e) => setApiKey(e.target.value), placeholder: settings.provider === "openai" ? "sk-..." : "sk-ant-...", className: "w-full px-4 py-2.5 pr-10 bg-slate-800 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent" }), - /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setShowApiKey(!showApiKey), className: "absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white", children: showApiKey ? /* @__PURE__ */ jsx(EyeOff, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Eye, { className: "w-4 h-4" }) }) - ] }), - /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-slate-500", children: settings.provider === "openai" ? "Get your API key from platform.openai.com" : "Get your API key from console.anthropic.com" }) - ] }), - settings.provider === "bedrock" && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [ - /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-slate-300 mb-2", children: "AWS Region" }), - /* @__PURE__ */ jsxs("div", { className: "relative", children: [ - /* @__PURE__ */ jsxs("select", { value: settings.awsRegion || "us-east-1", onChange: (e) => setAwsRegion(e.target.value), className: "w-full px-4 py-2.5 bg-slate-800 border border-slate-600 rounded-lg text-white appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent", children: [ - /* @__PURE__ */ jsx("option", { value: "us-east-1", children: "US East (N. Virginia)" }), - /* @__PURE__ */ jsx("option", { value: "us-west-2", children: "US West (Oregon)" }), - /* @__PURE__ */ jsx("option", { value: "eu-west-1", children: "EU (Ireland)" }), - /* @__PURE__ */ jsx("option", { value: "eu-central-1", children: "EU (Frankfurt)" }), - /* @__PURE__ */ jsx("option", { value: "ap-northeast-1", children: "Asia Pacific (Tokyo)" }) - ] }), - /* @__PURE__ */ jsx(ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" }) - ] }), - /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-slate-500", children: "Uses AWS credentials from ~/.aws/credentials or environment variables" }) - ] }), - /* @__PURE__ */ jsx("button", { onClick: onClose, className: "w-full mt-2 px-4 py-2.5 bg-emerald-600 hover:bg-emerald-500 text-white font-medium rounded-lg transition-colors", children: "Save Settings" }) - ] }) }); -} -function parseToolResult(result) { - if (typeof result === "object" && result !== null && !Array.isArray(result)) { - const obj = result; - if ("raw" in obj && typeof obj.raw === "string") { - try { - return JSON.parse(obj.raw); - } catch { - return obj; - } - } - return obj; - } - if (typeof result === "string") { - try { - return JSON.parse(result); - } catch { - return { - raw: result - }; - } - } - return {}; -} -function AnalyzeProjectCard({ - result -}) { - const data = parseToolResult(result); - console.log("AnalyzeProjectCard data:", data); - const isMonorepo = data.is_monorepo; - const projectCount = data.project_count || (data.project_names || []).length || 1; - const projectNames = data.project_names || []; - const rootPath = data.root_path || ""; - const status = data.status; - const frameworksDetected = data.frameworks_detected || []; - const languagesDetected = data.languages_detected || []; - const displayName = rootPath ? rootPath.split("/").pop() || "Project" : "Project Analysis"; - return /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-slate-800/90 to-slate-900/90 border border-emerald-500/40 rounded-xl overflow-hidden shadow-lg", children: [ - /* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-emerald-500/10 border-b border-emerald-500/20", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ - /* @__PURE__ */ jsx("div", { className: "p-2.5 bg-emerald-500/20 rounded-xl", children: /* @__PURE__ */ jsx(FolderTree, { className: "w-6 h-6 text-emerald-400" }) }), - /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsx("h3", { className: "font-semibold text-white text-lg", children: displayName }), - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [ - isMonorepo && /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 bg-purple-500/20 border border-purple-500/30 rounded text-xs text-purple-300", children: "Monorepo" }), - /* @__PURE__ */ jsxs("span", { className: "text-xs text-slate-400", children: [ - projectCount, - " project", - projectCount !== 1 ? "s" : "" - ] }), - status === "ANALYSIS_COMPLETE" && /* @__PURE__ */ jsx(CheckCircle2, { className: "w-3.5 h-3.5 text-emerald-400" }) - ] }) - ] }) - ] }) }), - /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [ - languagesDetected.length > 0 && /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsxs("h4", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5", children: [ - /* @__PURE__ */ jsx(Code2, { className: "w-3.5 h-3.5" }), - "Languages" - ] }), - /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: languagesDetected.map((lang, i) => /* @__PURE__ */ jsx("span", { className: "px-3 py-1.5 bg-blue-500/15 border border-blue-500/30 rounded-lg text-sm text-blue-300 font-medium", children: lang }, i)) }) - ] }), - frameworksDetected.length > 0 && /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsxs("h4", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5", children: [ - /* @__PURE__ */ jsx(Package, { className: "w-3.5 h-3.5" }), - "Frameworks & Libraries (", - frameworksDetected.length, - ")" - ] }), - /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5", children: [ - frameworksDetected.slice(0, 20).map((fw, i) => /* @__PURE__ */ jsx("span", { className: "px-2 py-1 bg-cyan-500/10 border border-cyan-500/25 rounded text-xs text-cyan-300", children: fw }, i)), - frameworksDetected.length > 20 && /* @__PURE__ */ jsxs("span", { className: "px-2 py-1 text-xs text-slate-500", children: [ - "+", - frameworksDetected.length - 20, - " more" - ] }) - ] }) - ] }), - projectNames.length > 1 && /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsxs("h4", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5", children: [ - /* @__PURE__ */ jsx(GitBranch, { className: "w-3.5 h-3.5" }), - "Projects (", - projectNames.length, - ")" - ] }), - /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-1.5 max-h-[120px] overflow-y-auto", children: projectNames.map((name, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 py-1.5 bg-slate-800/60 rounded text-xs text-slate-300 truncate", children: [ - /* @__PURE__ */ jsx(FileCode, { className: "w-3 h-3 text-slate-500 shrink-0" }), - /* @__PURE__ */ jsx("span", { className: "truncate", children: name }) - ] }, i)) }) - ] }), - rootPath && /* @__PURE__ */ jsx("div", { className: "pt-2 border-t border-slate-700/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-500", children: [ - /* @__PURE__ */ jsx(Terminal, { className: "w-3.5 h-3.5" }), - /* @__PURE__ */ jsx("code", { className: "font-mono", children: rootPath }) - ] }) }) - ] }) - ] }); -} -function SecurityScanCard({ - result -}) { - const [expanded, setExpanded] = useState(true); - const data = parseToolResult(result); - const vulnerabilities = data.vulnerabilities || []; - const findings = data.findings || []; - const score = data.security_score || data.score; - const criticalCount = vulnerabilities.filter((v) => v.severity === "critical" || v.severity === "CRITICAL").length; - const highCount = vulnerabilities.filter((v) => v.severity === "high" || v.severity === "HIGH").length; - return /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-slate-800/80 to-slate-900/80 border border-orange-500/30 rounded-xl overflow-hidden", children: [ - /* @__PURE__ */ jsxs("button", { onClick: () => setExpanded(!expanded), className: "w-full px-4 py-3 flex items-center justify-between hover:bg-slate-700/30 transition-colors", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ - /* @__PURE__ */ jsx("div", { className: "p-2 bg-orange-500/20 rounded-lg", children: /* @__PURE__ */ jsx(Shield, { className: "w-5 h-5 text-orange-400" }) }), - /* @__PURE__ */ jsxs("div", { className: "text-left", children: [ - /* @__PURE__ */ jsx("h3", { className: "font-semibold text-white", children: "Security Scan" }), - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [ - criticalCount > 0 && /* @__PURE__ */ jsxs("span", { className: "text-red-400", children: [ - criticalCount, - " Critical" - ] }), - highCount > 0 && /* @__PURE__ */ jsxs("span", { className: "text-orange-400", children: [ - highCount, - " High" - ] }), - score !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-emerald-400", children: [ - "Score: ", - score - ] }) - ] }) - ] }) - ] }), - /* @__PURE__ */ jsx(ChevronRight, { className: `w-5 h-5 text-slate-400 transition-transform ${expanded ? "rotate-90" : ""}` }) - ] }), - expanded && (vulnerabilities.length > 0 || findings.length > 0) && /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2 max-h-[200px] overflow-y-auto", children: [ - vulnerabilities.slice(0, 5).map((vuln, i) => /* @__PURE__ */ jsxs("div", { className: `p-3 rounded-lg border ${vuln.severity === "critical" || vuln.severity === "CRITICAL" ? "bg-red-500/10 border-red-500/30" : vuln.severity === "high" || vuln.severity === "HIGH" ? "bg-orange-500/10 border-orange-500/30" : "bg-yellow-500/10 border-yellow-500/30"}`, children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ - /* @__PURE__ */ jsx(AlertTriangle, { className: `w-4 h-4 ${vuln.severity === "critical" || vuln.severity === "CRITICAL" ? "text-red-400" : vuln.severity === "high" || vuln.severity === "HIGH" ? "text-orange-400" : "text-yellow-400"}` }), - /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white", children: vuln.title }) - ] }), - vuln.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-400 line-clamp-2", children: vuln.description }) - ] }, i)), - findings.slice(0, 5).map((finding, i) => /* @__PURE__ */ jsx("div", { className: "p-3 rounded-lg bg-slate-700/30 border border-slate-600/30", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-300", children: finding.message }) }, i)) - ] }) }) - ] }); -} -function RetrieveOutputCard({ - result, - args -}) { - const data = parseToolResult(result); - args.query || ""; - const projects = data.projects || []; - if (projects.length > 0) { - return /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-slate-800/80 to-slate-900/80 border border-violet-500/30 rounded-xl overflow-hidden", children: [ - /* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-violet-500/10 border-b border-violet-500/20", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ - /* @__PURE__ */ jsx(Server, { className: "w-5 h-5 text-violet-400" }), - /* @__PURE__ */ jsx("h3", { className: "font-semibold text-white", children: "Project Details" }), - /* @__PURE__ */ jsxs("span", { className: "text-xs text-slate-400", children: [ - "(", - projects.length, - " projects)" - ] }) - ] }) }), - /* @__PURE__ */ jsx("div", { className: "p-3 space-y-2 max-h-[300px] overflow-y-auto", children: projects.map((proj, i) => /* @__PURE__ */ jsxs("div", { className: "p-3 bg-slate-800/50 rounded-lg border border-slate-700/50", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [ - /* @__PURE__ */ jsx("span", { className: "font-medium text-white", children: proj.name }), - /* @__PURE__ */ jsx("span", { className: `px-2 py-0.5 rounded text-xs ${proj.category === "Frontend" ? "bg-blue-500/20 text-blue-300" : proj.category === "Backend" ? "bg-green-500/20 text-green-300" : proj.category === "Tool" ? "bg-orange-500/20 text-orange-300" : "bg-slate-500/20 text-slate-300"}`, children: proj.category }) - ] }), - proj.languages.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 mb-1", children: proj.languages.map((lang, j) => /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 bg-blue-500/10 rounded text-xs text-blue-300", children: lang }, j)) }), - proj.frameworks.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [ - proj.frameworks.slice(0, 5).map((fw, j) => /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 bg-cyan-500/10 rounded text-xs text-cyan-300", children: fw }, j)), - proj.frameworks.length > 5 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-slate-500", children: [ - "+", - proj.frameworks.length - 5 - ] }) - ] }), - proj.path && /* @__PURE__ */ jsx("div", { className: "mt-1.5 text-xs text-slate-500 font-mono truncate", children: proj.path || "./" }) - ] }, i)) }) - ] }); - } - const isMonorepo = data.is_monorepo; - const projectCount = data.project_count || 0; - const projectNames = data.project_names || []; - const rootPath = data.root_path || ""; - if (projectNames.length > 0) { - const displayName = rootPath ? rootPath.split("/").pop() || "Workspace" : "Workspace Analysis"; - return /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-slate-800/90 to-slate-900/90 border border-indigo-500/40 rounded-xl overflow-hidden shadow-lg", children: [ - /* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-indigo-500/10 border-b border-indigo-500/20", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ - /* @__PURE__ */ jsx("div", { className: "p-2.5 bg-indigo-500/20 rounded-xl", children: /* @__PURE__ */ jsx(FolderTree, { className: "w-6 h-6 text-indigo-400" }) }), - /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsx("h3", { className: "font-semibold text-white text-lg", children: displayName }), - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [ - isMonorepo && /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 bg-purple-500/20 border border-purple-500/30 rounded text-xs text-purple-300", children: "Monorepo" }), - /* @__PURE__ */ jsxs("span", { className: "text-xs text-slate-400", children: [ - projectCount || projectNames.length, - " projects detected" - ] }) - ] }) - ] }) - ] }) }), - /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [ - /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 text-sm", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ - /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded-lg bg-emerald-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx(Package, { className: "w-4 h-4 text-emerald-400" }) }), - /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsx("div", { className: "text-white font-semibold", children: projectNames.length }), - /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500", children: "Projects" }) - ] }) - ] }) }), - /* @__PURE__ */ jsxs("div", { children: [ - /* @__PURE__ */ jsxs("h4", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1.5", children: [ - /* @__PURE__ */ jsx(GitBranch, { className: "w-3.5 h-3.5" }), - "Projects" - ] }), - /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-1.5 max-h-[200px] overflow-y-auto", children: [ - projectNames.slice(0, 30).map((name, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 py-1.5 bg-slate-800/60 rounded text-xs text-slate-300 truncate", children: [ - /* @__PURE__ */ jsx(FileCode, { className: "w-3 h-3 text-slate-500 shrink-0" }), - /* @__PURE__ */ jsx("span", { className: "truncate", children: name }) - ] }, i)), - projectNames.length > 30 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center px-2 py-1.5 bg-slate-800/40 rounded text-xs text-slate-500", children: [ - "+", - projectNames.length - 30, - " more" - ] }) - ] }) - ] }), - rootPath && /* @__PURE__ */ jsx("div", { className: "pt-2 border-t border-slate-700/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-500", children: [ - /* @__PURE__ */ jsx(Terminal, { className: "w-3.5 h-3.5" }), - /* @__PURE__ */ jsx("code", { className: "font-mono truncate", children: rootPath }) - ] }) }) - ] }) - ] }); - } - return null; -} -function GenericToolCard({ - toolName, - result, - isError -}) { - const [expanded, setExpanded] = useState(false); - const displayData = parseToolResult(result); - const isRawString = "raw" in displayData && typeof displayData.raw === "string" && Object.keys(displayData).length === 1; - const getIcon = () => { - if (toolName.includes("k8s") || toolName.includes("kube")) return /* @__PURE__ */ jsx(Server, { className: "w-5 h-5" }); - if (toolName.includes("terraform")) return /* @__PURE__ */ jsx(Code2, { className: "w-5 h-5" }); - if (toolName.includes("docker") || toolName.includes("hadolint")) return /* @__PURE__ */ jsx(Package, { className: "w-5 h-5" }); - if (toolName.includes("git")) return /* @__PURE__ */ jsx(GitBranch, { className: "w-5 h-5" }); - if (toolName.includes("file") || toolName.includes("read")) return /* @__PURE__ */ jsx(FileCode, { className: "w-5 h-5" }); - if (toolName.includes("security") || toolName.includes("vuln")) return /* @__PURE__ */ jsx(Shield, { className: "w-5 h-5" }); - if (toolName.includes("list") || toolName.includes("directory")) return /* @__PURE__ */ jsx(FolderTree, { className: "w-5 h-5" }); - return /* @__PURE__ */ jsx(Database, { className: "w-5 h-5" }); - }; - const hasActualError = isError && typeof displayData === "object" && displayData !== null && "error" in displayData; - const borderColor = hasActualError ? "border-red-500/30" : "border-slate-600/50"; - const iconBg = hasActualError ? "bg-red-500/20" : "bg-slate-700/50"; - const iconColor = hasActualError ? "text-red-400" : "text-slate-400"; - return /* @__PURE__ */ jsxs("div", { className: `bg-gradient-to-br from-slate-800/60 to-slate-900/60 border ${borderColor} rounded-xl overflow-hidden`, children: [ - /* @__PURE__ */ jsxs("button", { onClick: () => setExpanded(!expanded), className: "w-full px-4 py-3 flex items-center justify-between hover:bg-slate-700/30 transition-colors", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ - /* @__PURE__ */ jsx("div", { className: `p-2 ${iconBg} rounded-lg ${iconColor}`, children: getIcon() }), - /* @__PURE__ */ jsxs("div", { className: "text-left", children: [ - /* @__PURE__ */ jsx("h3", { className: "font-medium text-white", children: toolName.replace(/_/g, " ") }), - hasActualError && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-400", children: "Error" }) - ] }) - ] }), - /* @__PURE__ */ jsx(ChevronRight, { className: `w-5 h-5 text-slate-400 transition-transform ${expanded ? "rotate-90" : ""}` }) - ] }), - expanded && /* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: /* @__PURE__ */ jsx("pre", { className: "p-3 bg-slate-900/80 rounded-lg text-xs text-slate-300 overflow-x-auto max-h-[300px] overflow-y-auto whitespace-pre-wrap break-words", children: isRawString ? displayData.raw : JSON.stringify(displayData, null, 2) }) }) - ] }); -} -function canRenderRetrieveOutput(result) { - const data = parseToolResult(result); - const projects = data.projects || []; - const projectNames = data.project_names || []; - return projects.length > 0 || projectNames.length > 0; -} -function ToolResultCard({ - toolResult -}) { - const { - tool_name, - args, - result, - is_error - } = toolResult; - switch (tool_name) { - case "analyze_project": - return /* @__PURE__ */ jsx(AnalyzeProjectCard, { result }); - case "retrieve_output": { - if (canRenderRetrieveOutput(result)) { - return /* @__PURE__ */ jsx(RetrieveOutputCard, { result, args }); - } - return /* @__PURE__ */ jsx(GenericToolCard, { toolName: tool_name, result, isError: is_error }); - } - case "security_scan": - case "check_vulnerabilities": - return /* @__PURE__ */ jsx(SecurityScanCard, { result }); - default: - return /* @__PURE__ */ jsx(GenericToolCard, { toolName: tool_name, result, isError: is_error }); - } -} -function renderAgentProgress(state) { - const hasSteps = state?.steps && state.steps.length > 0; - const hasToolResults = state?.tool_results && state.tool_results.length > 0; - if (!hasSteps && !hasToolResults) { - return null; - } - const completedCount = state?.steps?.filter((s) => s.status === "completed").length || 0; - const totalSteps = state?.steps?.length || 0; - const progressPercent = totalSteps > 0 ? completedCount / totalSteps * 100 : 0; - const isComplete = totalSteps > 0 && completedCount === totalSteps; - return /* @__PURE__ */ jsxs("div", { className: "space-y-4 mb-4", children: [ - hasSteps && !isComplete && /* @__PURE__ */ jsxs("div", { className: "p-4 bg-slate-900/80 border border-slate-700/50 rounded-xl backdrop-blur-sm", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ - /* @__PURE__ */ jsx(Wrench, { className: "w-4 h-4 text-cyan-400 animate-pulse" }), - /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-slate-200", children: "Agent Progress" }) - ] }), - /* @__PURE__ */ jsxs("span", { className: "text-xs text-slate-400", children: [ - completedCount, - "/", - totalSteps, - " complete" - ] }) - ] }), - /* @__PURE__ */ jsx("div", { className: "h-1.5 bg-slate-700 rounded-full overflow-hidden mb-3", children: /* @__PURE__ */ jsx("div", { className: "h-full bg-gradient-to-r from-emerald-500 to-cyan-500 rounded-full transition-all duration-500 ease-out", style: { - width: `${progressPercent}%` - } }) }), - /* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-[150px] overflow-y-auto", children: state.steps?.map((step, index) => { - const isCompleted = step.status === "completed"; - const isPending = step.status === "pending"; - const isCurrentPending = isPending && index === state.steps?.findIndex((s) => s.status === "pending"); - return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-300 ${isCompleted ? "bg-emerald-500/10 border border-emerald-500/20" : isCurrentPending ? "bg-cyan-500/10 border border-cyan-500/30" : "bg-slate-800/50 border border-slate-700/30"}`, children: [ - isCompleted ? /* @__PURE__ */ jsx(CheckCircle2, { className: "w-4 h-4 text-emerald-400 shrink-0" }) : isCurrentPending ? /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 text-cyan-400 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(Circle, { className: "w-4 h-4 text-slate-500 shrink-0" }), - /* @__PURE__ */ jsx("span", { className: `text-sm truncate ${isCompleted ? "text-emerald-300" : isCurrentPending ? "text-cyan-300" : "text-slate-400"}`, children: step.description }) - ] }, index); - }) }), - state.current_tool && /* @__PURE__ */ jsx("div", { className: "mt-3 pt-3 border-t border-slate-700/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-400", children: [ - /* @__PURE__ */ jsx("span", { className: "w-2 h-2 bg-cyan-400 rounded-full animate-pulse" }), - /* @__PURE__ */ jsxs("span", { children: [ - "Running: ", - /* @__PURE__ */ jsx("code", { className: "text-cyan-400", children: state.current_tool }) - ] }) - ] }) }) - ] }), - hasToolResults && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-400 uppercase tracking-wider font-medium px-1", children: [ - /* @__PURE__ */ jsx(Database, { className: "w-3.5 h-3.5" }), - /* @__PURE__ */ jsx("span", { children: "Tool Results" }) - ] }), - state.tool_results.map((toolResult, index) => /* @__PURE__ */ jsx(ToolResultCard, { toolResult }, index)) - ] }) - ] }); -} -function GenerativeUIRenderer({ - useCoAgentStateRender, - CopilotChat -}) { - useCoAgentStateRender({ - name: "syncable", - render: ({ - state - }) => renderAgentProgress(state) - }); - return /* @__PURE__ */ jsx(CopilotChat, { className: "h-full", labels: { - title: "Syncable Agent", - initial: "Hi! I'm the Syncable agent. How can I help you today?", - placeholder: "Type your message..." - } }); -} -function ChatWithGenerativeUI({ - CopilotChat -}) { - const [useCoAgentStateRender, setUseCoAgentStateRender] = useState(null); - useEffect(() => { - import("@copilotkit/react-core").then((mod) => { - setUseCoAgentStateRender(() => mod.useCoAgentStateRender); - }); - }, []); - if (!useCoAgentStateRender) { - return /* @__PURE__ */ jsx(CopilotChat, { className: "h-full", labels: { - title: "Syncable Agent", - initial: "Hi! I'm the Syncable agent. How can I help you today?", - placeholder: "Type your message..." - } }); - } - return /* @__PURE__ */ jsx(GenerativeUIRenderer, { useCoAgentStateRender, CopilotChat }); -} -function AgentChat() { - const [CopilotChat, setCopilotChat] = useState(null); - const [showSettings, setShowSettings] = useState(false); - const { - settings - } = useAgentSettings(); - useEffect(() => { - import("@copilotkit/react-ui").then((mod) => { - setCopilotChat(() => mod.CopilotChat); - }); - }, []); - return /* @__PURE__ */ jsxs("main", { className: "min-h-screen bg-slate-950 relative overflow-hidden", children: [ - /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-linear-to-br from-slate-950 via-slate-900 to-slate-950" }), - /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(34,211,238,0.1),transparent_50%)]" }), - /* @__PURE__ */ jsxs("div", { className: "relative z-10 max-w-5xl mx-auto px-4 sm:px-6 py-8", children: [ - /* @__PURE__ */ jsxs("header", { className: "flex flex-col items-center text-center mb-6", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [ - /* @__PURE__ */ jsx("div", { className: "p-3 rounded-2xl bg-linear-to-br from-emerald-500/20 to-cyan-600/20 border border-emerald-500/30 shadow-[0_0_30px_rgba(16,185,129,0.15)]", children: /* @__PURE__ */ jsx(Bot, { className: "w-8 h-8 text-emerald-400" }) }), - /* @__PURE__ */ jsx("h1", { className: "text-4xl font-bold tracking-tight bg-linear-to-r from-emerald-400 via-cyan-400 to-blue-400 bg-clip-text text-transparent", children: "Agent Chat" }) - ] }), - /* @__PURE__ */ jsx("p", { className: "text-slate-400 max-w-md text-base leading-relaxed", children: "Chat with the Syncable agent via AG-UI protocol. Messages are processed by the AG-UI server and streamed back in real-time." }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-between", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-slate-400", children: [ - /* @__PURE__ */ jsx("span", { className: "px-2 py-1 bg-slate-800/50 rounded text-xs", children: settings.provider === "openai" ? "OpenAI" : settings.provider === "anthropic" ? "Anthropic" : "Bedrock" }), - /* @__PURE__ */ jsx("span", { className: "px-2 py-1 bg-slate-800/50 rounded text-xs font-mono truncate max-w-[200px]", children: settings.model.split("/").pop()?.split(":")[0] || settings.model }) - ] }), - /* @__PURE__ */ jsxs("button", { onClick: () => setShowSettings(true), className: "flex items-center gap-2 px-3 py-1.5 bg-slate-800 hover:bg-slate-700 border border-slate-700 rounded-lg text-sm text-slate-300 hover:text-white transition-colors", children: [ - /* @__PURE__ */ jsx(Settings, { className: "w-4 h-4" }), - "Settings" - ] }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "bg-slate-900/50 border border-slate-800 rounded-2xl overflow-hidden h-[calc(100vh-280px)] min-h-[500px] relative", children: [ - showSettings && /* @__PURE__ */ jsx(SettingsPanel, { onClose: () => setShowSettings(false) }), - CopilotChat ? /* @__PURE__ */ jsx(ChatWithGenerativeUI, { CopilotChat }) : /* @__PURE__ */ jsx("div", { className: "h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 text-emerald-400 animate-spin" }) }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "mt-6 p-4 bg-slate-900/30 border border-slate-800/50 rounded-xl", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-slate-400 text-sm", children: [ - /* @__PURE__ */ jsx(Terminal, { className: "w-4 h-4" }), - /* @__PURE__ */ jsx("span", { children: "AG-UI Server: " }), - /* @__PURE__ */ jsx("code", { className: "px-2 py-0.5 bg-slate-800 rounded text-emerald-400 text-xs", children: "http://localhost:9090" }) - ] }), - /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-slate-500", children: "Messages are sent via POST /message and responses streamed via SSE/WebSocket." }) - ] }) - ] }) - ] }); -} -export { - AgentChat as component -}; diff --git a/tests/ag-ui-app/frontend/dist/server/assets/api-C6MFPO9a.js b/tests/ag-ui-app/frontend/dist/server/assets/api-C6MFPO9a.js deleted file mode 100644 index a35fbbef..00000000 --- a/tests/ag-ui-app/frontend/dist/server/assets/api-C6MFPO9a.js +++ /dev/null @@ -1,22 +0,0 @@ -import { c as createServerRpc, a as createServerFn } from "../server.js"; -import "@tanstack/history"; -import "@tanstack/router-core/ssr/client"; -import "@tanstack/router-core"; -import "node:async_hooks"; -import "@tanstack/router-core/ssr/server"; -import "h3-v2"; -import "tiny-invariant"; -import "seroval"; -import "react/jsx-runtime"; -import "@tanstack/react-router/ssr/server"; -import "@tanstack/react-router"; -const getApiBase_createServerFn_handler = createServerRpc("cc35041ac8534be1df357d50dcecc9c79d69232d0d2da0a89a0d930737c7ec36", (opts, signal) => getApiBase.__executeServer(opts, signal)); -const getApiBase = createServerFn({ - method: "GET" -}).handler(getApiBase_createServerFn_handler, () => { - const base = process.env.API_BASE || "http://localhost:3001"; - return base.endsWith("/api") ? base : `${base}/api`; -}); -export { - getApiBase_createServerFn_handler -}; diff --git a/tests/ag-ui-app/frontend/dist/server/assets/index-Cv7xvle-.js b/tests/ag-ui-app/frontend/dist/server/assets/index-Cv7xvle-.js deleted file mode 100644 index bc5794c5..00000000 --- a/tests/ag-ui-app/frontend/dist/server/assets/index-Cv7xvle-.js +++ /dev/null @@ -1,982 +0,0 @@ -import { jsxs, jsx, Fragment } from "react/jsx-runtime"; -import { useState, useCallback, useEffect } from "react"; -import { History, MessageSquare, Lightbulb, Briefcase, Smile, Heart, Zap, Minus, Loader2, Square, Send, Check, Copy, ChevronRight, Sparkles, Trash2, Clock, X, RefreshCw } from "lucide-react"; -import { T as TSS_SERVER_FUNCTION, g as getServerFnById, a as createServerFn } from "../server.js"; -import "@tanstack/history"; -import "@tanstack/router-core/ssr/client"; -import "@tanstack/router-core"; -import "node:async_hooks"; -import "@tanstack/router-core/ssr/server"; -import "h3-v2"; -import "tiny-invariant"; -import "seroval"; -import "@tanstack/react-router/ssr/server"; -import "@tanstack/react-router"; -function BackgroundEffects() { - return /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 pointer-events-none overflow-hidden", children: [ - /* @__PURE__ */ jsx( - "div", - { - className: "absolute -top-48 -left-48 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl animate-slow-pulse", - "aria-hidden": "true" - } - ), - /* @__PURE__ */ jsx( - "div", - { - className: "absolute -bottom-48 -right-48 w-96 h-96 bg-violet-500/10 rounded-full blur-3xl animate-slow-pulse", - style: { animationDelay: "4s" }, - "aria-hidden": "true" - } - ), - /* @__PURE__ */ jsx( - "div", - { - className: "absolute top-1/2 -left-24 w-64 h-64 bg-teal-500/5 rounded-full blur-3xl animate-slow-pulse", - style: { animationDelay: "2s" }, - "aria-hidden": "true" - } - ), - /* @__PURE__ */ jsx( - "div", - { - className: "absolute -top-24 right-1/4 w-48 h-48 bg-fuchsia-500/5 rounded-full blur-3xl animate-slow-pulse", - style: { animationDelay: "6s" }, - "aria-hidden": "true" - } - ), - /* @__PURE__ */ jsx( - "div", - { - className: "absolute inset-0 opacity-30", - style: { - backgroundImage: ` - linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px), - linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px) - `, - backgroundSize: "60px 60px" - }, - "aria-hidden": "true" - } - ), - /* @__PURE__ */ jsx( - "div", - { - className: "absolute inset-0 bg-gradient-to-b from-transparent via-slate-950/50 to-slate-950", - "aria-hidden": "true" - } - ) - ] }); -} -function MessageInputCard({ - message, - context, - intent, - onMessageChange, - onContextChange, - onIntentChange, - disabled = false -}) { - return /* @__PURE__ */ jsxs("div", { className: "space-y-5 animate-glass-reveal", children: [ - /* @__PURE__ */ jsxs("div", { className: "bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-5 transition-all duration-300 hover:bg-white/[0.07] hover:border-white/15", children: [ - /* @__PURE__ */ jsxs( - "label", - { - htmlFor: "context-input", - className: "flex items-center gap-2 text-sm font-medium text-slate-300 mb-3", - children: [ - /* @__PURE__ */ jsx(History, { className: "w-4 h-4 text-violet-400" }), - "Conversation context", - /* @__PURE__ */ jsx("span", { className: "text-slate-500 font-normal", children: "(optional)" }) - ] - } - ), - /* @__PURE__ */ jsx( - "textarea", - { - id: "context-input", - value: context, - onChange: (e) => onContextChange(e.target.value), - disabled, - placeholder: "Provide background or previous messages in the conversation...\n\ne.g., 'We've been discussing the Q3 budget. They initially proposed $50k but I countered with $35k. This is their response to my counter-offer.'", - rows: 3, - className: "\n w-full bg-slate-800/60 backdrop-blur-sm border border-white/10 rounded-xl p-4\n text-slate-100 text-[15px] leading-relaxed placeholder:text-slate-500 resize-none\n focus:outline-none focus:border-violet-500/50 focus:shadow-[0_0_30px_rgba(139,92,246,0.1)]\n transition-all duration-200\n disabled:opacity-50 disabled:cursor-not-allowed\n " - } - ) - ] }), - /* @__PURE__ */ jsxs("div", { className: "bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-5 transition-all duration-300 hover:bg-white/[0.07] hover:border-white/15", children: [ - /* @__PURE__ */ jsxs( - "label", - { - htmlFor: "message-input", - className: "flex items-center gap-2 text-sm font-medium text-slate-300 mb-3", - children: [ - /* @__PURE__ */ jsx(MessageSquare, { className: "w-4 h-4 text-cyan-400" }), - "Message you received", - /* @__PURE__ */ jsx("span", { className: "text-rose-400", children: "*" }) - ] - } - ), - /* @__PURE__ */ jsx( - "textarea", - { - id: "message-input", - value: message, - onChange: (e) => onMessageChange(e.target.value), - disabled, - placeholder: "Paste the message you need to reply to...\n\ne.g., 'Hi, I wanted to follow up on our meeting yesterday. When would be a good time to reschedule?'", - rows: 4, - className: "\n w-full bg-slate-800/60 backdrop-blur-sm border border-white/10 rounded-xl p-4\n text-slate-100 text-[15px] leading-relaxed placeholder:text-slate-500 resize-none\n focus:outline-none focus:border-cyan-500/50 focus:shadow-[0_0_30px_rgba(34,211,238,0.1)]\n transition-all duration-200\n disabled:opacity-50 disabled:cursor-not-allowed\n " - } - ) - ] }), - /* @__PURE__ */ jsxs("div", { className: "bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-5 transition-all duration-300 hover:bg-white/[0.07] hover:border-white/15", children: [ - /* @__PURE__ */ jsxs( - "label", - { - htmlFor: "intent-input", - className: "flex items-center gap-2 text-sm font-medium text-slate-300 mb-3", - children: [ - /* @__PURE__ */ jsx(Lightbulb, { className: "w-4 h-4 text-amber-400" }), - "What you want to say", - /* @__PURE__ */ jsx("span", { className: "text-slate-500 font-normal", children: "(optional)" }) - ] - } - ), - /* @__PURE__ */ jsx( - "textarea", - { - id: "intent-input", - value: intent, - onChange: (e) => onIntentChange(e.target.value), - disabled, - placeholder: "Describe what you want to communicate in your reply...\n\ne.g., 'I want to accept their offer but negotiate a faster timeline. Also mention I need the contract by Friday.'", - rows: 3, - className: "\n w-full bg-slate-800/60 backdrop-blur-sm border border-white/10 rounded-xl p-4\n text-slate-100 text-[15px] leading-relaxed placeholder:text-slate-500 resize-none\n focus:outline-none focus:border-amber-500/50 focus:shadow-[0_0_30px_rgba(245,158,11,0.1)]\n transition-all duration-200\n disabled:opacity-50 disabled:cursor-not-allowed\n " - } - ) - ] }) - ] }); -} -const TONES = [ - { - value: "professional", - label: "Professional", - icon: /* @__PURE__ */ jsx(Briefcase, { className: "w-4 h-4" }), - description: "Formal, business-appropriate" - }, - { - value: "friendly", - label: "Friendly", - icon: /* @__PURE__ */ jsx(Smile, { className: "w-4 h-4" }), - description: "Warm and personable" - }, - { - value: "apologetic", - label: "Apologetic", - icon: /* @__PURE__ */ jsx(Heart, { className: "w-4 h-4" }), - description: "Sincere and understanding" - }, - { - value: "assertive", - label: "Assertive", - icon: /* @__PURE__ */ jsx(Zap, { className: "w-4 h-4" }), - description: "Direct and confident" - }, - { - value: "neutral", - label: "Neutral", - icon: /* @__PURE__ */ jsx(Minus, { className: "w-4 h-4" }), - description: "Balanced and objective" - } -]; -function ToneSelector({ selected, onChange, disabled = false }) { - return /* @__PURE__ */ jsxs("div", { className: "space-y-3 animate-fade-in-up", style: { animationDelay: "100ms" }, children: [ - /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-slate-300", children: "Select tone" }), - /* @__PURE__ */ jsx( - "div", - { - className: "flex flex-wrap gap-2", - role: "radiogroup", - "aria-label": "Reply tone selection", - children: TONES.map((tone) => { - const isSelected = selected === tone.value; - return /* @__PURE__ */ jsxs( - "button", - { - onClick: () => onChange(tone.value), - disabled, - role: "radio", - "aria-checked": isSelected, - title: tone.description, - className: ` - group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium - transition-all duration-200 border cursor-pointer - ${isSelected ? "bg-gradient-to-r from-cyan-500/20 to-violet-500/20 border-cyan-500/40 text-cyan-300 shadow-[0_0_20px_rgba(34,211,238,0.15)]" : "bg-white/5 border-white/10 text-slate-400 hover:bg-white/10 hover:border-white/20 hover:text-slate-200"} - disabled:opacity-50 disabled:cursor-not-allowed - focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:ring-offset-2 focus:ring-offset-slate-950 - `, - children: [ - /* @__PURE__ */ jsx( - "span", - { - className: `transition-all duration-200 ${isSelected ? "text-cyan-400 scale-110" : "text-slate-500 group-hover:text-slate-300 group-hover:scale-110"}`, - children: tone.icon - } - ), - /* @__PURE__ */ jsx("span", { children: tone.label }) - ] - }, - tone.value - ); - }) - } - ) - ] }); -} -function GenerateButton({ - onClick, - onStop, - isLoading = false, - disabled = false -}) { - const handleClick = () => { - if (isLoading && onStop) { - onStop(); - } else { - onClick(); - } - }; - return /* @__PURE__ */ jsxs( - "button", - { - onClick: handleClick, - disabled: disabled && !isLoading, - className: ` - group relative flex items-center justify-center gap-3 px-8 py-4 rounded-2xl font-semibold text-base - transition-all duration-300 overflow-hidden cursor-pointer - ${isLoading ? "bg-slate-800/80 border border-cyan-500/40 text-cyan-300 animate-glow-pulse" : "bg-gradient-to-r from-cyan-500 to-violet-600 text-white shadow-lg shadow-cyan-500/25 hover:shadow-[0_0_40px_rgba(34,211,238,0.4)] hover:from-cyan-400 hover:to-violet-500"} - ${!isLoading && !disabled ? "transform hover:scale-[1.03] active:scale-[0.98]" : ""} - disabled:opacity-40 disabled:cursor-not-allowed disabled:transform-none disabled:shadow-none - focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:ring-offset-2 focus:ring-offset-slate-950 - `, - "aria-label": isLoading ? "Stop generating" : "Generate reply suggestions", - children: [ - !isLoading && !disabled && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 -translate-x-full group-hover:translate-x-full transition-transform duration-700 bg-gradient-to-r from-transparent via-white/20 to-transparent" }), - /* @__PURE__ */ jsx("span", { className: "relative flex items-center gap-3", children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [ - /* @__PURE__ */ jsx(Loader2, { className: "w-5 h-5 animate-spin" }), - /* @__PURE__ */ jsx("span", { children: "Generating..." }), - onStop && /* @__PURE__ */ jsx(Square, { className: "w-4 h-4 ml-1 opacity-70" }) - ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [ - /* @__PURE__ */ jsx(Send, { className: "w-5 h-5 transition-transform duration-200 group-hover:translate-x-0.5" }), - /* @__PURE__ */ jsx("span", { children: "Generate Replies" }) - ] }) }) - ] - } - ); -} -const TONE_BADGE_STYLES = { - professional: "bg-blue-500/10 text-blue-300 border-blue-500/20", - friendly: "bg-emerald-500/10 text-emerald-300 border-emerald-500/20", - apologetic: "bg-amber-500/10 text-amber-300 border-amber-500/20", - assertive: "bg-rose-500/10 text-rose-300 border-rose-500/20", - neutral: "bg-slate-500/10 text-slate-300 border-slate-500/20" -}; -function ReplyCard({ - reply, - tone, - isCopied, - onCopy, - onSelect, - animationDelay = 0 -}) { - return /* @__PURE__ */ jsxs( - "div", - { - className: "group bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-6\n hover:bg-white/[0.08] hover:border-cyan-500/30 hover:shadow-[0_0_40px_rgba(34,211,238,0.1)]\n cursor-pointer transition-all duration-300 animate-fade-in-up", - style: { - animationDelay: `${animationDelay}ms`, - animationFillMode: "both" - }, - onClick: onSelect, - role: "button", - tabIndex: 0, - onKeyDown: (e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - onSelect(); - } - }, - "aria-label": `Reply option ${reply.reply_index + 1}. ${reply.content}`, - children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [ - /* @__PURE__ */ jsxs( - "span", - { - className: `px-3 py-1.5 rounded-full text-xs font-medium border ${TONE_BADGE_STYLES[tone]}`, - children: [ - "Option ", - reply.reply_index + 1 - ] - } - ), - /* @__PURE__ */ jsx( - "button", - { - onClick: (e) => { - e.stopPropagation(); - onCopy(); - }, - className: "flex items-center gap-2 px-3 py-1.5 rounded-lg cursor-pointer\n bg-white/5 hover:bg-white/10\n text-slate-400 hover:text-slate-200\n opacity-0 group-hover:opacity-100\n transition-all duration-200\n focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:opacity-100", - "aria-label": isCopied ? "Copied to clipboard" : "Copy to clipboard", - children: isCopied ? /* @__PURE__ */ jsxs(Fragment, { children: [ - /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-emerald-400" }), - /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-emerald-400", children: "Copied!" }) - ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [ - /* @__PURE__ */ jsx(Copy, { className: "w-4 h-4" }), - /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Copy" }) - ] }) - } - ) - ] }), - /* @__PURE__ */ jsx("p", { className: "text-slate-100 leading-relaxed mb-5 whitespace-pre-wrap text-[15px]", children: reply.content }), - /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs( - "button", - { - onClick: (e) => { - e.stopPropagation(); - onSelect(); - }, - className: "flex items-center gap-1.5 text-sm font-medium text-cyan-400 hover:text-cyan-300\n transition-all duration-200 cursor-pointer group/btn", - children: [ - /* @__PURE__ */ jsx("span", { children: "Use this reply" }), - /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4 transition-transform duration-200 group-hover/btn:translate-x-1" }) - ] - } - ) }) - ] - } - ); -} -function ReplyCardSkeleton({ delay = 0 }) { - return /* @__PURE__ */ jsxs( - "div", - { - className: "bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-6 animate-fade-in-up", - style: { animationDelay: `${delay}ms`, animationFillMode: "both" }, - children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [ - /* @__PURE__ */ jsx("div", { className: "h-7 w-20 bg-slate-700/50 rounded-full animate-shimmer" }), - /* @__PURE__ */ jsx("div", { className: "h-8 w-16 bg-slate-700/50 rounded-lg animate-shimmer" }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "space-y-3 mb-5", children: [ - /* @__PURE__ */ jsx("div", { className: "h-4 bg-slate-700/50 rounded-lg w-full animate-shimmer" }), - /* @__PURE__ */ jsx("div", { className: "h-4 bg-slate-700/50 rounded-lg w-11/12 animate-shimmer" }), - /* @__PURE__ */ jsx("div", { className: "h-4 bg-slate-700/50 rounded-lg w-4/5 animate-shimmer" }) - ] }), - /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx("div", { className: "h-5 w-28 bg-slate-700/50 rounded-lg animate-shimmer" }) }) - ] - } - ); -} -function ReplyOptionsSection({ - replies, - isLoading, - tone, - copiedId, - onCopy, - onSelect, - streamedContent -}) { - if (isLoading) { - return /* @__PURE__ */ jsxs("section", { "aria-live": "polite", "aria-busy": "true", className: "animate-fade-in-up", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-4", children: [ - /* @__PURE__ */ jsx(Sparkles, { className: "w-5 h-5 text-cyan-400 animate-pulse" }), - /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-slate-100", children: "Generating replies..." }) - ] }), - streamedContent && /* @__PURE__ */ jsxs("div", { className: "mb-4 p-4 bg-slate-800/50 rounded-xl border border-cyan-500/20", children: [ - /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-400 mb-2", children: "AI is thinking..." }), - /* @__PURE__ */ jsxs("p", { className: "text-slate-300 text-sm font-mono whitespace-pre-wrap", children: [ - streamedContent, - /* @__PURE__ */ jsx("span", { className: "inline-block w-2 h-4 bg-cyan-400 ml-1 animate-pulse" }) - ] }) - ] }), - /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: [0, 1, 2].map((index) => /* @__PURE__ */ jsx(ReplyCardSkeleton, { delay: index * 100 }, index)) }) - ] }); - } - if (replies.length === 0) { - return /* @__PURE__ */ jsxs("section", { className: "flex flex-col items-center justify-center py-16 text-center animate-fade-in-up", children: [ - /* @__PURE__ */ jsx("div", { className: "w-20 h-20 rounded-2xl bg-gradient-to-br from-white/5 to-white/[0.02] border border-white/10 flex items-center justify-center mb-5", children: /* @__PURE__ */ jsx(MessageSquare, { className: "w-10 h-10 text-slate-500" }) }), - /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-slate-300 mb-2", children: "No replies yet" }), - /* @__PURE__ */ jsxs("p", { className: "text-sm text-slate-500 max-w-sm leading-relaxed", children: [ - "Paste a message above, select your preferred tone, and click", - /* @__PURE__ */ jsx("span", { className: "text-cyan-400 font-medium", children: ' "Generate Replies" ' }), - "to get AI-powered suggestions." - ] }), - /* @__PURE__ */ jsx("div", { className: "flex gap-2 mt-6", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx( - "div", - { - className: "w-2 h-2 rounded-full bg-slate-700", - style: { opacity: 0.3 + i * 0.2 } - }, - i - )) }) - ] }); - } - return /* @__PURE__ */ jsxs("section", { "aria-live": "polite", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ - /* @__PURE__ */ jsx(Sparkles, { className: "w-5 h-5 text-cyan-400" }), - /* @__PURE__ */ jsxs("h2", { className: "text-xl font-semibold text-slate-100", children: [ - replies.length, - " reply suggestion", - replies.length !== 1 ? "s" : "" - ] }) - ] }), - /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-500 bg-white/5 px-3 py-1 rounded-full", children: "Click to copy" }) - ] }), - /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: replies.map((reply, index) => /* @__PURE__ */ jsx( - ReplyCard, - { - reply, - tone, - isCopied: copiedId === reply.id, - onCopy: () => onCopy(reply.id, reply.content), - onSelect: () => onSelect(reply.id, reply.content), - animationDelay: index * 100 - }, - reply.id - )) }) - ] }); -} -const TONE_COLORS = { - professional: "bg-blue-500/20 text-blue-300", - friendly: "bg-emerald-500/20 text-emerald-300", - apologetic: "bg-amber-500/20 text-amber-300", - assertive: "bg-rose-500/20 text-rose-300", - neutral: "bg-slate-500/20 text-slate-300" -}; -function formatRelativeTime(dateString) { - const date = new Date(dateString); - const now = /* @__PURE__ */ new Date(); - const diffMs = now.getTime() - date.getTime(); - const diffMins = Math.floor(diffMs / 6e4); - const diffHours = Math.floor(diffMins / 60); - const diffDays = Math.floor(diffHours / 24); - if (diffMins < 1) return "Just now"; - if (diffMins < 60) return `${diffMins}m ago`; - if (diffHours < 24) return `${diffHours}h ago`; - if (diffDays < 7) return `${diffDays}d ago`; - return date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); -} -function truncate(text, maxLength) { - if (text.length <= maxLength) return text; - return text.slice(0, maxLength).trim() + "..."; -} -function HistoryItem({ - conversation, - onClick, - onDelete, - animationDelay = 0 -}) { - const replyCount = conversation.replies?.length || 0; - return /* @__PURE__ */ jsxs( - "div", - { - className: "group relative bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20\n rounded-xl p-3 cursor-pointer transition-all duration-200 animate-fade-in-up", - style: { animationDelay: `${animationDelay}ms`, animationFillMode: "both" }, - onClick, - role: "button", - tabIndex: 0, - onKeyDown: (e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - onClick(); - } - }, - children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [ - /* @__PURE__ */ jsx( - "span", - { - className: `px-2 py-0.5 rounded text-xs font-medium ${TONE_COLORS[conversation.tone]}`, - children: conversation.tone - } - ), - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ - /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-500", children: formatRelativeTime(conversation.created_at) }), - /* @__PURE__ */ jsx( - "button", - { - onClick: (e) => { - e.stopPropagation(); - onDelete(); - }, - className: "p-1.5 rounded-lg opacity-0 group-hover:opacity-100 hover:bg-rose-500/20\n text-slate-500 hover:text-rose-400 transition-all duration-200 cursor-pointer\n focus:outline-none focus:opacity-100 focus:ring-2 focus:ring-rose-500/50", - "aria-label": "Delete conversation", - children: /* @__PURE__ */ jsx(Trash2, { className: "w-3.5 h-3.5" }) - } - ) - ] }) - ] }), - /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-300 leading-relaxed mb-2", children: truncate(conversation.original_message, 80) }), - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-slate-500", children: [ - /* @__PURE__ */ jsx(MessageSquare, { className: "w-3.5 h-3.5" }), - /* @__PURE__ */ jsxs("span", { children: [ - replyCount, - " repl", - replyCount !== 1 ? "ies" : "y" - ] }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-cyan-400 opacity-0 group-hover:opacity-100 transition-opacity", children: [ - /* @__PURE__ */ jsx("span", { children: "Load" }), - /* @__PURE__ */ jsx(ChevronRight, { className: "w-3.5 h-3.5" }) - ] }) - ] }) - ] - } - ); -} -function HistoryDrawer({ - isOpen, - onClose, - history, - isLoading, - onSelectConversation, - onDeleteConversation -}) { - if (!isOpen) return null; - return /* @__PURE__ */ jsxs(Fragment, { children: [ - /* @__PURE__ */ jsx( - "div", - { - className: "fixed inset-0 bg-black/70 backdrop-blur-sm z-40 animate-backdrop-fade-in cursor-pointer", - onClick: onClose, - "aria-hidden": "true" - } - ), - /* @__PURE__ */ jsxs( - "aside", - { - className: "fixed top-0 right-0 h-full w-full sm:w-[400px] bg-slate-900/95 backdrop-blur-xl\n border-l border-white/10 shadow-2xl z-50 flex flex-col animate-slide-in-right", - role: "dialog", - "aria-modal": "true", - "aria-labelledby": "history-title", - children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-5 border-b border-white/10", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ - /* @__PURE__ */ jsx("div", { className: "p-2 rounded-xl bg-cyan-500/10 border border-cyan-500/20", children: /* @__PURE__ */ jsx(Clock, { className: "w-5 h-5 text-cyan-400" }) }), - /* @__PURE__ */ jsx("h2", { id: "history-title", className: "text-lg font-semibold text-slate-100", children: "History" }) - ] }), - /* @__PURE__ */ jsx( - "button", - { - onClick: onClose, - className: "p-2.5 rounded-xl hover:bg-white/10 text-slate-400 hover:text-slate-100\n transition-all duration-200 cursor-pointer\n focus:outline-none focus:ring-2 focus:ring-cyan-500/50", - "aria-label": "Close history", - children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" }) - } - ) - ] }), - /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-4", children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-48 text-slate-400", children: [ - /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin mb-3 text-cyan-400" }), - /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Loading history..." }) - ] }) : history.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-48 text-center", children: [ - /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(Clock, { className: "w-8 h-8 text-slate-600" }) }), - /* @__PURE__ */ jsx("p", { className: "text-slate-300 font-medium", children: "No history yet" }), - /* @__PURE__ */ jsx("p", { className: "text-slate-500 text-sm mt-1", children: "Your generated replies will appear here" }) - ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: history.map((conversation, index) => /* @__PURE__ */ jsx( - HistoryItem, - { - conversation, - onClick: () => onSelectConversation(conversation), - onDelete: () => onDeleteConversation(conversation.id), - animationDelay: index * 50 - }, - conversation.id - )) }) }), - history.length > 0 && /* @__PURE__ */ jsx("div", { className: "p-4 border-t border-white/10", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-slate-500 text-center", children: [ - history.length, - " conversation", - history.length !== 1 ? "s" : "", - " saved" - ] }) }) - ] - } - ) - ] }); -} -const createSsrRpc = (functionId, importer) => { - const url = "/_serverFn/" + functionId; - const fn = async (...args) => { - const serverFn = await getServerFnById(functionId); - return serverFn(...args); - }; - return Object.assign(fn, { - url, - functionId, - [TSS_SERVER_FUNCTION]: true - }); -}; -const getApiBase = createServerFn({ - method: "GET" -}).handler(createSsrRpc("cc35041ac8534be1df357d50dcecc9c79d69232d0d2da0a89a0d930737c7ec36")); -const api = { - /** - * Generate smart replies with SSE streaming - * Returns a fetch Response that can be streamed - */ - generateReplies: async (message, tone, context, intent, signal) => { - return fetch(`${await getApiBase()}/replies/generate`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - message, - tone, - context, - intent - }), - signal - }); - }, - /** - * Get conversation history - */ - getHistory: async (limit = 50) => { - const res = await fetch(`${await getApiBase()}/history?limit=${limit}`); - if (!res.ok) { - throw new Error("Failed to fetch history"); - } - const data = await res.json(); - return data.data; - }, - /** - * Get a single conversation by ID - */ - getConversation: async (id) => { - const res = await fetch(`${await getApiBase()}/history/${id}`); - if (!res.ok) { - throw new Error("Conversation not found"); - } - const data = await res.json(); - return data.data; - }, - /** - * Delete a conversation - */ - deleteConversation: async (id) => { - const res = await fetch(`${await getApiBase()}/history/${id}`, { - method: "DELETE" - }); - if (!res.ok) { - throw new Error("Failed to delete conversation"); - } - }, - /** - * Save generated replies to database - */ - saveReplies: async (conversationId, replies) => { - const res = await fetch(`${await getApiBase()}/replies/save`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - conversationId, - replies - }) - }); - if (!res.ok) { - throw new Error("Failed to save replies"); - } - }, - /** - * Health check - */ - healthCheck: async () => { - try { - const res = await fetch(`${await getApiBase()}.replace('/api', '')}/health`); - return res.ok; - } catch { - return false; - } - } -}; -function useSmartReply() { - const [isGenerating, setIsGenerating] = useState(false); - const [streamedContent, setStreamedContent] = useState(""); - const [replies, setReplies] = useState([]); - const [conversationId, setConversationId] = useState(null); - const [error, setError] = useState(null); - const [abortController, setAbortController] = useState(null); - const generate = useCallback(async (message, tone, context, intent) => { - setIsGenerating(true); - setReplies([]); - setConversationId(null); - setStreamedContent(""); - setError(null); - const controller = new AbortController(); - setAbortController(controller); - try { - const response = await api.generateReplies(message, tone, context, intent, controller.signal); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error || `HTTP ${response.status}`); - } - const convId = response.headers.get("X-Conversation-Id"); - if (convId) { - setConversationId(convId); - } - const reader = response.body?.getReader(); - if (!reader) { - throw new Error("No response body"); - } - const decoder = new TextDecoder(); - let accumulated = ""; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - const text = decoder.decode(value, { stream: true }); - const lines = text.split("\n"); - for (const line of lines) { - if (line.startsWith("data: ")) { - const data = line.slice(6).trim(); - if (data === "[DONE]") continue; - try { - const chunk = JSON.parse(data); - if (chunk.type === "text" && chunk.content) { - accumulated += chunk.content; - setStreamedContent(accumulated); - } else if (chunk.type === "content" && chunk.content) { - accumulated = chunk.content; - setStreamedContent(accumulated); - } else if (chunk.type === "error") { - throw new Error(chunk.error?.message || "Stream error"); - } - } catch (parseErr) { - if (data && !data.startsWith("{")) { - accumulated += data; - setStreamedContent(accumulated); - } - } - } - } - } - const parsedReplies = parseReplies(accumulated); - setReplies(parsedReplies); - if (convId && parsedReplies.length > 0) { - try { - await api.saveReplies(convId, parsedReplies.map((r) => r.content)); - } catch (e) { - console.error("Failed to save replies:", e); - } - } - } catch (err) { - if (err instanceof Error && err.name === "AbortError") { - return; - } - const errorMessage = err instanceof Error ? err.message : "Failed to generate replies"; - setError(errorMessage); - console.error("Generation error:", err); - } finally { - setIsGenerating(false); - setAbortController(null); - } - }, []); - const stop = useCallback(() => { - if (abortController) { - abortController.abort(); - setIsGenerating(false); - } - }, [abortController]); - const reset = useCallback(() => { - stop(); - setStreamedContent(""); - setReplies([]); - setConversationId(null); - setError(null); - }, [stop]); - return { - isGenerating, - streamedContent, - replies, - conversationId, - error, - generate, - stop, - reset, - setReplies - }; -} -function parseReplies(content) { - try { - const jsonMatch = content.match(/\[[\s\S]*\]/); - if (jsonMatch) { - const parsed = JSON.parse(jsonMatch[0]); - if (Array.isArray(parsed)) { - return parsed.slice(0, 3).map((text, index) => ({ - id: `reply-${Date.now()}-${index}`, - content: String(text), - reply_index: index - })); - } - } - } catch (e) { - console.error("Failed to parse replies:", e, content); - } - return []; -} -function useHistory() { - const [history, setHistory] = useState([]); - const [isOpen, setIsOpen] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - const fetchHistory = useCallback(async () => { - setIsLoading(true); - setError(null); - try { - const data = await api.getHistory(50); - setHistory(data); - } catch (err) { - const message = err instanceof Error ? err.message : "Failed to fetch history"; - setError(message); - console.error("History fetch error:", err); - } finally { - setIsLoading(false); - } - }, []); - const loadConversation = useCallback(async (id) => { - try { - return await api.getConversation(id); - } catch (err) { - console.error("Load conversation error:", err); - return null; - } - }, []); - const deleteConversation = useCallback(async (id) => { - try { - await api.deleteConversation(id); - setHistory((prev) => prev.filter((c) => c.id !== id)); - } catch (err) { - console.error("Delete conversation error:", err); - throw err; - } - }, []); - const refreshHistory = fetchHistory; - useEffect(() => { - if (isOpen) { - fetchHistory(); - } - }, [isOpen, fetchHistory]); - return { - history, - isOpen, - isLoading, - error, - setIsOpen, - fetchHistory, - loadConversation, - deleteConversation, - refreshHistory - }; -} -function SmartReplyApp() { - const [message, setMessage] = useState(""); - const [context, setContext] = useState(""); - const [intent, setIntent] = useState(""); - const [selectedTone, setSelectedTone] = useState("professional"); - const [copiedId, setCopiedId] = useState(null); - const { - isGenerating, - streamedContent, - replies, - error, - generate, - stop, - reset, - setReplies - } = useSmartReply(); - const { - history, - isOpen: isHistoryOpen, - isLoading: isHistoryLoading, - setIsOpen: setIsHistoryOpen, - deleteConversation, - refreshHistory - } = useHistory(); - const handleGenerate = useCallback(async () => { - if (!message.trim() || isGenerating) return; - await generate(message, selectedTone, context, intent); - refreshHistory(); - }, [message, selectedTone, context, intent, isGenerating, generate, refreshHistory]); - const handleCopy = useCallback(async (replyId, content) => { - try { - await navigator.clipboard.writeText(content); - setCopiedId(replyId); - setTimeout(() => setCopiedId(null), 2e3); - } catch (err) { - console.error("Failed to copy:", err); - } - }, []); - const handleSelect = useCallback(async (replyId, content) => { - await handleCopy(replyId, content); - }, [handleCopy]); - const handleLoadConversation = useCallback((conversation) => { - setMessage(conversation.original_message); - setContext(conversation.context || ""); - setIntent(conversation.intent || ""); - setSelectedTone(conversation.tone); - setReplies(conversation.replies); - setIsHistoryOpen(false); - }, [setReplies, setIsHistoryOpen]); - const handleDeleteConversation = useCallback(async (id) => { - try { - await deleteConversation(id); - } catch (err) { - console.error("Failed to delete:", err); - } - }, [deleteConversation]); - const handleReset = useCallback(() => { - setMessage(""); - setContext(""); - setIntent(""); - reset(); - }, [reset]); - return /* @__PURE__ */ jsxs("main", { className: "min-h-screen bg-slate-950 relative overflow-hidden", children: [ - /* @__PURE__ */ jsx(BackgroundEffects, {}), - /* @__PURE__ */ jsxs("div", { className: "relative z-10 max-w-2xl mx-auto px-4 sm:px-6 py-12 sm:py-16", children: [ - /* @__PURE__ */ jsxs("header", { className: "flex flex-col items-center text-center mb-10 animate-fade-in-up", children: [ - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [ - /* @__PURE__ */ jsx("div", { className: "p-3 rounded-2xl bg-gradient-to-br from-cyan-500/20 to-violet-600/20 border border-cyan-500/30 shadow-[0_0_30px_rgba(34,211,238,0.15)]", children: /* @__PURE__ */ jsx(Sparkles, { className: "w-8 h-8 text-cyan-400" }) }), - /* @__PURE__ */ jsx("h1", { className: "text-4xl sm:text-5xl font-bold tracking-tight bg-gradient-to-r from-cyan-400 via-blue-400 to-violet-400 bg-clip-text text-transparent", children: "Smart Reply" }) - ] }), - /* @__PURE__ */ jsx("p", { className: "text-slate-400 max-w-md text-base sm:text-lg leading-relaxed", children: "Provide context and intent to get AI-powered reply suggestions tailored to your needs." }), - /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mt-6", children: [ - /* @__PURE__ */ jsxs("button", { onClick: () => setIsHistoryOpen(true), className: "flex items-center gap-2 px-4 py-2.5 rounded-xl bg-white/5 border border-white/10\n text-slate-400 hover:text-slate-100 hover:bg-white/10 hover:border-white/20\n transition-all duration-200 text-sm font-medium cursor-pointer\n focus:outline-none focus:ring-2 focus:ring-cyan-500/50", "aria-label": "Open history", children: [ - /* @__PURE__ */ jsx(Clock, { className: "w-4 h-4" }), - /* @__PURE__ */ jsx("span", { children: "History" }) - ] }), - (message || context || intent || replies.length > 0) && /* @__PURE__ */ jsxs("button", { onClick: handleReset, className: "flex items-center gap-2 px-4 py-2.5 rounded-xl bg-white/5 border border-white/10\n text-slate-400 hover:text-slate-100 hover:bg-white/10 hover:border-white/20\n transition-all duration-200 text-sm font-medium cursor-pointer\n focus:outline-none focus:ring-2 focus:ring-cyan-500/50", "aria-label": "Reset form", children: [ - /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4" }), - /* @__PURE__ */ jsx("span", { children: "Reset" }) - ] }) - ] }) - ] }), - /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [ - /* @__PURE__ */ jsx(MessageInputCard, { message, context, intent, onMessageChange: setMessage, onContextChange: setContext, onIntentChange: setIntent, disabled: isGenerating }), - /* @__PURE__ */ jsx(ToneSelector, { selected: selectedTone, onChange: setSelectedTone, disabled: isGenerating }), - /* @__PURE__ */ jsx("div", { className: "flex justify-center pt-4", children: /* @__PURE__ */ jsx(GenerateButton, { onClick: handleGenerate, onStop: stop, isLoading: isGenerating, disabled: !message.trim() }) }), - error && /* @__PURE__ */ jsx("div", { className: "bg-rose-500/10 border border-rose-500/20 rounded-2xl p-5 animate-fade-in-up", children: /* @__PURE__ */ jsx("p", { className: "text-rose-400 text-center text-sm font-medium", children: error }) }), - /* @__PURE__ */ jsx(ReplyOptionsSection, { replies, isLoading: isGenerating, tone: selectedTone, copiedId, onCopy: handleCopy, onSelect: handleSelect, streamedContent }) - ] }), - /* @__PURE__ */ jsx("footer", { className: "mt-16 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-600", children: "Powered by AI" }) }) - ] }), - /* @__PURE__ */ jsx(HistoryDrawer, { isOpen: isHistoryOpen, onClose: () => setIsHistoryOpen(false), history, isLoading: isHistoryLoading, onSelectConversation: handleLoadConversation, onDeleteConversation: handleDeleteConversation }) - ] }); -} -export { - SmartReplyApp as component -}; diff --git a/tests/ag-ui-app/frontend/dist/server/assets/router-wq7tchx4.js b/tests/ag-ui-app/frontend/dist/server/assets/router-wq7tchx4.js deleted file mode 100644 index a2170e5c..00000000 --- a/tests/ag-ui-app/frontend/dist/server/assets/router-wq7tchx4.js +++ /dev/null @@ -1,210 +0,0 @@ -import { createRootRoute, HeadContent, Link, Scripts, createFileRoute, lazyRouteComponent, createRouter } from "@tanstack/react-router"; -import { jsx, Fragment, jsxs } from "react/jsx-runtime"; -import { useContext, createContext, useState, useEffect } from "react"; -const appCss = "/assets/styles-DnGqQgkE.css"; -const STORAGE_KEY = "syncable-agent-settings"; -const DEFAULT_SETTINGS = { - provider: "openai", - model: "gpt-5.2", - apiKey: "", - awsRegion: "us-east-1" -}; -const MODELS_BY_PROVIDER = { - openai: [ - { id: "gpt-5.2", name: "GPT-5.2 - Latest reasoning model (Dec 2025)" }, - { id: "gpt-5.2-mini", name: "GPT-5.2 Mini - Fast and affordable" }, - { id: "gpt-4o", name: "GPT-4o - Multimodal workhorse" }, - { id: "o1-preview", name: "o1-preview - Advanced reasoning" } - ], - anthropic: [ - { id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5 - Most capable (Nov 2025)" }, - { id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5 - Balanced (Sep 2025)" }, - { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5 - Fast (Oct 2025)" }, - { id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4 - Previous gen" } - ], - bedrock: [ - { id: "global.anthropic.claude-opus-4-5-20251101-v1:0", name: "Claude Opus 4.5 - Most capable (Nov 2025)" }, - { id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", name: "Claude Sonnet 4.5 - Balanced (Sep 2025)" }, - { id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", name: "Claude Haiku 4.5 - Fast (Oct 2025)" }, - { id: "global.anthropic.claude-sonnet-4-20250514-v1:0", name: "Claude Sonnet 4 - Previous gen" } - ] -}; -const AgentSettingsContext = createContext(null); -function AgentSettingsProvider({ children }) { - const [settings, setSettings] = useState(DEFAULT_SETTINGS); - useEffect(() => { - try { - const stored = localStorage.getItem(STORAGE_KEY); - if (stored) { - const parsed = JSON.parse(stored); - setSettings({ ...DEFAULT_SETTINGS, ...parsed }); - } - } catch (e) { - console.error("Failed to load agent settings:", e); - } - }, []); - useEffect(() => { - try { - localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); - } catch (e) { - console.error("Failed to save agent settings:", e); - } - }, [settings]); - const setProvider = (provider) => { - setSettings((prev) => ({ - ...prev, - provider, - model: MODELS_BY_PROVIDER[provider][0].id - // Reset to first model - })); - }; - const setModel = (model) => { - setSettings((prev) => ({ ...prev, model })); - }; - const setApiKey = (apiKey) => { - setSettings((prev) => ({ ...prev, apiKey })); - }; - const setAwsRegion = (awsRegion) => { - setSettings((prev) => ({ ...prev, awsRegion })); - }; - const availableModels = MODELS_BY_PROVIDER[settings.provider]; - return /* @__PURE__ */ jsx( - AgentSettingsContext.Provider, - { - value: { - settings, - setProvider, - setModel, - setApiKey, - setAwsRegion, - availableModels - }, - children - } - ); -} -function useAgentSettings() { - const context = useContext(AgentSettingsContext); - if (!context) { - throw new Error("useAgentSettings must be used within AgentSettingsProvider"); - } - return context; -} -const AGENT_URL = typeof window !== "undefined" ? "http://localhost:9090" : "http://localhost:9090"; -function CopilotKitInner({ children }) { - const [CopilotKit, setCopilotKit] = useState(null); - const { settings } = useAgentSettings(); - useEffect(() => { - Promise.all([ - import("@copilotkit/react-core"), - import("@copilotkit/react-ui/styles.css") - ]).then(([mod]) => { - setCopilotKit(() => mod.CopilotKit); - }); - }, []); - if (!CopilotKit) { - return /* @__PURE__ */ jsx(Fragment, { children }); - } - const forwardedProps = { - provider: settings.provider, - model: settings.model, - apiKey: settings.apiKey, - awsRegion: settings.awsRegion - }; - return /* @__PURE__ */ jsx( - CopilotKit, - { - runtimeUrl: AGENT_URL, - properties: forwardedProps, - agent: "syncable", - children - } - ); -} -function CopilotKitWrapper({ children }) { - return /* @__PURE__ */ jsx(AgentSettingsProvider, { children: /* @__PURE__ */ jsx(CopilotKitInner, { children }) }); -} -const Route$2 = createRootRoute({ - head: () => ({ - meta: [ - { charSet: "utf-8" }, - { name: "viewport", content: "width=device-width, initial-scale=1" }, - { title: "Smart Reply Generator" }, - { name: "description", content: "AI-powered reply suggestions for your messages" } - ], - links: [ - { rel: "stylesheet", href: appCss }, - { rel: "icon", href: "/favicon.ico" } - ] - }), - shellComponent: RootDocument -}); -function RootDocument({ children }) { - return /* @__PURE__ */ jsxs("html", { lang: "en", className: "dark", children: [ - /* @__PURE__ */ jsx("head", { children: /* @__PURE__ */ jsx(HeadContent, {}) }), - /* @__PURE__ */ jsxs("body", { className: "bg-slate-950 antialiased", children: [ - /* @__PURE__ */ jsxs("nav", { className: "fixed top-4 right-4 z-50 flex gap-2", children: [ - /* @__PURE__ */ jsx( - Link, - { - to: "/", - className: "px-3 py-1.5 text-xs font-medium rounded-lg bg-slate-800/80 text-slate-300 hover:bg-slate-700 hover:text-white border border-slate-700 backdrop-blur-sm transition-all", - activeProps: { className: "bg-cyan-600/20 text-cyan-400 border-cyan-500/30" }, - children: "Smart Reply" - } - ), - /* @__PURE__ */ jsx( - Link, - { - to: "/agent", - className: "px-3 py-1.5 text-xs font-medium rounded-lg bg-slate-800/80 text-slate-300 hover:bg-slate-700 hover:text-white border border-slate-700 backdrop-blur-sm transition-all", - activeProps: { className: "bg-emerald-600/20 text-emerald-400 border-emerald-500/30" }, - children: "Agent Chat" - } - ) - ] }), - /* @__PURE__ */ jsx(CopilotKitWrapper, { children }), - /* @__PURE__ */ jsx(Scripts, {}) - ] }) - ] }); -} -const $$splitComponentImporter$1 = () => import("./agent-M-s1KgCM.js"); -const Route$1 = createFileRoute("/agent")({ - component: lazyRouteComponent($$splitComponentImporter$1, "component") -}); -const $$splitComponentImporter = () => import("./index-Cv7xvle-.js"); -const Route = createFileRoute("/")({ - component: lazyRouteComponent($$splitComponentImporter, "component") -}); -const AgentRoute = Route$1.update({ - id: "/agent", - path: "/agent", - getParentRoute: () => Route$2 -}); -const IndexRoute = Route.update({ - id: "/", - path: "/", - getParentRoute: () => Route$2 -}); -const rootRouteChildren = { - IndexRoute, - AgentRoute -}; -const routeTree = Route$2._addFileChildren(rootRouteChildren)._addFileTypes(); -const getRouter = () => { - const router2 = createRouter({ - routeTree, - context: {}, - scrollRestoration: true, - defaultPreloadStaleTime: 0 - }); - return router2; -}; -const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - getRouter -}, Symbol.toStringTag, { value: "Module" })); -export { - router as r, - useAgentSettings as u -}; diff --git a/tests/ag-ui-app/frontend/dist/server/assets/start-HYkvq4Ni.js b/tests/ag-ui-app/frontend/dist/server/assets/start-HYkvq4Ni.js deleted file mode 100644 index 61f341ad..00000000 --- a/tests/ag-ui-app/frontend/dist/server/assets/start-HYkvq4Ni.js +++ /dev/null @@ -1,4 +0,0 @@ -const startInstance = void 0; -export { - startInstance -}; diff --git a/tests/ag-ui-app/frontend/dist/server/server.js b/tests/ag-ui-app/frontend/dist/server/server.js deleted file mode 100644 index d29aed19..00000000 --- a/tests/ag-ui-app/frontend/dist/server/server.js +++ /dev/null @@ -1,1256 +0,0 @@ -import { createMemoryHistory } from "@tanstack/history"; -import { mergeHeaders } from "@tanstack/router-core/ssr/client"; -import { parseRedirect, isRedirect, defaultSerovalPlugins, makeSerovalPlugin, rootRouteId, createRawStreamRPCPlugin, isNotFound, createSerializationAdapter, isResolvedRedirect, executeRewriteInput } from "@tanstack/router-core"; -import { AsyncLocalStorage } from "node:async_hooks"; -import { getOrigin, attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server"; -import { H3Event, toResponse } from "h3-v2"; -import invariant from "tiny-invariant"; -import { toCrossJSONStream, fromJSON, toCrossJSONAsync } from "seroval"; -import { jsx } from "react/jsx-runtime"; -import { defineHandlerCallback, renderRouterToStream } from "@tanstack/react-router/ssr/server"; -import { RouterProvider } from "@tanstack/react-router"; -function StartServer(props) { - return /* @__PURE__ */ jsx(RouterProvider, { router: props.router }); -} -const defaultStreamHandler = defineHandlerCallback( - ({ request, router, responseHeaders }) => renderRouterToStream({ - request, - router, - responseHeaders, - children: /* @__PURE__ */ jsx(StartServer, { router }) - }) -); -const TSS_FORMDATA_CONTEXT = "__TSS_CONTEXT"; -const TSS_SERVER_FUNCTION = /* @__PURE__ */ Symbol.for("TSS_SERVER_FUNCTION"); -const TSS_SERVER_FUNCTION_FACTORY = /* @__PURE__ */ Symbol.for( - "TSS_SERVER_FUNCTION_FACTORY" -); -const X_TSS_SERIALIZED = "x-tss-serialized"; -const X_TSS_RAW_RESPONSE = "x-tss-raw"; -const TSS_CONTENT_TYPE_FRAMED = "application/x-tss-framed"; -const FrameType = { - /** Seroval JSON chunk (NDJSON line) */ - JSON: 0, - /** Raw stream data chunk */ - CHUNK: 1, - /** Raw stream end (EOF) */ - END: 2, - /** Raw stream error */ - ERROR: 3 -}; -const FRAME_HEADER_SIZE = 9; -const TSS_FRAMED_PROTOCOL_VERSION = 1; -const TSS_CONTENT_TYPE_FRAMED_VERSIONED = `${TSS_CONTENT_TYPE_FRAMED}; v=${TSS_FRAMED_PROTOCOL_VERSION}`; -const GLOBAL_STORAGE_KEY = /* @__PURE__ */ Symbol.for("tanstack-start:start-storage-context"); -const globalObj$1 = globalThis; -if (!globalObj$1[GLOBAL_STORAGE_KEY]) { - globalObj$1[GLOBAL_STORAGE_KEY] = new AsyncLocalStorage(); -} -const startStorage = globalObj$1[GLOBAL_STORAGE_KEY]; -async function runWithStartContext(context, fn) { - return startStorage.run(context, fn); -} -function getStartContext(opts) { - const context = startStorage.getStore(); - if (!context && opts?.throwIfNotFound !== false) { - throw new Error( - `No Start context found in AsyncLocalStorage. Make sure you are using the function within the server runtime.` - ); - } - return context; -} -const getStartOptions = () => getStartContext().startOptions; -const getStartContextServerOnly = getStartContext; -function isSafeKey(key) { - return key !== "__proto__" && key !== "constructor" && key !== "prototype"; -} -function safeObjectMerge(target, source) { - const result = /* @__PURE__ */ Object.create(null); - if (target) { - for (const key of Object.keys(target)) { - if (isSafeKey(key)) result[key] = target[key]; - } - } - if (source && typeof source === "object") { - for (const key of Object.keys(source)) { - if (isSafeKey(key)) result[key] = source[key]; - } - } - return result; -} -function createNullProtoObject(source) { - if (!source) return /* @__PURE__ */ Object.create(null); - const obj = /* @__PURE__ */ Object.create(null); - for (const key of Object.keys(source)) { - if (isSafeKey(key)) obj[key] = source[key]; - } - return obj; -} -const createServerFn = (options, __opts) => { - const resolvedOptions = __opts || options || {}; - if (typeof resolvedOptions.method === "undefined") { - resolvedOptions.method = "GET"; - } - const res = { - options: resolvedOptions, - middleware: (middleware) => { - const newMiddleware = [...resolvedOptions.middleware || []]; - middleware.map((m) => { - if (TSS_SERVER_FUNCTION_FACTORY in m) { - if (m.options.middleware) { - newMiddleware.push(...m.options.middleware); - } - } else { - newMiddleware.push(m); - } - }); - const newOptions = { - ...resolvedOptions, - middleware: newMiddleware - }; - const res2 = createServerFn(void 0, newOptions); - res2[TSS_SERVER_FUNCTION_FACTORY] = true; - return res2; - }, - inputValidator: (inputValidator) => { - const newOptions = { ...resolvedOptions, inputValidator }; - return createServerFn(void 0, newOptions); - }, - handler: (...args) => { - const [extractedFn, serverFn] = args; - const newOptions = { ...resolvedOptions, extractedFn, serverFn }; - const resolvedMiddleware = [ - ...newOptions.middleware || [], - serverFnBaseToMiddleware(newOptions) - ]; - return Object.assign( - async (opts) => { - const result = await executeMiddleware$1(resolvedMiddleware, "client", { - ...extractedFn, - ...newOptions, - data: opts?.data, - headers: opts?.headers, - signal: opts?.signal, - context: createNullProtoObject() - }); - const redirect = parseRedirect(result.error); - if (redirect) { - throw redirect; - } - if (result.error) throw result.error; - return result.result; - }, - { - // This copies over the URL, function ID - ...extractedFn, - // The extracted function on the server-side calls - // this function - __executeServer: async (opts, signal) => { - const startContext = getStartContextServerOnly(); - const serverContextAfterGlobalMiddlewares = startContext.contextAfterGlobalMiddlewares; - const ctx = { - ...extractedFn, - ...opts, - context: safeObjectMerge( - serverContextAfterGlobalMiddlewares, - opts.context - ), - signal, - request: startContext.request - }; - const result = await executeMiddleware$1( - resolvedMiddleware, - "server", - ctx - ).then((d) => ({ - // Only send the result and sendContext back to the client - result: d.result, - error: d.error, - context: d.sendContext - })); - return result; - } - } - ); - } - }; - const fun = (options2) => { - const newOptions = { - ...resolvedOptions, - ...options2 - }; - return createServerFn(void 0, newOptions); - }; - return Object.assign(fun, res); -}; -async function executeMiddleware$1(middlewares, env, opts) { - const globalMiddlewares = getStartOptions()?.functionMiddleware || []; - let flattenedMiddlewares = flattenMiddlewares([ - ...globalMiddlewares, - ...middlewares - ]); - if (env === "server") { - const startContext = getStartContextServerOnly({ throwIfNotFound: false }); - if (startContext?.executedRequestMiddlewares) { - flattenedMiddlewares = flattenedMiddlewares.filter( - (m) => !startContext.executedRequestMiddlewares.has(m) - ); - } - } - const callNextMiddleware = async (ctx) => { - const nextMiddleware = flattenedMiddlewares.shift(); - if (!nextMiddleware) { - return ctx; - } - try { - if ("inputValidator" in nextMiddleware.options && nextMiddleware.options.inputValidator && env === "server") { - ctx.data = await execValidator( - nextMiddleware.options.inputValidator, - ctx.data - ); - } - let middlewareFn = void 0; - if (env === "client") { - if ("client" in nextMiddleware.options) { - middlewareFn = nextMiddleware.options.client; - } - } else if ("server" in nextMiddleware.options) { - middlewareFn = nextMiddleware.options.server; - } - if (middlewareFn) { - const userNext = async (userCtx = {}) => { - const nextCtx = { - ...ctx, - ...userCtx, - context: safeObjectMerge(ctx.context, userCtx.context), - sendContext: safeObjectMerge(ctx.sendContext, userCtx.sendContext), - headers: mergeHeaders(ctx.headers, userCtx.headers), - result: userCtx.result !== void 0 ? userCtx.result : userCtx instanceof Response ? userCtx : ctx.result, - error: userCtx.error ?? ctx.error - }; - try { - return await callNextMiddleware(nextCtx); - } catch (error) { - return { - ...nextCtx, - error - }; - } - }; - const result = await middlewareFn({ - ...ctx, - next: userNext - }); - if (isRedirect(result)) { - return { - ...ctx, - error: result - }; - } - if (result instanceof Response) { - return { - ...ctx, - result - }; - } - if (!result) { - throw new Error( - "User middleware returned undefined. You must call next() or return a result in your middlewares." - ); - } - return result; - } - return callNextMiddleware(ctx); - } catch (error) { - return { - ...ctx, - error - }; - } - }; - return callNextMiddleware({ - ...opts, - headers: opts.headers || {}, - sendContext: opts.sendContext || {}, - context: opts.context || createNullProtoObject() - }); -} -function flattenMiddlewares(middlewares, maxDepth = 100) { - const seen = /* @__PURE__ */ new Set(); - const flattened = []; - const recurse = (middleware, depth) => { - if (depth > maxDepth) { - throw new Error( - `Middleware nesting depth exceeded maximum of ${maxDepth}. Check for circular references.` - ); - } - middleware.forEach((m) => { - if (m.options.middleware) { - recurse(m.options.middleware, depth + 1); - } - if (!seen.has(m)) { - seen.add(m); - flattened.push(m); - } - }); - }; - recurse(middlewares, 0); - return flattened; -} -async function execValidator(validator, input) { - if (validator == null) return {}; - if ("~standard" in validator) { - const result = await validator["~standard"].validate(input); - if (result.issues) - throw new Error(JSON.stringify(result.issues, void 0, 2)); - return result.value; - } - if ("parse" in validator) { - return validator.parse(input); - } - if (typeof validator === "function") { - return validator(input); - } - throw new Error("Invalid validator type!"); -} -function serverFnBaseToMiddleware(options) { - return { - "~types": void 0, - options: { - inputValidator: options.inputValidator, - client: async ({ next, sendContext, ...ctx }) => { - const payload = { - ...ctx, - // switch the sendContext over to context - context: sendContext - }; - const res = await options.extractedFn?.(payload); - return next(res); - }, - server: async ({ next, ...ctx }) => { - const result = await options.serverFn?.(ctx); - return next({ - ...ctx, - result - }); - } - } - }; -} -function getDefaultSerovalPlugins() { - const start = getStartOptions(); - const adapters = start?.serializationAdapters; - return [ - ...adapters?.map(makeSerovalPlugin) ?? [], - ...defaultSerovalPlugins - ]; -} -const GLOBAL_EVENT_STORAGE_KEY = /* @__PURE__ */ Symbol.for("tanstack-start:event-storage"); -const globalObj = globalThis; -if (!globalObj[GLOBAL_EVENT_STORAGE_KEY]) { - globalObj[GLOBAL_EVENT_STORAGE_KEY] = new AsyncLocalStorage(); -} -const eventStorage = globalObj[GLOBAL_EVENT_STORAGE_KEY]; -function isPromiseLike(value) { - return typeof value.then === "function"; -} -function getSetCookieValues(headers) { - const headersWithSetCookie = headers; - if (typeof headersWithSetCookie.getSetCookie === "function") { - return headersWithSetCookie.getSetCookie(); - } - const value = headers.get("set-cookie"); - return value ? [value] : []; -} -function mergeEventResponseHeaders(response, event) { - if (response.ok) { - return; - } - const eventSetCookies = getSetCookieValues(event.res.headers); - if (eventSetCookies.length === 0) { - return; - } - const responseSetCookies = getSetCookieValues(response.headers); - response.headers.delete("set-cookie"); - for (const cookie of responseSetCookies) { - response.headers.append("set-cookie", cookie); - } - for (const cookie of eventSetCookies) { - response.headers.append("set-cookie", cookie); - } -} -function attachResponseHeaders(value, event) { - if (isPromiseLike(value)) { - return value.then((resolved) => { - if (resolved instanceof Response) { - mergeEventResponseHeaders(resolved, event); - } - return resolved; - }); - } - if (value instanceof Response) { - mergeEventResponseHeaders(value, event); - } - return value; -} -function requestHandler(handler) { - return (request, requestOpts) => { - const h3Event = new H3Event(request); - const response = eventStorage.run( - { h3Event }, - () => handler(request, requestOpts) - ); - return toResponse(attachResponseHeaders(response, h3Event), h3Event); - }; -} -function getH3Event() { - const event = eventStorage.getStore(); - if (!event) { - throw new Error( - `No StartEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.` - ); - } - return event.h3Event; -} -function getResponse() { - const event = getH3Event(); - return event.res; -} -async function getStartManifest() { - const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-Bh9ReuAT.js"); - const startManifest = tsrStartManifest(); - const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {}; - rootRoute.assets = rootRoute.assets || []; - let script = `import('${startManifest.clientEntry}')`; - rootRoute.assets.push({ - tag: "script", - attrs: { - type: "module", - async: true - }, - children: script - }); - const manifest2 = { - routes: Object.fromEntries( - Object.entries(startManifest.routes).flatMap(([k, v]) => { - const result = {}; - let hasData = false; - if (v.preloads && v.preloads.length > 0) { - result["preloads"] = v.preloads; - hasData = true; - } - if (v.assets && v.assets.length > 0) { - result["assets"] = v.assets; - hasData = true; - } - if (!hasData) { - return []; - } - return [[k, result]]; - }) - ) - }; - return manifest2; -} -const textEncoder$1 = new TextEncoder(); -const EMPTY_PAYLOAD = new Uint8Array(0); -function encodeFrame(type, streamId, payload) { - const frame = new Uint8Array(FRAME_HEADER_SIZE + payload.length); - frame[0] = type; - frame[1] = streamId >>> 24 & 255; - frame[2] = streamId >>> 16 & 255; - frame[3] = streamId >>> 8 & 255; - frame[4] = streamId & 255; - frame[5] = payload.length >>> 24 & 255; - frame[6] = payload.length >>> 16 & 255; - frame[7] = payload.length >>> 8 & 255; - frame[8] = payload.length & 255; - frame.set(payload, FRAME_HEADER_SIZE); - return frame; -} -function encodeJSONFrame(json) { - return encodeFrame(FrameType.JSON, 0, textEncoder$1.encode(json)); -} -function encodeChunkFrame(streamId, chunk) { - return encodeFrame(FrameType.CHUNK, streamId, chunk); -} -function encodeEndFrame(streamId) { - return encodeFrame(FrameType.END, streamId, EMPTY_PAYLOAD); -} -function encodeErrorFrame(streamId, error) { - const message = error instanceof Error ? error.message : String(error ?? "Unknown error"); - return encodeFrame(FrameType.ERROR, streamId, textEncoder$1.encode(message)); -} -function createMultiplexedStream(jsonStream, rawStreams) { - let activePumps = 1 + rawStreams.size; - let controllerRef = null; - let cancelled = false; - const cancelReaders = []; - const safeEnqueue = (chunk) => { - if (cancelled || !controllerRef) return; - try { - controllerRef.enqueue(chunk); - } catch { - } - }; - const safeError = (err) => { - if (cancelled || !controllerRef) return; - try { - controllerRef.error(err); - } catch { - } - }; - const safeClose = () => { - if (cancelled || !controllerRef) return; - try { - controllerRef.close(); - } catch { - } - }; - const checkComplete = () => { - activePumps--; - if (activePumps === 0) { - safeClose(); - } - }; - return new ReadableStream({ - start(controller) { - controllerRef = controller; - cancelReaders.length = 0; - const pumpJSON = async () => { - const reader = jsonStream.getReader(); - cancelReaders.push(() => { - reader.cancel().catch(() => { - }); - }); - try { - while (true) { - const { done, value } = await reader.read(); - if (cancelled) break; - if (done) break; - safeEnqueue(encodeJSONFrame(value)); - } - } catch (error) { - safeError(error); - } finally { - reader.releaseLock(); - checkComplete(); - } - }; - const pumpRawStream = async (streamId, stream) => { - const reader = stream.getReader(); - cancelReaders.push(() => { - reader.cancel().catch(() => { - }); - }); - try { - while (true) { - const { done, value } = await reader.read(); - if (cancelled) break; - if (done) { - safeEnqueue(encodeEndFrame(streamId)); - break; - } - safeEnqueue(encodeChunkFrame(streamId, value)); - } - } catch (error) { - safeEnqueue(encodeErrorFrame(streamId, error)); - } finally { - reader.releaseLock(); - checkComplete(); - } - }; - pumpJSON(); - for (const [streamId, stream] of rawStreams) { - pumpRawStream(streamId, stream); - } - }, - cancel() { - cancelled = true; - controllerRef = null; - for (const cancelReader of cancelReaders) { - cancelReader(); - } - cancelReaders.length = 0; - } - }); -} -const manifest = { "cc35041ac8534be1df357d50dcecc9c79d69232d0d2da0a89a0d930737c7ec36": { - functionName: "getApiBase_createServerFn_handler", - importer: () => import("./assets/api-C6MFPO9a.js") -} }; -async function getServerFnById(id) { - const serverFnInfo = manifest[id]; - if (!serverFnInfo) { - throw new Error("Server function info not found for " + id); - } - const fnModule = await serverFnInfo.importer(); - if (!fnModule) { - console.info("serverFnInfo", serverFnInfo); - throw new Error("Server function module not resolved for " + id); - } - const action = fnModule[serverFnInfo.functionName]; - if (!action) { - console.info("serverFnInfo", serverFnInfo); - console.info("fnModule", fnModule); - throw new Error( - `Server function module export not resolved for serverFn ID: ${id}` - ); - } - return action; -} -let serovalPlugins = void 0; -const textEncoder = new TextEncoder(); -const FORM_DATA_CONTENT_TYPES = [ - "multipart/form-data", - "application/x-www-form-urlencoded" -]; -const MAX_PAYLOAD_SIZE = 1e6; -const handleServerAction = async ({ - request, - context, - serverFnId -}) => { - const controller = new AbortController(); - const signal = controller.signal; - const abort = () => controller.abort(); - request.signal.addEventListener("abort", abort); - const method = request.method; - const methodLower = method.toLowerCase(); - const url = new URL(request.url); - const action = await getServerFnById(serverFnId); - const isServerFn = request.headers.get("x-tsr-serverFn") === "true"; - if (!serovalPlugins) { - serovalPlugins = getDefaultSerovalPlugins(); - } - const contentType = request.headers.get("Content-Type"); - function parsePayload(payload) { - const parsedPayload = fromJSON(payload, { plugins: serovalPlugins }); - return parsedPayload; - } - const response = await (async () => { - try { - let serializeResult = function(res2) { - let nonStreamingBody = void 0; - const alsResponse = getResponse(); - if (res2 !== void 0) { - const rawStreams = /* @__PURE__ */ new Map(); - const rawStreamPlugin = createRawStreamRPCPlugin( - (id, stream2) => { - rawStreams.set(id, stream2); - } - ); - const plugins = [rawStreamPlugin, ...serovalPlugins || []]; - let done = false; - const callbacks = { - onParse: (value) => { - nonStreamingBody = value; - }, - onDone: () => { - done = true; - }, - onError: (error) => { - throw error; - } - }; - toCrossJSONStream(res2, { - refs: /* @__PURE__ */ new Map(), - plugins, - onParse(value) { - callbacks.onParse(value); - }, - onDone() { - callbacks.onDone(); - }, - onError: (error) => { - callbacks.onError(error); - } - }); - if (done && rawStreams.size === 0) { - return new Response( - nonStreamingBody ? JSON.stringify(nonStreamingBody) : void 0, - { - status: alsResponse.status, - statusText: alsResponse.statusText, - headers: { - "Content-Type": "application/json", - [X_TSS_SERIALIZED]: "true" - } - } - ); - } - if (rawStreams.size > 0) { - const jsonStream = new ReadableStream({ - start(controller2) { - callbacks.onParse = (value) => { - controller2.enqueue(JSON.stringify(value) + "\n"); - }; - callbacks.onDone = () => { - try { - controller2.close(); - } catch { - } - }; - callbacks.onError = (error) => controller2.error(error); - if (nonStreamingBody !== void 0) { - callbacks.onParse(nonStreamingBody); - } - } - }); - const multiplexedStream = createMultiplexedStream( - jsonStream, - rawStreams - ); - return new Response(multiplexedStream, { - status: alsResponse.status, - statusText: alsResponse.statusText, - headers: { - "Content-Type": TSS_CONTENT_TYPE_FRAMED_VERSIONED, - [X_TSS_SERIALIZED]: "true" - } - }); - } - const stream = new ReadableStream({ - start(controller2) { - callbacks.onParse = (value) => controller2.enqueue( - textEncoder.encode(JSON.stringify(value) + "\n") - ); - callbacks.onDone = () => { - try { - controller2.close(); - } catch (error) { - controller2.error(error); - } - }; - callbacks.onError = (error) => controller2.error(error); - if (nonStreamingBody !== void 0) { - callbacks.onParse(nonStreamingBody); - } - } - }); - return new Response(stream, { - status: alsResponse.status, - statusText: alsResponse.statusText, - headers: { - "Content-Type": "application/x-ndjson", - [X_TSS_SERIALIZED]: "true" - } - }); - } - return new Response(void 0, { - status: alsResponse.status, - statusText: alsResponse.statusText - }); - }; - let res = await (async () => { - if (FORM_DATA_CONTENT_TYPES.some( - (type) => contentType && contentType.includes(type) - )) { - invariant( - methodLower !== "get", - "GET requests with FormData payloads are not supported" - ); - const formData = await request.formData(); - const serializedContext = formData.get(TSS_FORMDATA_CONTEXT); - formData.delete(TSS_FORMDATA_CONTEXT); - const params = { - context, - data: formData - }; - if (typeof serializedContext === "string") { - try { - const parsedContext = JSON.parse(serializedContext); - const deserializedContext = fromJSON(parsedContext, { - plugins: serovalPlugins - }); - if (typeof deserializedContext === "object" && deserializedContext) { - params.context = safeObjectMerge( - context, - deserializedContext - ); - } - } catch (e) { - if (process.env.NODE_ENV === "development") { - console.warn("Failed to parse FormData context:", e); - } - } - } - return await action(params, signal); - } - if (methodLower === "get") { - const payloadParam = url.searchParams.get("payload"); - if (payloadParam && payloadParam.length > MAX_PAYLOAD_SIZE) { - throw new Error("Payload too large"); - } - const payload2 = payloadParam ? parsePayload(JSON.parse(payloadParam)) : {}; - payload2.context = safeObjectMerge(context, payload2.context); - return await action(payload2, signal); - } - if (methodLower !== "post") { - throw new Error("expected POST method"); - } - let jsonPayload; - if (contentType?.includes("application/json")) { - jsonPayload = await request.json(); - } - const payload = jsonPayload ? parsePayload(jsonPayload) : {}; - payload.context = safeObjectMerge(payload.context, context); - return await action(payload, signal); - })(); - const unwrapped = res.result || res.error; - if (isNotFound(res)) { - res = isNotFoundResponse(res); - } - if (!isServerFn) { - return unwrapped; - } - if (unwrapped instanceof Response) { - if (isRedirect(unwrapped)) { - return unwrapped; - } - unwrapped.headers.set(X_TSS_RAW_RESPONSE, "true"); - return unwrapped; - } - return serializeResult(res); - } catch (error) { - if (error instanceof Response) { - return error; - } - if (isNotFound(error)) { - return isNotFoundResponse(error); - } - console.info(); - console.info("Server Fn Error!"); - console.info(); - console.error(error); - console.info(); - const serializedError = JSON.stringify( - await Promise.resolve( - toCrossJSONAsync(error, { - refs: /* @__PURE__ */ new Map(), - plugins: serovalPlugins - }) - ) - ); - const response2 = getResponse(); - return new Response(serializedError, { - status: response2.status ?? 500, - statusText: response2.statusText, - headers: { - "Content-Type": "application/json", - [X_TSS_SERIALIZED]: "true" - } - }); - } - })(); - request.signal.removeEventListener("abort", abort); - return response; -}; -function isNotFoundResponse(error) { - const { headers, ...rest } = error; - return new Response(JSON.stringify(rest), { - status: 404, - headers: { - "Content-Type": "application/json", - ...headers || {} - } - }); -} -const HEADERS = { - TSS_SHELL: "X-TSS_SHELL" -}; -const createServerRpc = (functionId, splitImportFn) => { - return Object.assign(splitImportFn, { - functionId, - [TSS_SERVER_FUNCTION]: true - }); -}; -const ServerFunctionSerializationAdapter = createSerializationAdapter({ - key: "$TSS/serverfn", - test: (v) => { - if (typeof v !== "function") return false; - if (!(TSS_SERVER_FUNCTION in v)) return false; - return !!v[TSS_SERVER_FUNCTION]; - }, - toSerializable: ({ functionId }) => ({ functionId }), - fromSerializable: ({ functionId }) => { - const fn = async (opts, signal) => { - const serverFn = await getServerFnById(functionId); - const result = await serverFn(opts ?? {}, signal); - return result.result; - }; - return createServerRpc(functionId, fn); - } -}); -function getStartResponseHeaders(opts) { - const headers = mergeHeaders( - { - "Content-Type": "text/html; charset=utf-8" - }, - ...opts.router.state.matches.map((match) => { - return match.headers; - }) - ); - return headers; -} -let entriesPromise; -let manifestPromise; -async function loadEntries() { - const routerEntry = await import("./assets/router-wq7tchx4.js").then((n) => n.r); - const startEntry = await import("./assets/start-HYkvq4Ni.js"); - return { startEntry, routerEntry }; -} -function getEntries() { - if (!entriesPromise) { - entriesPromise = loadEntries(); - } - return entriesPromise; -} -function getManifest() { - if (!manifestPromise) { - manifestPromise = getStartManifest(); - } - return manifestPromise; -} -const ROUTER_BASEPATH = "/"; -const SERVER_FN_BASE = "/_serverFn/"; -const IS_PRERENDERING = process.env.TSS_PRERENDERING === "true"; -const IS_SHELL_ENV = process.env.TSS_SHELL === "true"; -const IS_DEV = process.env.NODE_ENV === "development"; -const ERR_NO_RESPONSE = IS_DEV ? `It looks like you forgot to return a response from your server route handler. If you want to defer to the app router, make sure to have a component set in this route.` : "Internal Server Error"; -const ERR_NO_DEFER = IS_DEV ? `You cannot defer to the app router if there is no component defined on this route.` : "Internal Server Error"; -function throwRouteHandlerError() { - throw new Error(ERR_NO_RESPONSE); -} -function throwIfMayNotDefer() { - throw new Error(ERR_NO_DEFER); -} -function isSpecialResponse(value) { - return value instanceof Response || isRedirect(value); -} -function handleCtxResult(result) { - if (isSpecialResponse(result)) { - return { response: result }; - } - return result; -} -function executeMiddleware(middlewares, ctx) { - let index = -1; - const next = async (nextCtx) => { - if (nextCtx) { - if (nextCtx.context) { - ctx.context = safeObjectMerge(ctx.context, nextCtx.context); - } - for (const key of Object.keys(nextCtx)) { - if (key !== "context") { - ctx[key] = nextCtx[key]; - } - } - } - index++; - const middleware = middlewares[index]; - if (!middleware) return ctx; - let result; - try { - result = await middleware({ ...ctx, next }); - } catch (err) { - if (isSpecialResponse(err)) { - ctx.response = err; - return ctx; - } - throw err; - } - const normalized = handleCtxResult(result); - if (normalized) { - if (normalized.response !== void 0) { - ctx.response = normalized.response; - } - if (normalized.context) { - ctx.context = safeObjectMerge(ctx.context, normalized.context); - } - } - return ctx; - }; - return next(); -} -function handlerToMiddleware(handler, mayDefer = false) { - if (mayDefer) { - return handler; - } - return async (ctx) => { - const response = await handler({ ...ctx, next: throwIfMayNotDefer }); - if (!response) { - throwRouteHandlerError(); - } - return response; - }; -} -function createStartHandler(cb) { - const startRequestResolver = async (request, requestOpts) => { - let router = null; - let cbWillCleanup = false; - try { - const url = new URL(request.url); - const href = url.href.replace(url.origin, ""); - const origin = getOrigin(request); - const entries = await getEntries(); - const startOptions = await entries.startEntry.startInstance?.getOptions() || {}; - const serializationAdapters = [ - ...startOptions.serializationAdapters || [], - ServerFunctionSerializationAdapter - ]; - const requestStartOptions = { - ...startOptions, - serializationAdapters - }; - const flattenedRequestMiddlewares = startOptions.requestMiddleware ? flattenMiddlewares(startOptions.requestMiddleware) : []; - const executedRequestMiddlewares = new Set( - flattenedRequestMiddlewares - ); - const getRouter = async () => { - if (router) return router; - router = await entries.routerEntry.getRouter(); - let isShell = IS_SHELL_ENV; - if (IS_PRERENDERING && !isShell) { - isShell = request.headers.get(HEADERS.TSS_SHELL) === "true"; - } - const history = createMemoryHistory({ - initialEntries: [href] - }); - router.update({ - history, - isShell, - isPrerendering: IS_PRERENDERING, - origin: router.options.origin ?? origin, - ...{ - defaultSsr: requestStartOptions.defaultSsr, - serializationAdapters: [ - ...requestStartOptions.serializationAdapters, - ...router.options.serializationAdapters || [] - ] - }, - basepath: ROUTER_BASEPATH - }); - return router; - }; - if (SERVER_FN_BASE && url.pathname.startsWith(SERVER_FN_BASE)) { - const serverFnId = url.pathname.slice(SERVER_FN_BASE.length).split("/")[0]; - if (!serverFnId) { - throw new Error("Invalid server action param for serverFnId"); - } - const serverFnHandler = async ({ context }) => { - return runWithStartContext( - { - getRouter, - startOptions: requestStartOptions, - contextAfterGlobalMiddlewares: context, - request, - executedRequestMiddlewares - }, - () => handleServerAction({ - request, - context: requestOpts?.context, - serverFnId - }) - ); - }; - const middlewares2 = flattenedRequestMiddlewares.map( - (d) => d.options.server - ); - const ctx2 = await executeMiddleware([...middlewares2, serverFnHandler], { - request, - context: createNullProtoObject(requestOpts?.context) - }); - return handleRedirectResponse(ctx2.response, request, getRouter); - } - const executeRouter = async (serverContext) => { - const acceptHeader = request.headers.get("Accept") || "*/*"; - const acceptParts = acceptHeader.split(","); - const supportedMimeTypes = ["*/*", "text/html"]; - const isSupported = supportedMimeTypes.some( - (mimeType) => acceptParts.some((part) => part.trim().startsWith(mimeType)) - ); - if (!isSupported) { - return Response.json( - { error: "Only HTML requests are supported here" }, - { status: 500 } - ); - } - const manifest2 = await getManifest(); - const routerInstance = await getRouter(); - attachRouterServerSsrUtils({ - router: routerInstance, - manifest: manifest2 - }); - routerInstance.update({ additionalContext: { serverContext } }); - await routerInstance.load(); - if (routerInstance.state.redirect) { - return routerInstance.state.redirect; - } - await routerInstance.serverSsr.dehydrate(); - const responseHeaders = getStartResponseHeaders({ - router: routerInstance - }); - cbWillCleanup = true; - return cb({ - request, - router: routerInstance, - responseHeaders - }); - }; - const requestHandlerMiddleware = async ({ context }) => { - return runWithStartContext( - { - getRouter, - startOptions: requestStartOptions, - contextAfterGlobalMiddlewares: context, - request, - executedRequestMiddlewares - }, - async () => { - try { - return await handleServerRoutes({ - getRouter, - request, - url, - executeRouter, - context, - executedRequestMiddlewares - }); - } catch (err) { - if (err instanceof Response) { - return err; - } - throw err; - } - } - ); - }; - const middlewares = flattenedRequestMiddlewares.map( - (d) => d.options.server - ); - const ctx = await executeMiddleware( - [...middlewares, requestHandlerMiddleware], - { request, context: createNullProtoObject(requestOpts?.context) } - ); - return handleRedirectResponse(ctx.response, request, getRouter); - } finally { - if (router && !cbWillCleanup) { - router.serverSsr?.cleanup(); - } - router = null; - } - }; - return requestHandler(startRequestResolver); -} -async function handleRedirectResponse(response, request, getRouter) { - if (!isRedirect(response)) { - return response; - } - if (isResolvedRedirect(response)) { - if (request.headers.get("x-tsr-serverFn") === "true") { - return Response.json( - { ...response.options, isSerializedRedirect: true }, - { headers: response.headers } - ); - } - return response; - } - const opts = response.options; - if (opts.to && typeof opts.to === "string" && !opts.to.startsWith("/")) { - throw new Error( - `Server side redirects must use absolute paths via the 'href' or 'to' options. The redirect() method's "to" property accepts an internal path only. Use the "href" property to provide an external URL. Received: ${JSON.stringify(opts)}` - ); - } - if (["params", "search", "hash"].some( - (d) => typeof opts[d] === "function" - )) { - throw new Error( - `Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys( - opts - ).filter((d) => typeof opts[d] === "function").map((d) => `"${d}"`).join(", ")}` - ); - } - const router = await getRouter(); - const redirect = router.resolveRedirect(response); - if (request.headers.get("x-tsr-serverFn") === "true") { - return Response.json( - { ...response.options, isSerializedRedirect: true }, - { headers: response.headers } - ); - } - return redirect; -} -async function handleServerRoutes({ - getRouter, - request, - url, - executeRouter, - context, - executedRequestMiddlewares -}) { - const router = await getRouter(); - const rewrittenUrl = executeRewriteInput(router.rewrite, url); - const pathname = rewrittenUrl.pathname; - const { matchedRoutes, foundRoute, routeParams } = router.getMatchedRoutes(pathname); - const isExactMatch = foundRoute && routeParams["**"] === void 0; - const routeMiddlewares = []; - for (const route of matchedRoutes) { - const serverMiddleware = route.options.server?.middleware; - if (serverMiddleware) { - const flattened = flattenMiddlewares(serverMiddleware); - for (const m of flattened) { - if (!executedRequestMiddlewares.has(m)) { - routeMiddlewares.push(m.options.server); - } - } - } - } - const server2 = foundRoute?.options.server; - if (server2?.handlers && isExactMatch) { - const handlers = typeof server2.handlers === "function" ? server2.handlers({ createHandlers: (d) => d }) : server2.handlers; - const requestMethod = request.method.toUpperCase(); - const handler = handlers[requestMethod] ?? handlers["ANY"]; - if (handler) { - const mayDefer = !!foundRoute.options.component; - if (typeof handler === "function") { - routeMiddlewares.push(handlerToMiddleware(handler, mayDefer)); - } else { - if (handler.middleware?.length) { - const handlerMiddlewares = flattenMiddlewares(handler.middleware); - for (const m of handlerMiddlewares) { - routeMiddlewares.push(m.options.server); - } - } - if (handler.handler) { - routeMiddlewares.push(handlerToMiddleware(handler.handler, mayDefer)); - } - } - } - } - routeMiddlewares.push((ctx2) => executeRouter(ctx2.context)); - const ctx = await executeMiddleware(routeMiddlewares, { - request, - context, - params: routeParams, - pathname - }); - return ctx.response; -} -const fetch = createStartHandler(defaultStreamHandler); -function createServerEntry(entry) { - return { - async fetch(...args) { - return await entry.fetch(...args); - } - }; -} -const server = createServerEntry({ fetch }); -export { - TSS_SERVER_FUNCTION as T, - createServerFn as a, - createServerRpc as c, - createServerEntry, - server as default, - getServerFnById as g -}; diff --git a/tests/ag-ui-app/frontend/todos.json b/tests/ag-ui-app/frontend/todos.json deleted file mode 100644 index 30ebcf9e..00000000 --- a/tests/ag-ui-app/frontend/todos.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "id": 1, - "name": "Get groceries" - }, - { - "id": 2, - "name": "Buy a new phone" - }, - { - "id": 3, - "name": "sadasdasd" - } -] \ No newline at end of file diff --git a/tests/ag-ui-app/services/contact-intelligence/data/contacts.db b/tests/ag-ui-app/services/contact-intelligence/data/contacts.db deleted file mode 100644 index db7a7459cf0d014b0dc2333abb5541c58c2d6ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlG>7U}9o$P*7lCU|@t|AVoG{WYBBV;st3JAlr;ljiVtj n8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O6ovo*g{}s{ diff --git a/tests/ag-ui-app/services/contact-intelligence/data/contacts.db-shm b/tests/ag-ui-app/services/contact-intelligence/data/contacts.db-shm deleted file mode 100644 index 6884511577680a3b36ffe74536364531d0f7d99f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI)EmA`<5CGt93$&D<*3nB~D>wnSz@m`&N-UTv1d`zjTn2}z=>=#O(itrC)Fa={ z>?D(yHEd~&D-O4@yGq;s_)2q zzkWqsj?+5H{|`%!O6L5aq|E%NFTy((Ze- z^zOMox%(uf+;hL@c?O^HAD6l=u9u`cB{BWFZD9Jv`T5{b_+hE(#OvKwk;h&BZO1+N z_|TEq_@0De>he^+nAJ?V&)VDTl^&60Yg@1N>l6D_Cb55$vta!#AJ4U1Z`LbypSZ6r z_^0Guw?kUDBlvZ_jVMC^0R#|0009ILKmY**5LgKU&v`w8P&n*);W1Ntd`6$l=S*$V zEIEI@(UcNV6Y@`{|6)$LWuQB_V( zBocPHp4E(*@(O#osLyDok;iVzQ`f zrd4d+U&ky7H9n$L_l-`ZQc6-yi^6d=GBQ?qwL<3K?_z;XFA*0XJYxsJ&!0tg_000IagfB*sr zAaDbL<1t?#)Zg!U{(g6EJ#FMny=Vp4MX0=zmaZvls+VW4*`iUly?RtT{9Xx(=W|E( zVyPTvPgi2`_L_`Q)XS&i3ayvsvRSR@jqNPX|7FKSn!(8+-+^3IPNVKmY**5I_I{1Q0*~fz=}*p8YpF^#XHOUww9X z;7Kp-2v*M<%#|R300IagfB*srAbH!{bTx`&+XuJa zOI~1geHn8#2q1s}0tg_000IagfB*srTt}cLFEIbpmkZgGLyHaa0s+^tL{?zgc>#Z` z$CVeb7MKt~009ILKmY**5J2G83+(m#L%m_o?6ptz%VWhm&*}r$f2?nhDer!+UoPn+ zFF?P8X31NxzsFLeJ|0Cfq}*SloE%l<NfcHMHF<%Pr@uP?X8VQi26=%N z*LjM53zwZ22;A{hOPD}0z-{%fh>Jx40R#|0009ILKmY**5V(B>yyDWGdV%eWGhZwmGJa^Y$~?iB zB0R#|0009ILKmY**5I_I{1pa%0lBadBUAlXN6mF4*hLV$7ud#psuISW}JxAhG zhx3E`cK7VgPv_+EtYJ=PwVZZHFGePb}n7wpL): Contact | null; - deleteContact(id: string): boolean; - searchContacts(query: string): Contact[]; - recordInteraction(contactId: string, conversationId: string, direction: "inbound" | "outbound", summary: string): void; - getInteractions(contactId: string, limit?: number): ContactInteraction[]; -}; diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/db/index.js b/tests/ag-ui-app/services/contact-intelligence/dist/db/index.js deleted file mode 100644 index a4dc3a7e..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/db/index.js +++ /dev/null @@ -1,102 +0,0 @@ -import { db } from './schema.js'; -import { nanoid } from 'nanoid'; -function rowToContact(row) { - return { - ...row, - relationship: row.relationship, - formality: row.formality, - use_emojis: row.use_emojis === 1 - }; -} -export const dbOps = { - // Create a new contact - createContact(input) { - const id = nanoid(); - const now = new Date().toISOString(); - db.prepare(` - INSERT INTO contacts (id, name, email, relationship, company, notes, formality, use_emojis, preferred_tone, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `).run(id, input.name, input.email || null, input.relationship, input.company || null, input.notes || null, input.preferences?.formality || 'adaptive', input.preferences?.useEmojis ? 1 : 0, input.preferences?.preferredTone || null, now, now); - return this.getContact(id); - }, - // Get all contacts - getAllContacts() { - const rows = db.prepare('SELECT * FROM contacts ORDER BY name').all(); - return rows.map(rowToContact); - }, - // Get a single contact - getContact(id) { - const row = db.prepare('SELECT * FROM contacts WHERE id = ?').get(id); - return row ? rowToContact(row) : null; - }, - // Update a contact - updateContact(id, input) { - const existing = this.getContact(id); - if (!existing) - return null; - const updates = []; - const values = []; - if (input.name !== undefined) { - updates.push('name = ?'); - values.push(input.name); - } - if (input.email !== undefined) { - updates.push('email = ?'); - values.push(input.email || null); - } - if (input.relationship !== undefined) { - updates.push('relationship = ?'); - values.push(input.relationship); - } - if (input.company !== undefined) { - updates.push('company = ?'); - values.push(input.company || null); - } - if (input.notes !== undefined) { - updates.push('notes = ?'); - values.push(input.notes || null); - } - if (input.preferences?.formality !== undefined) { - updates.push('formality = ?'); - values.push(input.preferences.formality); - } - if (input.preferences?.useEmojis !== undefined) { - updates.push('use_emojis = ?'); - values.push(input.preferences.useEmojis ? 1 : 0); - } - if (input.preferences?.preferredTone !== undefined) { - updates.push('preferred_tone = ?'); - values.push(input.preferences.preferredTone || null); - } - if (updates.length === 0) - return existing; - updates.push('updated_at = ?'); - values.push(new Date().toISOString()); - values.push(id); - db.prepare(`UPDATE contacts SET ${updates.join(', ')} WHERE id = ?`).run(...values); - return this.getContact(id); - }, - // Delete a contact - deleteContact(id) { - const result = db.prepare('DELETE FROM contacts WHERE id = ?').run(id); - return result.changes > 0; - }, - // Search contacts by name (fuzzy) - searchContacts(query) { - const searchPattern = `%${query}%`; - const rows = db.prepare(`SELECT * FROM contacts WHERE name LIKE ? OR email LIKE ? ORDER BY name`).all(searchPattern, searchPattern); - return rows.map(rowToContact); - }, - // Record an interaction - recordInteraction(contactId, conversationId, direction, summary) { - const id = nanoid(); - db.prepare(` - INSERT INTO contact_interactions (id, contact_id, conversation_id, direction, summary) - VALUES (?, ?, ?, ?, ?) - `).run(id, contactId, conversationId, direction, summary); - }, - // Get interactions for a contact - getInteractions(contactId, limit = 10) { - return db.prepare(`SELECT * FROM contact_interactions WHERE contact_id = ? ORDER BY created_at DESC LIMIT ?`).all(contactId, limit); - } -}; diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/db/schema.d.ts b/tests/ag-ui-app/services/contact-intelligence/dist/db/schema.d.ts deleted file mode 100644 index 5c46a082..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/db/schema.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { type Database as DatabaseType } from 'better-sqlite3'; -export declare const db: DatabaseType; diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/db/schema.js b/tests/ag-ui-app/services/contact-intelligence/dist/db/schema.js deleted file mode 100644 index fb88762e..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/db/schema.js +++ /dev/null @@ -1,40 +0,0 @@ -import Database from 'better-sqlite3'; -import path from 'path'; -import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const dbPath = path.join(__dirname, '../../data/contacts.db'); -export const db = new Database(dbPath); -// Enable WAL mode for better concurrent access -db.pragma('journal_mode = WAL'); -// Create tables -db.exec(` - CREATE TABLE IF NOT EXISTS contacts ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - email TEXT, - relationship TEXT NOT NULL, - company TEXT, - notes TEXT, - formality TEXT DEFAULT 'adaptive', - use_emojis INTEGER DEFAULT 0, - preferred_tone TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS contact_interactions ( - id TEXT PRIMARY KEY, - contact_id TEXT, - conversation_id TEXT, - direction TEXT, - summary TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE - ); - - CREATE INDEX IF NOT EXISTS idx_contacts_name ON contacts(name); - CREATE INDEX IF NOT EXISTS idx_contacts_email ON contacts(email); - CREATE INDEX IF NOT EXISTS idx_interactions_contact ON contact_interactions(contact_id); -`); -console.log('[Contacts DB] Database initialized'); diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/index.d.ts b/tests/ag-ui-app/services/contact-intelligence/dist/index.d.ts deleted file mode 100644 index 10e9099c..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -import './db/schema.js'; diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/index.js b/tests/ag-ui-app/services/contact-intelligence/dist/index.js deleted file mode 100644 index 15c26838..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/index.js +++ /dev/null @@ -1,170 +0,0 @@ -import { Hono } from 'hono'; -import { cors } from 'hono/cors'; -import { serve } from '@hono/node-server'; -import { dbOps } from './db/index.js'; -import { matchMessageToContact, getContactSuggestions } from './matcher.js'; -import './db/schema.js'; // Initialize database -const app = new Hono(); -// Enable CORS for all origins (development) -app.use('*', cors()); -// Health check -app.get('/health', (c) => { - return c.json({ - status: 'ok', - service: 'contact-intelligence', - port: 3003 - }); -}); -// Validate relationship -function isValidRelationship(rel) { - return ['colleague', 'manager', 'client', 'vendor', 'friend', 'family', 'other'].includes(rel); -} -// Create a new contact -app.post('/api/contacts', async (c) => { - try { - const body = await c.req.json(); - if (!body.name?.trim()) { - return c.json({ error: 'Name is required' }, 400); - } - if (!body.relationship || !isValidRelationship(body.relationship)) { - return c.json({ - error: 'Invalid relationship. Must be one of: colleague, manager, client, vendor, friend, family, other' - }, 400); - } - const contact = dbOps.createContact({ - name: body.name.trim(), - email: body.email?.trim(), - relationship: body.relationship, - company: body.company?.trim(), - notes: body.notes?.trim(), - preferences: body.preferences - }); - console.log(`[Contacts] Created contact: ${contact.name} (${contact.id})`); - return c.json({ id: contact.id, created: true, contact }, 201); - } - catch (error) { - console.error('[Contacts] Create error:', error); - return c.json({ error: 'Failed to create contact' }, 500); - } -}); -// Get all contacts -app.get('/api/contacts', (c) => { - try { - const contacts = dbOps.getAllContacts(); - return c.json({ contacts, count: contacts.length }); - } - catch (error) { - console.error('[Contacts] List error:', error); - return c.json({ error: 'Failed to fetch contacts' }, 500); - } -}); -// Get a single contact -app.get('/api/contacts/:id', (c) => { - try { - const { id } = c.req.param(); - const contact = dbOps.getContact(id); - if (!contact) { - return c.json({ error: 'Contact not found' }, 404); - } - const suggestions = getContactSuggestions(contact); - const interactions = dbOps.getInteractions(id, 5); - return c.json({ contact, suggestions, recentInteractions: interactions }); - } - catch (error) { - console.error('[Contacts] Get error:', error); - return c.json({ error: 'Failed to fetch contact' }, 500); - } -}); -// Update a contact -app.put('/api/contacts/:id', async (c) => { - try { - const { id } = c.req.param(); - const body = await c.req.json(); - if (body.relationship && !isValidRelationship(body.relationship)) { - return c.json({ - error: 'Invalid relationship. Must be one of: colleague, manager, client, vendor, friend, family, other' - }, 400); - } - const contact = dbOps.updateContact(id, body); - if (!contact) { - return c.json({ error: 'Contact not found' }, 404); - } - console.log(`[Contacts] Updated contact: ${contact.name} (${contact.id})`); - return c.json({ contact, updated: true }); - } - catch (error) { - console.error('[Contacts] Update error:', error); - return c.json({ error: 'Failed to update contact' }, 500); - } -}); -// Delete a contact -app.delete('/api/contacts/:id', (c) => { - try { - const { id } = c.req.param(); - const deleted = dbOps.deleteContact(id); - if (!deleted) { - return c.json({ error: 'Contact not found' }, 404); - } - console.log(`[Contacts] Deleted contact: ${id}`); - return c.json({ deleted: true }); - } - catch (error) { - console.error('[Contacts] Delete error:', error); - return c.json({ error: 'Failed to delete contact' }, 500); - } -}); -// Match a message to a contact -app.post('/api/contacts/match', async (c) => { - try { - const body = await c.req.json(); - const { message } = body; - if (!message?.trim()) { - return c.json({ error: 'Message is required' }, 400); - } - console.log(`[Contacts] Matching message: "${message.substring(0, 50)}..."`); - const result = matchMessageToContact(message.trim()); - return c.json(result); - } - catch (error) { - console.error('[Contacts] Match error:', error); - return c.json({ error: 'Failed to match contact' }, 500); - } -}); -// Record an interaction -app.post('/api/contacts/:id/interactions', async (c) => { - try { - const { id } = c.req.param(); - const body = await c.req.json(); - const contact = dbOps.getContact(id); - if (!contact) { - return c.json({ error: 'Contact not found' }, 404); - } - dbOps.recordInteraction(id, body.conversationId, body.direction, body.summary); - return c.json({ recorded: true }); - } - catch (error) { - console.error('[Contacts] Interaction error:', error); - return c.json({ error: 'Failed to record interaction' }, 500); - } -}); -// Start server -const PORT = parseInt(process.env.PORT || '3003', 10); -console.log(` -======================================== - Contact Intelligence Service - Port: ${PORT} - Endpoints: - GET /health - POST /api/contacts - GET /api/contacts - GET /api/contacts/:id - PUT /api/contacts/:id - DELETE /api/contacts/:id - POST /api/contacts/match - POST /api/contacts/:id/interactions -======================================== -`); -serve({ - fetch: app.fetch, - port: PORT -}); diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/matcher.d.ts b/tests/ag-ui-app/services/contact-intelligence/dist/matcher.d.ts deleted file mode 100644 index 4169e6a6..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/matcher.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { type Contact } from './db/index.js'; -export interface MatchResult { - matchedContact: Contact | null; - confidence: number; - relationshipContext: string; -} -/** - * Try to match a message to a known contact - */ -export declare function matchMessageToContact(message: string): MatchResult; -/** - * Get communication suggestions based on a contact - */ -export declare function getContactSuggestions(contact: Contact): string[]; diff --git a/tests/ag-ui-app/services/contact-intelligence/dist/matcher.js b/tests/ag-ui-app/services/contact-intelligence/dist/matcher.js deleted file mode 100644 index e63fa5e2..00000000 --- a/tests/ag-ui-app/services/contact-intelligence/dist/matcher.js +++ /dev/null @@ -1,140 +0,0 @@ -import { dbOps } from './db/index.js'; -// Relationship descriptions for context -const relationshipDescriptions = { - manager: 'your manager', - colleague: 'your colleague', - client: 'a client', - vendor: 'a vendor/supplier', - friend: 'a friend', - family: 'a family member', - other: 'a contact' -}; -// Formality suggestions -const formalitySuggestions = { - formal: 'Use formal, professional language.', - casual: 'You can use casual, friendly language.', - adaptive: 'Match the tone of their message.' -}; -/** - * Try to match a message to a known contact - */ -export function matchMessageToContact(message) { - const contacts = dbOps.getAllContacts(); - if (contacts.length === 0) { - return { - matchedContact: null, - confidence: 0, - relationshipContext: 'No contacts in database.' - }; - } - // Extract potential names/emails from the message - const lowerMessage = message.toLowerCase(); - let bestMatch = null; - let bestScore = 0; - for (const contact of contacts) { - let score = 0; - // Check for name match - const nameParts = contact.name.toLowerCase().split(' '); - const fullName = contact.name.toLowerCase(); - // Exact full name match - if (lowerMessage.includes(fullName)) { - score += 1.0; - } - else { - // Check individual name parts - for (const part of nameParts) { - if (part.length >= 3 && lowerMessage.includes(part)) { - score += 0.5; - } - } - } - // Check for email match - if (contact.email && lowerMessage.includes(contact.email.toLowerCase())) { - score += 0.8; - } - // Check for company match - if (contact.company && lowerMessage.includes(contact.company.toLowerCase())) { - score += 0.3; - } - if (score > bestScore) { - bestScore = score; - bestMatch = contact; - } - } - // Normalize confidence (cap at 1.0) - const confidence = Math.min(1.0, bestScore); - if (bestMatch && confidence >= 0.3) { - const context = buildRelationshipContext(bestMatch); - return { - matchedContact: bestMatch, - confidence, - relationshipContext: context - }; - } - return { - matchedContact: null, - confidence: 0, - relationshipContext: 'No matching contact found. Respond professionally.' - }; -} -/** - * Build a human-readable context string for the relationship - */ -function buildRelationshipContext(contact) { - const parts = []; - // Basic relationship - const relDesc = relationshipDescriptions[contact.relationship]; - if (contact.company) { - parts.push(`This is ${relDesc} at ${contact.company}.`); - } - else { - parts.push(`This is ${relDesc}.`); - } - // Communication preferences - parts.push(formalitySuggestions[contact.formality]); - if (contact.use_emojis) { - parts.push('They appreciate emoji use.'); - } - if (contact.preferred_tone) { - parts.push(`Preferred tone: ${contact.preferred_tone}.`); - } - if (contact.notes) { - parts.push(`Note: ${contact.notes}`); - } - return parts.join(' '); -} -/** - * Get communication suggestions based on a contact - */ -export function getContactSuggestions(contact) { - const suggestions = []; - switch (contact.relationship) { - case 'manager': - suggestions.push('Be respectful and concise'); - suggestions.push('Focus on solutions, not problems'); - break; - case 'client': - suggestions.push('Be professional and helpful'); - suggestions.push('Acknowledge their concerns'); - break; - case 'colleague': - suggestions.push('Be collaborative'); - suggestions.push('Offer assistance if appropriate'); - break; - case 'friend': - case 'family': - suggestions.push('Be warm and personal'); - suggestions.push('Show genuine interest'); - break; - case 'vendor': - suggestions.push('Be clear about expectations'); - suggestions.push('Keep communication professional'); - break; - default: - suggestions.push('Be professional and courteous'); - } - if (contact.formality === 'formal') { - suggestions.push('Use proper salutations and sign-offs'); - } - return suggestions; -} diff --git a/tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db b/tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db deleted file mode 100644 index db7a7459cf0d014b0dc2333abb5541c58c2d6ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlG>7U}9o$P*7lCU|@t|AVoG{WYBBV;st3JAlr;ljiVtj n8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O6ovo*g{}s{ diff --git a/tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db-shm b/tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db-shm deleted file mode 100644 index 43c0d52d4e538ff295441d964682ee6120b69c15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI)$4x^)6h`4=f^D)fU>-S{XpAvAtXR1LdO+;hQv(e^6%;@L6hH$k5M96wvL$cX zf#&?uXo8+*-u()=*?XN%a!|Sqn_?KPtoitGU1_~Fo<8o%_tl%1+UM=-^HuiC?>l$L zd%wEUeID20ZF<7!4WqBU?+ngV%-8KcY;8FMVM zLX}N+sI$)@C!D9rV%13q6d0kz1QlkOXNgtT*`mfCO^!I_A_)bHOjBSW&9bdpfgmu% zDC0~r%`6Knv&IJ7?9$+XW6p?udW`}KD4>7>3Mim}0tzUgfC36Apnw7jD4>7>3Mim} Y0tzUgfC36Apnw7jD4>7>3jA$>Zw|yPbN~PV diff --git a/tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db-wal b/tests/ag-ui-app/services/sentiment-analysis/data/sentiment.db-wal deleted file mode 100644 index cfb79ae6b5e98537009a7e9e52803d4e086c6b06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243112 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 diff --git a/tests/ag-ui-app/services/writing-style/data/style.db b/tests/ag-ui-app/services/writing-style/data/style.db deleted file mode 100644 index db7a7459cf0d014b0dc2333abb5541c58c2d6ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlG>7U}9o$P*7lCU|@t|AVoG{WYBBV;st3JAlr;ljiVtj n8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O6ovo*g{}s{ diff --git a/tests/ag-ui-app/services/writing-style/data/style.db-shm b/tests/ag-ui-app/services/writing-style/data/style.db-shm deleted file mode 100644 index 74b0d227c1ab374623d79b8bdffbe30e1bc98c3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI)yG;W@5CG7zjemS?^E(P4qy$PJBzA;EN<=~g1t3ud0dzrtNN9l;C;)cR9db%= z(u#W{t#-89yP0_fZ1r&xIo8tZMa+)UK27WP;_CAG>FoIP{_gzr^?La@e|vwpd0A9{ zyq|vc6ZyYiD^VZEPMxIM(NAlTR?c%j!#W?OeO%|0de1#J=goKdtobhATg~%bL4W`O z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyP?|t5y%8~v_1G+}G5ru|U7#Pc)@vw0piF^5%*$*{R|HxY z7{+cKv|dF40woHJVlNI$Y)nrCS{InacI5XM1%d7fOn=`T?cVGr?z+H6>_^pg1oL-P zU@I0Ko!r<22oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72>d^RZvx{e Ang9R* diff --git a/tests/ag-ui-app/services/writing-style/data/style.db-wal b/tests/ag-ui-app/services/writing-style/data/style.db-wal deleted file mode 100644 index 0af81f1cd8cb8c615281b4e5098168b7df84d02b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103032 zcmeI*eQX1k4aS-5)WgrKakjfcB|0DHU;sdiQn0G zj=j1`NQcxo?^7OpkMZvn$I;z=zQ14SLFY+n&l}q$>7$Z4z4zE+>W=es>h8%?&hYtL z-xQ1F#^s04KmL&?#?D2K-4o3jx{@uHrZhtdioG2U=}tuv+d9N&hq;e6nfrIzGva&o zcn$Nd9y+AG5AW{w{X=qWJu7WJ>-$NojaY^N0tg_000IagfB*srAb`NC0w)$^m%pb+ zUhFfpvAkY37V^4U)~2S#+t_26#zwcaz8o7XeekJYD&lR1$>*T(ghKTe-d z=QS~QTCP}7mlN%+o|p)`m)EeFQ8cu?-GlvQ(RizomFdj?*j2o9^{(Qr-w^|V_9_-)Dc|ucmz)Kt;Ws^OdStA@lItRMIAxI8!!(MKmY**5I_I{1Q0*~ z0R#|eUjg&pzstTZFuKp1b^PnT-%&@AS$u#xf>boASc!h> z2pV2kj7LBn!By80nCJLzJ1_9VTh86|qHpPD@&X&?UlC6f0R#|0009ILKmY**5I_Kd zs}X3*3k?2c?*U^p2uS?YaN4x|!^$Oo+sbvpgS0a>d4sg`H*Ir<%38(hC*_0BEr z_p-iPBFUOH#}Ke<0nBrJkDV8It`b;${DILgkr()|{tU^ZJ=lb36js_ghJPFd={d0tg_000IagfB*srAb`L|5(voN9d5VZ zEvBF~_-J(~(EYvp5A5mf+tU|R`VJht^}xYkuV|Jz9zi`X@cw7MyZ7ClUp;H)1!Uin zBtDoBKmY**5I_I{1Q0*~0R#|0pq&J~9XmYM^#ai>huJi{j^Kr^V(`L?|M^5MFYuJ) zd#arpPFoN_009ILKmY**5I_I{1Q6Ju0x8)QIuwvS!LS|<&5Rv5T1*UlZa6hNc=x@< z{lVmfR=B^cWQ!%Gq-*JkTwz=(R*bTq*VBfcQA_%Cexb8DFK}D$!C=48F#8&)Ul(}t z^y1h5{;j3Y*YW}vCEvvj>c!JW009ILKmY**5I_I{1Q0*~0b5{ON66Fif`Ztfso%k0 z?frK8tygdOckQ~s+mi2X`w)Cb009ILKmY**5I_I{1Q0*~fsHM2T}QyvoFNeHsg5yd z*AZO$)JuQ;&BrdjSF0mfkSD0m z8MC?Q?IWXVeE-N?b}W9|vCsqM+<2jw&0e9bpmiNVm+50)7kK^1AMSlj{o*in1nu>z zp*08~fB*srAbB;+(M=Rk{dU~RymG!kJ|Np;r z1hJV%uRpvqvSj839KK(O@d(5hCIk>b009ILKmY**5I_I{1Q59P1-5sD>): void; - getSampleCount(): number; - getRecentSamples(limit?: number): StyleSample[]; - clearAll(): void; -}; diff --git a/tests/ag-ui-app/services/writing-style/dist/db/index.js b/tests/ag-ui-app/services/writing-style/dist/db/index.js deleted file mode 100644 index f5c76307..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/db/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import { db } from './schema.js'; -import { nanoid } from 'nanoid'; -export const dbOps = { - // Save a writing sample - saveSample(content, type) { - const id = nanoid(); - const words = content.split(/\s+/).filter(w => w.length > 0); - const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 0); - db.prepare(` - INSERT INTO style_samples (id, content, type, word_count, sentence_count) - VALUES (?, ?, ?, ?, ?) - `).run(id, content, type, words.length, sentences.length); - return { - id, - content, - type, - word_count: words.length, - sentence_count: sentences.length, - created_at: new Date().toISOString() - }; - }, - // Update or increment a pattern - updatePattern(type, value) { - const existing = db.prepare('SELECT * FROM style_patterns WHERE pattern_type = ? AND value = ?').get(type, value); - if (existing) { - db.prepare(` - UPDATE style_patterns - SET frequency = frequency + 1, last_used = CURRENT_TIMESTAMP - WHERE id = ? - `).run(existing.id); - } - else { - const id = nanoid(); - db.prepare(` - INSERT INTO style_patterns (id, pattern_type, value, frequency) - VALUES (?, ?, ?, 1) - `).run(id, type, value); - } - }, - // Get patterns by type - getPatterns(type, limit = 10) { - return db.prepare('SELECT * FROM style_patterns WHERE pattern_type = ? ORDER BY frequency DESC LIMIT ?').all(type, limit); - }, - // Get all patterns - getAllPatterns() { - return db.prepare('SELECT * FROM style_patterns ORDER BY pattern_type, frequency DESC').all(); - }, - // Get the style profile - getProfile() { - return db.prepare('SELECT * FROM style_profile WHERE id = ?').get('default'); - }, - // Update the style profile - updateProfile(updates) { - const sets = []; - const values = []; - for (const [key, value] of Object.entries(updates)) { - if (value !== undefined) { - sets.push(`${key} = ?`); - values.push(value); - } - } - if (sets.length > 0) { - sets.push('updated_at = CURRENT_TIMESTAMP'); - values.push('default'); - db.prepare(`UPDATE style_profile SET ${sets.join(', ')} WHERE id = ?`).run(...values); - } - }, - // Get sample count - getSampleCount() { - const result = db.prepare('SELECT COUNT(*) as count FROM style_samples').get(); - return result?.count || 0; - }, - // Get recent samples - getRecentSamples(limit = 20) { - return db.prepare('SELECT * FROM style_samples ORDER BY created_at DESC LIMIT ?').all(limit); - }, - // Clear all data - clearAll() { - db.exec(` - DELETE FROM style_samples; - DELETE FROM style_patterns; - UPDATE style_profile SET - avg_sentence_length = 0, - avg_word_length = 0, - vocabulary_level = 'mixed', - emoji_usage = 0, - exclamation_frequency = 0, - question_frequency = 0, - total_samples = 0, - updated_at = CURRENT_TIMESTAMP - WHERE id = 'default'; - `); - } -}; diff --git a/tests/ag-ui-app/services/writing-style/dist/db/schema.d.ts b/tests/ag-ui-app/services/writing-style/dist/db/schema.d.ts deleted file mode 100644 index 5c46a082..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/db/schema.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { type Database as DatabaseType } from 'better-sqlite3'; -export declare const db: DatabaseType; diff --git a/tests/ag-ui-app/services/writing-style/dist/db/schema.js b/tests/ag-ui-app/services/writing-style/dist/db/schema.js deleted file mode 100644 index 8f3a8e41..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/db/schema.js +++ /dev/null @@ -1,47 +0,0 @@ -import Database from 'better-sqlite3'; -import path from 'path'; -import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const dbPath = path.join(__dirname, '../../data/style.db'); -export const db = new Database(dbPath); -// Enable WAL mode for better concurrent access -db.pragma('journal_mode = WAL'); -// Create tables -db.exec(` - CREATE TABLE IF NOT EXISTS style_samples ( - id TEXT PRIMARY KEY, - content TEXT NOT NULL, - type TEXT NOT NULL, - word_count INTEGER, - sentence_count INTEGER, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS style_patterns ( - id TEXT PRIMARY KEY, - pattern_type TEXT NOT NULL, - value TEXT NOT NULL, - frequency INTEGER DEFAULT 1, - last_used DATETIME DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS style_profile ( - id TEXT PRIMARY KEY DEFAULT 'default', - avg_sentence_length REAL DEFAULT 0, - avg_word_length REAL DEFAULT 0, - vocabulary_level TEXT DEFAULT 'mixed', - emoji_usage REAL DEFAULT 0, - exclamation_frequency REAL DEFAULT 0, - question_frequency REAL DEFAULT 0, - total_samples INTEGER DEFAULT 0, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP - ); - - CREATE INDEX IF NOT EXISTS idx_patterns_type ON style_patterns(pattern_type); - CREATE INDEX IF NOT EXISTS idx_samples_type ON style_samples(type); - - -- Initialize default profile if not exists - INSERT OR IGNORE INTO style_profile (id) VALUES ('default'); -`); -console.log('[Style DB] Database initialized'); diff --git a/tests/ag-ui-app/services/writing-style/dist/index.d.ts b/tests/ag-ui-app/services/writing-style/dist/index.d.ts deleted file mode 100644 index 10e9099c..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -import './db/schema.js'; diff --git a/tests/ag-ui-app/services/writing-style/dist/index.js b/tests/ag-ui-app/services/writing-style/dist/index.js deleted file mode 100644 index c8065aca..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/index.js +++ /dev/null @@ -1,137 +0,0 @@ -import { Hono } from 'hono'; -import { cors } from 'hono/cors'; -import { serve } from '@hono/node-server'; -import { dbOps } from './db/index.js'; -import { learnFromSample, enhanceReply } from './learner.js'; -import './db/schema.js'; // Initialize database -const app = new Hono(); -// Enable CORS for all origins (development) -app.use('*', cors()); -// Health check -app.get('/health', (c) => { - return c.json({ - status: 'ok', - service: 'writing-style', - port: 3004 - }); -}); -// Validate sample type -function isValidSampleType(type) { - return ['selected_reply', 'custom_edit', 'sent_message'].includes(type); -} -// Learn from a writing sample -app.post('/api/learn', async (c) => { - try { - const body = await c.req.json(); - const { content, type } = body; - if (!content?.trim()) { - return c.json({ error: 'Content is required' }, 400); - } - if (!type || !isValidSampleType(type)) { - return c.json({ - error: 'Invalid type. Must be one of: selected_reply, custom_edit, sent_message' - }, 400); - } - console.log(`[Style] Learning from ${type}: "${content.substring(0, 50)}..."`); - const result = learnFromSample(content.trim(), type); - return c.json({ - learned: result.learned, - patterns_updated: result.patternsUpdated, - extracted: result.extracted - }); - } - catch (error) { - console.error('[Style] Learn error:', error); - return c.json({ error: 'Failed to learn from sample' }, 500); - } -}); -// Get the user's style profile -app.get('/api/profile', (c) => { - try { - const profile = dbOps.getProfile(); - const greetings = dbOps.getPatterns('greeting', 5); - const signoffs = dbOps.getPatterns('signoff', 5); - const phrases = dbOps.getPatterns('phrase', 10); - return c.json({ - totalSamples: profile.total_samples, - averageSentenceLength: profile.avg_sentence_length, - commonGreetings: greetings.map(p => p.value), - commonSignoffs: signoffs.map(p => p.value), - vocabularyLevel: profile.vocabulary_level, - usesEmojis: profile.emoji_usage > 0.01, - emojiFrequency: profile.emoji_usage, - commonPhrases: phrases.map(p => p.value), - punctuationStyle: { - exclamationFrequency: profile.exclamation_frequency, - questionFrequency: profile.question_frequency - }, - lastUpdated: profile.updated_at - }); - } - catch (error) { - console.error('[Style] Profile error:', error); - return c.json({ error: 'Failed to get profile' }, 500); - } -}); -// Enhance a reply based on learned style -app.post('/api/enhance', async (c) => { - try { - const body = await c.req.json(); - const { reply } = body; - if (!reply?.trim()) { - return c.json({ error: 'Reply is required' }, 400); - } - console.log(`[Style] Enhancing reply: "${reply.substring(0, 50)}..."`); - const result = enhanceReply(reply.trim()); - return c.json(result); - } - catch (error) { - console.error('[Style] Enhance error:', error); - return c.json({ error: 'Failed to enhance reply' }, 500); - } -}); -// Get all patterns (for debugging/admin) -app.get('/api/patterns', (c) => { - try { - const patterns = dbOps.getAllPatterns(); - return c.json({ - patterns, - count: patterns.length - }); - } - catch (error) { - console.error('[Style] Patterns error:', error); - return c.json({ error: 'Failed to get patterns' }, 500); - } -}); -// Clear all learned data -app.delete('/api/profile', (c) => { - try { - dbOps.clearAll(); - console.log('[Style] Profile cleared'); - return c.json({ cleared: true }); - } - catch (error) { - console.error('[Style] Clear error:', error); - return c.json({ error: 'Failed to clear profile' }, 500); - } -}); -// Start server -const PORT = parseInt(process.env.PORT || '3004', 10); -console.log(` -======================================== - Writing Style Service - Port: ${PORT} - Endpoints: - GET /health - POST /api/learn - GET /api/profile - POST /api/enhance - GET /api/patterns - DELETE /api/profile -======================================== -`); -serve({ - fetch: app.fetch, - port: PORT -}); diff --git a/tests/ag-ui-app/services/writing-style/dist/learner.d.ts b/tests/ag-ui-app/services/writing-style/dist/learner.d.ts deleted file mode 100644 index 3979239e..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/learner.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { type SampleType } from './db/index.js'; -export interface LearnResult { - learned: boolean; - patternsUpdated: number; - extracted: { - greetings: string[]; - signoffs: string[]; - phrases: string[]; - stats: { - wordCount: number; - sentenceCount: number; - avgWordLength: number; - avgSentenceLength: number; - emojiCount: number; - exclamationCount: number; - questionCount: number; - }; - }; -} -/** - * Learn from a text sample - */ -export declare function learnFromSample(content: string, type: SampleType): LearnResult; -export interface EnhanceResult { - enhanced: string; - changes: Array<{ - original: string; - replacement: string; - reason: string; - }>; -} -/** - * Enhance a reply based on learned style - */ -export declare function enhanceReply(reply: string): EnhanceResult; diff --git a/tests/ag-ui-app/services/writing-style/dist/learner.js b/tests/ag-ui-app/services/writing-style/dist/learner.js deleted file mode 100644 index 3606895a..00000000 --- a/tests/ag-ui-app/services/writing-style/dist/learner.js +++ /dev/null @@ -1,287 +0,0 @@ -import { dbOps } from './db/index.js'; -// Common greetings to detect -const COMMON_GREETINGS = [ - 'hi', 'hey', 'hello', 'dear', 'good morning', 'good afternoon', 'good evening', - 'greetings', 'hiya', 'howdy', 'sup', 'yo' -]; -// Common sign-offs to detect -const COMMON_SIGNOFFS = [ - 'best', 'thanks', 'thank you', 'regards', 'cheers', 'sincerely', 'warmly', - 'take care', 'best regards', 'kind regards', 'warm regards', 'many thanks', - 'talk soon', 'later', 'bye', 'ciao', 'xo', 'love' -]; -// Professional words (indicate formal style) -const PROFESSIONAL_WORDS = [ - 'regarding', 'furthermore', 'therefore', 'accordingly', 'pursuant', - 'subsequently', 'henceforth', 'aforementioned', 'herewith', 'notwithstanding', - 'acknowledge', 'appreciate', 'consideration', 'implementation' -]; -// Casual indicators -const CASUAL_INDICATORS = [ - 'gonna', 'wanna', 'kinda', 'gotta', 'yeah', 'nope', 'yup', 'cool', - 'awesome', 'stuff', 'things', 'btw', 'fyi', 'asap', 'lol', 'haha' -]; -/** - * Learn from a text sample - */ -export function learnFromSample(content, type) { - const trimmed = content.trim(); - if (!trimmed) { - return { - learned: false, - patternsUpdated: 0, - extracted: { greetings: [], signoffs: [], phrases: [], stats: getEmptyStats() } - }; - } - // Save the sample - dbOps.saveSample(trimmed, type); - // Extract patterns - const greetings = extractGreetings(trimmed); - const signoffs = extractSignoffs(trimmed); - const phrases = extractPhrases(trimmed); - const stats = analyzeStats(trimmed); - let patternsUpdated = 0; - // Save extracted patterns - for (const greeting of greetings) { - dbOps.updatePattern('greeting', greeting); - patternsUpdated++; - } - for (const signoff of signoffs) { - dbOps.updatePattern('signoff', signoff); - patternsUpdated++; - } - for (const phrase of phrases) { - dbOps.updatePattern('phrase', phrase); - patternsUpdated++; - } - // Update the aggregated profile - updateAggregatedProfile(); - return { - learned: true, - patternsUpdated, - extracted: { greetings, signoffs, phrases, stats } - }; -} -/** - * Extract greetings from the start of text - */ -function extractGreetings(text) { - const found = []; - const lowerText = text.toLowerCase(); - const firstLine = text.split('\n')[0].trim(); - const lowerFirst = firstLine.toLowerCase(); - for (const greeting of COMMON_GREETINGS) { - if (lowerFirst.startsWith(greeting)) { - // Get the actual casing from the original - const match = firstLine.substring(0, greeting.length + 20).split(/[,!\n]/)[0].trim(); - if (match && match.length < 30) { - found.push(match); - break; - } - } - } - return found; -} -/** - * Extract sign-offs from the end of text - */ -function extractSignoffs(text) { - const found = []; - const lines = text.split('\n').filter(l => l.trim()); - const lastLines = lines.slice(-3); - for (const line of lastLines) { - const lowerLine = line.toLowerCase().trim(); - for (const signoff of COMMON_SIGNOFFS) { - if (lowerLine.startsWith(signoff) || lowerLine === signoff) { - const cleanSignoff = line.trim().replace(/[,!.]+$/, '').trim(); - if (cleanSignoff && cleanSignoff.length < 30) { - found.push(cleanSignoff); - } - break; - } - } - } - return [...new Set(found)]; -} -/** - * Extract common phrases (3-5 word combinations) - */ -function extractPhrases(text) { - const phrases = []; - const words = text.split(/\s+/); - // Look for common phrase patterns - const commonPhrases = [ - "I'd be happy to", - "Let me know if", - "Thanks for reaching out", - "I hope this helps", - "Please let me know", - "Looking forward to", - "Feel free to", - "Don't hesitate to", - "I wanted to", - "Just wanted to", - "Hope you're doing well", - "Hope this finds you well" - ]; - const lowerText = text.toLowerCase(); - for (const phrase of commonPhrases) { - if (lowerText.includes(phrase.toLowerCase())) { - phrases.push(phrase); - } - } - return phrases.slice(0, 3); // Max 3 phrases per sample -} -/** - * Analyze text statistics - */ -function analyzeStats(text) { - const words = text.split(/\s+/).filter(w => w.length > 0); - const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0); - // Count emojis (rough pattern) - const emojiRegex = /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu; - const emojiCount = (text.match(emojiRegex) || []).length; - // Count punctuation - const exclamationCount = (text.match(/!/g) || []).length; - const questionCount = (text.match(/\?/g) || []).length; - // Calculate averages - const totalWordLength = words.reduce((sum, w) => sum + w.replace(/[^a-zA-Z]/g, '').length, 0); - const avgWordLength = words.length > 0 ? totalWordLength / words.length : 0; - const totalSentenceLength = sentences.reduce((sum, s) => sum + s.split(/\s+/).length, 0); - const avgSentenceLength = sentences.length > 0 ? totalSentenceLength / sentences.length : 0; - return { - wordCount: words.length, - sentenceCount: sentences.length, - avgWordLength: Math.round(avgWordLength * 100) / 100, - avgSentenceLength: Math.round(avgSentenceLength * 100) / 100, - emojiCount, - exclamationCount, - questionCount - }; -} -function getEmptyStats() { - return { - wordCount: 0, - sentenceCount: 0, - avgWordLength: 0, - avgSentenceLength: 0, - emojiCount: 0, - exclamationCount: 0, - questionCount: 0 - }; -} -/** - * Update the aggregated profile based on all samples - */ -function updateAggregatedProfile() { - const samples = dbOps.getRecentSamples(50); - if (samples.length === 0) - return; - // Calculate aggregate stats - let totalWords = 0; - let totalSentences = 0; - let totalEmojis = 0; - let totalExclamations = 0; - let totalQuestions = 0; - let professionalScore = 0; - let casualScore = 0; - for (const sample of samples) { - const stats = analyzeStats(sample.content); - totalWords += stats.wordCount; - totalSentences += stats.sentenceCount; - totalEmojis += stats.emojiCount; - totalExclamations += stats.exclamationCount; - totalQuestions += stats.questionCount; - // Check vocabulary - const lower = sample.content.toLowerCase(); - for (const word of PROFESSIONAL_WORDS) { - if (lower.includes(word)) - professionalScore++; - } - for (const word of CASUAL_INDICATORS) { - if (lower.includes(word)) - casualScore++; - } - } - // Determine vocabulary level - let vocabularyLevel = 'mixed'; - if (professionalScore > casualScore * 2) { - vocabularyLevel = 'professional'; - } - else if (casualScore > professionalScore * 2) { - vocabularyLevel = 'casual'; - } - const avgSentenceLength = totalSentences > 0 ? totalWords / totalSentences : 0; - const emojiUsage = totalWords > 0 ? totalEmojis / totalWords : 0; - const exclamationFreq = totalSentences > 0 ? totalExclamations / totalSentences : 0; - const questionFreq = totalSentences > 0 ? totalQuestions / totalSentences : 0; - dbOps.updateProfile({ - avg_sentence_length: Math.round(avgSentenceLength * 100) / 100, - vocabulary_level: vocabularyLevel, - emoji_usage: Math.round(emojiUsage * 1000) / 1000, - exclamation_frequency: Math.round(exclamationFreq * 100) / 100, - question_frequency: Math.round(questionFreq * 100) / 100, - total_samples: samples.length - }); -} -/** - * Enhance a reply based on learned style - */ -export function enhanceReply(reply) { - const changes = []; - let enhanced = reply; - // Get top patterns - const greetings = dbOps.getPatterns('greeting', 5); - const signoffs = dbOps.getPatterns('signoff', 5); - const profile = dbOps.getProfile(); - if (profile.total_samples < 3) { - // Not enough data to make suggestions - return { enhanced, changes }; - } - // Suggest greeting replacement - const topGreeting = greetings[0]?.value; - if (topGreeting) { - const greetingPatterns = ['Hello', 'Hi', 'Hey', 'Dear']; - for (const pattern of greetingPatterns) { - if (enhanced.startsWith(pattern) && !enhanced.startsWith(topGreeting)) { - const regex = new RegExp(`^${pattern}(\\s|,|!)`); - if (regex.test(enhanced)) { - const newReply = enhanced.replace(regex, `${topGreeting}$1`); - if (newReply !== enhanced) { - changes.push({ - original: pattern, - replacement: topGreeting, - reason: `You typically use "${topGreeting}" as your greeting` - }); - enhanced = newReply; - break; - } - } - } - } - } - // Suggest signoff replacement - const topSignoff = signoffs[0]?.value; - if (topSignoff) { - const signoffPatterns = ['Best', 'Thanks', 'Regards', 'Cheers', 'Sincerely']; - const lines = enhanced.split('\n'); - const lastLineIdx = lines.length - 1; - for (const pattern of signoffPatterns) { - if (lines[lastLineIdx].trim().startsWith(pattern) && - !lines[lastLineIdx].trim().startsWith(topSignoff)) { - const regex = new RegExp(`^${pattern}`); - if (regex.test(lines[lastLineIdx].trim())) { - lines[lastLineIdx] = lines[lastLineIdx].replace(regex, topSignoff); - changes.push({ - original: pattern, - replacement: topSignoff, - reason: `Your preferred sign-off is "${topSignoff}"` - }); - enhanced = lines.join('\n'); - break; - } - } - } - } - return { enhanced, changes }; -}