github-archiver 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [1.1.1](https://github.com/mynameistito/github-archiver/compare/v1.1.0...v1.1.1) (2026-01-12)
2
2
 
3
+ ## 1.5.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#14](https://github.com/mynameistito/github-archiver/pull/14) [`d76e709`](https://github.com/mynameistito/github-archiver/commit/d76e709ca56284f6ebf3b321a9c53fdd6f5f6853) Thanks [@mynameistito](https://github.com/mynameistito)! - Bump Dependencies
8
+
9
+ ## 1.5.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#12](https://github.com/mynameistito/github-archiver/pull/12) [`23948d1`](https://github.com/mynameistito/github-archiver/commit/23948d152cd2bfa5f42074b45477c338981f80ce) Thanks [@mynameistito](https://github.com/mynameistito)! - Update dependencies
14
+
3
15
  ## 1.5.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/commands/archive.ts", "../src/constants/messages.ts", "../src/constants/paths.ts", "../src/services/archiver.ts", "../src/types/error.ts", "../src/utils/errors.ts", "../src/utils/logger.ts", "../src/constants/defaults.ts", "../src/services/auth-manager.ts", "../src/utils/config.ts", "../src/services/github.ts", "../src/utils/input-handler.ts", "../src/utils/parser.ts", "../src/utils/formatting.ts", "../src/utils/progress.ts", "../src/commands/auth.ts"],
4
- "sourcesContent": ["import { Command } from \"commander\";\nimport { createArchiveCommand } from \"./commands/archive\";\nimport { createAuthCommand } from \"./commands/auth\";\nimport { PATHS } from \"./constants/paths\";\nimport { createLogger, initializeLogger, setLogger } from \"./utils/logger\";\n\nconst VERSION = \"1.0.6\";\nconst DESCRIPTION = \"Archive GitHub repositories via CLI\";\n\nasync function main() {\n try {\n await initializeLogger(PATHS.LOG_DIR);\n\n const fileLogger = createLogger();\n\n setLogger(fileLogger);\n\n const program = new Command();\n\n program\n .name(\"github-archiver\")\n .description(DESCRIPTION)\n .version(VERSION, \"-v, --version\", \"Display version\");\n\n program.addCommand(createAuthCommand());\n program.addCommand(createArchiveCommand());\n\n program.addHelpCommand(true);\n\n await program.parseAsync(process.argv);\n\n if (!process.argv.slice(2).length) {\n program.outputHelp();\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Fatal error: ${message}`);\n process.exit(1);\n }\n}\n\nmain();\n", "import { Command } from \"commander\";\nimport { MESSAGES } from \"../constants/messages\";\nimport { PATHS } from \"../constants/paths\";\nimport { Archiver } from \"../services/archiver\";\nimport { AuthManager } from \"../services/auth-manager\";\nimport { GitHubService } from \"../services/github\";\nimport type {\n ArchiveOptions,\n ArchiveResult,\n CommandOptions,\n RepositoryIdentifier,\n} from \"../types\";\nimport { InputHandler } from \"../utils/input-handler\";\nimport { getLogger } from \"../utils/logger\";\nimport { ProgressDisplay } from \"../utils/progress\";\n\nconst logger = getLogger();\n\nexport function createArchiveCommand(): Command {\n return new Command(\"archive\")\n .description(\"Archive GitHub repositories\")\n .option(\"--file <path>\", \"Read repository URLs from file\")\n .option(\"--stdin\", \"Read repository URLs from standard input\")\n .option(\"--dry-run\", \"Validate without archiving\", false)\n .option(\"--concurrency <n>\", \"Number of parallel operations\", \"3\")\n .option(\"--timeout <n>\", \"API timeout in seconds\", \"300\")\n .option(\"--verbose\", \"Verbose logging\", false)\n .option(\"--force\", \"Skip confirmations\", false)\n .action(async (options: CommandOptions) => {\n await archiveCommand(options);\n });\n}\n\nexport async function archiveCommand(options: CommandOptions): Promise<void> {\n try {\n logger.info(\"Archive command started\", { options });\n\n const { concurrency, timeout } = validateOptions(options);\n\n const authManager = new AuthManager(PATHS.APP_DIR);\n const inputHandler = new InputHandler();\n const progressDisplay = new ProgressDisplay();\n\n const githubService = await authenticateUser(authManager);\n\n const { repositories, parseErrors } = await getRepositories(\n options,\n inputHandler\n );\n\n if (parseErrors.length > 0) {\n logParseErrors(parseErrors);\n }\n\n if (repositories.length === 0) {\n console.error(`\u274C ${MESSAGES.NO_REPOS_PROVIDED}`);\n process.exit(1);\n }\n\n showRepositoriesPreview(repositories, options.dryRun);\n\n await confirmOperation(options, inputHandler);\n\n const archiveOptions: ArchiveOptions = {\n dryRun: options.dryRun,\n concurrency,\n timeout,\n force: options.force,\n verbose: options.verbose,\n };\n\n console.log(\"\");\n console.log(`${MESSAGES.ARCHIVING_START} (concurrency: ${concurrency})`);\n console.log(\"\");\n\n const archiver = new Archiver(githubService, archiveOptions);\n const results = await archiveRepositories(\n archiver,\n repositories,\n archiveOptions,\n progressDisplay\n );\n\n displayResults(archiver, results, progressDisplay);\n } catch (error) {\n handleArchiveError(error);\n }\n}\n\nexport function validateOptions(options: CommandOptions): {\n concurrency: number;\n timeout: number;\n} {\n const concurrency = Number.parseInt(options.concurrency, 10);\n const timeout = Number.parseInt(options.timeout, 10);\n\n if (concurrency < 1 || concurrency > 50) {\n console.error(\"\u274C Concurrency must be between 1 and 50\");\n process.exit(1);\n }\n\n if (timeout < 10 || timeout > 3600) {\n console.error(\"\u274C Timeout must be between 10 and 3600 seconds\");\n process.exit(1);\n }\n\n return { concurrency, timeout };\n}\n\nexport async function authenticateUser(\n authManager: AuthManager\n): Promise<GitHubService> {\n console.log(\"\uD83D\uDD10 Checking authentication...\");\n const token = await authManager.getToken();\n\n if (!token) {\n console.error(`\u274C ${MESSAGES.NO_TOKEN}`);\n console.error(\" Run: github-archiver auth login\");\n process.exit(1);\n }\n\n const githubService = new GitHubService(token);\n\n try {\n const authenticatedUser = await githubService.validateAuth();\n console.log(`\u2705 Authenticated as: ${authenticatedUser}`);\n return githubService;\n } catch (error) {\n console.error(\"\u274C Authentication failed\");\n logger.error(\"Auth validation failed:\", error);\n process.exit(1);\n }\n}\n\nexport async function getRepositories(\n options: CommandOptions,\n inputHandler: InputHandler\n): Promise<{\n repositories: RepositoryIdentifier[];\n parseErrors: Array<{ url: string; error: string; line: number }>;\n}> {\n console.log(\"\");\n console.log(\"\uD83D\uDCDD Getting repositories...\");\n\n let repositories: RepositoryIdentifier[] = [];\n let parseErrors: Array<{ url: string; error: string; line: number }> = [];\n\n if (options.file) {\n logger.info(`Using file input: ${options.file}`);\n const result = await inputHandler.getRepositoriesFromFile(options.file);\n repositories = result.repos;\n parseErrors = result.errors;\n } else if (options.stdin) {\n logger.info(\"Using stdin input\");\n console.log(\n \"Enter repository URLs (one per line, press Ctrl+D to finish):\"\n );\n const result = await inputHandler.getRepositoriesFromStdin();\n repositories = result.repos;\n parseErrors = result.errors;\n } else {\n logger.info(\"Using interactive CLI input\");\n const result = await inputHandler.getRepositoriesFromInteractive();\n repositories = result.repos;\n parseErrors = result.errors;\n }\n\n return { repositories, parseErrors };\n}\n\nexport function logParseErrors(\n parseErrors: Array<{ url: string; error: string; line: number }>\n): void {\n console.warn(`\u26A0\uFE0F Found ${parseErrors.length} invalid repositories:`);\n for (const err of parseErrors) {\n console.warn(` Line ${err.line}: ${err.error}`);\n }\n console.warn(\"\");\n}\n\nexport function showRepositoriesPreview(\n repositories: RepositoryIdentifier[],\n dryRun: boolean\n): void {\n console.log(\n `\uD83D\uDCCB Will ${dryRun ? \"validate\" : \"archive\"} ${repositories.length} repositories:`\n );\n for (let index = 0; index < Math.min(repositories.length, 5); index++) {\n const repo = repositories[index];\n if (repo) {\n console.log(` ${index + 1}. ${repo.owner}/${repo.repo}`);\n }\n }\n if (repositories.length > 5) {\n console.log(` ... and ${repositories.length - 5} more`);\n }\n}\n\nexport async function confirmOperation(\n options: CommandOptions,\n inputHandler: InputHandler\n): Promise<void> {\n console.log(\"\");\n if (!(options.force || options.dryRun)) {\n const confirmed = await inputHandler.promptForConfirmation(\n \"Are you sure you want to archive these repositories?\"\n );\n\n if (!confirmed) {\n console.log(\"\u274C Cancelled\");\n process.exit(1);\n }\n } else if (options.dryRun) {\n console.log(`\u2139\uFE0F ${MESSAGES.DRY_RUN_MODE}`);\n }\n}\n\nexport async function archiveRepositories(\n archiver: Archiver,\n repositories: RepositoryIdentifier[],\n options: ArchiveOptions,\n progressDisplay: ProgressDisplay\n): Promise<ArchiveResult[]> {\n const results = await archiver.archiveRepositories(repositories, options);\n\n const summary = archiver.getSummary();\n const progress = {\n completed: summary.successful + summary.failed + summary.skipped,\n failed: summary.failed,\n total: repositories.length,\n };\n\n if (progressDisplay.shouldUpdate()) {\n console.log(`\\r${progressDisplay.getProgressBar(progress)}`);\n }\n\n return results;\n}\n\nexport function displayResults(\n archiver: Archiver,\n results: ArchiveResult[],\n progressDisplay: ProgressDisplay\n): void {\n console.log(\"\");\n console.log(\"\");\n\n const summary = archiver.getSummary();\n console.log(progressDisplay.getSummaryBox(summary));\n\n if (summary.failed > 0) {\n console.log(\"\");\n console.log(\"\u274C Failed repositories:\");\n for (const r of results) {\n if (!r.success) {\n console.log(` ${r.owner}/${r.repo}: ${r.message}`);\n }\n }\n }\n\n if (summary.skipped > 0) {\n console.log(\"\");\n console.log(\"\u26A0\uFE0F Skipped repositories:\");\n for (const r of results) {\n if (r.success && !r.archived) {\n console.log(` ${r.owner}/${r.repo}: ${r.message}`);\n }\n }\n }\n\n if (summary.failed > 0) {\n logger.warn(`Archive command completed with ${summary.failed} failures`);\n process.exit(1);\n } else {\n console.log(\"\");\n console.log(\"\u2705 All repositories processed successfully!\");\n logger.info(\"Archive command completed successfully\");\n process.exit(0);\n }\n}\n\nexport function handleArchiveError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u274C Error: ${message}`);\n logger.error(\"Archive command failed:\", error);\n\n provideErrorGuidance(message);\n\n process.exit(1);\n}\n\nexport function provideErrorGuidance(message: string): void {\n const lowerMessage = message.toLowerCase();\n\n if (\n lowerMessage.includes(\"token\") ||\n lowerMessage.includes(\"authentication\")\n ) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\n \" 1. Make sure you have a valid GitHub Personal Access Token\"\n );\n console.log(' 2. The token needs \"repo\" scope to archive repositories');\n console.log(\" 3. Run: github-archiver auth login\");\n return;\n }\n\n if (lowerMessage.includes(\"rate limit\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. GitHub API rate limit has been exceeded\");\n console.log(\" 2. Wait a few minutes and try again\");\n console.log(\" 3. Use lower concurrency: --concurrency 1\");\n return;\n }\n\n if (lowerMessage.includes(\"permission\") || lowerMessage.includes(\"403\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. You must be the repository owner or have push access\");\n console.log(\" 2. Check that your GitHub token has correct permissions\");\n console.log(\" 3. Verify you have push access to the repositories\");\n return;\n }\n\n if (lowerMessage.includes(\"not found\") || lowerMessage.includes(\"404\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. Make sure the repository URL is correct\");\n console.log(\" 2. The repository may have been deleted\");\n console.log(\" 3. Check your GitHub access to the repository\");\n return;\n }\n\n if (lowerMessage.includes(\"network\") || lowerMessage.includes(\"timeout\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. Check your internet connection\");\n console.log(\" 2. GitHub API may be temporarily unavailable\");\n console.log(\" 3. Try again in a moment\");\n console.log(\" 4. Increase timeout: --timeout 600\");\n }\n}\n", "export const MESSAGES = {\n AUTH_SUCCESS: \"Successfully authenticated with GitHub\",\n ARCHIVE_SUCCESS: \"Repository archived successfully\",\n TOKEN_SAVED: \"GitHub token saved successfully\",\n TOKEN_REMOVED: \"GitHub token removed\",\n INVALID_TOKEN: \"Invalid or expired GitHub token\",\n REPO_NOT_FOUND: \"Repository not found\",\n ALREADY_ARCHIVED: \"Repository is already archived\",\n PERMISSION_DENIED: \"You do not have permission to archive this repository\",\n RATE_LIMITED: \"GitHub API rate limit exceeded. Please try again later\",\n NETWORK_ERROR: \"Network error. Please check your connection\",\n INVALID_URL: \"Invalid GitHub repository URL\",\n NO_TOKEN:\n 'No GitHub token found. Please run \"github-archiver auth login\" first',\n OPENING_EDITOR: \"Opening text editor for repository URLs...\",\n NO_REPOS_PROVIDED: \"No repositories provided\",\n ARCHIVING_START: \"Starting to archive repositories...\",\n ARCHIVING_COMPLETE: \"Archiving complete\",\n DRY_RUN_MODE: \"Running in dry-run mode (no repositories will be archived)\",\n CONFIRM_ARCHIVE: \"Are you sure you want to archive these repositories?\",\n ENTER_TOKEN:\n \"Enter your GitHub Personal Access Token (will not be displayed):\",\n CONFIRM_LOGOUT: \"Are you sure you want to remove the stored token?\",\n};\n", "import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst appDir = join(homedir(), \".github-archiver\");\n\nexport const PATHS = {\n APP_DIR: appDir,\n CONFIG_FILE: join(appDir, \"config.json\"),\n LOG_DIR: join(appDir, \"logs\"),\n COMBINED_LOG: join(appDir, \"logs\", \"combined.log\"),\n ERROR_LOG: join(appDir, \"logs\", \"errors.log\"),\n};\n", "import PQueue from \"p-queue\";\nimport type {\n ArchiveOptions,\n ArchiveResult,\n RepositoryIdentifier,\n} from \"../types\";\nimport { ErrorCode } from \"../types\";\nimport { isArchiveError } from \"../utils/errors\";\nimport { getLogger } from \"../utils/logger\";\nimport type { GitHubService } from \"./github\";\n\nconst logger = getLogger();\n\nexport class Archiver {\n private readonly queue: PQueue;\n private results: ArchiveResult[] = [];\n private readonly gitHubService: GitHubService;\n private completed = 0;\n private failed = 0;\n private readonly startTime: number;\n\n constructor(gitHubService: GitHubService, options: ArchiveOptions) {\n this.gitHubService = gitHubService;\n this.startTime = Date.now();\n this.queue = new PQueue({\n concurrency: options.concurrency,\n timeout: options.timeout * 1000,\n interval: 1000,\n intervalCap: options.concurrency,\n });\n\n logger.info(\"Archiver initialized\", {\n concurrency: options.concurrency,\n timeout: options.timeout,\n dryRun: options.dryRun,\n });\n }\n\n async archiveRepositories(\n repos: RepositoryIdentifier[],\n options: ArchiveOptions\n ): Promise<ArchiveResult[]> {\n this.results = [];\n this.completed = 0;\n this.failed = 0;\n\n logger.info(`Starting to archive ${repos.length} repositories`, {\n dryRun: options.dryRun,\n });\n\n const tasks = repos.map((repo) =>\n this.queue.add(() => this.archiveRepository(repo, options))\n );\n\n await Promise.all(tasks);\n\n return this.results;\n }\n\n private async archiveRepository(\n repo: RepositoryIdentifier,\n options: ArchiveOptions\n ): Promise<void> {\n const startTime = Date.now();\n\n try {\n if (options.dryRun) {\n console.log(`\uD83D\uDD0D Validating ${repo.owner}/${repo.repo}...`);\n logger.info(`[DRY-RUN] Would archive ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: false,\n duration: Date.now() - startTime,\n message: \"[DRY-RUN] Validation successful\",\n };\n this.results.push(result);\n this.completed++;\n return;\n }\n\n const validation = await this.gitHubService.validateRepository(\n repo.owner,\n repo.repo\n );\n\n if (!validation.exists) {\n logger.warn(`Repository not found: ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: false,\n archived: false,\n error: \"Repository not found\",\n duration: Date.now() - startTime,\n message: \"Repository does not exist on GitHub\",\n };\n this.results.push(result);\n this.failed++;\n return;\n }\n\n if (validation.isArchived) {\n logger.info(`Repository already archived: ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: false,\n duration: Date.now() - startTime,\n message: \"Repository is already archived\",\n };\n this.results.push(result);\n this.completed++;\n return;\n }\n\n console.log(`\uD83D\uDCE6 Archiving ${repo.owner}/${repo.repo}...`);\n await this.gitHubService.archiveRepository(repo.owner, repo.repo);\n\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: true,\n duration: Date.now() - startTime,\n message: \"Repository archived successfully\",\n };\n this.results.push(result);\n this.completed++;\n logger.info(`\u2713 Archived ${repo.owner}/${repo.repo}`, {\n duration: result.duration,\n });\n } catch (error) {\n if (isArchiveError(error) && error.code === ErrorCode.ALREADY_ARCHIVED) {\n logger.info(`Repository already archived: ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: false,\n duration: Date.now() - startTime,\n message: \"Repository is already archived\",\n };\n this.results.push(result);\n this.completed++;\n return;\n }\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.error(\n `\u2717 Failed to archive ${repo.owner}/${repo.repo}: ${errorMessage}`\n );\n\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: false,\n archived: false,\n error: errorMessage,\n duration: Date.now() - startTime,\n message: `Error: ${errorMessage}`,\n };\n this.results.push(result);\n this.failed++;\n }\n }\n\n getProgress(): { completed: number; failed: number; total: number } {\n return {\n completed: this.completed,\n failed: this.failed,\n total: this.results.length,\n };\n }\n\n getSummary(): {\n successful: number;\n failed: number;\n skipped: number;\n totalDuration: number;\n } {\n const successful = this.results.filter(\n (r) => r.success && r.archived\n ).length;\n const failed = this.results.filter((r) => !r.success).length;\n const skipped = this.results.filter((r) => r.success && !r.archived).length;\n const totalDuration = Date.now() - this.startTime;\n\n return { successful, failed, skipped, totalDuration };\n }\n}\n", "export const ErrorCode = {\n INVALID_AUTH: \"INVALID_AUTH\",\n REPO_NOT_FOUND: \"REPO_NOT_FOUND\",\n ALREADY_ARCHIVED: \"ALREADY_ARCHIVED\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n RATE_LIMITED: \"RATE_LIMITED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n INVALID_URL: \"INVALID_URL\",\n PARSE_ERROR: \"PARSE_ERROR\",\n EDITOR_ERROR: \"EDITOR_ERROR\",\n CONFIG_ERROR: \"CONFIG_ERROR\",\n FILE_ERROR: \"FILE_ERROR\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nexport class ArchiveError extends Error {\n code: ErrorCode;\n statusCode?: number;\n retryable: boolean;\n\n constructor(\n code: ErrorCode,\n message: string,\n statusCode?: number,\n retryable = false\n ) {\n super(message);\n this.name = \"ArchiveError\";\n this.code = code;\n this.statusCode = statusCode;\n this.retryable = retryable;\n Object.setPrototypeOf(this, ArchiveError.prototype);\n }\n}\n", "import { MESSAGES } from \"../constants/messages\";\nimport { ArchiveError, ErrorCode } from \"../types\";\n\nexport function isArchiveError(error: unknown): error is ArchiveError {\n return error instanceof ArchiveError;\n}\n\nexport function getErrorMessage(error: unknown): string {\n if (isArchiveError(error)) {\n return error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\nexport function createAuthError(\n message: string,\n statusCode?: number\n): ArchiveError {\n return new ArchiveError(ErrorCode.INVALID_AUTH, message, statusCode, true);\n}\n\nexport function createRepoNotFoundError(\n owner: string,\n repo: string\n): ArchiveError {\n return new ArchiveError(\n ErrorCode.REPO_NOT_FOUND,\n `Repository ${owner}/${repo} not found`,\n 404,\n false\n );\n}\n\nexport function createAlreadyArchivedError(\n owner: string,\n repo: string\n): ArchiveError {\n return new ArchiveError(\n ErrorCode.ALREADY_ARCHIVED,\n `Repository ${owner}/${repo} is already archived`,\n 422,\n false\n );\n}\n\nexport function createPermissionError(\n owner: string,\n repo: string\n): ArchiveError {\n return new ArchiveError(\n ErrorCode.PERMISSION_DENIED,\n `Permission denied for ${owner}/${repo}. You must be the repository owner or have push access.`,\n 403,\n false\n );\n}\n\nexport function createRateLimitError(): ArchiveError {\n return new ArchiveError(\n ErrorCode.RATE_LIMITED,\n MESSAGES.RATE_LIMITED,\n 429,\n true\n );\n}\n\nexport function createNetworkError(message?: string): ArchiveError {\n return new ArchiveError(\n ErrorCode.NETWORK_ERROR,\n message || MESSAGES.NETWORK_ERROR,\n undefined,\n true\n );\n}\n\nexport function createInvalidUrlError(url: string): ArchiveError {\n return new ArchiveError(ErrorCode.INVALID_URL, `Invalid GitHub URL: ${url}`);\n}\n\nexport function createConfigError(message: string): ArchiveError {\n return new ArchiveError(ErrorCode.CONFIG_ERROR, message);\n}\n\nexport function createFileError(message: string): ArchiveError {\n return new ArchiveError(ErrorCode.FILE_ERROR, message);\n}\n\nexport function createEditorError(message: string): ArchiveError {\n return new ArchiveError(ErrorCode.EDITOR_ERROR, message);\n}\n", "import { promises as fs } from \"node:fs\";\nimport winston from \"winston\";\nimport { DEFAULT_LOG_LEVEL } from \"../constants/defaults\";\nimport { PATHS } from \"../constants/paths\";\nimport type { Config } from \"../types\";\n\nlet loggerInstance: winston.Logger;\n\nexport async function initializeLogger(logDir: string): Promise<void> {\n try {\n await fs.mkdir(logDir, { recursive: true });\n } catch (error) {\n console.error(\"Failed to create log directory:\", error);\n }\n}\n\nexport function createLogger(config?: Partial<Config>): winston.Logger {\n const level = config?.logLevel || DEFAULT_LOG_LEVEL;\n const logDir = config?.logDir || PATHS.LOG_DIR;\n\n return winston.createLogger({\n level,\n format: winston.format.combine(\n winston.format.timestamp({ format: \"YYYY-MM-DD HH:mm:ss\" }),\n winston.format.errors({ stack: true }),\n winston.format.json()\n ),\n defaultMeta: { service: \"github-archiver\" },\n transports: [\n new winston.transports.File({\n filename: `${logDir}/errors.log`,\n level: \"error\",\n }),\n new winston.transports.File({\n filename: `${logDir}/combined.log`,\n }),\n ],\n });\n}\n\nexport function createConsoleLogger(): winston.Logger {\n return winston.createLogger({\n format: winston.format.combine(\n winston.format.colorize(),\n winston.format.printf(({ level, message, ...metadata }) => {\n const meta = Object.keys(metadata).length\n ? JSON.stringify(metadata)\n : \"\";\n return `${level}: ${message} ${meta}`;\n })\n ),\n transports: [new winston.transports.Console()],\n });\n}\n\nexport function getLogger(): winston.Logger {\n if (!loggerInstance) {\n loggerInstance = createLogger();\n }\n return loggerInstance;\n}\n\nexport function setLogger(logger: winston.Logger): void {\n loggerInstance = logger;\n}\n", "export const DEFAULT_CONCURRENCY = 3;\nexport const DEFAULT_TIMEOUT = 300; // seconds\nexport const DEFAULT_LOG_LEVEL = \"info\" as const;\nexport const MAX_RETRIES = 3;\nexport const RETRY_DELAY_MS = 1000; // base delay for exponential backoff\nexport const GITHUB_API_URL = \"https://api.github.com\";\n", "import { Octokit } from \"octokit\";\nimport { PATHS } from \"../constants/paths\";\nimport type { Config, StoredCredentials } from \"../types\";\nimport { ConfigManager } from \"../utils/config\";\nimport { createAuthError } from \"../utils/errors\";\n\nexport class AuthManager {\n private readonly configManager: ConfigManager;\n\n constructor(configDir: string = PATHS.APP_DIR) {\n this.configManager = new ConfigManager(configDir);\n }\n\n async saveToken(token: string): Promise<void> {\n if (!token || typeof token !== \"string\") {\n throw createAuthError(\"Invalid token format\");\n }\n\n const octokit = new Octokit({ auth: token });\n\n try {\n const { data } = await octokit.rest.users.getAuthenticated();\n\n const credentials: StoredCredentials = {\n token,\n savedAt: new Date().toISOString(),\n githubUser: data.login,\n };\n\n await this.configManager.saveConfig(credentials);\n } catch (error) {\n throw createAuthError(\n `Failed to validate token: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async getToken(): Promise<string | undefined> {\n if (process.env.GH_TOKEN) {\n return process.env.GH_TOKEN;\n }\n\n const credentials = await this.configManager.getStoredCredentials();\n return credentials?.token;\n }\n\n async removeToken(): Promise<void> {\n await this.configManager.removeStoredCredentials();\n }\n\n async validateToken(\n token: string\n ): Promise<{ valid: boolean; user?: string }> {\n try {\n const octokit = new Octokit({ auth: token });\n const { data } = await octokit.rest.users.getAuthenticated();\n return {\n valid: true,\n user: data.login,\n };\n } catch (_error) {\n return {\n valid: false,\n };\n }\n }\n\n getStoredCredentials(): Promise<StoredCredentials | null> {\n return this.configManager.getStoredCredentials();\n }\n\n ensureConfigDir(): Promise<void> {\n return this.configManager.ensureConfigDir();\n }\n\n loadConfig(): Promise<Config> {\n return this.configManager.loadConfig();\n }\n}\n", "import { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n DEFAULT_CONCURRENCY,\n DEFAULT_LOG_LEVEL,\n DEFAULT_TIMEOUT,\n} from \"../constants/defaults\";\nimport { PATHS } from \"../constants/paths\";\nimport type { Config, StoredCredentials } from \"../types\";\nimport { createConfigError, createFileError } from \"./errors\";\n\nexport class ConfigManager {\n private readonly configDir: string;\n private readonly configFile: string;\n\n constructor(configDir: string = PATHS.APP_DIR) {\n this.configDir = configDir;\n this.configFile = join(configDir, \"config.json\");\n }\n\n async loadConfig(): Promise<Config> {\n try {\n const token = process.env.GH_TOKEN;\n const fileCredentials = await this.loadFileCredentials();\n\n return {\n token: token || fileCredentials?.token,\n concurrency: DEFAULT_CONCURRENCY,\n timeout: DEFAULT_TIMEOUT,\n logLevel: DEFAULT_LOG_LEVEL,\n logDir: join(this.configDir, \"logs\"),\n configDir: this.configDir,\n };\n } catch (error) {\n throw createConfigError(\n `Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async saveConfig(credentials: StoredCredentials): Promise<void> {\n try {\n await this.ensureConfigDir();\n await fs.writeFile(\n this.configFile,\n JSON.stringify(credentials, null, 2),\n \"utf-8\"\n );\n } catch (error) {\n throw createFileError(\n `Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async ensureConfigDir(): Promise<void> {\n try {\n await fs.mkdir(this.configDir, { recursive: true });\n } catch (error) {\n throw createFileError(\n `Failed to create config directory: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async getStoredCredentials(): Promise<StoredCredentials | null> {\n try {\n const data = await fs.readFile(this.configFile, \"utf-8\");\n return JSON.parse(data) as StoredCredentials;\n } catch {\n return null;\n }\n }\n\n async removeStoredCredentials(): Promise<void> {\n try {\n await fs.unlink(this.configFile);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw createFileError(\n `Failed to remove stored credentials: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n\n private async loadFileCredentials(): Promise<StoredCredentials | null> {\n try {\n const data = await fs.readFile(this.configFile, \"utf-8\");\n return JSON.parse(data) as StoredCredentials;\n } catch {\n return null;\n }\n }\n}\n", "import { Octokit } from \"octokit\";\nimport { MAX_RETRIES, RETRY_DELAY_MS } from \"../constants/defaults\";\nimport type { RateLimitInfo } from \"../types\";\nimport {\n createAlreadyArchivedError,\n createNetworkError,\n createPermissionError,\n createRateLimitError,\n createRepoNotFoundError,\n} from \"../utils/errors\";\nimport { getLogger } from \"../utils/logger\";\n\nconst logger = getLogger();\n\ninterface OctokitError {\n status: number;\n response?: {\n data?: {\n message: string;\n };\n };\n message: string;\n}\n\nexport class GitHubService {\n private readonly octokit: Octokit;\n\n constructor(token: string) {\n this.octokit = new Octokit({ auth: token });\n }\n\n async archiveRepository(owner: string, repo: string): Promise<void> {\n const startTime = Date.now();\n logger.debug(`Attempting to archive ${owner}/${repo}`);\n\n await this.retryWithBackoff(async () => {\n try {\n await this.octokit.rest.repos.update({\n owner,\n repo,\n archived: true,\n });\n const duration = Date.now() - startTime;\n logger.info(`Successfully archived ${owner}/${repo} in ${duration}ms`);\n } catch (error) {\n this.handleArchiveError(error, owner, repo);\n }\n });\n }\n\n private handleArchiveError(\n error: unknown,\n owner: string,\n repo: string\n ): never {\n const err = this.parseOctokitError(error);\n logger.error(`Failed to archive ${owner}/${repo}: ${err.message}`, {\n status: err.status,\n code: err.code,\n });\n\n if (err.status === 404) {\n throw createRepoNotFoundError(owner, repo);\n }\n\n if (err.status === 403 || err.message.includes(\"permission\")) {\n throw createPermissionError(owner, repo);\n }\n\n if (err.status === 422 && err.message.includes(\"archived\")) {\n throw createAlreadyArchivedError(owner, repo);\n }\n\n if (err.message.includes(\"API rate limit\")) {\n throw createRateLimitError();\n }\n\n if (\n err.message.includes(\"ECONNREFUSED\") ||\n err.message.includes(\"ETIMEDOUT\") ||\n err.message.includes(\"EHOSTUNREACH\") ||\n err.message.includes(\"socket hang up\")\n ) {\n throw createNetworkError(err.message);\n }\n\n throw error;\n }\n\n private parseOctokitError(error: unknown): {\n message: string;\n status: number;\n code: string | undefined;\n } {\n const message = error instanceof Error ? error.message : String(error);\n const err = error as unknown as OctokitError;\n return {\n message,\n status: err?.status || 0,\n code: err?.response?.data?.message,\n };\n }\n\n async validateRepository(\n owner: string,\n repo: string\n ): Promise<{ exists: boolean; isArchived: boolean }> {\n try {\n logger.debug(`Validating repository ${owner}/${repo}`);\n const { data } = await this.octokit.rest.repos.get({\n owner,\n repo,\n });\n\n const result = {\n exists: true,\n isArchived: data.archived,\n };\n logger.debug(`Repository ${owner}/${repo} validated`, result);\n return result;\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"404\")) {\n logger.debug(`Repository ${owner}/${repo} does not exist`);\n return {\n exists: false,\n isArchived: false,\n };\n }\n\n logger.error(`Error validating ${owner}/${repo}:`, error);\n throw error;\n }\n }\n\n async getRateLimitStatus(): Promise<RateLimitInfo> {\n try {\n logger.debug(\"Fetching rate limit status\");\n const { data } = await this.octokit.rest.rateLimit.get();\n\n const info: RateLimitInfo = {\n remaining: data.rate.remaining,\n reset: new Date(data.rate.reset * 1000),\n limit: data.rate.limit,\n };\n\n logger.debug(\"Rate limit status retrieved\", {\n remaining: info.remaining,\n limit: info.limit,\n resetAt: info.reset.toISOString(),\n });\n\n return info;\n } catch (error) {\n logger.error(\"Failed to fetch rate limit status:\", error);\n throw error;\n }\n }\n\n async validateAuth(): Promise<string> {\n try {\n logger.debug(\"Validating authentication\");\n const { data } = await this.octokit.rest.users.getAuthenticated();\n logger.info(`Authenticated as user: ${data.login}`);\n return data.login;\n } catch (error) {\n logger.error(\"Authentication validation failed:\", error);\n throw createNetworkError(\n error instanceof Error ? error.message : \"Authentication failed\"\n );\n }\n }\n\n private async retryWithBackoff<T>(\n fn: () => Promise<T>,\n maxRetries: number = MAX_RETRIES\n ): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error as Error;\n\n const message = lastError.message.toLowerCase();\n const isRetryable =\n message.includes(\"rate limit\") ||\n message.includes(\"timeout\") ||\n message.includes(\"econnrefused\") ||\n message.includes(\"etimedout\") ||\n message.includes(\"ehostunreach\") ||\n message.includes(\"socket hang up\");\n\n if (!isRetryable || attempt === maxRetries - 1) {\n throw error;\n }\n\n const delay = RETRY_DELAY_MS * 2 ** attempt;\n logger.warn(\n `Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms`,\n {\n error: message,\n }\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError || new Error(\"Unknown error during retry\");\n }\n}\n", "import { promises as fs } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport type { RepositoryIdentifier } from \"../types\";\nimport { getLogger } from \"./logger\";\nimport { URLParser } from \"./parser\";\n\nconst logger = getLogger();\n\nexport class InputHandler {\n getRepositoriesFromInteractive(): Promise<{\n repos: RepositoryIdentifier[];\n errors: Array<{ url: string; error: string; line: number }>;\n }> {\n logger.info(\"Starting interactive CLI input for repositories\");\n\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(\"\");\n console.log(\"\uD83D\uDCDD Enter repository URLs one at a time:\");\n console.log(\n \" (Press CTRL+D to finish, or leave empty and press Enter to skip)\"\n );\n console.log(\"\");\n\n const urls: string[] = [];\n let lineNumber = 1;\n let isFinished = false;\n\n const promptNext = () => {\n rl.question(`[${lineNumber}] > `, (input) => {\n const trimmedInput = input.trim();\n\n if (trimmedInput === \"\") {\n lineNumber++;\n promptNext();\n return;\n }\n\n urls.push(trimmedInput);\n lineNumber++;\n promptNext();\n });\n };\n\n const finishInput = () => {\n if (isFinished) {\n return;\n }\n isFinished = true;\n rl.close();\n const result = URLParser.parseRepositoriesBatch(urls);\n\n if (result.invalid.length > 0) {\n logger.warn(`Found ${result.invalid.length} invalid repositories`, {\n errors: result.invalid,\n });\n }\n\n logger.info(`Parsed ${result.valid.length} valid repositories`);\n resolve({\n repos: result.valid,\n errors: result.invalid,\n });\n };\n\n rl.on(\"close\", () => {\n finishInput();\n });\n\n promptNext();\n });\n }\n\n async getRepositoriesFromFile(filePath: string): Promise<{\n repos: RepositoryIdentifier[];\n errors: Array<{ url: string; error: string; line: number }>;\n }> {\n logger.info(`Reading repositories from file: ${filePath}`);\n\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n const result = URLParser.parseRepositoriesBatch(lines);\n\n if (result.invalid.length > 0) {\n logger.warn(`Found ${result.invalid.length} invalid entries in file`, {\n errors: result.invalid,\n });\n }\n\n logger.info(`Parsed ${result.valid.length} valid repositories from file`);\n return {\n repos: result.valid,\n errors: result.invalid,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to read file ${filePath}: ${message}`);\n throw new Error(`Failed to read file: ${message}`);\n }\n }\n\n getRepositoriesFromStdin(): Promise<{\n repos: RepositoryIdentifier[];\n errors: Array<{ url: string; error: string; line: number }>;\n }> {\n logger.info(\"Reading repositories from stdin\");\n\n return new Promise((resolve) => {\n const lines: string[] = [];\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.on(\"line\", (line) => {\n lines.push(line);\n });\n\n rl.on(\"close\", () => {\n const result = URLParser.parseRepositoriesBatch(lines);\n\n if (result.invalid.length > 0) {\n logger.warn(\n `Found ${result.invalid.length} invalid entries from stdin`,\n {\n errors: result.invalid,\n }\n );\n }\n\n logger.info(\n `Parsed ${result.valid.length} valid repositories from stdin`\n );\n resolve({\n repos: result.valid,\n errors: result.invalid,\n });\n });\n });\n }\n\n promptForConfirmation(message: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(`${message} [y/N]: `, (answer) => {\n rl.close();\n const confirmed = answer.toLowerCase() === \"y\";\n logger.info(`User confirmation: ${confirmed}`);\n resolve(confirmed);\n });\n });\n }\n}\n", "import type { RepositoryIdentifier } from \"../types\";\nimport { ArchiveError, ErrorCode } from \"../types\";\nimport { getLogger } from \"./logger\";\n\nconst logger = getLogger();\nconst NAME_REGEX = /^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$/;\n\nconst GITHUB_URL_PATTERNS: RegExp[] = [\n // https://github.com/owner/repo or https://github.com/owner/repo.git\n /^(?:https?:\\/\\/)?(?:www\\.)?github\\.com[/:]([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?(?:\\/)?$/i,\n // git@github.com:owner/repo.git or git@github.com:owner/repo\n /^git@github\\.com:([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?$/i,\n // owner/repo (shorthand)\n /^([\\w.-]+)\\/([\\w.-]+)$/,\n];\n\nfunction parseRepositoryUrl(url: string): RepositoryIdentifier {\n const trimmed = url.trim();\n\n if (!trimmed) {\n throw new ArchiveError(ErrorCode.INVALID_URL, \"URL cannot be empty\");\n }\n\n for (const pattern of GITHUB_URL_PATTERNS) {\n const match = trimmed.match(pattern);\n if (match) {\n const owner = match[1];\n const repo = match[2];\n\n if (!(owner && repo)) {\n throw new ArchiveError(\n ErrorCode.INVALID_URL,\n `Invalid GitHub URL format: ${url}`\n );\n }\n\n if (!isValidName(owner)) {\n throw new ArchiveError(\n ErrorCode.INVALID_URL,\n `Invalid owner name: ${owner}. Owner names must contain only alphanumeric characters, hyphens, or periods.`\n );\n }\n\n if (!isValidName(repo)) {\n throw new ArchiveError(\n ErrorCode.INVALID_URL,\n `Invalid repository name: ${repo}. Repository names must contain only alphanumeric characters, hyphens, underscores, or periods.`\n );\n }\n\n const normalizedUrl = `https://github.com/${owner}/${repo}`;\n\n logger.debug(\"Parsed repository URL\", {\n original: trimmed,\n owner,\n repo,\n normalized: normalizedUrl,\n });\n\n return {\n owner,\n repo,\n url: normalizedUrl,\n };\n }\n }\n\n throw new ArchiveError(ErrorCode.INVALID_URL, `Invalid GitHub URL: ${url}`);\n}\n\nfunction isValidName(name: string): boolean {\n return NAME_REGEX.test(name);\n}\n\nfunction parseRepositoriesBatch(urls: string[]): {\n valid: RepositoryIdentifier[];\n invalid: Array<{ url: string; error: string; line: number }>;\n} {\n const valid: RepositoryIdentifier[] = [];\n const invalid: Array<{ url: string; error: string; line: number }> = [];\n\n for (const [index, url] of urls.entries()) {\n const lineNumber = index + 1;\n const trimmed = url.trim();\n\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n\n try {\n const parsed = parseRepositoryUrl(trimmed);\n valid.push(parsed);\n logger.debug(`Line ${lineNumber}: Valid repository`, {\n owner: parsed.owner,\n repo: parsed.repo,\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n invalid.push({\n url: trimmed,\n error: errorMessage,\n line: lineNumber,\n });\n logger.warn(`Line ${lineNumber}: Invalid repository`, {\n url: trimmed,\n error: errorMessage,\n });\n }\n }\n\n logger.info(\"Batch parsing complete\", {\n total: urls.length,\n valid: valid.length,\n invalid: invalid.length,\n skipped: urls.length - valid.length - invalid.length,\n });\n\n return { valid, invalid };\n}\n\nexport const URLParser = {\n parseRepositoryUrl,\n parseRepositoriesBatch,\n};\n", "function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${ms}ms`;\n }\n if (ms < 60_000) {\n return `${(ms / 1000).toFixed(1)}s`;\n }\n const minutes = Math.floor(ms / 60_000);\n const seconds = ((ms % 60_000) / 1000).toFixed(0);\n return `${minutes}m ${seconds}s`;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) {\n return \"0 B\";\n }\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${(bytes / k ** i).toFixed(2)} ${sizes[i]}`;\n}\n\nfunction formatPercent(value: number, total: number): string {\n if (total === 0) {\n return \"0%\";\n }\n return `${((value / total) * 100).toFixed(1)}%`;\n}\n\nfunction createProgressBar(\n completed: number,\n total: number,\n width = 30\n): string {\n if (total === 0) {\n return \"[ ]\";\n }\n const filledWidth = Math.round((completed / total) * width);\n const emptyWidth = width - filledWidth;\n const filled = \"=\".repeat(filledWidth);\n const empty = \" \".repeat(emptyWidth);\n const percentage = ((completed / total) * 100).toFixed(0);\n return `[${filled}${empty}] ${percentage}%`;\n}\n\nfunction centerText(text: string, width: number): string {\n const padding = Math.max(0, (width - text.length) / 2);\n return \" \".repeat(Math.floor(padding)) + text;\n}\n\nfunction truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n return `${text.substring(0, maxLength - 3)}...`;\n}\n\nexport const Formatting = {\n formatDuration,\n formatBytes,\n formatPercent,\n createProgressBar,\n centerText,\n truncate,\n};\n", "import { Formatting } from \"./formatting\";\n\nexport interface ProgressUpdate {\n completed: number;\n failed: number;\n total: number;\n current?: {\n owner: string;\n repo: string;\n };\n}\n\nexport class ProgressDisplay {\n private readonly startTime: number;\n private lastUpdate = 0;\n private readonly updateInterval = 500; // ms\n\n constructor() {\n this.startTime = Date.now();\n }\n\n shouldUpdate(): boolean {\n const now = Date.now();\n if (now - this.lastUpdate >= this.updateInterval) {\n this.lastUpdate = now;\n return true;\n }\n return false;\n }\n\n getProgressBar(progress: ProgressUpdate): string {\n const { completed, total, current } = progress;\n const percent = total > 0 ? (completed / total) * 100 : 0;\n const bar = Formatting.createProgressBar(completed, total, 25);\n\n let line = `${bar} ${completed}/${total} (${percent.toFixed(0)}%)`;\n\n if (current) {\n line += ` - ${current.owner}/${current.repo}`;\n }\n\n return line;\n }\n getSummaryBox(summary: {\n successful: number;\n failed: number;\n skipped: number;\n totalDuration: number;\n }): string {\n const { successful, failed, skipped, totalDuration } = summary;\n const total = successful + failed + skipped;\n const duration = Formatting.formatDuration(totalDuration);\n\n const createLine = (\n label: string,\n value: string,\n extraPadding = 0\n ): string => {\n const valueStr = String(value);\n const contentLength = label.length + valueStr.length;\n const padding = 31 - contentLength + extraPadding;\n return `\u2551 ${label}${\" \".repeat(Math.max(1, padding))}${valueStr} \u2551`;\n };\n\n const lines = [\n \"\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\",\n \"\u2551 Archive Operation Summary \u2551\",\n \"\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\",\n createLine(\"\u2705 Successful:\", String(successful), 2),\n createLine(\"\u26A0\uFE0F Skipped:\", String(skipped), 3),\n createLine(\"\u274C Failed:\", String(failed), 2),\n \"\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\",\n createLine(\"Total:\", String(total), 3),\n createLine(\"Duration:\", duration, 3),\n \"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\",\n ];\n\n return lines.join(\"\\n\");\n }\n\n getElapsedTime(): string {\n const elapsed = Date.now() - this.startTime;\n return Formatting.formatDuration(elapsed);\n }\n\n getEstimatedTimeRemaining(completed: number, total: number): string | null {\n if (completed === 0) {\n return null;\n }\n\n const elapsed = Date.now() - this.startTime;\n const avgTime = elapsed / completed;\n const remaining = (total - completed) * avgTime;\n\n return remaining > 0 ? Formatting.formatDuration(remaining) : null;\n }\n}\n", "import { createInterface } from \"node:readline\";\nimport { Command } from \"commander\";\nimport { MESSAGES } from \"../constants/messages\";\nimport { PATHS } from \"../constants/paths\";\nimport { AuthManager } from \"../services/auth-manager\";\nimport { getLogger } from \"../utils/logger\";\n\nconst authManager = new AuthManager(PATHS.APP_DIR);\n\nexport function createLoginCommand(): Command {\n return new Command(\"login\")\n .description(\"Set up GitHub authentication with a Personal Access Token\")\n .action(async () => {\n try {\n await authManager.ensureConfigDir();\n\n const token = await promptForToken();\n\n if (!token) {\n console.error(\"No token provided. Aborting.\");\n process.exit(1);\n }\n\n console.log(\"Validating token...\");\n await authManager.saveToken(token);\n\n const credentials = await authManager.getStoredCredentials();\n console.log(`\u2713 ${MESSAGES.AUTH_SUCCESS}`);\n console.log(` User: ${credentials?.githubUser}`);\n\n getLogger().info(\"Token saved successfully\", {\n user: credentials?.githubUser,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Failed to save token\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function createLogoutCommand(): Command {\n return new Command(\"logout\")\n .description(\"Remove stored GitHub authentication token\")\n .action(async () => {\n try {\n const credentials = await authManager.getStoredCredentials();\n\n if (!credentials) {\n console.log(\"No stored credentials found.\");\n return;\n }\n\n const confirmed = await confirmAction(MESSAGES.CONFIRM_LOGOUT);\n\n if (!confirmed) {\n console.log(\"Cancelled.\");\n return;\n }\n\n await authManager.removeToken();\n console.log(`\u2713 ${MESSAGES.TOKEN_REMOVED}`);\n getLogger().info(\"Token removed\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Failed to remove token\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function createStatusCommand(): Command {\n return new Command(\"status\")\n .description(\"Check authentication status\")\n .action(async () => {\n try {\n const token = await authManager.getToken();\n\n if (!token) {\n console.log(\"\u2717 Not authenticated\");\n console.log(\n ' Run \"github-archiver auth login\" to set up authentication'\n );\n return;\n }\n\n console.log(\"\u2713 Authenticated\");\n\n const validation = await authManager.validateToken(token);\n\n if (validation.valid) {\n console.log(` User: ${validation.user}`);\n const credentials = await authManager.getStoredCredentials();\n if (credentials) {\n console.log(\n ` Saved at: ${new Date(credentials.savedAt).toLocaleString()}`\n );\n }\n getLogger().info(\"Authentication status check passed\");\n } else {\n console.log(\"\u2717 Token is invalid or expired\");\n getLogger().warn(\"Token validation failed\");\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Failed to check auth status\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function createValidateCommand(): Command {\n return new Command(\"validate\")\n .description(\"Validate stored authentication token\")\n .action(async () => {\n try {\n const token = await authManager.getToken();\n\n if (!token) {\n console.log(\"\u2717 No token found\");\n process.exit(1);\n }\n\n console.log(\"Validating token...\");\n const validation = await authManager.validateToken(token);\n\n if (validation.valid) {\n console.log(`\u2713 Token is valid (user: ${validation.user})`);\n getLogger().info(\"Token validation successful\", {\n user: validation.user,\n });\n } else {\n console.log(\"\u2717 Token is invalid or expired\");\n getLogger().warn(\"Token validation failed\");\n process.exit(1);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Token validation error\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function promptForToken(): Promise<string> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(MESSAGES.ENTER_TOKEN, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport function confirmAction(message: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(`${message} [y/N]: `, (answer) => {\n rl.close();\n resolve(answer.toLowerCase() === \"y\");\n });\n });\n}\n\nexport function createAuthCommand(): Command {\n return new Command(\"auth\")\n .description(\"Manage GitHub authentication\")\n .addCommand(createLoginCommand())\n .addCommand(createLogoutCommand())\n .addCommand(createStatusCommand())\n .addCommand(createValidateCommand());\n}\n"],
4
+ "sourcesContent": ["import { Command } from \"commander\";\nimport { createArchiveCommand } from \"./commands/archive\";\nimport { createAuthCommand } from \"./commands/auth\";\nimport { PATHS } from \"./constants/paths\";\nimport { createLogger, initializeLogger, setLogger } from \"./utils/logger\";\n\nconst VERSION = \"1.0.6\";\nconst DESCRIPTION = \"Archive GitHub repositories via CLI\";\n\nasync function main() {\n try {\n await initializeLogger(PATHS.LOG_DIR);\n\n const fileLogger = createLogger();\n\n setLogger(fileLogger);\n\n const program = new Command();\n\n program\n .name(\"github-archiver\")\n .description(DESCRIPTION)\n .version(VERSION, \"-v, --version\", \"Display version\");\n\n program.addCommand(createAuthCommand());\n program.addCommand(createArchiveCommand());\n\n program.addHelpCommand(true);\n\n await program.parseAsync(process.argv);\n\n if (!process.argv.slice(2).length) {\n program.outputHelp();\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Fatal error: ${message}`);\n process.exit(1);\n }\n}\n\nmain();\n", "import { Command } from \"commander\";\nimport { MESSAGES } from \"../constants/messages\";\nimport { PATHS } from \"../constants/paths\";\nimport { Archiver } from \"../services/archiver\";\nimport { AuthManager } from \"../services/auth-manager\";\nimport { GitHubService } from \"../services/github\";\nimport type {\n ArchiveOptions,\n ArchiveResult,\n CommandOptions,\n RepositoryIdentifier,\n} from \"../types\";\nimport { InputHandler } from \"../utils/input-handler\";\nimport { getLogger } from \"../utils/logger\";\nimport { ProgressDisplay } from \"../utils/progress\";\n\nconst logger = getLogger();\n\nexport function createArchiveCommand(): Command {\n return new Command(\"archive\")\n .description(\"Archive GitHub repositories\")\n .option(\"--file <path>\", \"Read repository URLs from file\")\n .option(\"--stdin\", \"Read repository URLs from standard input\")\n .option(\"--dry-run\", \"Validate without archiving\", false)\n .option(\"--concurrency <n>\", \"Number of parallel operations\", \"3\")\n .option(\"--timeout <n>\", \"API timeout in seconds\", \"300\")\n .option(\"--verbose\", \"Verbose logging\", false)\n .option(\"--force\", \"Skip confirmations\", false)\n .action(async (options: CommandOptions) => {\n await archiveCommand(options);\n });\n}\n\nexport async function archiveCommand(options: CommandOptions): Promise<void> {\n try {\n logger.info(\"Archive command started\", { options });\n\n const { concurrency, timeout } = validateOptions(options);\n\n const authManager = new AuthManager(PATHS.APP_DIR);\n const inputHandler = new InputHandler();\n const progressDisplay = new ProgressDisplay();\n\n const githubService = await authenticateUser(authManager);\n\n const { repositories, parseErrors } = await getRepositories(\n options,\n inputHandler\n );\n\n if (parseErrors.length > 0) {\n logParseErrors(parseErrors);\n }\n\n if (repositories.length === 0) {\n console.error(`\u274C ${MESSAGES.NO_REPOS_PROVIDED}`);\n process.exit(1);\n }\n\n showRepositoriesPreview(repositories, options.dryRun);\n\n await confirmOperation(options, inputHandler);\n\n const archiveOptions: ArchiveOptions = {\n dryRun: options.dryRun,\n concurrency,\n timeout,\n force: options.force,\n verbose: options.verbose,\n };\n\n console.log(\"\");\n console.log(`${MESSAGES.ARCHIVING_START} (concurrency: ${concurrency})`);\n console.log(\"\");\n\n const archiver = new Archiver(githubService, archiveOptions);\n const results = await archiveRepositories(\n archiver,\n repositories,\n archiveOptions,\n progressDisplay\n );\n\n displayResults(archiver, results, progressDisplay);\n } catch (error) {\n handleArchiveError(error);\n }\n}\n\nexport function validateOptions(options: CommandOptions): {\n concurrency: number;\n timeout: number;\n} {\n const concurrency = Number.parseInt(options.concurrency, 10);\n const timeout = Number.parseInt(options.timeout, 10);\n\n if (concurrency < 1 || concurrency > 50) {\n console.error(\"\u274C Concurrency must be between 1 and 50\");\n process.exit(1);\n }\n\n if (timeout < 10 || timeout > 3600) {\n console.error(\"\u274C Timeout must be between 10 and 3600 seconds\");\n process.exit(1);\n }\n\n return { concurrency, timeout };\n}\n\nexport async function authenticateUser(\n authManager: AuthManager\n): Promise<GitHubService> {\n console.log(\"\uD83D\uDD10 Checking authentication...\");\n const token = await authManager.getToken();\n\n if (!token) {\n console.error(`\u274C ${MESSAGES.NO_TOKEN}`);\n console.error(\" Run: github-archiver auth login\");\n process.exit(1);\n }\n\n const githubService = new GitHubService(token);\n\n try {\n const authenticatedUser = await githubService.validateAuth();\n console.log(`\u2705 Authenticated as: ${authenticatedUser}`);\n return githubService;\n } catch (error) {\n console.error(\"\u274C Authentication failed\");\n logger.error(\"Auth validation failed:\", error);\n process.exit(1);\n }\n}\n\nexport async function getRepositories(\n options: CommandOptions,\n inputHandler: InputHandler\n): Promise<{\n repositories: RepositoryIdentifier[];\n parseErrors: Array<{ url: string; error: string; line: number }>;\n}> {\n console.log(\"\");\n console.log(\"\uD83D\uDCDD Getting repositories...\");\n\n let repositories: RepositoryIdentifier[] = [];\n let parseErrors: Array<{ url: string; error: string; line: number }> = [];\n\n if (options.file) {\n logger.info(`Using file input: ${options.file}`);\n const result = await inputHandler.getRepositoriesFromFile(options.file);\n repositories = result.repos;\n parseErrors = result.errors;\n } else if (options.stdin) {\n logger.info(\"Using stdin input\");\n console.log(\n \"Enter repository URLs (one per line, press Ctrl+D to finish):\"\n );\n const result = await inputHandler.getRepositoriesFromStdin();\n repositories = result.repos;\n parseErrors = result.errors;\n } else {\n logger.info(\"Using interactive CLI input\");\n const result = await inputHandler.getRepositoriesFromInteractive();\n repositories = result.repos;\n parseErrors = result.errors;\n }\n\n return { repositories, parseErrors };\n}\n\nexport function logParseErrors(\n parseErrors: Array<{ url: string; error: string; line: number }>\n): void {\n console.warn(`\u26A0\uFE0F Found ${parseErrors.length} invalid repositories:`);\n for (const err of parseErrors) {\n console.warn(` Line ${err.line}: ${err.error}`);\n }\n console.warn(\"\");\n}\n\nexport function showRepositoriesPreview(\n repositories: RepositoryIdentifier[],\n dryRun: boolean\n): void {\n console.log(\n `\uD83D\uDCCB Will ${dryRun ? \"validate\" : \"archive\"} ${repositories.length} repositories:`\n );\n for (let index = 0; index < Math.min(repositories.length, 5); index++) {\n const repo = repositories[index];\n if (repo) {\n console.log(` ${index + 1}. ${repo.owner}/${repo.repo}`);\n }\n }\n if (repositories.length > 5) {\n console.log(` ... and ${repositories.length - 5} more`);\n }\n}\n\nexport async function confirmOperation(\n options: CommandOptions,\n inputHandler: InputHandler\n): Promise<void> {\n console.log(\"\");\n if (!(options.force || options.dryRun)) {\n const confirmed = await inputHandler.promptForConfirmation(\n \"Are you sure you want to archive these repositories?\"\n );\n\n if (!confirmed) {\n console.log(\"\u274C Cancelled\");\n process.exit(1);\n }\n } else if (options.dryRun) {\n console.log(`\u2139\uFE0F ${MESSAGES.DRY_RUN_MODE}`);\n }\n}\n\nexport async function archiveRepositories(\n archiver: Archiver,\n repositories: RepositoryIdentifier[],\n options: ArchiveOptions,\n progressDisplay: ProgressDisplay\n): Promise<ArchiveResult[]> {\n const results = await archiver.archiveRepositories(repositories, options);\n\n const summary = archiver.getSummary();\n const progress = {\n completed: summary.successful + summary.failed + summary.skipped,\n failed: summary.failed,\n total: repositories.length,\n };\n\n if (progressDisplay.shouldUpdate()) {\n console.log(`\\r${progressDisplay.getProgressBar(progress)}`);\n }\n\n return results;\n}\n\nexport function displayResults(\n archiver: Archiver,\n results: ArchiveResult[],\n progressDisplay: ProgressDisplay\n): void {\n console.log(\"\");\n console.log(\"\");\n\n const summary = archiver.getSummary();\n console.log(progressDisplay.getSummaryBox(summary));\n\n if (summary.failed > 0) {\n console.log(\"\");\n console.log(\"\u274C Failed repositories:\");\n for (const r of results) {\n if (!r.success) {\n console.log(` ${r.owner}/${r.repo}: ${r.message}`);\n }\n }\n }\n\n if (summary.skipped > 0) {\n console.log(\"\");\n console.log(\"\u26A0\uFE0F Skipped repositories:\");\n for (const r of results) {\n if (r.success && !r.archived) {\n console.log(` ${r.owner}/${r.repo}: ${r.message}`);\n }\n }\n }\n\n if (summary.failed > 0) {\n logger.warn(`Archive command completed with ${summary.failed} failures`);\n process.exit(1);\n } else {\n console.log(\"\");\n console.log(\"\u2705 All repositories processed successfully!\");\n logger.info(\"Archive command completed successfully\");\n process.exit(0);\n }\n}\n\nexport function handleArchiveError(error: unknown): never {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u274C Error: ${message}`);\n logger.error(\"Archive command failed:\", error);\n\n provideErrorGuidance(message);\n\n process.exit(1);\n}\n\nexport function provideErrorGuidance(message: string): void {\n const lowerMessage = message.toLowerCase();\n\n if (\n lowerMessage.includes(\"token\") ||\n lowerMessage.includes(\"authentication\")\n ) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\n \" 1. Make sure you have a valid GitHub Personal Access Token\"\n );\n console.log(' 2. The token needs \"repo\" scope to archive repositories');\n console.log(\" 3. Run: github-archiver auth login\");\n return;\n }\n\n if (lowerMessage.includes(\"rate limit\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. GitHub API rate limit has been exceeded\");\n console.log(\" 2. Wait a few minutes and try again\");\n console.log(\" 3. Use lower concurrency: --concurrency 1\");\n return;\n }\n\n if (lowerMessage.includes(\"permission\") || lowerMessage.includes(\"403\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. You must be the repository owner or have push access\");\n console.log(\" 2. Check that your GitHub token has correct permissions\");\n console.log(\" 3. Verify you have push access to the repositories\");\n return;\n }\n\n if (lowerMessage.includes(\"not found\") || lowerMessage.includes(\"404\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. Make sure the repository URL is correct\");\n console.log(\" 2. The repository may have been deleted\");\n console.log(\" 3. Check your GitHub access to the repository\");\n return;\n }\n\n if (lowerMessage.includes(\"network\") || lowerMessage.includes(\"timeout\")) {\n console.log(\"\");\n console.log(\"\uD83D\uDCA1 Troubleshooting:\");\n console.log(\" 1. Check your internet connection\");\n console.log(\" 2. GitHub API may be temporarily unavailable\");\n console.log(\" 3. Try again in a moment\");\n console.log(\" 4. Increase timeout: --timeout 600\");\n }\n}\n", "export const MESSAGES = {\n AUTH_SUCCESS: \"Successfully authenticated with GitHub\",\n ARCHIVE_SUCCESS: \"Repository archived successfully\",\n TOKEN_SAVED: \"GitHub token saved successfully\",\n TOKEN_REMOVED: \"GitHub token removed\",\n INVALID_TOKEN: \"Invalid or expired GitHub token\",\n REPO_NOT_FOUND: \"Repository not found\",\n ALREADY_ARCHIVED: \"Repository is already archived\",\n PERMISSION_DENIED: \"You do not have permission to archive this repository\",\n RATE_LIMITED: \"GitHub API rate limit exceeded. Please try again later\",\n NETWORK_ERROR: \"Network error. Please check your connection\",\n INVALID_URL: \"Invalid GitHub repository URL\",\n NO_TOKEN:\n 'No GitHub token found. Please run \"github-archiver auth login\" first',\n OPENING_EDITOR: \"Opening text editor for repository URLs...\",\n NO_REPOS_PROVIDED: \"No repositories provided\",\n ARCHIVING_START: \"Starting to archive repositories...\",\n ARCHIVING_COMPLETE: \"Archiving complete\",\n DRY_RUN_MODE: \"Running in dry-run mode (no repositories will be archived)\",\n CONFIRM_ARCHIVE: \"Are you sure you want to archive these repositories?\",\n ENTER_TOKEN:\n \"Enter your GitHub Personal Access Token (will not be displayed):\",\n CONFIRM_LOGOUT: \"Are you sure you want to remove the stored token?\",\n};\n", "import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst appDir = join(homedir(), \".github-archiver\");\n\nexport const PATHS = {\n APP_DIR: appDir,\n CONFIG_FILE: join(appDir, \"config.json\"),\n LOG_DIR: join(appDir, \"logs\"),\n COMBINED_LOG: join(appDir, \"logs\", \"combined.log\"),\n ERROR_LOG: join(appDir, \"logs\", \"errors.log\"),\n};\n", "import PQueue from \"p-queue\";\nimport type {\n ArchiveOptions,\n ArchiveResult,\n RepositoryIdentifier,\n} from \"../types\";\nimport { ErrorCode } from \"../types\";\nimport { isArchiveError } from \"../utils/errors\";\nimport { getLogger } from \"../utils/logger\";\nimport type { GitHubService } from \"./github\";\n\nconst logger = getLogger();\n\nexport class Archiver {\n private readonly queue: PQueue;\n private results: ArchiveResult[] = [];\n private readonly gitHubService: GitHubService;\n private completed = 0;\n private failed = 0;\n private readonly startTime: number;\n\n constructor(gitHubService: GitHubService, options: ArchiveOptions) {\n this.gitHubService = gitHubService;\n this.startTime = Date.now();\n this.queue = new PQueue({\n concurrency: options.concurrency,\n timeout: options.timeout * 1000,\n interval: 1000,\n intervalCap: options.concurrency,\n });\n\n logger.info(\"Archiver initialized\", {\n concurrency: options.concurrency,\n timeout: options.timeout,\n dryRun: options.dryRun,\n });\n }\n\n async archiveRepositories(\n repos: RepositoryIdentifier[],\n options: ArchiveOptions\n ): Promise<ArchiveResult[]> {\n this.results = [];\n this.completed = 0;\n this.failed = 0;\n\n logger.info(`Starting to archive ${repos.length} repositories`, {\n dryRun: options.dryRun,\n });\n\n const tasks = repos.map((repo) =>\n this.queue.add(() => this.archiveRepository(repo, options))\n );\n\n await Promise.all(tasks);\n\n return this.results;\n }\n\n private async archiveRepository(\n repo: RepositoryIdentifier,\n options: ArchiveOptions\n ): Promise<void> {\n const startTime = Date.now();\n\n try {\n if (options.dryRun) {\n console.log(`\uD83D\uDD0D Validating ${repo.owner}/${repo.repo}...`);\n logger.info(`[DRY-RUN] Would archive ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: false,\n duration: Date.now() - startTime,\n message: \"[DRY-RUN] Validation successful\",\n };\n this.results.push(result);\n this.completed++;\n return;\n }\n\n const validation = await this.gitHubService.validateRepository(\n repo.owner,\n repo.repo\n );\n\n if (!validation.exists) {\n logger.warn(`Repository not found: ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: false,\n archived: false,\n error: \"Repository not found\",\n duration: Date.now() - startTime,\n message: \"Repository does not exist on GitHub\",\n };\n this.results.push(result);\n this.failed++;\n return;\n }\n\n if (validation.isArchived) {\n logger.info(`Repository already archived: ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: false,\n duration: Date.now() - startTime,\n message: \"Repository is already archived\",\n };\n this.results.push(result);\n this.completed++;\n return;\n }\n\n console.log(`\uD83D\uDCE6 Archiving ${repo.owner}/${repo.repo}...`);\n await this.gitHubService.archiveRepository(repo.owner, repo.repo);\n\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: true,\n duration: Date.now() - startTime,\n message: \"Repository archived successfully\",\n };\n this.results.push(result);\n this.completed++;\n logger.info(`\u2713 Archived ${repo.owner}/${repo.repo}`, {\n duration: result.duration,\n });\n } catch (error) {\n if (isArchiveError(error) && error.code === ErrorCode.ALREADY_ARCHIVED) {\n logger.info(`Repository already archived: ${repo.owner}/${repo.repo}`);\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: true,\n archived: false,\n duration: Date.now() - startTime,\n message: \"Repository is already archived\",\n };\n this.results.push(result);\n this.completed++;\n return;\n }\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.error(\n `\u2717 Failed to archive ${repo.owner}/${repo.repo}: ${errorMessage}`\n );\n\n const result: ArchiveResult = {\n owner: repo.owner,\n repo: repo.repo,\n success: false,\n archived: false,\n error: errorMessage,\n duration: Date.now() - startTime,\n message: `Error: ${errorMessage}`,\n };\n this.results.push(result);\n this.failed++;\n }\n }\n\n getProgress(): { completed: number; failed: number; total: number } {\n return {\n completed: this.completed,\n failed: this.failed,\n total: this.results.length,\n };\n }\n\n getSummary(): {\n successful: number;\n failed: number;\n skipped: number;\n totalDuration: number;\n } {\n const successful = this.results.filter(\n (r) => r.success && r.archived\n ).length;\n const failed = this.results.filter((r) => !r.success).length;\n const skipped = this.results.filter((r) => r.success && !r.archived).length;\n const totalDuration = Date.now() - this.startTime;\n\n return { successful, failed, skipped, totalDuration };\n }\n}\n", "export const ErrorCode = {\n INVALID_AUTH: \"INVALID_AUTH\",\n REPO_NOT_FOUND: \"REPO_NOT_FOUND\",\n ALREADY_ARCHIVED: \"ALREADY_ARCHIVED\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n RATE_LIMITED: \"RATE_LIMITED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n INVALID_URL: \"INVALID_URL\",\n PARSE_ERROR: \"PARSE_ERROR\",\n EDITOR_ERROR: \"EDITOR_ERROR\",\n CONFIG_ERROR: \"CONFIG_ERROR\",\n FILE_ERROR: \"FILE_ERROR\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nexport class ArchiveError extends Error {\n code: ErrorCode;\n statusCode?: number;\n retryable: boolean;\n\n constructor(\n code: ErrorCode,\n message: string,\n statusCode?: number,\n retryable = false\n ) {\n super(message);\n this.name = \"ArchiveError\";\n this.code = code;\n this.statusCode = statusCode;\n this.retryable = retryable;\n Object.setPrototypeOf(this, ArchiveError.prototype);\n }\n}\n", "import { MESSAGES } from \"../constants/messages\";\nimport { ArchiveError, ErrorCode } from \"../types\";\n\nexport function isArchiveError(error: unknown): error is ArchiveError {\n return error instanceof ArchiveError;\n}\n\nexport function getErrorMessage(error: unknown): string {\n if (isArchiveError(error)) {\n return error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\nexport function createAuthError(\n message: string,\n statusCode?: number\n): ArchiveError {\n return new ArchiveError(ErrorCode.INVALID_AUTH, message, statusCode, true);\n}\n\nexport function createRepoNotFoundError(\n owner: string,\n repo: string\n): ArchiveError {\n return new ArchiveError(\n ErrorCode.REPO_NOT_FOUND,\n `Repository ${owner}/${repo} not found`,\n 404,\n false\n );\n}\n\nexport function createAlreadyArchivedError(\n owner: string,\n repo: string\n): ArchiveError {\n return new ArchiveError(\n ErrorCode.ALREADY_ARCHIVED,\n `Repository ${owner}/${repo} is already archived`,\n 422,\n false\n );\n}\n\nexport function createPermissionError(\n owner: string,\n repo: string\n): ArchiveError {\n return new ArchiveError(\n ErrorCode.PERMISSION_DENIED,\n `Permission denied for ${owner}/${repo}. You must be the repository owner or have push access.`,\n 403,\n false\n );\n}\n\nexport function createRateLimitError(): ArchiveError {\n return new ArchiveError(\n ErrorCode.RATE_LIMITED,\n MESSAGES.RATE_LIMITED,\n 429,\n true\n );\n}\n\nexport function createNetworkError(message?: string): ArchiveError {\n return new ArchiveError(\n ErrorCode.NETWORK_ERROR,\n message || MESSAGES.NETWORK_ERROR,\n undefined,\n true\n );\n}\n\nexport function createInvalidUrlError(url: string): ArchiveError {\n return new ArchiveError(ErrorCode.INVALID_URL, `Invalid GitHub URL: ${url}`);\n}\n\nexport function createConfigError(message: string): ArchiveError {\n return new ArchiveError(ErrorCode.CONFIG_ERROR, message);\n}\n\nexport function createFileError(message: string): ArchiveError {\n return new ArchiveError(ErrorCode.FILE_ERROR, message);\n}\n\nexport function createEditorError(message: string): ArchiveError {\n return new ArchiveError(ErrorCode.EDITOR_ERROR, message);\n}\n", "import { promises as fs } from \"node:fs\";\nimport winston from \"winston\";\nimport { DEFAULT_LOG_LEVEL } from \"../constants/defaults\";\nimport { PATHS } from \"../constants/paths\";\nimport type { Config } from \"../types\";\n\nlet loggerInstance: winston.Logger;\n\nexport async function initializeLogger(logDir: string): Promise<void> {\n try {\n await fs.mkdir(logDir, { recursive: true });\n } catch (error) {\n console.error(\"Failed to create log directory:\", error);\n }\n}\n\nexport function createLogger(config?: Partial<Config>): winston.Logger {\n const level = config?.logLevel || DEFAULT_LOG_LEVEL;\n const logDir = config?.logDir || PATHS.LOG_DIR;\n\n return winston.createLogger({\n level,\n format: winston.format.combine(\n winston.format.timestamp({ format: \"YYYY-MM-DD HH:mm:ss\" }),\n winston.format.errors({ stack: true }),\n winston.format.json()\n ),\n defaultMeta: { service: \"github-archiver\" },\n transports: [\n new winston.transports.File({\n filename: `${logDir}/errors.log`,\n level: \"error\",\n }),\n new winston.transports.File({\n filename: `${logDir}/combined.log`,\n }),\n ],\n });\n}\n\nexport function createConsoleLogger(): winston.Logger {\n return winston.createLogger({\n format: winston.format.combine(\n winston.format.colorize(),\n winston.format.printf(({ level, message, ...metadata }) => {\n const meta = Object.keys(metadata).length\n ? JSON.stringify(metadata)\n : \"\";\n return `${level}: ${message} ${meta}`;\n })\n ),\n transports: [new winston.transports.Console()],\n });\n}\n\nexport function getLogger(): winston.Logger {\n if (!loggerInstance) {\n loggerInstance = createLogger();\n }\n return loggerInstance;\n}\n\nexport function setLogger(logger: winston.Logger): void {\n loggerInstance = logger;\n}\n", "export const DEFAULT_CONCURRENCY = 3;\nexport const DEFAULT_TIMEOUT = 300; // seconds\nexport const DEFAULT_LOG_LEVEL = \"info\" as const;\nexport const MAX_RETRIES = 3;\nexport const RETRY_DELAY_MS = 1000; // base delay for exponential backoff\nexport const GITHUB_API_URL = \"https://api.github.com\";\n", "import { Octokit } from \"octokit\";\nimport { PATHS } from \"../constants/paths\";\nimport type { Config, StoredCredentials } from \"../types\";\nimport { ConfigManager } from \"../utils/config\";\nimport { createAuthError } from \"../utils/errors\";\n\nexport class AuthManager {\n private readonly configManager: ConfigManager;\n\n constructor(configDir: string = PATHS.APP_DIR) {\n this.configManager = new ConfigManager(configDir);\n }\n\n async saveToken(token: string): Promise<void> {\n if (!token || typeof token !== \"string\") {\n throw createAuthError(\"Invalid token format\");\n }\n\n const octokit = new Octokit({ auth: token });\n\n try {\n const { data } = await octokit.rest.users.getAuthenticated();\n\n const credentials: StoredCredentials = {\n token,\n savedAt: new Date().toISOString(),\n githubUser: data.login,\n };\n\n await this.configManager.saveConfig(credentials);\n } catch (error) {\n throw createAuthError(\n `Failed to validate token: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async getToken(): Promise<string | undefined> {\n if (process.env.GH_TOKEN) {\n return process.env.GH_TOKEN;\n }\n\n const credentials = await this.configManager.getStoredCredentials();\n return credentials?.token;\n }\n\n async removeToken(): Promise<void> {\n await this.configManager.removeStoredCredentials();\n }\n\n async validateToken(\n token: string\n ): Promise<{ valid: boolean; user?: string }> {\n try {\n const octokit = new Octokit({ auth: token });\n const { data } = await octokit.rest.users.getAuthenticated();\n return {\n valid: true,\n user: data.login,\n };\n } catch (_error) {\n return {\n valid: false,\n };\n }\n }\n\n getStoredCredentials(): Promise<StoredCredentials | null> {\n return this.configManager.getStoredCredentials();\n }\n\n ensureConfigDir(): Promise<void> {\n return this.configManager.ensureConfigDir();\n }\n\n loadConfig(): Promise<Config> {\n return this.configManager.loadConfig();\n }\n}\n", "import { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n DEFAULT_CONCURRENCY,\n DEFAULT_LOG_LEVEL,\n DEFAULT_TIMEOUT,\n} from \"../constants/defaults\";\nimport { PATHS } from \"../constants/paths\";\nimport type { Config, StoredCredentials } from \"../types\";\nimport { createConfigError, createFileError } from \"./errors\";\n\nexport class ConfigManager {\n private readonly configDir: string;\n private readonly configFile: string;\n\n constructor(configDir: string = PATHS.APP_DIR) {\n this.configDir = configDir;\n this.configFile = join(configDir, \"config.json\");\n }\n\n async loadConfig(): Promise<Config> {\n try {\n const token = process.env.GH_TOKEN;\n const fileCredentials = await this.loadFileCredentials();\n\n return {\n token: token || fileCredentials?.token,\n concurrency: DEFAULT_CONCURRENCY,\n timeout: DEFAULT_TIMEOUT,\n logLevel: DEFAULT_LOG_LEVEL,\n logDir: join(this.configDir, \"logs\"),\n configDir: this.configDir,\n };\n } catch (error) {\n throw createConfigError(\n `Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async saveConfig(credentials: StoredCredentials): Promise<void> {\n try {\n await this.ensureConfigDir();\n await fs.writeFile(\n this.configFile,\n JSON.stringify(credentials, null, 2),\n \"utf-8\"\n );\n } catch (error) {\n throw createFileError(\n `Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async ensureConfigDir(): Promise<void> {\n try {\n await fs.mkdir(this.configDir, { recursive: true });\n } catch (error) {\n throw createFileError(\n `Failed to create config directory: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n async getStoredCredentials(): Promise<StoredCredentials | null> {\n try {\n const data = await fs.readFile(this.configFile, \"utf-8\");\n return JSON.parse(data) as StoredCredentials;\n } catch {\n return null;\n }\n }\n\n async removeStoredCredentials(): Promise<void> {\n try {\n await fs.unlink(this.configFile);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw createFileError(\n `Failed to remove stored credentials: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n\n private async loadFileCredentials(): Promise<StoredCredentials | null> {\n try {\n const data = await fs.readFile(this.configFile, \"utf-8\");\n return JSON.parse(data) as StoredCredentials;\n } catch {\n return null;\n }\n }\n}\n", "import { Octokit } from \"octokit\";\nimport { MAX_RETRIES, RETRY_DELAY_MS } from \"../constants/defaults\";\nimport type { RateLimitInfo } from \"../types\";\nimport {\n createAlreadyArchivedError,\n createNetworkError,\n createPermissionError,\n createRateLimitError,\n createRepoNotFoundError,\n} from \"../utils/errors\";\nimport { getLogger } from \"../utils/logger\";\n\nconst logger = getLogger();\n\ninterface OctokitError {\n message: string;\n response?: {\n data?: {\n message: string;\n };\n };\n status: number;\n}\n\nexport class GitHubService {\n private readonly octokit: Octokit;\n\n constructor(token: string) {\n this.octokit = new Octokit({ auth: token });\n }\n\n async archiveRepository(owner: string, repo: string): Promise<void> {\n const startTime = Date.now();\n logger.debug(`Attempting to archive ${owner}/${repo}`);\n\n await this.retryWithBackoff(async () => {\n try {\n await this.octokit.rest.repos.update({\n owner,\n repo,\n archived: true,\n });\n const duration = Date.now() - startTime;\n logger.info(`Successfully archived ${owner}/${repo} in ${duration}ms`);\n } catch (error) {\n this.handleArchiveError(error, owner, repo);\n }\n });\n }\n\n private handleArchiveError(\n error: unknown,\n owner: string,\n repo: string\n ): never {\n const err = this.parseOctokitError(error);\n logger.error(`Failed to archive ${owner}/${repo}: ${err.message}`, {\n status: err.status,\n code: err.code,\n });\n\n if (err.status === 404) {\n throw createRepoNotFoundError(owner, repo);\n }\n\n if (err.status === 403 || err.message.includes(\"permission\")) {\n throw createPermissionError(owner, repo);\n }\n\n if (err.status === 422 && err.message.includes(\"archived\")) {\n throw createAlreadyArchivedError(owner, repo);\n }\n\n if (err.message.includes(\"API rate limit\")) {\n throw createRateLimitError();\n }\n\n if (\n err.message.includes(\"ECONNREFUSED\") ||\n err.message.includes(\"ETIMEDOUT\") ||\n err.message.includes(\"EHOSTUNREACH\") ||\n err.message.includes(\"socket hang up\")\n ) {\n throw createNetworkError(err.message);\n }\n\n throw error;\n }\n\n private parseOctokitError(error: unknown): {\n message: string;\n status: number;\n code: string | undefined;\n } {\n const message = error instanceof Error ? error.message : String(error);\n const err = error as unknown as OctokitError;\n return {\n message,\n status: err?.status || 0,\n code: err?.response?.data?.message,\n };\n }\n\n async validateRepository(\n owner: string,\n repo: string\n ): Promise<{ exists: boolean; isArchived: boolean }> {\n try {\n logger.debug(`Validating repository ${owner}/${repo}`);\n const { data } = await this.octokit.rest.repos.get({\n owner,\n repo,\n });\n\n const result = {\n exists: true,\n isArchived: data.archived,\n };\n logger.debug(`Repository ${owner}/${repo} validated`, result);\n return result;\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"404\")) {\n logger.debug(`Repository ${owner}/${repo} does not exist`);\n return {\n exists: false,\n isArchived: false,\n };\n }\n\n logger.error(`Error validating ${owner}/${repo}:`, error);\n throw error;\n }\n }\n\n async getRateLimitStatus(): Promise<RateLimitInfo> {\n try {\n logger.debug(\"Fetching rate limit status\");\n const { data } = await this.octokit.rest.rateLimit.get();\n\n const info: RateLimitInfo = {\n remaining: data.rate.remaining,\n reset: new Date(data.rate.reset * 1000),\n limit: data.rate.limit,\n };\n\n logger.debug(\"Rate limit status retrieved\", {\n remaining: info.remaining,\n limit: info.limit,\n resetAt: info.reset.toISOString(),\n });\n\n return info;\n } catch (error) {\n logger.error(\"Failed to fetch rate limit status:\", error);\n throw error;\n }\n }\n\n async validateAuth(): Promise<string> {\n try {\n logger.debug(\"Validating authentication\");\n const { data } = await this.octokit.rest.users.getAuthenticated();\n logger.info(`Authenticated as user: ${data.login}`);\n return data.login;\n } catch (error) {\n logger.error(\"Authentication validation failed:\", error);\n throw createNetworkError(\n error instanceof Error ? error.message : \"Authentication failed\"\n );\n }\n }\n\n private async retryWithBackoff<T>(\n fn: () => Promise<T>,\n maxRetries: number = MAX_RETRIES\n ): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error as Error;\n\n const message = lastError.message.toLowerCase();\n const isRetryable =\n message.includes(\"rate limit\") ||\n message.includes(\"timeout\") ||\n message.includes(\"econnrefused\") ||\n message.includes(\"etimedout\") ||\n message.includes(\"ehostunreach\") ||\n message.includes(\"socket hang up\");\n\n if (!isRetryable || attempt === maxRetries - 1) {\n throw error;\n }\n\n const delay = RETRY_DELAY_MS * 2 ** attempt;\n logger.warn(\n `Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms`,\n {\n error: message,\n }\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError || new Error(\"Unknown error during retry\");\n }\n}\n", "import { promises as fs } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport type { RepositoryIdentifier } from \"../types\";\nimport { getLogger } from \"./logger\";\nimport { URLParser } from \"./parser\";\n\nconst logger = getLogger();\n\nexport class InputHandler {\n getRepositoriesFromInteractive(): Promise<{\n repos: RepositoryIdentifier[];\n errors: Array<{ url: string; error: string; line: number }>;\n }> {\n logger.info(\"Starting interactive CLI input for repositories\");\n\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(\"\");\n console.log(\"\uD83D\uDCDD Enter repository URLs one at a time:\");\n console.log(\n \" (Press CTRL+D to finish, or leave empty and press Enter to skip)\"\n );\n console.log(\"\");\n\n const urls: string[] = [];\n let lineNumber = 1;\n let isFinished = false;\n\n const promptNext = () => {\n rl.question(`[${lineNumber}] > `, (input) => {\n const trimmedInput = input.trim();\n\n if (trimmedInput === \"\") {\n lineNumber++;\n promptNext();\n return;\n }\n\n urls.push(trimmedInput);\n lineNumber++;\n promptNext();\n });\n };\n\n const finishInput = () => {\n if (isFinished) {\n return;\n }\n isFinished = true;\n rl.close();\n const result = URLParser.parseRepositoriesBatch(urls);\n\n if (result.invalid.length > 0) {\n logger.warn(`Found ${result.invalid.length} invalid repositories`, {\n errors: result.invalid,\n });\n }\n\n logger.info(`Parsed ${result.valid.length} valid repositories`);\n resolve({\n repos: result.valid,\n errors: result.invalid,\n });\n };\n\n rl.on(\"close\", () => {\n finishInput();\n });\n\n promptNext();\n });\n }\n\n async getRepositoriesFromFile(filePath: string): Promise<{\n repos: RepositoryIdentifier[];\n errors: Array<{ url: string; error: string; line: number }>;\n }> {\n logger.info(`Reading repositories from file: ${filePath}`);\n\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n const result = URLParser.parseRepositoriesBatch(lines);\n\n if (result.invalid.length > 0) {\n logger.warn(`Found ${result.invalid.length} invalid entries in file`, {\n errors: result.invalid,\n });\n }\n\n logger.info(`Parsed ${result.valid.length} valid repositories from file`);\n return {\n repos: result.valid,\n errors: result.invalid,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to read file ${filePath}: ${message}`);\n throw new Error(`Failed to read file: ${message}`);\n }\n }\n\n getRepositoriesFromStdin(): Promise<{\n repos: RepositoryIdentifier[];\n errors: Array<{ url: string; error: string; line: number }>;\n }> {\n logger.info(\"Reading repositories from stdin\");\n\n return new Promise((resolve) => {\n const lines: string[] = [];\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.on(\"line\", (line) => {\n lines.push(line);\n });\n\n rl.on(\"close\", () => {\n const result = URLParser.parseRepositoriesBatch(lines);\n\n if (result.invalid.length > 0) {\n logger.warn(\n `Found ${result.invalid.length} invalid entries from stdin`,\n {\n errors: result.invalid,\n }\n );\n }\n\n logger.info(\n `Parsed ${result.valid.length} valid repositories from stdin`\n );\n resolve({\n repos: result.valid,\n errors: result.invalid,\n });\n });\n });\n }\n\n promptForConfirmation(message: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(`${message} [y/N]: `, (answer) => {\n rl.close();\n const confirmed = answer.toLowerCase() === \"y\";\n logger.info(`User confirmation: ${confirmed}`);\n resolve(confirmed);\n });\n });\n }\n}\n", "import type { RepositoryIdentifier } from \"../types\";\nimport { ArchiveError, ErrorCode } from \"../types\";\nimport { getLogger } from \"./logger\";\n\nconst logger = getLogger();\nconst NAME_REGEX = /^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$/;\n\nconst GITHUB_URL_PATTERNS: RegExp[] = [\n // https://github.com/owner/repo or https://github.com/owner/repo.git\n /^(?:https?:\\/\\/)?(?:www\\.)?github\\.com[/:]([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?(?:\\/)?$/i,\n // git@github.com:owner/repo.git or git@github.com:owner/repo\n /^git@github\\.com:([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?$/i,\n // owner/repo (shorthand)\n /^([\\w.-]+)\\/([\\w.-]+)$/,\n];\n\nfunction parseRepositoryUrl(url: string): RepositoryIdentifier {\n const trimmed = url.trim();\n\n if (!trimmed) {\n throw new ArchiveError(ErrorCode.INVALID_URL, \"URL cannot be empty\");\n }\n\n for (const pattern of GITHUB_URL_PATTERNS) {\n const match = trimmed.match(pattern);\n if (match) {\n const owner = match[1];\n const repo = match[2];\n\n if (!(owner && repo)) {\n throw new ArchiveError(\n ErrorCode.INVALID_URL,\n `Invalid GitHub URL format: ${url}`\n );\n }\n\n if (!isValidName(owner)) {\n throw new ArchiveError(\n ErrorCode.INVALID_URL,\n `Invalid owner name: ${owner}. Owner names must contain only alphanumeric characters, hyphens, or periods.`\n );\n }\n\n if (!isValidName(repo)) {\n throw new ArchiveError(\n ErrorCode.INVALID_URL,\n `Invalid repository name: ${repo}. Repository names must contain only alphanumeric characters, hyphens, underscores, or periods.`\n );\n }\n\n const normalizedUrl = `https://github.com/${owner}/${repo}`;\n\n logger.debug(\"Parsed repository URL\", {\n original: trimmed,\n owner,\n repo,\n normalized: normalizedUrl,\n });\n\n return {\n owner,\n repo,\n url: normalizedUrl,\n };\n }\n }\n\n throw new ArchiveError(ErrorCode.INVALID_URL, `Invalid GitHub URL: ${url}`);\n}\n\nfunction isValidName(name: string): boolean {\n return NAME_REGEX.test(name);\n}\n\nfunction parseRepositoriesBatch(urls: string[]): {\n valid: RepositoryIdentifier[];\n invalid: Array<{ url: string; error: string; line: number }>;\n} {\n const valid: RepositoryIdentifier[] = [];\n const invalid: Array<{ url: string; error: string; line: number }> = [];\n\n for (const [index, url] of urls.entries()) {\n const lineNumber = index + 1;\n const trimmed = url.trim();\n\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n\n try {\n const parsed = parseRepositoryUrl(trimmed);\n valid.push(parsed);\n logger.debug(`Line ${lineNumber}: Valid repository`, {\n owner: parsed.owner,\n repo: parsed.repo,\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n invalid.push({\n url: trimmed,\n error: errorMessage,\n line: lineNumber,\n });\n logger.warn(`Line ${lineNumber}: Invalid repository`, {\n url: trimmed,\n error: errorMessage,\n });\n }\n }\n\n logger.info(\"Batch parsing complete\", {\n total: urls.length,\n valid: valid.length,\n invalid: invalid.length,\n skipped: urls.length - valid.length - invalid.length,\n });\n\n return { valid, invalid };\n}\n\nexport const URLParser = {\n parseRepositoryUrl,\n parseRepositoriesBatch,\n};\n", "function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${ms}ms`;\n }\n if (ms < 60_000) {\n return `${(ms / 1000).toFixed(1)}s`;\n }\n const minutes = Math.floor(ms / 60_000);\n const seconds = ((ms % 60_000) / 1000).toFixed(0);\n return `${minutes}m ${seconds}s`;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) {\n return \"0 B\";\n }\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${(bytes / k ** i).toFixed(2)} ${sizes[i]}`;\n}\n\nfunction formatPercent(value: number, total: number): string {\n if (total === 0) {\n return \"0%\";\n }\n return `${((value / total) * 100).toFixed(1)}%`;\n}\n\nfunction createProgressBar(\n completed: number,\n total: number,\n width = 30\n): string {\n if (total === 0) {\n return \"[ ]\";\n }\n const filledWidth = Math.round((completed / total) * width);\n const emptyWidth = width - filledWidth;\n const filled = \"=\".repeat(filledWidth);\n const empty = \" \".repeat(emptyWidth);\n const percentage = ((completed / total) * 100).toFixed(0);\n return `[${filled}${empty}] ${percentage}%`;\n}\n\nfunction centerText(text: string, width: number): string {\n const padding = Math.max(0, (width - text.length) / 2);\n return \" \".repeat(Math.floor(padding)) + text;\n}\n\nfunction truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n return `${text.substring(0, maxLength - 3)}...`;\n}\n\nexport const Formatting = {\n formatDuration,\n formatBytes,\n formatPercent,\n createProgressBar,\n centerText,\n truncate,\n};\n", "import { Formatting } from \"./formatting\";\n\nexport interface ProgressUpdate {\n completed: number;\n current?: {\n owner: string;\n repo: string;\n };\n failed: number;\n total: number;\n}\n\nexport class ProgressDisplay {\n private readonly startTime: number;\n private lastUpdate = 0;\n private readonly updateInterval = 500; // ms\n\n constructor() {\n this.startTime = Date.now();\n }\n\n shouldUpdate(): boolean {\n const now = Date.now();\n if (now - this.lastUpdate >= this.updateInterval) {\n this.lastUpdate = now;\n return true;\n }\n return false;\n }\n\n getProgressBar(progress: ProgressUpdate): string {\n const { completed, total, current } = progress;\n const percent = total > 0 ? (completed / total) * 100 : 0;\n const bar = Formatting.createProgressBar(completed, total, 25);\n\n let line = `${bar} ${completed}/${total} (${percent.toFixed(0)}%)`;\n\n if (current) {\n line += ` - ${current.owner}/${current.repo}`;\n }\n\n return line;\n }\n getSummaryBox(summary: {\n successful: number;\n failed: number;\n skipped: number;\n totalDuration: number;\n }): string {\n const { successful, failed, skipped, totalDuration } = summary;\n const total = successful + failed + skipped;\n const duration = Formatting.formatDuration(totalDuration);\n\n const createLine = (\n label: string,\n value: string,\n extraPadding = 0\n ): string => {\n const valueStr = String(value);\n const contentLength = label.length + valueStr.length;\n const padding = 31 - contentLength + extraPadding;\n return `\u2551 ${label}${\" \".repeat(Math.max(1, padding))}${valueStr} \u2551`;\n };\n\n const lines = [\n \"\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\",\n \"\u2551 Archive Operation Summary \u2551\",\n \"\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\",\n createLine(\"\u2705 Successful:\", String(successful), 2),\n createLine(\"\u26A0\uFE0F Skipped:\", String(skipped), 3),\n createLine(\"\u274C Failed:\", String(failed), 2),\n \"\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\",\n createLine(\"Total:\", String(total), 3),\n createLine(\"Duration:\", duration, 3),\n \"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\",\n ];\n\n return lines.join(\"\\n\");\n }\n\n getElapsedTime(): string {\n const elapsed = Date.now() - this.startTime;\n return Formatting.formatDuration(elapsed);\n }\n\n getEstimatedTimeRemaining(completed: number, total: number): string | null {\n if (completed === 0) {\n return null;\n }\n\n const elapsed = Date.now() - this.startTime;\n const avgTime = elapsed / completed;\n const remaining = (total - completed) * avgTime;\n\n return remaining > 0 ? Formatting.formatDuration(remaining) : null;\n }\n}\n", "import { createInterface } from \"node:readline\";\nimport { Command } from \"commander\";\nimport { MESSAGES } from \"../constants/messages\";\nimport { PATHS } from \"../constants/paths\";\nimport { AuthManager } from \"../services/auth-manager\";\nimport { getLogger } from \"../utils/logger\";\n\nconst authManager = new AuthManager(PATHS.APP_DIR);\n\nexport function createLoginCommand(): Command {\n return new Command(\"login\")\n .description(\"Set up GitHub authentication with a Personal Access Token\")\n .action(async () => {\n try {\n await authManager.ensureConfigDir();\n\n const token = await promptForToken();\n\n if (!token) {\n console.error(\"No token provided. Aborting.\");\n process.exit(1);\n }\n\n console.log(\"Validating token...\");\n await authManager.saveToken(token);\n\n const credentials = await authManager.getStoredCredentials();\n console.log(`\u2713 ${MESSAGES.AUTH_SUCCESS}`);\n console.log(` User: ${credentials?.githubUser}`);\n\n getLogger().info(\"Token saved successfully\", {\n user: credentials?.githubUser,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Failed to save token\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function createLogoutCommand(): Command {\n return new Command(\"logout\")\n .description(\"Remove stored GitHub authentication token\")\n .action(async () => {\n try {\n const credentials = await authManager.getStoredCredentials();\n\n if (!credentials) {\n console.log(\"No stored credentials found.\");\n return;\n }\n\n const confirmed = await confirmAction(MESSAGES.CONFIRM_LOGOUT);\n\n if (!confirmed) {\n console.log(\"Cancelled.\");\n return;\n }\n\n await authManager.removeToken();\n console.log(`\u2713 ${MESSAGES.TOKEN_REMOVED}`);\n getLogger().info(\"Token removed\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Failed to remove token\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function createStatusCommand(): Command {\n return new Command(\"status\")\n .description(\"Check authentication status\")\n .action(async () => {\n try {\n const token = await authManager.getToken();\n\n if (!token) {\n console.log(\"\u2717 Not authenticated\");\n console.log(\n ' Run \"github-archiver auth login\" to set up authentication'\n );\n return;\n }\n\n console.log(\"\u2713 Authenticated\");\n\n const validation = await authManager.validateToken(token);\n\n if (validation.valid) {\n console.log(` User: ${validation.user}`);\n const credentials = await authManager.getStoredCredentials();\n if (credentials) {\n console.log(\n ` Saved at: ${new Date(credentials.savedAt).toLocaleString()}`\n );\n }\n getLogger().info(\"Authentication status check passed\");\n } else {\n console.log(\"\u2717 Token is invalid or expired\");\n getLogger().warn(\"Token validation failed\");\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Failed to check auth status\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function createValidateCommand(): Command {\n return new Command(\"validate\")\n .description(\"Validate stored authentication token\")\n .action(async () => {\n try {\n const token = await authManager.getToken();\n\n if (!token) {\n console.log(\"\u2717 No token found\");\n process.exit(1);\n }\n\n console.log(\"Validating token...\");\n const validation = await authManager.validateToken(token);\n\n if (validation.valid) {\n console.log(`\u2713 Token is valid (user: ${validation.user})`);\n getLogger().info(\"Token validation successful\", {\n user: validation.user,\n });\n } else {\n console.log(\"\u2717 Token is invalid or expired\");\n getLogger().warn(\"Token validation failed\");\n process.exit(1);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\u2717 Error: ${message}`);\n getLogger().error(\"Token validation error\", { error: message });\n process.exit(1);\n }\n });\n}\n\nexport function promptForToken(): Promise<string> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(MESSAGES.ENTER_TOKEN, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport function confirmAction(message: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(`${message} [y/N]: `, (answer) => {\n rl.close();\n resolve(answer.toLowerCase() === \"y\");\n });\n });\n}\n\nexport function createAuthCommand(): Command {\n return new Command(\"auth\")\n .description(\"Manage GitHub authentication\")\n .addCommand(createLoginCommand())\n .addCommand(createLogoutCommand())\n .addCommand(createStatusCommand())\n .addCommand(createValidateCommand());\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["Command", "Command", "MESSAGES", "homedir", "join", "appDir", "PATHS", "PQueue", "ErrorCode", "ArchiveError", "_ArchiveError", "code", "message", "statusCode", "retryable", "isArchiveError", "error", "ArchiveError", "createAuthError", "message", "statusCode", "ArchiveError", "ErrorCode", "createRepoNotFoundError", "owner", "repo", "createAlreadyArchivedError", "createPermissionError", "createRateLimitError", "MESSAGES", "createNetworkError", "createConfigError", "message", "ArchiveError", "ErrorCode", "createFileError", "fs", "winston", "DEFAULT_LOG_LEVEL", "loggerInstance", "initializeLogger", "logDir", "fs", "error", "createLogger", "config", "level", "DEFAULT_LOG_LEVEL", "PATHS", "winston", "getLogger", "loggerInstance", "createLogger", "setLogger", "logger", "logger", "getLogger", "Archiver", "gitHubService", "options", "PQueue", "repos", "tasks", "repo", "startTime", "result", "validation", "error", "isArchiveError", "ErrorCode", "errorMessage", "successful", "r", "failed", "skipped", "totalDuration", "Octokit", "fs", "join", "ConfigManager", "configDir", "PATHS", "join", "token", "fileCredentials", "DEFAULT_LOG_LEVEL", "error", "createConfigError", "credentials", "fs", "createFileError", "data", "AuthManager", "configDir", "PATHS", "ConfigManager", "token", "createAuthError", "octokit", "Octokit", "data", "credentials", "error", "Octokit", "logger", "getLogger", "GitHubService", "token", "Octokit", "owner", "repo", "startTime", "duration", "error", "err", "createRepoNotFoundError", "createPermissionError", "createAlreadyArchivedError", "createRateLimitError", "createNetworkError", "message", "data", "result", "info", "fn", "maxRetries", "lastError", "attempt", "delay", "resolve", "fs", "createInterface", "logger", "getLogger", "NAME_REGEX", "GITHUB_URL_PATTERNS", "parseRepositoryUrl", "url", "trimmed", "ArchiveError", "ErrorCode", "pattern", "match", "owner", "repo", "isValidName", "normalizedUrl", "name", "parseRepositoriesBatch", "urls", "valid", "invalid", "index", "lineNumber", "parsed", "error", "errorMessage", "URLParser", "logger", "getLogger", "InputHandler", "resolve", "rl", "createInterface", "urls", "lineNumber", "isFinished", "promptNext", "input", "trimmedInput", "finishInput", "result", "URLParser", "filePath", "lines", "fs", "error", "message", "line", "answer", "confirmed", "formatDuration", "ms", "minutes", "seconds", "formatBytes", "bytes", "k", "sizes", "formatPercent", "value", "total", "createProgressBar", "completed", "width", "filledWidth", "emptyWidth", "filled", "empty", "percentage", "centerText", "text", "padding", "truncate", "maxLength", "Formatting", "ProgressDisplay", "now", "progress", "completed", "total", "current", "percent", "line", "Formatting", "summary", "successful", "failed", "skipped", "totalDuration", "duration", "createLine", "label", "value", "extraPadding", "valueStr", "padding", "elapsed", "avgTime", "remaining", "logger", "getLogger", "createArchiveCommand", "Command", "options", "archiveCommand", "concurrency", "timeout", "validateOptions", "authManager", "AuthManager", "PATHS", "inputHandler", "InputHandler", "progressDisplay", "ProgressDisplay", "githubService", "authenticateUser", "repositories", "parseErrors", "getRepositories", "logParseErrors", "MESSAGES", "showRepositoriesPreview", "confirmOperation", "archiveOptions", "archiver", "Archiver", "results", "archiveRepositories", "displayResults", "error", "handleArchiveError", "token", "GitHubService", "authenticatedUser", "result", "err", "dryRun", "index", "repo", "summary", "progress", "r", "message", "provideErrorGuidance", "lowerMessage", "createInterface", "Command", "authManager", "AuthManager", "PATHS", "createLoginCommand", "Command", "token", "promptForToken", "credentials", "MESSAGES", "getLogger", "error", "message", "createLogoutCommand", "confirmAction", "createStatusCommand", "validation", "createValidateCommand", "resolve", "rl", "createInterface", "answer", "createAuthCommand", "VERSION", "DESCRIPTION", "main", "initializeLogger", "PATHS", "fileLogger", "createLogger", "setLogger", "program", "Command", "createAuthCommand", "createArchiveCommand", "error", "message"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-archiver",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Archive GitHub repositories via CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,21 +29,21 @@
29
29
  "prepublishOnly": "bun run build && bun run typecheck && bun run lint"
30
30
  },
31
31
  "dependencies": {
32
- "commander": "^14.0.2",
32
+ "commander": "^14.0.3",
33
33
  "octokit": "^5.0.5",
34
34
  "p-queue": "^9.1.0",
35
35
  "winston": "^3.19.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@biomejs/biome": "^2.3.11",
38
+ "@biomejs/biome": "^2.3.14",
39
39
  "@changesets/changelog-github": "^0.5.2",
40
40
  "@changesets/cli": "^2.29.8",
41
41
  "@changesets/get-github-info": "^0.7.0",
42
- "@types/bun": "^1.3.6",
42
+ "@types/bun": "^1.3.8",
43
43
  "esbuild": "^0.27.2",
44
44
  "tsx": "^4.21.0",
45
45
  "typescript": "^5.9.3",
46
- "ultracite": "^7.0.11"
46
+ "ultracite": "^7.1.4"
47
47
  },
48
48
  "keywords": [
49
49
  "github",