(sessionId: string, onProgress?: TeleportProgressCallback)
| 428 | * @returns The raw session log and branch name |
| 429 | */ |
| 430 | export async function teleportResumeCodeSession(sessionId: string, onProgress?: TeleportProgressCallback): Promise<TeleportRemoteResponse> { |
| 431 | if (!isPolicyAllowed('allow_remote_sessions')) { |
| 432 | throw new Error("Remote sessions are disabled by your organization's policy."); |
| 433 | } |
| 434 | logForDebugging(`Resuming code session ID: ${sessionId}`); |
| 435 | try { |
| 436 | const accessToken = getClaudeAIOAuthTokens()?.accessToken; |
| 437 | if (!accessToken) { |
| 438 | logEvent('tengu_teleport_resume_error', { |
| 439 | error_type: 'no_access_token' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS |
| 440 | }); |
| 441 | throw new Error('Claude Code web sessions require authentication with a Claude.ai account. API key authentication is not sufficient. Please run /login to authenticate, or check your authentication status with /status.'); |
| 442 | } |
| 443 | |
| 444 | // Get organization UUID |
| 445 | const orgUUID = await getOrganizationUUID(); |
| 446 | if (!orgUUID) { |
| 447 | logEvent('tengu_teleport_resume_error', { |
| 448 | error_type: 'no_org_uuid' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS |
| 449 | }); |
| 450 | throw new Error('Unable to get organization UUID for constructing session URL'); |
| 451 | } |
| 452 | |
| 453 | // Fetch and validate repository matches before resuming |
| 454 | onProgress?.('validating'); |
| 455 | const sessionData = await fetchSession(sessionId); |
| 456 | const repoValidation = await validateSessionRepository(sessionData); |
| 457 | switch (repoValidation.status) { |
| 458 | case 'match': |
| 459 | case 'no_repo_required': |
| 460 | // Proceed with teleport |
| 461 | break; |
| 462 | case 'not_in_repo': |
| 463 | { |
| 464 | logEvent('tengu_teleport_error_repo_not_in_git_dir_sessions_api', { |
| 465 | sessionId: sessionId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS |
| 466 | }); |
| 467 | // Include host for GHE users so they know which instance the repo is on |
| 468 | const notInRepoDisplay = repoValidation.sessionHost && repoValidation.sessionHost.toLowerCase() !== 'github.com' ? `${repoValidation.sessionHost}/${repoValidation.sessionRepo}` : repoValidation.sessionRepo; |
| 469 | throw new TeleportOperationError(`You must run claude --teleport ${sessionId} from a checkout of ${notInRepoDisplay}.`, chalk.red(`You must run claude --teleport ${sessionId} from a checkout of ${chalk.bold(notInRepoDisplay)}.\n`)); |
| 470 | } |
| 471 | case 'mismatch': |
| 472 | { |
| 473 | logEvent('tengu_teleport_error_repo_mismatch_sessions_api', { |
| 474 | sessionId: sessionId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS |
| 475 | }); |
| 476 | // Only include host prefix when hosts actually differ to disambiguate |
| 477 | // cross-instance mismatches; for same-host mismatches the host is noise. |
| 478 | const hostsDiffer = repoValidation.sessionHost && repoValidation.currentHost && repoValidation.sessionHost.replace(/:\d+$/, '').toLowerCase() !== repoValidation.currentHost.replace(/:\d+$/, '').toLowerCase(); |
| 479 | const sessionDisplay = hostsDiffer ? `${repoValidation.sessionHost}/${repoValidation.sessionRepo}` : repoValidation.sessionRepo; |
| 480 | const currentDisplay = hostsDiffer ? `${repoValidation.currentHost}/${repoValidation.currentRepo}` : repoValidation.currentRepo; |
| 481 | throw new TeleportOperationError(`You must run claude --teleport ${sessionId} from a checkout of ${sessionDisplay}.\nThis repo is ${currentDisplay}.`, chalk.red(`You must run claude --teleport ${sessionId} from a checkout of ${chalk.bold(sessionDisplay)}.\nThis repo is ${chalk.bold(currentDisplay)}.\n`)); |
| 482 | } |
| 483 | case 'error': |
| 484 | throw new TeleportOperationError(repoValidation.errorMessage || 'Failed to validate session repository', chalk.red(`Error: ${repoValidation.errorMessage || 'Failed to validate session repository'}\n`)); |
| 485 | default: |
| 486 | { |
| 487 | const _exhaustive: never = repoValidation.status; |
no test coverage detected