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
26 changes: 26 additions & 0 deletions backend/src/db/migrations/202610141200-secrets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Migration } from '../migration-runner'

const migration: Migration = {
id: '202610141200-secrets',
up(db) {
db.run(`
CREATE TABLE IF NOT EXISTS secrets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind TEXT NOT NULL CHECK (kind IN ('secret','variable')),
repo_id INTEGER REFERENCES repos(id) ON DELETE CASCADE,
environment TEXT NOT NULL DEFAULT '',
name TEXT NOT NULL,
value TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)
`)

db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_secrets_scope ON secrets(kind, COALESCE(repo_id, 0), environment, name)')
},
down(db) {
db.run('DROP TABLE IF EXISTS secrets')
},
}

export default migration
2 changes: 2 additions & 0 deletions backend/src/db/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import migration202610061345 from './202610061345-schedule-workspace-mode'
import migration202610061700 from './202610061700-multi-run-fusions'
import migration202610061701 from './202610061701-change-walkthroughs'
import migration202610081200 from './202610081200-change-walkthrough-sources'
import migration202610141200 from './202610141200-secrets'

export const allMigrations: Migration[] = [
migration001,
Expand Down Expand Up @@ -63,4 +64,5 @@ export const allMigrations: Migration[] = [
migration202610061700,
migration202610061701,
migration202610081200,
migration202610141200,
]
1 change: 1 addition & 0 deletions backend/src/db/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ export function deleteRepo(db: Database, id: number): void {
db.prepare('DELETE FROM multi_run_fusions WHERE multi_run_id IN (SELECT id FROM multi_runs WHERE repo_id = ?)').run(id)
db.prepare('DELETE FROM multi_run_entries WHERE multi_run_id IN (SELECT id FROM multi_runs WHERE repo_id = ?)').run(id)
db.prepare('DELETE FROM multi_runs WHERE repo_id = ?').run(id)
db.prepare('DELETE FROM secrets WHERE repo_id = ?').run(id)
for (const table of TABLES_WITH_REPO_ID) {
db.prepare(`DELETE FROM ${table} WHERE repo_id = ?`).run(id)
}
Expand Down
18 changes: 13 additions & 5 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { createMultiRunRoutes } from './routes/multi-runs'
import { createChangeWalkthroughRoutes } from './routes/change-walkthroughs'
import { createLogRoutes } from './routes/logs'
import { createPreviewRoutes, createPreviewAvailability } from './routes/preview'
import { createGlobalSecretRoutes } from './routes/secrets'
import { createPreviewGatewayApp, PreviewSessionStore } from './services/preview/gateway'
import { createInternalRoutes } from './routes/internal'
import { sweepStaleUploadSessions } from './routes/internal/repo-mirror-helpers'
Expand All @@ -64,6 +65,8 @@ import { ScheduleRunner, ScheduleService } from './services/schedules'
import { CredentialProvider } from './services/credential-provider'
import { TerminalService } from './services/terminal'
import { ProjectConfigService } from './services/project-config'
import { SecretsService } from './services/secrets'
import { RepoEnvService } from './services/repo-env'
import { RepoWorkspaceService } from './services/repo-workspace'
import { createGitService } from './services/git/GitService'
import { ScheduleWorktreeManager } from './services/schedule-worktree'
Expand Down Expand Up @@ -127,6 +130,10 @@ import { DEFAULT_AGENTS_MD } from './constants'

let ipcServer: IPCServer | undefined
const gitAuthService = new GitAuthService()
const gitService = createGitService(gitAuthService)
const projectConfigService = new ProjectConfigService(db, gitService, gitAuthService)
const secretsService = new SecretsService(db)
const repoEnvService = new RepoEnvService(db, projectConfigService, secretsService)
let openCodeSupervisor: OpenCodeSupervisor | undefined
async function ensureOpenCodeConfigFileExists(): Promise<void> {
await withOpenCodeConfigLock(foldLegacyConfigJsonSource)
Expand Down Expand Up @@ -225,7 +232,7 @@ try {

opencodeServerManager.setDatabase(db)
detectSandboxCapability()
void new SandboxRuntimeService(db).prepareWorkspaceSandboxOnBoot().catch((error) => {
void new SandboxRuntimeService(db, repoEnvService).prepareWorkspaceSandboxOnBoot().catch((error) => {
logger.warn('Workspace sandbox preparation failed:', error)
})
const openCodeStatus = await openCodeSupervisor.start()
Expand All @@ -242,10 +249,9 @@ try {
const terminalService = new TerminalService(
openCodeClient,
new CredentialProvider(db),
repoEnvService,
() => getOpenCodeUpstreamBaseUrl(opencodeServerManager.getEffectiveServerHost()),
)
const gitService = createGitService(gitAuthService)
const projectConfigService = new ProjectConfigService(db, gitService, gitAuthService)
const scheduleWorktreeManager = new ScheduleWorktreeManager(gitAuthService, db)
const scheduleService = new ScheduleService(db, openCodeClient, scheduleWorktreeManager)
scheduleService.setWorktreeRemovedHandler(async (directory) => {
Expand Down Expand Up @@ -341,14 +347,14 @@ app.route('/api/auth-info', createAuthInfoRoutes(auth, db))
app.route('/api/health', createHealthRoutes(db, openCodeSupervisor))

app.route('/api/mcp-oauth-proxy', createMcpOauthProxyRoutes(openCodeClient, requireAuth))
app.route('/api/internal', createInternalRoutes(db, scheduleService, notificationService, settingsService, openCodeClient, sessionPermissionModeService, repoWorkspaces, gitAuthService, sessionGoalService, multiRunService, changeWalkthroughService))
app.route('/api/internal', createInternalRoutes(db, scheduleService, notificationService, settingsService, openCodeClient, sessionPermissionModeService, repoWorkspaces, gitAuthService, sessionGoalService, multiRunService, changeWalkthroughService, repoEnvService))
app.route('/api/opencode-proxy', createOpenCodeProxyRoutes(db, settingsService, upgradeWebSocket))

const protectedApi = new Hono()
protectedApi.use('/*', requireAuth)

protectedApi.route('/repos', createRepoTerminalSocketRoutes(db, gitAuthService, openCodeClient, terminalService, upgradeWebSocket))
protectedApi.route('/repos', createRepoRoutes(db, gitAuthService, scheduleService, openCodeClient, terminalService, projectConfigService, repoWorkspaces))
protectedApi.route('/repos', createRepoRoutes(db, gitAuthService, scheduleService, openCodeClient, terminalService, projectConfigService, repoWorkspaces, secretsService))
protectedApi.route('/settings', createSettingsRoutes(db, gitAuthService, openCodeClient, openCodeSupervisor))
protectedApi.route('/files', createFileRoutes())
protectedApi.route('/filesystem', createFilesystemRoutes())
Expand All @@ -360,6 +366,8 @@ protectedApi.route('/sse', createSSERoutes())
protectedApi.route('/ssh', createSSHRoutes(gitAuthService))
protectedApi.route('/notifications', createNotificationRoutes(notificationService))
protectedApi.route('/prompt-templates', createPromptTemplateRoutes(db))
protectedApi.route('/secrets', createGlobalSecretRoutes(secretsService, 'secret'))
protectedApi.route('/variables', createGlobalSecretRoutes(secretsService, 'variable'))
protectedApi.route('/session-pins', createSessionPinRoutes(db))
protectedApi.route('/session-permission-modes', createSessionPermissionModeRoutes(sessionPermissionModeService))
protectedApi.route('/session-goals', createSessionGoalRoutes(sessionGoalService))
Expand Down
15 changes: 0 additions & 15 deletions backend/src/routes/internal/git-credentials.ts

This file was deleted.

8 changes: 5 additions & 3 deletions backend/src/routes/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { createInternalRepoMirrorRoutes as mirrorRoutes } from './repo-mirror'
import { createInternalOpenCodeWorkspacesRoutes } from './opencode-workspaces'
import { createInternalSessionRoutes } from './sessions'
import { createInternalAssistantRoutes } from './assistant'
import { createInternalGitCredentialsRoutes } from './git-credentials'
import { createInternalShellEnvRoutes } from './shell-env'
import { createInternalSandboxRoutes } from './sandbox'
import { createInternalSessionArtifactRoutes } from './session-artifacts'
import { createSessionGoalRoutes } from '../session-goals'
Expand All @@ -27,6 +27,7 @@ import type { GitAuthService } from '../../services/git-auth'
import type { SessionGoalService } from '../../services/session-goals'
import type { MultiRunService } from '../../services/multi-runs'
import type { ChangeWalkthroughService } from '../../services/change-walkthroughs'
import type { RepoEnvService } from '../../services/repo-env'

export function createInternalRoutes(
db: Database,
Expand All @@ -40,6 +41,7 @@ export function createInternalRoutes(
sessionGoals: SessionGoalService,
multiRuns: MultiRunService,
changeWalkthroughService: ChangeWalkthroughService,
repoEnvService: RepoEnvService,
) {
const app = new Hono()
app.use('/*', createInternalTokenMiddleware(db))
Expand All @@ -60,7 +62,7 @@ export function createInternalRoutes(
app.route('/assistant', createInternalAssistantRoutes(openCodeClient))
app.route('/change-walkthroughs', createChangeWalkthroughRoutes(changeWalkthroughService))
app.route('/session-artifacts', createInternalSessionArtifactRoutes(openCodeClient))
app.route('/git-credentials', createInternalGitCredentialsRoutes(db))
app.route('/sandbox', createInternalSandboxRoutes(db))
app.route('/shell-env', createInternalShellEnvRoutes(db, repoEnvService))
app.route('/sandbox', createInternalSandboxRoutes(db, repoEnvService))
return app
}
5 changes: 3 additions & 2 deletions backend/src/routes/internal/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { Hono } from 'hono'
import { z } from 'zod'
import type { Database } from 'bun:sqlite'
import { SandboxRuntimeService } from '../../services/sandbox/runtime'
import type { RepoEnvService } from '../../services/repo-env'
import { logger } from '../../utils/logger'

const SandboxShellRequestSchema = z.object({
directory: z.string().min(1),
enforced: z.boolean().optional(),
})

export function createInternalSandboxRoutes(db: Database) {
export function createInternalSandboxRoutes(db: Database, repoEnvService: RepoEnvService) {
const app = new Hono()

app.post('/shell', async (c) => {
Expand All @@ -27,7 +28,7 @@ export function createInternalSandboxRoutes(db: Database) {

try {
return c.json(
await new SandboxRuntimeService(db).planShell(parsed.data.directory, parsed.data.enforced === true),
await new SandboxRuntimeService(db, repoEnvService).planShell(parsed.data.directory, parsed.data.enforced === true),
)
} catch (error) {
logger.error('Failed to plan the sandbox shell', error)
Expand Down
22 changes: 22 additions & 0 deletions backend/src/routes/internal/shell-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Hono } from 'hono'
import type { Database } from 'bun:sqlite'
import { CredentialProvider } from '../../services/credential-provider'
import type { RepoEnvService } from '../../services/repo-env'

export function createInternalShellEnvRoutes(db: Database, repoEnvService: RepoEnvService) {
const app = new Hono()

app.get('/', async (c) => {
const provider = new CredentialProvider(db)
const cwd = c.req.query('cwd')
const ghEnv = provider.getGhCliEnv({ cwd })
if (!cwd) {
return c.json({ env: ghEnv, mask: [] })
}

const repoEnv = await repoEnvService.resolveForDirectory(cwd)
return c.json({ env: { ...ghEnv, ...repoEnv.env }, mask: repoEnv.maskValues })
})

return app
}
3 changes: 2 additions & 1 deletion backend/src/routes/repos.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createRepo, getRepoById, setRepoSetting } from '../db/queries'
import { createStubOpenCodeClient } from '../../test/helpers/stub-opencode-client'
import { createGitService } from '../services/git/GitService'
import { ProjectConfigService } from '../services/project-config'
import type { SecretsService } from '../services/secrets'
import { RepoWorkspaceService } from '../services/repo-workspace'
import type { GitAuthService } from '../services/git-auth'
import type { TerminalService } from '../services/terminal'
Expand Down Expand Up @@ -68,7 +69,7 @@ function createTestApp(
} as any
const projectConfigService = new ProjectConfigService(db, createGitService(stubGitAuthService), stubGitAuthService)
const repoWorkspaces = new RepoWorkspaceService(db, openCodeClient, stubGitAuthService, projectConfigService, terminalService, scheduleService)
app.route('/repos', createRepoRoutes(db, stubGitAuthService, scheduleService, openCodeClient, terminalService, projectConfigService, repoWorkspaces))
app.route('/repos', createRepoRoutes(db, stubGitAuthService, scheduleService, openCodeClient, terminalService, projectConfigService, repoWorkspaces, {} as unknown as SecretsService))
return app
}

Expand Down
5 changes: 5 additions & 0 deletions backend/src/routes/repos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import { isWorktreeError, openCodeLocation } from '@opencode-manager/shared/open
import { createRepoGitRoutes } from './repo-git'
import { createRepoTerminalRoutes } from './repo-terminals'
import { createRepoProjectConfigRoutes } from './repo-project-config'
import { createRepoSecretRoutes } from './secrets'
import { createScheduleRoutes } from './schedules'
import type { GitAuthService } from '../services/git-auth'
import type { TerminalService } from '../services/terminal'
import type { ProjectConfigService } from '../services/project-config'
import type { SecretsService } from '../services/secrets'
import type { RepoWorkspaceService } from '../services/repo-workspace'
import { createGitService } from '../services/git/GitService'
import { ScheduleService } from '../services/schedules'
Expand All @@ -47,6 +49,7 @@ export function createRepoRoutes(
terminalService: TerminalService,
projectConfigService: ProjectConfigService,
repoWorkspaces: RepoWorkspaceService,
secretsService: SecretsService,
) {
const app = new Hono()
const git = createGitService(gitAuthService)
Expand All @@ -55,6 +58,8 @@ export function createRepoRoutes(
app.route('/', createRepoTerminalRoutes(database, gitAuthService, openCodeClient, terminalService))
app.route('/', createRepoProjectConfigRoutes(database, gitAuthService, openCodeClient, projectConfigService, terminalService))
app.route('/:id/schedules', createScheduleRoutes(scheduleService))
app.route('/', createRepoSecretRoutes({ database, projectConfigService, secretsService }, 'secret'))
app.route('/', createRepoSecretRoutes({ database, projectConfigService, secretsService }, 'variable'))

app.post('/', async (c) => {
try {
Expand Down
Loading
Loading