()
| 57 | * Errors logged to debug, never thrown. |
| 58 | */ |
| 59 | export async function registerSession(): Promise<boolean> { |
| 60 | if (getAgentId() != null) return false |
| 61 | |
| 62 | const kind: SessionKind = envSessionKind() ?? 'interactive' |
| 63 | const dir = getSessionsDir() |
| 64 | const pidFile = join(dir, `${process.pid}.json`) |
| 65 | |
| 66 | registerCleanup(async () => { |
| 67 | try { |
| 68 | await unlink(pidFile) |
| 69 | } catch { |
| 70 | // ENOENT is fine (already deleted or never written) |
| 71 | } |
| 72 | }) |
| 73 | |
| 74 | try { |
| 75 | await mkdir(dir, { recursive: true, mode: 0o700 }) |
| 76 | await chmod(dir, 0o700) |
| 77 | await writeFile( |
| 78 | pidFile, |
| 79 | jsonStringify({ |
| 80 | pid: process.pid, |
| 81 | sessionId: getSessionId(), |
| 82 | cwd: getOriginalCwd(), |
| 83 | startedAt: Date.now(), |
| 84 | kind, |
| 85 | entrypoint: process.env.CLAUDE_CODE_ENTRYPOINT, |
| 86 | ...(feature('UDS_INBOX') |
| 87 | ? { messagingSocketPath: process.env.CLAUDE_CODE_MESSAGING_SOCKET } |
| 88 | : {}), |
| 89 | ...(feature('BG_SESSIONS') |
| 90 | ? { |
| 91 | name: process.env.CLAUDE_CODE_SESSION_NAME, |
| 92 | logPath: process.env.CLAUDE_CODE_SESSION_LOG, |
| 93 | agent: process.env.CLAUDE_CODE_AGENT, |
| 94 | } |
| 95 | : {}), |
| 96 | }), |
| 97 | ) |
| 98 | // --resume / /resume mutates getSessionId() via switchSession. Without |
| 99 | // this, the PID file's sessionId goes stale and `claude ps` sparkline |
| 100 | // reads the wrong transcript. |
| 101 | onSessionSwitch(id => { |
| 102 | void updatePidFile({ sessionId: id }) |
| 103 | }) |
| 104 | return true |
| 105 | } catch (e) { |
| 106 | logForDebugging(`[concurrentSessions] register failed: ${errorMessage(e)}`) |
| 107 | return false |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Update this session's name in its PID registry file so ListPeers |
no test coverage detected