Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion electron/db/migrations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type Database from 'better-sqlite3'
import { SCHEMA_V1, SCHEMA_V2, SCHEMA_V3, SCHEMA_V4, SCHEMA_V5, SCHEMA_V6, SCHEMA_V7, SCHEMA_V8, SCHEMA_V9, SCHEMA_V10, SCHEMA_V11, SCHEMA_V12, SCHEMA_V13, SCHEMA_V14, SCHEMA_V15, SCHEMA_V16, SCHEMA_V17, SCHEMA_V18, SCHEMA_V19, SCHEMA_V20, SCHEMA_V21, SCHEMA_V22, SCHEMA_V23, SCHEMA_V24, SCHEMA_V25, SCHEMA_V26, SCHEMA_V27, SCHEMA_V28, SCHEMA_V29, SCHEMA_V30, SCHEMA_V31, SCHEMA_V32, SCHEMA_V33, SCHEMA_V34, SCHEMA_V35, SCHEMA_V36, SCHEMA_V37, SCHEMA_V38, SCHEMA_V39, SCHEMA_V40, SCHEMA_V41, SCHEMA_V42, SCHEMA_V43, SCHEMA_V44 } from './schema'
import { SCHEMA_V1, SCHEMA_V2, SCHEMA_V3, SCHEMA_V4, SCHEMA_V5, SCHEMA_V6, SCHEMA_V7, SCHEMA_V8, SCHEMA_V9, SCHEMA_V10, SCHEMA_V11, SCHEMA_V12, SCHEMA_V13, SCHEMA_V14, SCHEMA_V15, SCHEMA_V16, SCHEMA_V17, SCHEMA_V18, SCHEMA_V19, SCHEMA_V20, SCHEMA_V21, SCHEMA_V22, SCHEMA_V23, SCHEMA_V24, SCHEMA_V25, SCHEMA_V26, SCHEMA_V27, SCHEMA_V28, SCHEMA_V29, SCHEMA_V30, SCHEMA_V31, SCHEMA_V32, SCHEMA_V33, SCHEMA_V34, SCHEMA_V35, SCHEMA_V36, SCHEMA_V37, SCHEMA_V38, SCHEMA_V39, SCHEMA_V40, SCHEMA_V41, SCHEMA_V42, SCHEMA_V43, SCHEMA_V44, SCHEMA_V45 } from './schema'

export function runMigrations(db: Database.Database) {
db.exec(`
Expand Down Expand Up @@ -526,6 +526,19 @@ export function runMigrations(db: Database.Database) {
})()
}

if (currentVersion < 45) {
db.transaction(() => {
const stmts = SCHEMA_V45.split(';').map((s) => s.trim()).filter(Boolean)
for (const stmt of stmts) {
try { db.exec(stmt) } catch (err) {
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase()
if (!msg.includes('duplicate column') && !msg.includes('already exists')) throw err
}
}
db.prepare('INSERT INTO _migrations (version) VALUES (?)').run(45)
})()
}

// Ensure Solana agent exists (idempotent — handles existing DBs before it was seeded)
try {
const hasSolanaAgent = db.prepare("SELECT id FROM agents WHERE id = 'solana-agent'").get()
Expand Down
10 changes: 9 additions & 1 deletion electron/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ CREATE TABLE IF NOT EXISTS projects (
aliases TEXT DEFAULT '[]',
wallet_id TEXT,
created_at INTEGER DEFAULT (CAST(unixepoch('now') * 1000 AS INTEGER)),
last_active INTEGER
last_active INTEGER,
pinned INTEGER NOT NULL DEFAULT 0,
branch TEXT
);

CREATE TABLE IF NOT EXISTS active_sessions (
Expand Down Expand Up @@ -1175,3 +1177,9 @@ CREATE TABLE IF NOT EXISTS forensic_bundle_wallet_index (
CREATE INDEX IF NOT EXISTS idx_forensic_bundle_wallet
ON forensic_bundle_wallet_index(wallet);
`

export const SCHEMA_V45 = `
ALTER TABLE projects ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;
ALTER TABLE projects ADD COLUMN branch TEXT;
CREATE INDEX IF NOT EXISTS idx_projects_pinned ON projects(pinned DESC, last_active DESC);
`
36 changes: 34 additions & 2 deletions electron/ipc/projects.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,45 @@
import { ipcMain, dialog } from 'electron'
import simpleGit from 'simple-git'
import { getDb } from '../db/db'
import { invalidatePathCache } from '../shared/pathValidation'
import { ipcHandler } from '../services/IpcHandlerFactory'
import type { ProjectCreateInput } from '../shared/types'
import type { Project, ProjectCreateInput } from '../shared/types'

/** Best-effort current branch; null when the path isn't a git repo or is gone. */
async function resolveBranch(path: string): Promise<string | null> {
try {
const branch = await simpleGit(path).revparse(['--abbrev-ref', 'HEAD'])
return branch.trim() || null
} catch {
return null
}
}

export function registerProjectHandlers() {
ipcMain.handle('projects:list', ipcHandler(async () => {
const db = getDb()
return db.prepare('SELECT * FROM projects ORDER BY last_active DESC, created_at DESC').all()
const rows = db
.prepare('SELECT * FROM projects ORDER BY pinned DESC, last_active DESC, created_at DESC')
.all() as Project[]

// Refresh the cached branch for each project so the recents list stays accurate.
const updateBranch = db.prepare('UPDATE projects SET branch = ? WHERE id = ?')
await Promise.all(
rows.map(async (row) => {
const branch = await resolveBranch(row.path)
if (branch !== row.branch) {
updateBranch.run(branch, row.id)
row.branch = branch
}
})
)
return rows
}))

ipcMain.handle('projects:setPinned', ipcHandler(async (_event, input: { id: string; pinned: boolean }) => {
const db = getDb()
db.prepare('UPDATE projects SET pinned = ? WHERE id = ?').run(input.pinned ? 1 : 0, input.id)
return db.prepare('SELECT * FROM projects WHERE id = ?').get(input.id) as Project
}))

ipcMain.handle('projects:create', ipcHandler(async (_event, project: ProjectCreateInput) => {
Expand Down
1 change: 1 addition & 0 deletions electron/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ contextBridge.exposeInMainWorld('daemon', {
create: (project: { name: string; path: string }) => ipcRenderer.invoke('projects:create', project),
delete: (id: string) => ipcRenderer.invoke('projects:delete', id),
openDialog: () => ipcRenderer.invoke('projects:openDialog'),
setPinned: (input: { id: string; pinned: boolean }) => ipcRenderer.invoke('projects:setPinned', input),
},

shell: {
Expand Down
2 changes: 2 additions & 0 deletions electron/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ export interface Project {
wallet_id: string | null
created_at: number
last_active: number | null
pinned: number
branch: string | null
}

export interface Agent {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
"brace-expansion": "5.0.6",
"dompurify": "3.4.0",
"esbuild": "0.25.12",
"hono": "4.12.18",
"hono": "4.12.21",
"lodash": "4.18.1",
"follow-redirects": "1.16.0",
"postcss": "8.5.10",
Expand Down
30 changes: 15 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading