generated from nhcarrigan/template
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6110cfc5b |
+1
-1
@@ -79,7 +79,7 @@
|
|||||||
"@vitest/coverage-v8": "^4.0.18",
|
"@vitest/coverage-v8": "^4.0.18",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-svelte": "^3.14.0",
|
"eslint-plugin-svelte": "3.15.0",
|
||||||
"globals": "^17.0.0",
|
"globals": "^17.0.0",
|
||||||
"jsdom": "^27.4.0",
|
"jsdom": "^27.4.0",
|
||||||
"prettier": "^3.8.0",
|
"prettier": "^3.8.0",
|
||||||
|
|||||||
Generated
+6
-6
@@ -160,8 +160,8 @@ importers:
|
|||||||
specifier: ^10.1.8
|
specifier: ^10.1.8
|
||||||
version: 10.1.8(eslint@9.39.2(jiti@2.6.1))
|
version: 10.1.8(eslint@9.39.2(jiti@2.6.1))
|
||||||
eslint-plugin-svelte:
|
eslint-plugin-svelte:
|
||||||
specifier: ^3.14.0
|
specifier: 3.15.0
|
||||||
version: 3.14.0(eslint@9.39.2(jiti@2.6.1))(svelte@5.46.3)
|
version: 3.15.0(eslint@9.39.2(jiti@2.6.1))(svelte@5.46.3)
|
||||||
globals:
|
globals:
|
||||||
specifier: ^17.0.0
|
specifier: ^17.0.0
|
||||||
version: 17.0.0
|
version: 17.0.0
|
||||||
@@ -1343,11 +1343,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=7.0.0'
|
eslint: '>=7.0.0'
|
||||||
|
|
||||||
eslint-plugin-svelte@3.14.0:
|
eslint-plugin-svelte@3.15.0:
|
||||||
resolution: {integrity: sha512-Isw0GvaMm0yHxAj71edAdGFh28ufYs+6rk2KlbbZphnqZAzrH3Se3t12IFh2H9+1F/jlDhBBL4oiOJmLqmYX0g==}
|
resolution: {integrity: sha512-QKB7zqfuB8aChOfBTComgDptMf2yxiJx7FE04nneCmtQzgTHvY8UJkuh8J2Rz7KB9FFV9aTHX6r7rdYGvG8T9Q==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.1 || ^9.0.0
|
eslint: ^8.57.1 || ^9.0.0 || ^10.0.0
|
||||||
svelte: ^3.37.0 || ^4.0.0 || ^5.0.0
|
svelte: ^3.37.0 || ^4.0.0 || ^5.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
svelte:
|
svelte:
|
||||||
@@ -3335,7 +3335,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.2(jiti@2.6.1)
|
eslint: 9.39.2(jiti@2.6.1)
|
||||||
|
|
||||||
eslint-plugin-svelte@3.14.0(eslint@9.39.2(jiti@2.6.1))(svelte@5.46.3):
|
eslint-plugin-svelte@3.15.0(eslint@9.39.2(jiti@2.6.1))(svelte@5.46.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|||||||
@@ -86,9 +86,8 @@ impl ContextWarning {
|
|||||||
/// Get the context window limit (in tokens) for a given model
|
/// Get the context window limit (in tokens) for a given model
|
||||||
fn get_context_window_limit(model: &str) -> u64 {
|
fn get_context_window_limit(model: &str) -> u64 {
|
||||||
match model {
|
match model {
|
||||||
// Claude 4.6 family
|
// Claude 4.6 family - 200K standard (1M beta available via header)
|
||||||
"claude-opus-4-6" => 200_000,
|
"claude-opus-4-6" => 200_000,
|
||||||
"claude-sonnet-4-6" => 1_000_000, // 1M token context window
|
|
||||||
// Claude 4.5 family - 200K standard context
|
// Claude 4.5 family - 200K standard context
|
||||||
"claude-opus-4-5-20251101"
|
"claude-opus-4-5-20251101"
|
||||||
| "claude-sonnet-4-5-20250929"
|
| "claude-sonnet-4-5-20250929"
|
||||||
@@ -503,7 +502,6 @@ pub fn calculate_cost(
|
|||||||
let (input_price_per_million, output_price_per_million) = match model {
|
let (input_price_per_million, output_price_per_million) = match model {
|
||||||
// Current generation (Claude 4.6)
|
// Current generation (Claude 4.6)
|
||||||
"claude-opus-4-6" => (5.0, 25.0),
|
"claude-opus-4-6" => (5.0, 25.0),
|
||||||
"claude-sonnet-4-6" => (3.0, 15.0),
|
|
||||||
|
|
||||||
// Previous generation (Claude 4.5)
|
// Previous generation (Claude 4.5)
|
||||||
"claude-opus-4-5-20251101" => (5.0, 25.0),
|
"claude-opus-4-5-20251101" => (5.0, 25.0),
|
||||||
|
|||||||
+16
-91
@@ -125,19 +125,15 @@ impl WslBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, app: AppHandle, options: ClaudeStartOptions) -> Result<(), String> {
|
pub fn start(&mut self, app: AppHandle, options: ClaudeStartOptions) -> Result<(), String> {
|
||||||
// If a process handle exists but the process has already exited (e.g. due to a
|
|
||||||
// failed working directory), clean up the stale handle so we can restart cleanly.
|
|
||||||
if let Some(ref mut process) = self.process {
|
|
||||||
if process.try_wait().map(|s| s.is_some()).unwrap_or(false) {
|
|
||||||
self.process = None;
|
|
||||||
self.stdin = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.process.is_some() {
|
if self.process.is_some() {
|
||||||
return Err("Process already running".to_string());
|
return Err("Process already running".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if Claude binary is installed before attempting to start
|
||||||
|
if Command::new("which").arg("claude").output().ok().is_none_or(|output| !output.status.success()) {
|
||||||
|
return Err("Claude Code is not installed. Please install it using:\n\ncurl -fsSL https://claude.ai/install.sh | bash".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// Load saved achievements and stats when starting a new session
|
// Load saved achievements and stats when starting a new session
|
||||||
let app_clone = app.clone();
|
let app_clone = app.clone();
|
||||||
let stats = self.stats.clone();
|
let stats = self.stats.clone();
|
||||||
@@ -266,30 +262,6 @@ impl WslBridge {
|
|||||||
} else {
|
} else {
|
||||||
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
|
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
|
||||||
tracing::debug!("Windows path - using wsl");
|
tracing::debug!("Windows path - using wsl");
|
||||||
|
|
||||||
// Check if Claude binary is installed inside WSL
|
|
||||||
let binary_check = Command::new("wsl")
|
|
||||||
.args(["-e", "bash", "-lc", "which claude"])
|
|
||||||
.output();
|
|
||||||
if let Ok(output) = binary_check {
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err("Claude Code is not installed. Please install it using:\n\ncurl -fsSL https://claude.ai/install.sh | bash".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the working directory exists inside WSL before spawning
|
|
||||||
let dir_check = Command::new("wsl")
|
|
||||||
.args(["-e", "test", "-d", working_dir])
|
|
||||||
.output();
|
|
||||||
if let Ok(output) = dir_check {
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(format!(
|
|
||||||
"Working directory does not exist: {}",
|
|
||||||
working_dir
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cmd = Command::new("wsl");
|
let mut cmd = Command::new("wsl");
|
||||||
|
|
||||||
// Build the claude command with all arguments
|
// Build the claude command with all arguments
|
||||||
@@ -1902,66 +1874,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stale_process_detection_with_try_wait() {
|
fn test_claude_binary_check_command_structure() {
|
||||||
// Spawn a real process that exits immediately so we can verify try_wait detects it
|
// Test that we're using the correct command to check for Claude binary
|
||||||
let mut child = Command::new("true").spawn().expect("Failed to spawn 'true'");
|
let output = Command::new("which").arg("claude").output();
|
||||||
|
|
||||||
// Wait for it to exit
|
// The command should execute successfully (even if claude is not found)
|
||||||
let _ = child.wait();
|
// We're just verifying the command structure is valid
|
||||||
|
assert!(output.is_ok(), "which command should execute without error");
|
||||||
|
|
||||||
// try_wait on an already-exited process should return Some(_)
|
// Verify the check logic returns a boolean
|
||||||
let status = child.try_wait();
|
// This is the same logic used in start() to check if claude is installed
|
||||||
assert!(
|
let _result = output.ok().is_none_or(|o| !o.status.success());
|
||||||
status.is_ok(),
|
// If claude is not installed, _result will be true (show error)
|
||||||
"try_wait should not error on an exited process"
|
// If claude is installed, _result will be false (proceed with connection)
|
||||||
);
|
|
||||||
// The process has already been waited on, so try_wait might return None or Some
|
|
||||||
// depending on the OS - what matters is that the call succeeds
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_stale_process_is_some_after_exit() {
|
|
||||||
// Verify the logic used in start(): a process that has exited is detected
|
|
||||||
// and the handle is cleaned up so start() can proceed
|
|
||||||
let mut child = Command::new("true").spawn().expect("Failed to spawn 'true'");
|
|
||||||
|
|
||||||
// Let it exit
|
|
||||||
let _ = child.wait();
|
|
||||||
|
|
||||||
// This mirrors the check in start()
|
|
||||||
let has_exited = child
|
|
||||||
.try_wait()
|
|
||||||
.map(|s| s.is_some())
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
// After wait(), try_wait() returns None (already reaped), which means
|
|
||||||
// unwrap_or(false) → false. The important thing is the call doesn't panic
|
|
||||||
// and the control flow logic compiles and runs correctly.
|
|
||||||
let _ = has_exited; // suppress unused warning
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the WSL binary check command structure without executing it (for testing)
|
|
||||||
#[cfg(test)]
|
|
||||||
fn build_wsl_binary_check_args() -> Vec<&'static str> {
|
|
||||||
vec!["-e", "bash", "-lc", "which claude"]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wsl_binary_check_command_structure() {
|
|
||||||
// Windows path: verify Claude is detected inside WSL via `wsl -e bash -lc "which claude"`
|
|
||||||
let args = build_wsl_binary_check_args();
|
|
||||||
assert_eq!(args[0], "-e");
|
|
||||||
assert_eq!(args[1], "bash");
|
|
||||||
assert_eq!(args[2], "-lc");
|
|
||||||
assert_eq!(args[3], "which claude");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_linux_binary_check_does_not_panic() {
|
|
||||||
// Linux/WSL path: find_claude_binary() searches Linux filesystem paths.
|
|
||||||
// We just verify it runs without panicking; whether it returns Some depends
|
|
||||||
// on whether Claude is actually installed in this environment.
|
|
||||||
let _result = find_claude_binary();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -83,9 +83,8 @@
|
|||||||
{ value: "", label: "Default (from ~/.claude)" },
|
{ value: "", label: "Default (from ~/.claude)" },
|
||||||
// Current generation (Claude 4.6)
|
// Current generation (Claude 4.6)
|
||||||
{ value: "claude-opus-4-6", label: "Claude Opus 4.6 (Most Capable)" },
|
{ value: "claude-opus-4-6", label: "Claude Opus 4.6 (Most Capable)" },
|
||||||
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 (Recommended)" },
|
|
||||||
// Previous generation (Claude 4.5)
|
// Previous generation (Claude 4.5)
|
||||||
{ value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
|
{ value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5 (Recommended)" },
|
||||||
{ value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5 (Fast & Cheap)" },
|
{ value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5 (Fast & Cheap)" },
|
||||||
{ value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5" },
|
{ value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5" },
|
||||||
// Previous generation (Claude 4.x)
|
// Previous generation (Claude 4.x)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export type BudgetType = "token" | "cost";
|
|||||||
export const MODEL_PRICING: Record<string, { input: number; output: number }> = {
|
export const MODEL_PRICING: Record<string, { input: number; output: number }> = {
|
||||||
// Current generation (Claude 4.6)
|
// Current generation (Claude 4.6)
|
||||||
"claude-opus-4-6": { input: 5.0, output: 25.0 },
|
"claude-opus-4-6": { input: 5.0, output: 25.0 },
|
||||||
"claude-sonnet-4-6": { input: 3.0, output: 15.0 },
|
|
||||||
// Previous generation (Claude 4.5)
|
// Previous generation (Claude 4.5)
|
||||||
"claude-opus-4-5-20251101": { input: 5.0, output: 25.0 },
|
"claude-opus-4-5-20251101": { input: 5.0, output: 25.0 },
|
||||||
"claude-sonnet-4-5-20250929": { input: 3.0, output: 15.0 },
|
"claude-sonnet-4-5-20250929": { input: 3.0, output: 15.0 },
|
||||||
|
|||||||
Reference in New Issue
Block a user