Skip to content

Commit 5602ee7

Browse files
Stabilize project SSH terminal links (#309)
* feat(api): persist project SSH terminal sessions * fix(api): address terminal session review feedback * fix(api): clarify terminal persistence diagnostics * fix(api): tighten terminal session request ids * feat: stabilize project ssh terminal links * fix: address ssh terminal ci review feedback * fix: type api terminal test runner * fix: serialize active terminal persistence * fix: address coderabbit review docs --------- Co-authored-by: rikohomeless <163776849+rikohomeless@users.noreply.github.com>
1 parent a0d41a7 commit 5602ee7

32 files changed

Lines changed: 2372 additions & 635 deletions

packages/api/src/api/schema.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ export const UpProjectRequestSchema = Schema.Struct({
161161
})
162162

163163
export const StartProjectTerminalSessionRequestSchema = Schema.Struct({
164-
requestId: Schema.String
164+
requestId: Schema.UUID
165+
})
166+
167+
export const ActiveProjectTerminalSessionRequestSchema = Schema.Struct({
168+
sessionId: Schema.String
165169
})
166170

167171
export const ProjectPortForwardRequestSchema = Schema.Struct({

packages/api/src/http.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { federationJsonLdResponseContentType, type ApplyProjectRequest } from ".
1515
import {
1616
AuthMenuRequestSchema,
1717
AuthTerminalSessionRequestSchema,
18+
ActiveProjectTerminalSessionRequestSchema,
1819
ApplyProjectRequestSchema,
1920
ApplyAllRequestSchema,
2021
CodexAuthImportRequestSchema,
@@ -138,7 +139,9 @@ import {
138139
getProjectTerminalSession,
139140
listProjectTerminalSessions,
140141
lookupTerminalSessionById,
142+
readProjectTerminalSessions,
141143
readProjectTerminalImage,
144+
setProjectActiveTerminalSession,
142145
startTerminalSession
143146
} from "./services/terminal-sessions.js"
144147
import {
@@ -420,6 +423,8 @@ const readCodexAuthLogoutRequest = () => HttpServerRequest.schemaBodyJson(CodexA
420423
const readProjectAuthRequest = () => HttpServerRequest.schemaBodyJson(ProjectAuthRequestSchema)
421424
const readProjectPromptUpdateRequest = () => HttpServerRequest.schemaBodyJson(ProjectPromptUpdateRequestSchema)
422425
const readProjectSkillUpdateRequest = () => HttpServerRequest.schemaBodyJson(ProjectSkillUpdateRequestSchema)
426+
const readActiveProjectTerminalSessionRequest = () =>
427+
HttpServerRequest.schemaBodyJson(ActiveProjectTerminalSessionRequestSchema)
423428

424429
const skillScopeFromId = (scopeId: string): ProjectSkillScope | null => {
425430
switch (scopeId) {
@@ -1438,7 +1443,7 @@ export const makeRouter = () => {
14381443
projectKeyParams.pipe(
14391444
Effect.flatMap(({ projectKey }) =>
14401445
getProjectItemByKey(projectKey).pipe(
1441-
Effect.map((project) => ({ sessions: listProjectTerminalSessions(project.projectDir) }))
1446+
Effect.flatMap((project) => readProjectTerminalSessions(project.projectDir))
14421447
)
14431448
),
14441449
Effect.flatMap((body) => jsonResponse(body, 200)),
@@ -1484,7 +1489,11 @@ export const makeRouter = () => {
14841489
HttpRouter.get(
14851490
"/projects/:projectId/terminal-sessions",
14861491
projectParams.pipe(
1487-
Effect.flatMap(({ projectId }) => Effect.succeed({ sessions: listProjectTerminalSessions(projectId) })),
1492+
Effect.flatMap(({ projectId }) =>
1493+
listProjectTerminalSessions(projectId).pipe(
1494+
Effect.map((sessions) => ({ sessions }))
1495+
)
1496+
),
14881497
Effect.flatMap((body) => jsonResponse(body, 200)),
14891498
Effect.catchAll(errorResponse)
14901499
)
@@ -1567,6 +1576,18 @@ export const makeRouter = () => {
15671576
)
15681577

15691578
const withProjectTerminalStart = withProjectLifecycle.pipe(
1579+
HttpRouter.put(
1580+
"/projects/by-key/:projectKey/terminal-sessions/active",
1581+
Effect.gen(function*(_) {
1582+
const { projectKey } = yield* _(projectKeyParams)
1583+
const request = yield* _(readActiveProjectTerminalSessionRequest())
1584+
const project = yield* _(getProjectItemByKey(projectKey))
1585+
const session = yield* _(setProjectActiveTerminalSession(project.projectDir, request.sessionId))
1586+
return yield* _(jsonResponse({ ok: true, session }, 200))
1587+
}).pipe(
1588+
Effect.catchAll(errorResponse)
1589+
)
1590+
),
15701591
HttpRouter.post(
15711592
"/projects/by-key/:projectKey/terminal-sessions/start",
15721593
projectKeyParams.pipe(

packages/api/src/services/container-tasks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,14 @@ export const readContainerTaskSnapshot = (
366366
)
367367
)
368368
const tasks = buildContainerTasks(processes, managedAgentPids, includeDefault)
369+
const terminalSessions = yield* _(listProjectTerminalSessions(project.id))
369370
return {
370371
projectId: project.id,
371372
containerName: project.containerName,
372373
generatedAt: new Date().toISOString(),
373374
sshConnections: distinctSshConnections(tasks),
374375
tasks,
375-
terminalSessions: listProjectTerminalSessions(project.id),
376+
terminalSessions,
376377
agents: listAgents(project.id)
377378
}
378379
})

0 commit comments

Comments
 (0)