- #511: Add gemini-3-flash model to GeminiAgent (type, RPM limits, validation) - #517: Replace PowerShell with WMIC for Windows process management (fixes Git Bash/WSL) - #527: Add Apple Silicon Homebrew paths for bun and uv detection - #531: Remove duplicate type definitions from export-memories.ts using bridge file 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ function isBunInstalled() {
|
|||||||
// Check common installation paths (handles fresh installs before PATH reload)
|
// Check common installation paths (handles fresh installs before PATH reload)
|
||||||
const bunPaths = IS_WINDOWS
|
const bunPaths = IS_WINDOWS
|
||||||
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||||
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun'];
|
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun', '/opt/homebrew/bin/bun'];
|
||||||
|
|
||||||
return bunPaths.some(existsSync);
|
return bunPaths.some(existsSync);
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ function getBunPath() {
|
|||||||
// Check common installation paths
|
// Check common installation paths
|
||||||
const bunPaths = IS_WINDOWS
|
const bunPaths = IS_WINDOWS
|
||||||
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||||
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun'];
|
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun', '/opt/homebrew/bin/bun'];
|
||||||
|
|
||||||
for (const bunPath of bunPaths) {
|
for (const bunPath of bunPaths) {
|
||||||
if (existsSync(bunPath)) return bunPath;
|
if (existsSync(bunPath)) return bunPath;
|
||||||
@@ -102,7 +102,7 @@ function isUvInstalled() {
|
|||||||
// Check common installation paths (handles fresh installs before PATH reload)
|
// Check common installation paths (handles fresh installs before PATH reload)
|
||||||
const uvPaths = IS_WINDOWS
|
const uvPaths = IS_WINDOWS
|
||||||
? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')]
|
? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')]
|
||||||
: [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv'];
|
: [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv', '/opt/homebrew/bin/uv'];
|
||||||
|
|
||||||
return uvPaths.some(existsSync);
|
return uvPaths.some(existsSync);
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ function installBun() {
|
|||||||
// Try common installation paths
|
// Try common installation paths
|
||||||
const bunPaths = IS_WINDOWS
|
const bunPaths = IS_WINDOWS
|
||||||
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||||
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun'];
|
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun', '/opt/homebrew/bin/bun'];
|
||||||
|
|
||||||
for (const bunPath of bunPaths) {
|
for (const bunPath of bunPaths) {
|
||||||
if (existsSync(bunPath)) {
|
if (existsSync(bunPath)) {
|
||||||
@@ -221,7 +221,7 @@ function installUv() {
|
|||||||
// Try common installation paths
|
// Try common installation paths
|
||||||
const uvPaths = IS_WINDOWS
|
const uvPaths = IS_WINDOWS
|
||||||
? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')]
|
? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')]
|
||||||
: [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv'];
|
: [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv', '/opt/homebrew/bin/uv'];
|
||||||
|
|
||||||
for (const uvPath of uvPaths) {
|
for (const uvPath of uvPaths) {
|
||||||
if (existsSync(uvPath)) {
|
if (existsSync(uvPath)) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -9,80 +9,13 @@ import { writeFileSync } from 'fs';
|
|||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { SettingsDefaultsManager } from '../src/shared/SettingsDefaultsManager';
|
import { SettingsDefaultsManager } from '../src/shared/SettingsDefaultsManager';
|
||||||
|
import type {
|
||||||
interface ObservationRecord {
|
ObservationRecord,
|
||||||
id: number;
|
SdkSessionRecord,
|
||||||
memory_session_id: string;
|
SessionSummaryRecord,
|
||||||
project: string;
|
UserPromptRecord,
|
||||||
text: string | null;
|
ExportData
|
||||||
type: string;
|
} from './types/export.js';
|
||||||
title: string;
|
|
||||||
subtitle: string | null;
|
|
||||||
facts: string | null;
|
|
||||||
narrative: string | null;
|
|
||||||
concepts: string | null;
|
|
||||||
files_read: string | null;
|
|
||||||
files_modified: string | null;
|
|
||||||
prompt_number: number;
|
|
||||||
discovery_tokens: number | null;
|
|
||||||
created_at: string;
|
|
||||||
created_at_epoch: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SdkSessionRecord {
|
|
||||||
id: number;
|
|
||||||
content_session_id: string;
|
|
||||||
memory_session_id: string;
|
|
||||||
project: string;
|
|
||||||
user_prompt: string;
|
|
||||||
started_at: string;
|
|
||||||
started_at_epoch: number;
|
|
||||||
completed_at: string | null;
|
|
||||||
completed_at_epoch: number | null;
|
|
||||||
status: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SessionSummaryRecord {
|
|
||||||
id: number;
|
|
||||||
memory_session_id: string;
|
|
||||||
project: string;
|
|
||||||
request: string | null;
|
|
||||||
investigated: string | null;
|
|
||||||
learned: string | null;
|
|
||||||
completed: string | null;
|
|
||||||
next_steps: string | null;
|
|
||||||
files_read: string | null;
|
|
||||||
files_edited: string | null;
|
|
||||||
notes: string | null;
|
|
||||||
prompt_number: number;
|
|
||||||
discovery_tokens: number | null;
|
|
||||||
created_at: string;
|
|
||||||
created_at_epoch: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserPromptRecord {
|
|
||||||
id: number;
|
|
||||||
content_session_id: string;
|
|
||||||
prompt_number: number;
|
|
||||||
prompt_text: string;
|
|
||||||
created_at: string;
|
|
||||||
created_at_epoch: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExportData {
|
|
||||||
exportedAt: string;
|
|
||||||
exportedAtEpoch: number;
|
|
||||||
query: string;
|
|
||||||
project?: string;
|
|
||||||
totalObservations: number;
|
|
||||||
totalSessions: number;
|
|
||||||
totalSummaries: number;
|
|
||||||
totalPrompts: number;
|
|
||||||
observations: ObservationRecord[];
|
|
||||||
sessions: SdkSessionRecord[];
|
|
||||||
summaries: SessionSummaryRecord[];
|
|
||||||
prompts: UserPromptRecord[];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function exportMemories(query: string, outputFile: string, project?: string) {
|
async function exportMemories(query: string, outputFile: string, project?: string) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ const IS_WINDOWS = process.platform === 'win32';
|
|||||||
// Common installation paths (handles fresh installs before PATH reload)
|
// Common installation paths (handles fresh installs before PATH reload)
|
||||||
const BUN_COMMON_PATHS = IS_WINDOWS
|
const BUN_COMMON_PATHS = IS_WINDOWS
|
||||||
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||||
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun'];
|
: [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun', '/opt/homebrew/bin/bun'];
|
||||||
|
|
||||||
const UV_COMMON_PATHS = IS_WINDOWS
|
const UV_COMMON_PATHS = IS_WINDOWS
|
||||||
? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')]
|
? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')]
|
||||||
: [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv'];
|
: [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv', '/opt/homebrew/bin/uv'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Bun executable path (from PATH or common install locations)
|
* Get the Bun executable path (from PATH or common install locations)
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Export/Import types for memory data
|
||||||
|
*
|
||||||
|
* These types represent the structure of exported memory data.
|
||||||
|
* They are aligned with the actual database schema and include all fields
|
||||||
|
* needed for complete data export and import operations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observation record as stored in the database and exported
|
||||||
|
*/
|
||||||
|
export interface ObservationRecord {
|
||||||
|
id: number;
|
||||||
|
memory_session_id: string;
|
||||||
|
project: string;
|
||||||
|
text: string | null;
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string | null;
|
||||||
|
facts: string | null;
|
||||||
|
narrative: string | null;
|
||||||
|
concepts: string | null;
|
||||||
|
files_read: string | null;
|
||||||
|
files_modified: string | null;
|
||||||
|
prompt_number: number;
|
||||||
|
discovery_tokens: number | null;
|
||||||
|
created_at: string;
|
||||||
|
created_at_epoch: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SDK Session record as stored in the database and exported
|
||||||
|
*/
|
||||||
|
export interface SdkSessionRecord {
|
||||||
|
id: number;
|
||||||
|
content_session_id: string;
|
||||||
|
memory_session_id: string;
|
||||||
|
project: string;
|
||||||
|
user_prompt: string;
|
||||||
|
started_at: string;
|
||||||
|
started_at_epoch: number;
|
||||||
|
completed_at: string | null;
|
||||||
|
completed_at_epoch: number | null;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session Summary record as stored in the database and exported
|
||||||
|
*/
|
||||||
|
export interface SessionSummaryRecord {
|
||||||
|
id: number;
|
||||||
|
memory_session_id: string;
|
||||||
|
project: string;
|
||||||
|
request: string | null;
|
||||||
|
investigated: string | null;
|
||||||
|
learned: string | null;
|
||||||
|
completed: string | null;
|
||||||
|
next_steps: string | null;
|
||||||
|
files_read: string | null;
|
||||||
|
files_edited: string | null;
|
||||||
|
notes: string | null;
|
||||||
|
prompt_number: number;
|
||||||
|
discovery_tokens: number | null;
|
||||||
|
created_at: string;
|
||||||
|
created_at_epoch: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Prompt record as stored in the database and exported
|
||||||
|
*/
|
||||||
|
export interface UserPromptRecord {
|
||||||
|
id: number;
|
||||||
|
content_session_id: string;
|
||||||
|
prompt_number: number;
|
||||||
|
prompt_text: string;
|
||||||
|
created_at: string;
|
||||||
|
created_at_epoch: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete export data structure
|
||||||
|
*/
|
||||||
|
export interface ExportData {
|
||||||
|
exportedAt: string;
|
||||||
|
exportedAtEpoch: number;
|
||||||
|
query: string;
|
||||||
|
project?: string;
|
||||||
|
totalObservations: number;
|
||||||
|
totalSessions: number;
|
||||||
|
totalSummaries: number;
|
||||||
|
totalPrompts: number;
|
||||||
|
observations: ObservationRecord[];
|
||||||
|
sessions: SdkSessionRecord[];
|
||||||
|
summaries: SessionSummaryRecord[];
|
||||||
|
prompts: UserPromptRecord[];
|
||||||
|
}
|
||||||
@@ -88,12 +88,15 @@ export async function getChildProcesses(parentPid: number): Promise<number[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cmd = `powershell -Command "Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq ${parentPid} } | Select-Object -ExpandProperty ProcessId"`;
|
const cmd = `wmic process where "parentprocessid=${parentPid}" get processid /format:list`;
|
||||||
const { stdout } = await execAsync(cmd, { timeout: 60000 });
|
const { stdout } = await execAsync(cmd, { timeout: 60000 });
|
||||||
return stdout
|
return stdout
|
||||||
.trim()
|
.trim()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(s => parseInt(s.trim(), 10))
|
.map(line => {
|
||||||
|
const match = line.match(/ProcessId=(\d+)/i);
|
||||||
|
return match ? parseInt(match[1], 10) : NaN;
|
||||||
|
})
|
||||||
.filter(n => !isNaN(n) && Number.isInteger(n) && n > 0);
|
.filter(n => !isNaN(n) && Number.isInteger(n) && n > 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Shutdown cleanup - failure is non-critical, continue without child process cleanup
|
// Shutdown cleanup - failure is non-critical, continue without child process cleanup
|
||||||
@@ -167,8 +170,8 @@ export async function cleanupOrphanedProcesses(): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
// Windows: Use PowerShell Get-CimInstance to find chroma-mcp processes
|
// Windows: Use WMIC to find chroma-mcp processes (avoids PowerShell $_ issues in Git Bash/WSL)
|
||||||
const cmd = `powershell -Command "Get-CimInstance Win32_Process | Where-Object { $_.Name -like '*python*' -and $_.CommandLine -like '*chroma-mcp*' } | Select-Object -ExpandProperty ProcessId"`;
|
const cmd = `wmic process where "name like '%python%' and commandline like '%chroma-mcp%'" get processid /format:list`;
|
||||||
const { stdout } = await execAsync(cmd, { timeout: 60000 });
|
const { stdout } = await execAsync(cmd, { timeout: 60000 });
|
||||||
|
|
||||||
if (!stdout.trim()) {
|
if (!stdout.trim()) {
|
||||||
@@ -176,14 +179,17 @@ export async function cleanupOrphanedProcesses(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pidStrings = stdout.trim().split('\n');
|
const lines = stdout.trim().split('\n');
|
||||||
for (const pidStr of pidStrings) {
|
for (const line of lines) {
|
||||||
const pid = parseInt(pidStr.trim(), 10);
|
const match = line.match(/ProcessId=(\d+)/i);
|
||||||
|
if (match) {
|
||||||
|
const pid = parseInt(match[1], 10);
|
||||||
// SECURITY: Validate PID is positive integer before adding to list
|
// SECURITY: Validate PID is positive integer before adding to list
|
||||||
if (!isNaN(pid) && Number.isInteger(pid) && pid > 0) {
|
if (!isNaN(pid) && Number.isInteger(pid) && pid > 0) {
|
||||||
pids.push(pid);
|
pids.push(pid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Unix: Use ps aux | grep
|
// Unix: Use ps aux | grep
|
||||||
const { stdout } = await execAsync('ps aux | grep "chroma-mcp" | grep -v grep || true');
|
const { stdout } = await execAsync('ps aux | grep "chroma-mcp" | grep -v grep || true');
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export type GeminiModel =
|
|||||||
| 'gemini-2.5-flash'
|
| 'gemini-2.5-flash'
|
||||||
| 'gemini-2.5-pro'
|
| 'gemini-2.5-pro'
|
||||||
| 'gemini-2.0-flash'
|
| 'gemini-2.0-flash'
|
||||||
| 'gemini-2.0-flash-lite';
|
| 'gemini-2.0-flash-lite'
|
||||||
|
| 'gemini-3-flash';
|
||||||
|
|
||||||
// Free tier RPM limits by model (requests per minute)
|
// Free tier RPM limits by model (requests per minute)
|
||||||
const GEMINI_RPM_LIMITS: Record<GeminiModel, number> = {
|
const GEMINI_RPM_LIMITS: Record<GeminiModel, number> = {
|
||||||
@@ -45,6 +46,7 @@ const GEMINI_RPM_LIMITS: Record<GeminiModel, number> = {
|
|||||||
'gemini-2.5-pro': 5,
|
'gemini-2.5-pro': 5,
|
||||||
'gemini-2.0-flash': 15,
|
'gemini-2.0-flash': 15,
|
||||||
'gemini-2.0-flash-lite': 30,
|
'gemini-2.0-flash-lite': 30,
|
||||||
|
'gemini-3-flash': 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Track last request time for rate limiting
|
// Track last request time for rate limiting
|
||||||
@@ -373,6 +375,7 @@ export class GeminiAgent {
|
|||||||
'gemini-2.5-pro',
|
'gemini-2.5-pro',
|
||||||
'gemini-2.0-flash',
|
'gemini-2.0-flash',
|
||||||
'gemini-2.0-flash-lite',
|
'gemini-2.0-flash-lite',
|
||||||
|
'gemini-3-flash',
|
||||||
];
|
];
|
||||||
|
|
||||||
let model: GeminiModel;
|
let model: GeminiModel;
|
||||||
|
|||||||
Reference in New Issue
Block a user