github-archiver 1.0.8 → 1.1.0

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,32 +1,60 @@
1
1
  ## [1.0.8](https://github.com/mynameistito/github-archiver/compare/v1.0.7...v1.0.8) (2026-01-11)
2
2
 
3
- ## [1.0.7](https://github.com/mynameistito/github-archiver/compare/v1.0.6...v1.0.7) (2026-01-11)
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`3322224`](https://github.com/mynameistito/github-archiver/commit/3322224c8518cf6ce3a0a9afbbcbe3b283c5427d) Thanks [@mynameistito](https://github.com/mynameistito)! - Replace 'done' text input with CTRL+D hotkey
8
+
9
+ - Remove 'done' command requirement for finishing repository input
10
+ - Implement native CTRL+D (EOF) signal handling via readline close event
11
+ - Add guard flag to prevent double execution
12
+ - Update help text to guide users to press CTRL+D instead of typing 'done'
13
+ - Maintains cross-platform compatibility (Windows, Mac, Linux)
14
+
15
+ - [`3322224`](https://github.com/mynameistito/github-archiver/commit/3322224c8518cf6ce3a0a9afbbcbe3b283c5427d) Thanks [@mynameistito](https://github.com/mynameistito)! - Improve dry-run logging with console output and fix summary box alignment
16
+
17
+ - Add console output to dry-run validation to show progress in real-time (🔍 emoji)
18
+ - Fix startTime initialization in Archiver to properly calculate operation duration
19
+ - Refactor summary box formatting with dynamic padding that accounts for emoji character widths
20
+ - Ensure all numeric values in summary box are properly right-aligned
4
21
 
22
+ ## 1.0.9
23
+
24
+ ### Patch Changes
25
+
26
+ - [`230c310`](https://github.com/mynameistito/github-archiver/commit/230c3106625c6b4170aff58dc8a79ed78f7a6938) Thanks [@mynameistito](https://github.com/mynameistito)! - Migrate from semantic-release to @changesets/cli for better release control
27
+
28
+ - Replace semantic-release with @changesets/cli for explicit version management
29
+ - Add GitHub-linked changelog generation with PR/commit references
30
+ - Update to two-step release process (Version Packages PR + manual merge)
31
+ - Integrate with bun for improved package management
32
+ - Update Node requirement to 22+ (aligns with actual dependencies)
33
+ - Update CI/CD workflows to use bun exclusively
34
+
35
+ ## [1.0.7](https://github.com/mynameistito/github-archiver/compare/v1.0.6...v1.0.7) (2026-01-11)
5
36
 
6
37
  ### Bug Fixes
7
38
 
8
- * update version number in source code to match package.json ([f9ef48c](https://github.com/mynameistito/github-archiver/commit/f9ef48c344cf46e2ebb37141a9db211bddf57d47))
39
+ - update version number in source code to match package.json ([f9ef48c](https://github.com/mynameistito/github-archiver/commit/f9ef48c344cf46e2ebb37141a9db211bddf57d47))
9
40
 
10
41
  ## [1.0.6](https://github.com/mynameistito/github-archiver/compare/v1.0.5...v1.0.6) (2026-01-11)
11
42
 
12
-
13
43
  ### Bug Fixes
14
44
 
15
- * ensure ESM output in bundled dist file ([48a242a](https://github.com/mynameistito/github-archiver/commit/48a242ad0b15c16e78c66aafab69ba84b64590b9))
45
+ - ensure ESM output in bundled dist file ([48a242a](https://github.com/mynameistito/github-archiver/commit/48a242ad0b15c16e78c66aafab69ba84b64590b9))
16
46
 
17
47
  ## [1.0.5](https://github.com/mynameistito/github-archiver/compare/v1.0.4...v1.0.5) (2026-01-11)
18
48
 
19
-
20
49
  ### Bug Fixes
21
50
 
22
- * build output as ESM to match package.json type ([e0aadd7](https://github.com/mynameistito/github-archiver/commit/e0aadd740c559333375e57b0f34e7e2d6a04240c))
51
+ - build output as ESM to match package.json type ([e0aadd7](https://github.com/mynameistito/github-archiver/commit/e0aadd740c559333375e57b0f34e7e2d6a04240c))
23
52
 
24
53
  ## [1.0.4](https://github.com/mynameistito/github-archiver/compare/v1.0.3...v1.0.4) (2026-01-11)
25
54
 
26
-
27
55
  ### Bug Fixes
28
56
 
29
- * add repository URL to package.json for npm provenance ([02ddb1c](https://github.com/mynameistito/github-archiver/commit/02ddb1c9f4b6a7b7ca91497b2633011accf94356))
57
+ - add repository URL to package.json for npm provenance ([02ddb1c](https://github.com/mynameistito/github-archiver/commit/02ddb1c9f4b6a7b7ca91497b2633011accf94356))
30
58
 
31
59
  ## [1.0.3](https://github.com/mynameistito/github-archiver/compare/v1.0.2...v1.0.3) (2026-01-11)
32
60
 
@@ -34,17 +62,15 @@
34
62
 
35
63
  ## [1.0.1](https://github.com/mynameistito/github-archiver/compare/v1.0.0...v1.0.1) (2026-01-11)
36
64
 
37
-
38
65
  ### Bug Fixes
39
66
 
40
- * correct relative import path in CLI wrapper ([cc64796](https://github.com/mynameistito/github-archiver/commit/cc64796d2d0a06175faf053d63e9c1e8ffe0d371))
67
+ - correct relative import path in CLI wrapper ([cc64796](https://github.com/mynameistito/github-archiver/commit/cc64796d2d0a06175faf053d63e9c1e8ffe0d371))
41
68
 
42
69
  # 1.0.0 (2026-01-11)
43
70
 
44
-
45
71
  ### Bug Fixes
46
72
 
47
- * separate CLI wrapper to resolve ESM shebang conflict ([fc9918a](https://github.com/mynameistito/github-archiver/commit/fc9918a46953634b1ad15a0f64175937acb4f29b))
73
+ - separate CLI wrapper to resolve ESM shebang conflict ([fc9918a](https://github.com/mynameistito/github-archiver/commit/fc9918a46953634b1ad15a0f64175937acb4f29b))
48
74
 
49
75
  # Changelog
50
76
 
@@ -56,6 +82,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
56
82
  ## [1.0.0] - 2025-01-11
57
83
 
58
84
  ### Added
85
+
59
86
  - Initial release of GitHub Archiver CLI
60
87
  - Mass archive GitHub repositories with parallel processing
61
88
  - Three input methods: editor, file, and stdin
@@ -77,6 +104,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
104
  - Biome formatting and linting enforcement
78
105
 
79
106
  ### Commands
107
+
80
108
  - `auth login` - Authenticate with GitHub
81
109
  - `auth logout` - Remove stored token
82
110
  - `auth status` - Check authentication status
@@ -84,6 +112,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
84
112
  - `archive` - Archive repositories with multiple options
85
113
 
86
114
  ### Features
115
+
87
116
  - Parallel processing with p-queue
88
117
  - Retry logic with exponential backoff (1s → 2s → 4s)
89
118
  - Repository validation (exists, permissions, already archived)
@@ -93,6 +122,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
93
122
  - Support for configuration files and comments in input
94
123
 
95
124
  ### Documentation
125
+
96
126
  - Comprehensive README with quick start guide
97
127
  - Installation instructions
98
128
  - Command reference with examples
@@ -101,6 +131,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
101
131
  - Architecture overview
102
132
 
103
133
  ### Testing
134
+
104
135
  - 34 unit tests covering:
105
136
  - URL parsing (20+ tests)
106
137
  - Output formatting (15+ tests)
@@ -108,6 +139,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
108
139
  - Vitest configuration for fast testing
109
140
 
110
141
  ### Code Quality
142
+
111
143
  - TypeScript strict mode enabled
112
144
  - 0 compilation errors
113
145
  - 0 implicit any types
@@ -119,6 +151,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
119
151
  ## Unreleased
120
152
 
121
153
  ### Planned Features
154
+
122
155
  - [ ] Batch progress reporting with live updates
123
156
  - [ ] Support for organization-wide archiving
124
157
  - [ ] Integration tests for archive command
@@ -130,6 +163,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
130
163
  - [ ] Undo/rollback capability (unarchive)
131
164
 
132
165
  ### Under Consideration
166
+
133
167
  - Docker image distribution
134
168
  - NPX support for one-time use
135
169
  - Scheduled archiving via cron
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{Command as Me}from"commander";import{Command as Ae}from"commander";var g={AUTH_SUCCESS:"Successfully authenticated with GitHub",ARCHIVE_SUCCESS:"Repository archived successfully",TOKEN_SAVED:"GitHub token saved successfully",TOKEN_REMOVED:"GitHub token removed",INVALID_TOKEN:"Invalid or expired GitHub token",REPO_NOT_FOUND:"Repository not found",ALREADY_ARCHIVED:"Repository is already archived",PERMISSION_DENIED:"You do not have permission to archive this repository",RATE_LIMITED:"GitHub API rate limit exceeded. Please try again later",NETWORK_ERROR:"Network error. Please check your connection",INVALID_URL:"Invalid GitHub repository URL",NO_TOKEN:'No GitHub token found. Please run "github-archiver auth login" first',OPENING_EDITOR:"Opening text editor for repository URLs...",NO_REPOS_PROVIDED:"No repositories provided",ARCHIVING_START:"Starting to archive repositories...",ARCHIVING_COMPLETE:"Archiving complete",DRY_RUN_MODE:"Running in dry-run mode (no repositories will be archived)",CONFIRM_ARCHIVE:"Are you sure you want to archive these repositories?",ENTER_TOKEN:"Enter your GitHub Personal Access Token (will not be displayed):",CONFIRM_LOGOUT:"Are you sure you want to remove the stored token?"};import{homedir as ie}from"node:os";import{join as A}from"node:path";var w=A(ie(),".github-archiver"),p={APP_DIR:w,CONFIG_FILE:A(w,"config.json"),LOG_DIR:A(w,"logs"),COMBINED_LOG:A(w,"logs","combined.log"),ERROR_LOG:A(w,"logs","errors.log")};import se from"p-queue";var u={INVALID_AUTH:"INVALID_AUTH",REPO_NOT_FOUND:"REPO_NOT_FOUND",ALREADY_ARCHIVED:"ALREADY_ARCHIVED",PERMISSION_DENIED:"PERMISSION_DENIED",RATE_LIMITED:"RATE_LIMITED",NETWORK_ERROR:"NETWORK_ERROR",INVALID_URL:"INVALID_URL",PARSE_ERROR:"PARSE_ERROR",EDITOR_ERROR:"EDITOR_ERROR",CONFIG_ERROR:"CONFIG_ERROR",FILE_ERROR:"FILE_ERROR"},l=class r extends Error{constructor(e,t,i,o=!1){super(t),this.name="ArchiveError",this.code=e,this.statusCode=i,this.retryable=o,Object.setPrototypeOf(this,r.prototype)}};function V(r){return r instanceof l}function U(r,e){return new l(u.INVALID_AUTH,r,e,!0)}function B(r,e){return new l(u.REPO_NOT_FOUND,`Repository ${r}/${e} not found`,404,!1)}function Y(r,e){return new l(u.ALREADY_ARCHIVED,`Repository ${r}/${e} is already archived`,422,!1)}function K(r,e){return new l(u.PERMISSION_DENIED,`Permission denied for ${r}/${e}. You must be the repository owner or have push access.`,403,!1)}function W(){return new l(u.RATE_LIMITED,g.RATE_LIMITED,429,!0)}function x(r){return new l(u.NETWORK_ERROR,r||g.NETWORK_ERROR,void 0,!0)}function z(r){return new l(u.CONFIG_ERROR,r)}function O(r){return new l(u.FILE_ERROR,r)}import{promises as ne}from"node:fs";import y from"winston";var S="info";var T;async function j(r){try{await ne.mkdir(r,{recursive:!0})}catch(e){console.error("Failed to create log directory:",e)}}function H(r){let e=r?.logLevel||S,t=r?.logDir||p.LOG_DIR;return y.createLogger({level:e,format:y.format.combine(y.format.timestamp({format:"YYYY-MM-DD HH:mm:ss"}),y.format.errors({stack:!0}),y.format.json()),defaultMeta:{service:"github-archiver"},transports:[new y.transports.File({filename:`${t}/errors.log`,level:"error"}),new y.transports.File({filename:`${t}/combined.log`})]})}function c(){return T||(T=H()),T}function q(r){T=r}var v=c(),D=class{constructor(e,t){this.results=[];this.completed=0;this.failed=0;this.startTime=0;this.gitHubService=e,this.queue=new se({concurrency:t.concurrency,timeout:t.timeout*1e3,interval:1e3,intervalCap:t.concurrency}),v.info("Archiver initialized",{concurrency:t.concurrency,timeout:t.timeout,dryRun:t.dryRun})}async archiveRepositories(e,t){this.results=[],this.completed=0,this.failed=0,v.info(`Starting to archive ${e.length} repositories`,{dryRun:t.dryRun});let i=e.map(o=>this.queue.add(()=>this.archiveRepository(o,t)));return await Promise.all(i),this.results}async archiveRepository(e,t){let i=Date.now();try{if(t.dryRun){v.info(`[DRY-RUN] Would archive ${e.owner}/${e.repo}`);let s={owner:e.owner,repo:e.repo,success:!0,archived:!1,duration:Date.now()-i,message:"[DRY-RUN] Validation successful"};this.results.push(s),this.completed++;return}let o=await this.gitHubService.validateRepository(e.owner,e.repo);if(!o.exists){v.warn(`Repository not found: ${e.owner}/${e.repo}`);let s={owner:e.owner,repo:e.repo,success:!1,archived:!1,error:"Repository not found",duration:Date.now()-i,message:"Repository does not exist on GitHub"};this.results.push(s),this.failed++;return}if(o.isArchived){v.info(`Repository already archived: ${e.owner}/${e.repo}`);let s={owner:e.owner,repo:e.repo,success:!0,archived:!1,duration:Date.now()-i,message:"Repository is already archived"};this.results.push(s),this.completed++;return}console.log(`\u{1F4E6} Archiving ${e.owner}/${e.repo}...`),await this.gitHubService.archiveRepository(e.owner,e.repo);let n={owner:e.owner,repo:e.repo,success:!0,archived:!0,duration:Date.now()-i,message:"Repository archived successfully"};this.results.push(n),this.completed++,v.info(`\u2713 Archived ${e.owner}/${e.repo}`,{duration:n.duration})}catch(o){if(V(o)&&o.code===u.ALREADY_ARCHIVED){v.info(`Repository already archived: ${e.owner}/${e.repo}`);let a={owner:e.owner,repo:e.repo,success:!0,archived:!1,duration:Date.now()-i,message:"Repository is already archived"};this.results.push(a),this.completed++;return}let n=o instanceof Error?o.message:String(o);v.error(`\u2717 Failed to archive ${e.owner}/${e.repo}: ${n}`);let s={owner:e.owner,repo:e.repo,success:!1,archived:!1,error:n,duration:Date.now()-i,message:`Error: ${n}`};this.results.push(s),this.failed++}}getProgress(){return{completed:this.completed,failed:this.failed,total:this.results.length}}getSummary(){let e=this.results.filter(n=>n.success&&n.archived).length,t=this.results.filter(n=>!n.success).length,i=this.results.filter(n=>n.success&&!n.archived).length,o=Date.now()-this.startTime;return{successful:e,failed:t,skipped:i,totalDuration:o}}};import{Octokit as X}from"octokit";import{promises as I}from"node:fs";import{join as J}from"node:path";var L=class{constructor(e=p.APP_DIR){this.configDir=e,this.configFile=J(e,"config.json")}async loadConfig(){try{let e=process.env.GH_TOKEN,t=await this.loadFileCredentials();return{token:e||t?.token,concurrency:3,timeout:300,logLevel:S,logDir:J(this.configDir,"logs"),configDir:this.configDir}}catch(e){throw z(`Failed to load configuration: ${e instanceof Error?e.message:String(e)}`)}}async saveConfig(e){try{await this.ensureConfigDir(),await I.writeFile(this.configFile,JSON.stringify(e,null,2),"utf-8")}catch(t){throw O(`Failed to save configuration: ${t instanceof Error?t.message:String(t)}`)}}async ensureConfigDir(){try{await I.mkdir(this.configDir,{recursive:!0})}catch(e){throw O(`Failed to create config directory: ${e instanceof Error?e.message:String(e)}`)}}async getStoredCredentials(){try{let e=await I.readFile(this.configFile,"utf-8");return JSON.parse(e)}catch{return null}}async removeStoredCredentials(){try{await I.unlink(this.configFile)}catch(e){if(e.code!=="ENOENT")throw O(`Failed to remove stored credentials: ${e instanceof Error?e.message:String(e)}`)}}async loadFileCredentials(){try{let e=await I.readFile(this.configFile,"utf-8");return JSON.parse(e)}catch{return null}}};var E=class{constructor(e=p.APP_DIR){this.configManager=new L(e)}async saveToken(e){if(!e||typeof e!="string")throw U("Invalid token format");let t=new X({auth:e});try{let{data:i}=await t.rest.users.getAuthenticated(),o={token:e,savedAt:new Date().toISOString(),githubUser:i.login};await this.configManager.saveConfig(o)}catch(i){throw U(`Failed to validate token: ${i instanceof Error?i.message:String(i)}`)}}async getToken(){return process.env.GH_TOKEN?process.env.GH_TOKEN:(await this.configManager.getStoredCredentials())?.token}async removeToken(){await this.configManager.removeStoredCredentials()}async validateToken(e){try{let t=new X({auth:e}),{data:i}=await t.rest.users.getAuthenticated();return{valid:!0,user:i.login}}catch{return{valid:!1}}}getStoredCredentials(){return this.configManager.getStoredCredentials()}ensureConfigDir(){return this.configManager.ensureConfigDir()}loadConfig(){return this.configManager.loadConfig()}};import{Octokit as de}from"octokit";var d=c(),_=class{constructor(e){this.octokit=new de({auth:e})}async archiveRepository(e,t){let i=Date.now();d.debug(`Attempting to archive ${e}/${t}`),await this.retryWithBackoff(async()=>{try{await this.octokit.rest.repos.update({owner:e,repo:t,archived:!0});let o=Date.now()-i;d.info(`Successfully archived ${e}/${t} in ${o}ms`)}catch(o){this.handleArchiveError(o,e,t)}})}handleArchiveError(e,t,i){let o=this.parseOctokitError(e);throw d.error(`Failed to archive ${t}/${i}: ${o.message}`,{status:o.status,code:o.code}),o.status===404?B(t,i):o.status===403||o.message.includes("permission")?K(t,i):o.status===422&&o.message.includes("archived")?Y(t,i):o.message.includes("API rate limit")?W():o.message.includes("ECONNREFUSED")||o.message.includes("ETIMEDOUT")||o.message.includes("EHOSTUNREACH")||o.message.includes("socket hang up")?x(o.message):e}parseOctokitError(e){let t=e instanceof Error?e.message:String(e),i=e;return{message:t,status:i?.status||0,code:i?.response?.data?.message}}async validateRepository(e,t){try{d.debug(`Validating repository ${e}/${t}`);let{data:i}=await this.octokit.rest.repos.get({owner:e,repo:t}),o={exists:!0,isArchived:i.archived};return d.debug(`Repository ${e}/${t} validated`,o),o}catch(i){if(i instanceof Error&&i.message.includes("404"))return d.debug(`Repository ${e}/${t} does not exist`),{exists:!1,isArchived:!1};throw d.error(`Error validating ${e}/${t}:`,i),i}}async getRateLimitStatus(){try{d.debug("Fetching rate limit status");let{data:e}=await this.octokit.rest.rateLimit.get(),t={remaining:e.rate.remaining,reset:new Date(e.rate.reset*1e3),limit:e.rate.limit};return d.debug("Rate limit status retrieved",{remaining:t.remaining,limit:t.limit,resetAt:t.reset.toISOString()}),t}catch(e){throw d.error("Failed to fetch rate limit status:",e),e}}async validateAuth(){try{d.debug("Validating authentication");let{data:e}=await this.octokit.rest.users.getAuthenticated();return d.info(`Authenticated as user: ${e.login}`),e.login}catch(e){throw d.error("Authentication validation failed:",e),x(e instanceof Error?e.message:"Authentication failed")}}async retryWithBackoff(e,t=3){let i;for(let o=0;o<t;o++)try{return await e()}catch(n){i=n;let s=i.message.toLowerCase();if(!(s.includes("rate limit")||s.includes("timeout")||s.includes("econnrefused")||s.includes("etimedout")||s.includes("ehostunreach")||s.includes("socket hang up"))||o===t-1)throw n;let m=1e3*2**o;d.warn(`Retry attempt ${o+1}/${t} after ${m}ms`,{error:s}),await new Promise($=>setTimeout($,m))}throw i||new Error("Unknown error during retry")}};import{promises as fe}from"node:fs";import{createInterface as M}from"node:readline";var P=c(),me=/^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$/,ge=[/^(?:https?:\/\/)?(?:www\.)?github\.com[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:\/)?$/i,/^git@github\.com:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/i,/^([\w.-]+)\/([\w.-]+)$/];function Q(r){let e=r.trim();if(!e)throw new l(u.INVALID_URL,"URL cannot be empty");for(let t of ge){let i=e.match(t);if(i){let o=i[1],n=i[2];if(!(o&&n))throw new l(u.INVALID_URL,`Invalid GitHub URL format: ${r}`);if(!Z(o))throw new l(u.INVALID_URL,`Invalid owner name: ${o}. Owner names must contain only alphanumeric characters, hyphens, or periods.`);if(!Z(n))throw new l(u.INVALID_URL,`Invalid repository name: ${n}. Repository names must contain only alphanumeric characters, hyphens, underscores, or periods.`);let s=`https://github.com/${o}/${n}`;return P.debug("Parsed repository URL",{original:e,owner:o,repo:n,normalized:s}),{owner:o,repo:n,url:s}}}throw new l(u.INVALID_URL,`Invalid GitHub URL: ${r}`)}function Z(r){return me.test(r)}function pe(r){let e=[],t=[];for(let[i,o]of r.entries()){let n=i+1,s=o.trim();if(!(!s||s.startsWith("#")))try{let a=Q(s);e.push(a),P.debug(`Line ${n}: Valid repository`,{owner:a.owner,repo:a.repo})}catch(a){let m=a instanceof Error?a.message:String(a);t.push({url:s,error:m,line:n}),P.warn(`Line ${n}: Invalid repository`,{url:s,error:m})}}return P.info("Batch parsing complete",{total:r.length,valid:e.length,invalid:t.length,skipped:r.length-e.length-t.length}),{valid:e,invalid:t}}var k={parseRepositoryUrl:Q,parseRepositoriesBatch:pe};var f=c(),N=class{getRepositoriesFromInteractive(){return f.info("Starting interactive CLI input for repositories"),new Promise(e=>{let t=M({input:process.stdin,output:process.stdout});console.log(""),console.log("\u{1F4DD} Enter repository URLs one at a time:"),console.log(' (Type "done" when finished, or leave empty and press Enter to skip)'),console.log("");let i=[],o=1,n=()=>{t.question(`[${o}] > `,a=>{let m=a.trim();if(m.toLowerCase()==="done"){s();return}if(m===""){o++,n();return}i.push(m),o++,n()})},s=()=>{t.close();let a=k.parseRepositoriesBatch(i);a.invalid.length>0&&f.warn(`Found ${a.invalid.length} invalid repositories`,{errors:a.invalid}),f.info(`Parsed ${a.valid.length} valid repositories`),e({repos:a.valid,errors:a.invalid})};t.on("close",()=>{s()}),n()})}async getRepositoriesFromFile(e){f.info(`Reading repositories from file: ${e}`);try{let i=(await fe.readFile(e,"utf-8")).split(`
2
- `),o=k.parseRepositoriesBatch(i);return o.invalid.length>0&&f.warn(`Found ${o.invalid.length} invalid entries in file`,{errors:o.invalid}),f.info(`Parsed ${o.valid.length} valid repositories from file`),{repos:o.valid,errors:o.invalid}}catch(t){let i=t instanceof Error?t.message:String(t);throw f.error(`Failed to read file ${e}: ${i}`),new Error(`Failed to read file: ${i}`)}}getRepositoriesFromStdin(){return f.info("Reading repositories from stdin"),new Promise(e=>{let t=[],i=M({input:process.stdin,output:process.stdout});i.on("line",o=>{t.push(o)}),i.on("close",()=>{let o=k.parseRepositoriesBatch(t);o.invalid.length>0&&f.warn(`Found ${o.invalid.length} invalid entries from stdin`,{errors:o.invalid}),f.info(`Parsed ${o.valid.length} valid repositories from stdin`),e({repos:o.valid,errors:o.invalid})})})}promptForConfirmation(e){return new Promise(t=>{let i=M({input:process.stdin,output:process.stdout});i.question(`${e} [y/N]: `,o=>{i.close();let n=o.toLowerCase()==="y";f.info(`User confirmation: ${n}`),t(n)})})}};function he(r){if(r<1e3)return`${r}ms`;if(r<6e4)return`${(r/1e3).toFixed(1)}s`;let e=Math.floor(r/6e4),t=(r%6e4/1e3).toFixed(0);return`${e}m ${t}s`}function ve(r){if(r===0)return"0 B";let e=1024,t=["B","KB","MB","GB"],i=Math.floor(Math.log(r)/Math.log(e));return`${(r/e**i).toFixed(2)} ${t[i]}`}function Re(r,e){return e===0?"0%":`${(r/e*100).toFixed(1)}%`}function ye(r,e,t=30){if(e===0)return"[ ]";let i=Math.round(r/e*t),o=t-i,n="=".repeat(i),s=" ".repeat(o),a=(r/e*100).toFixed(0);return`[${n}${s}] ${a}%`}function Ee(r,e){let t=Math.max(0,(e-r.length)/2);return" ".repeat(Math.floor(t))+r}function we(r,e){return r.length<=e?r:`${r.substring(0,e-3)}...`}var b={formatDuration:he,formatBytes:ve,formatPercent:Re,createProgressBar:ye,centerText:Ee,truncate:we};var F=class{constructor(){this.lastUpdate=0;this.updateInterval=500;this.startTime=Date.now()}shouldUpdate(){let e=Date.now();return e-this.lastUpdate>=this.updateInterval?(this.lastUpdate=e,!0):!1}getProgressBar(e){let{completed:t,total:i,current:o}=e,n=i>0?t/i*100:0,a=`${b.createProgressBar(t,i,25)} ${t}/${i} (${n.toFixed(0)}%)`;return o&&(a+=` - ${o.owner}/${o.repo}`),a}getSummaryBox(e){let{successful:t,failed:i,skipped:o,totalDuration:n}=e,s=t+i+o,a=b.formatDuration(n);return["\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","\u2551 Archive Operation Summary \u2551","\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",`\u2551 \u2705 Successful: ${String(t).padEnd(20)} \u2551`,`\u2551 \u26A0\uFE0F Skipped: ${String(o).padEnd(20)} \u2551`,`\u2551 \u274C Failed: ${String(i).padEnd(20)} \u2551`,"\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",`\u2551 Total: ${String(s).padEnd(20)} \u2551`,`\u2551 Duration: ${String(a).padEnd(20)} \u2551`,"\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"].join(`
3
- `)}getElapsedTime(){let e=Date.now()-this.startTime;return b.formatDuration(e)}getEstimatedTimeRemaining(e,t){if(e===0)return null;let o=(Date.now()-this.startTime)/e,n=(t-e)*o;return n>0?b.formatDuration(n):null}};var R=c();function ee(){return new Ae("archive").description("Archive GitHub repositories").option("--file <path>","Read repository URLs from file").option("--stdin","Read repository URLs from standard input").option("--dry-run","Validate without archiving",!1).option("--concurrency <n>","Number of parallel operations","3").option("--timeout <n>","API timeout in seconds","300").option("--verbose","Verbose logging",!1).option("--force","Skip confirmations",!1).action(async r=>{await Ie(r)})}async function Ie(r){try{R.info("Archive command started",{options:r});let{concurrency:e,timeout:t}=be(r),i=new E(p.APP_DIR),o=new N,n=new F,s=await Ce(i),{repositories:a,parseErrors:m}=await $e(r,o);m.length>0&&Oe(m),a.length===0&&(console.error(`\u274C ${g.NO_REPOS_PROVIDED}`),process.exit(1)),Se(a,r.dryRun),await Te(r,o);let $={dryRun:r.dryRun,concurrency:e,timeout:t,force:r.force,verbose:r.verbose};console.log(""),console.log(`${g.ARCHIVING_START} (concurrency: ${e})`),console.log("");let G=new D(s,$),oe=await De(G,a,$,n);Le(G,oe,n)}catch(e){_e(e)}}function be(r){let e=Number.parseInt(r.concurrency,10),t=Number.parseInt(r.timeout,10);return(e<1||e>50)&&(console.error("\u274C Concurrency must be between 1 and 50"),process.exit(1)),(t<10||t>3600)&&(console.error("\u274C Timeout must be between 10 and 3600 seconds"),process.exit(1)),{concurrency:e,timeout:t}}async function Ce(r){console.log("\u{1F510} Checking authentication...");let e=await r.getToken();e||(console.error(`\u274C ${g.NO_TOKEN}`),console.error(" Run: github-archiver auth login"),process.exit(1));let t=new _(e);try{let i=await t.validateAuth();return console.log(`\u2705 Authenticated as: ${i}`),t}catch(i){console.error("\u274C Authentication failed"),R.error("Auth validation failed:",i),process.exit(1)}}async function $e(r,e){console.log(""),console.log("\u{1F4DD} Getting repositories...");let t=[],i=[];if(r.file){R.info(`Using file input: ${r.file}`);let o=await e.getRepositoriesFromFile(r.file);t=o.repos,i=o.errors}else if(r.stdin){R.info("Using stdin input"),console.log("Enter repository URLs (one per line, press Ctrl+D to finish):");let o=await e.getRepositoriesFromStdin();t=o.repos,i=o.errors}else{R.info("Using interactive CLI input");let o=await e.getRepositoriesFromInteractive();t=o.repos,i=o.errors}return{repositories:t,parseErrors:i}}function Oe(r){console.warn(`\u26A0\uFE0F Found ${r.length} invalid repositories:`);for(let e of r)console.warn(` Line ${e.line}: ${e.error}`);console.warn("")}function Se(r,e){console.log(`\u{1F4CB} Will ${e?"validate":"archive"} ${r.length} repositories:`);for(let t=0;t<Math.min(r.length,5);t++){let i=r[t];i&&console.log(` ${t+1}. ${i.owner}/${i.repo}`)}r.length>5&&console.log(` ... and ${r.length-5} more`)}async function Te(r,e){console.log(""),r.force||r.dryRun?r.dryRun&&console.log(`\u2139\uFE0F ${g.DRY_RUN_MODE}`):await e.promptForConfirmation("Are you sure you want to archive these repositories?")||(console.log("\u274C Cancelled"),process.exit(1))}async function De(r,e,t,i){let o=await r.archiveRepositories(e,t),n=r.getSummary(),s={completed:n.successful+n.failed+n.skipped,failed:n.failed,total:e.length};return i.shouldUpdate()&&console.log(`\r${i.getProgressBar(s)}`),o}function Le(r,e,t){console.log(""),console.log("");let i=r.getSummary();if(console.log(t.getSummaryBox(i)),i.failed>0){console.log(""),console.log("\u274C Failed repositories:");for(let o of e)o.success||console.log(` ${o.owner}/${o.repo}: ${o.message}`)}if(i.skipped>0){console.log(""),console.log("\u26A0\uFE0F Skipped repositories:");for(let o of e)o.success&&!o.archived&&console.log(` ${o.owner}/${o.repo}: ${o.message}`)}i.failed>0?(R.warn(`Archive command completed with ${i.failed} failures`),process.exit(1)):(console.log(""),console.log("\u2705 All repositories processed successfully!"),R.info("Archive command completed successfully"),process.exit(0))}function _e(r){let e=r instanceof Error?r.message:String(r);console.error(`\u274C Error: ${e}`),R.error("Archive command failed:",r),Pe(e),process.exit(1)}function Pe(r){let e=r.toLowerCase();if(e.includes("token")||e.includes("authentication")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. Make sure you have a valid GitHub Personal Access Token"),console.log(' 2. The token needs "repo" scope to archive repositories'),console.log(" 3. Run: github-archiver auth login");return}if(e.includes("rate limit")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. GitHub API rate limit has been exceeded"),console.log(" 2. Wait a few minutes and try again"),console.log(" 3. Use lower concurrency: --concurrency 1");return}if(e.includes("permission")||e.includes("403")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. You must be the repository owner or have push access"),console.log(" 2. Check that your GitHub token has correct permissions"),console.log(" 3. Verify you have push access to the repositories");return}if(e.includes("not found")||e.includes("404")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. Make sure the repository URL is correct"),console.log(" 2. The repository may have been deleted"),console.log(" 3. Check your GitHub access to the repository");return}(e.includes("network")||e.includes("timeout"))&&(console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. Check your internet connection"),console.log(" 2. GitHub API may be temporarily unavailable"),console.log(" 3. Try again in a moment"),console.log(" 4. Increase timeout: --timeout 600"))}import{createInterface as re}from"node:readline";import{Command as C}from"commander";var h=new E(p.APP_DIR);function ke(){return new C("login").description("Set up GitHub authentication with a Personal Access Token").action(async()=>{try{await h.ensureConfigDir();let r=await xe();r||(console.error("No token provided. Aborting."),process.exit(1)),console.log("Validating token..."),await h.saveToken(r);let e=await h.getStoredCredentials();console.log(`\u2713 ${g.AUTH_SUCCESS}`),console.log(` User: ${e?.githubUser}`),c().info("Token saved successfully",{user:e?.githubUser})}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),c().error("Failed to save token",{error:e}),process.exit(1)}})}function Ne(){return new C("logout").description("Remove stored GitHub authentication token").action(async()=>{try{if(!await h.getStoredCredentials()){console.log("No stored credentials found.");return}if(!await He(g.CONFIRM_LOGOUT)){console.log("Cancelled.");return}await h.removeToken(),console.log(`\u2713 ${g.TOKEN_REMOVED}`),c().info("Token removed")}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),c().error("Failed to remove token",{error:e}),process.exit(1)}})}function Fe(){return new C("status").description("Check authentication status").action(async()=>{try{let r=await h.getToken();if(!r){console.log("\u2717 Not authenticated"),console.log(' Run "github-archiver auth login" to set up authentication');return}console.log("\u2713 Authenticated");let e=await h.validateToken(r);if(e.valid){console.log(` User: ${e.user}`);let t=await h.getStoredCredentials();t&&console.log(` Saved at: ${new Date(t.savedAt).toLocaleString()}`),c().info("Authentication status check passed")}else console.log("\u2717 Token is invalid or expired"),c().warn("Token validation failed")}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),c().error("Failed to check auth status",{error:e}),process.exit(1)}})}function Ue(){return new C("validate").description("Validate stored authentication token").action(async()=>{try{let r=await h.getToken();r||(console.log("\u2717 No token found"),process.exit(1)),console.log("Validating token...");let e=await h.validateToken(r);e.valid?(console.log(`\u2713 Token is valid (user: ${e.user})`),c().info("Token validation successful",{user:e.user})):(console.log("\u2717 Token is invalid or expired"),c().warn("Token validation failed"),process.exit(1))}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),c().error("Token validation error",{error:e}),process.exit(1)}})}function xe(){return new Promise(r=>{let e=re({input:process.stdin,output:process.stdout});e.question(g.ENTER_TOKEN,t=>{e.close(),r(t.trim())})})}function He(r){return new Promise(e=>{let t=re({input:process.stdin,output:process.stdout});t.question(`${r} [y/N]: `,i=>{t.close(),e(i.toLowerCase()==="y")})})}function te(){return new C("auth").description("Manage GitHub authentication").addCommand(ke()).addCommand(Ne()).addCommand(Fe()).addCommand(Ue())}var Ge="1.0.6",Ve="Archive GitHub repositories via CLI";async function Be(){try{await j(p.LOG_DIR);let r=H();q(r);let e=new Me;e.name("github-archiver").description(Ve).version(Ge,"-v, --version","Display version"),e.addCommand(te()),e.addCommand(ee()),e.addHelpCommand(!0),await e.parseAsync(process.argv),process.argv.slice(2).length||e.outputHelp()}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Fatal error: ${e}`),process.exit(1)}}Be();
1
+ import{Command as Be}from"commander";import{Command as Ce}from"commander";var m={AUTH_SUCCESS:"Successfully authenticated with GitHub",ARCHIVE_SUCCESS:"Repository archived successfully",TOKEN_SAVED:"GitHub token saved successfully",TOKEN_REMOVED:"GitHub token removed",INVALID_TOKEN:"Invalid or expired GitHub token",REPO_NOT_FOUND:"Repository not found",ALREADY_ARCHIVED:"Repository is already archived",PERMISSION_DENIED:"You do not have permission to archive this repository",RATE_LIMITED:"GitHub API rate limit exceeded. Please try again later",NETWORK_ERROR:"Network error. Please check your connection",INVALID_URL:"Invalid GitHub repository URL",NO_TOKEN:'No GitHub token found. Please run "github-archiver auth login" first',OPENING_EDITOR:"Opening text editor for repository URLs...",NO_REPOS_PROVIDED:"No repositories provided",ARCHIVING_START:"Starting to archive repositories...",ARCHIVING_COMPLETE:"Archiving complete",DRY_RUN_MODE:"Running in dry-run mode (no repositories will be archived)",CONFIRM_ARCHIVE:"Are you sure you want to archive these repositories?",ENTER_TOKEN:"Enter your GitHub Personal Access Token (will not be displayed):",CONFIRM_LOGOUT:"Are you sure you want to remove the stored token?"};import{homedir as ae}from"node:os";import{join as b}from"node:path";var I=b(ae(),".github-archiver"),f={APP_DIR:I,CONFIG_FILE:b(I,"config.json"),LOG_DIR:b(I,"logs"),COMBINED_LOG:b(I,"logs","combined.log"),ERROR_LOG:b(I,"logs","errors.log")};import le from"p-queue";var d={INVALID_AUTH:"INVALID_AUTH",REPO_NOT_FOUND:"REPO_NOT_FOUND",ALREADY_ARCHIVED:"ALREADY_ARCHIVED",PERMISSION_DENIED:"PERMISSION_DENIED",RATE_LIMITED:"RATE_LIMITED",NETWORK_ERROR:"NETWORK_ERROR",INVALID_URL:"INVALID_URL",PARSE_ERROR:"PARSE_ERROR",EDITOR_ERROR:"EDITOR_ERROR",CONFIG_ERROR:"CONFIG_ERROR",FILE_ERROR:"FILE_ERROR"},u=class r extends Error{constructor(e,t,i,o=!1){super(t),this.name="ArchiveError",this.code=e,this.statusCode=i,this.retryable=o,Object.setPrototypeOf(this,r.prototype)}};function Y(r){return r instanceof u}function H(r,e){return new u(d.INVALID_AUTH,r,e,!0)}function K(r,e){return new u(d.REPO_NOT_FOUND,`Repository ${r}/${e} not found`,404,!1)}function W(r,e){return new u(d.ALREADY_ARCHIVED,`Repository ${r}/${e} is already archived`,422,!1)}function z(r,e){return new u(d.PERMISSION_DENIED,`Permission denied for ${r}/${e}. You must be the repository owner or have push access.`,403,!1)}function j(){return new u(d.RATE_LIMITED,m.RATE_LIMITED,429,!0)}function M(r){return new u(d.NETWORK_ERROR,r||m.NETWORK_ERROR,void 0,!0)}function q(r){return new u(d.CONFIG_ERROR,r)}function O(r){return new u(d.FILE_ERROR,r)}import{promises as ce}from"node:fs";import E from"winston";var T="info";var D;async function J(r){try{await ce.mkdir(r,{recursive:!0})}catch(e){console.error("Failed to create log directory:",e)}}function G(r){let e=r?.logLevel||T,t=r?.logDir||f.LOG_DIR;return E.createLogger({level:e,format:E.format.combine(E.format.timestamp({format:"YYYY-MM-DD HH:mm:ss"}),E.format.errors({stack:!0}),E.format.json()),defaultMeta:{service:"github-archiver"},transports:[new E.transports.File({filename:`${t}/errors.log`,level:"error"}),new E.transports.File({filename:`${t}/combined.log`})]})}function l(){return D||(D=G()),D}function X(r){D=r}var R=l(),L=class{constructor(e,t){this.results=[];this.completed=0;this.failed=0;this.gitHubService=e,this.startTime=Date.now(),this.queue=new le({concurrency:t.concurrency,timeout:t.timeout*1e3,interval:1e3,intervalCap:t.concurrency}),R.info("Archiver initialized",{concurrency:t.concurrency,timeout:t.timeout,dryRun:t.dryRun})}async archiveRepositories(e,t){this.results=[],this.completed=0,this.failed=0,R.info(`Starting to archive ${e.length} repositories`,{dryRun:t.dryRun});let i=e.map(o=>this.queue.add(()=>this.archiveRepository(o,t)));return await Promise.all(i),this.results}async archiveRepository(e,t){let i=Date.now();try{if(t.dryRun){console.log(`\u{1F50D} Validating ${e.owner}/${e.repo}...`),R.info(`[DRY-RUN] Would archive ${e.owner}/${e.repo}`);let s={owner:e.owner,repo:e.repo,success:!0,archived:!1,duration:Date.now()-i,message:"[DRY-RUN] Validation successful"};this.results.push(s),this.completed++;return}let o=await this.gitHubService.validateRepository(e.owner,e.repo);if(!o.exists){R.warn(`Repository not found: ${e.owner}/${e.repo}`);let s={owner:e.owner,repo:e.repo,success:!1,archived:!1,error:"Repository not found",duration:Date.now()-i,message:"Repository does not exist on GitHub"};this.results.push(s),this.failed++;return}if(o.isArchived){R.info(`Repository already archived: ${e.owner}/${e.repo}`);let s={owner:e.owner,repo:e.repo,success:!0,archived:!1,duration:Date.now()-i,message:"Repository is already archived"};this.results.push(s),this.completed++;return}console.log(`\u{1F4E6} Archiving ${e.owner}/${e.repo}...`),await this.gitHubService.archiveRepository(e.owner,e.repo);let n={owner:e.owner,repo:e.repo,success:!0,archived:!0,duration:Date.now()-i,message:"Repository archived successfully"};this.results.push(n),this.completed++,R.info(`\u2713 Archived ${e.owner}/${e.repo}`,{duration:n.duration})}catch(o){if(Y(o)&&o.code===d.ALREADY_ARCHIVED){R.info(`Repository already archived: ${e.owner}/${e.repo}`);let a={owner:e.owner,repo:e.repo,success:!0,archived:!1,duration:Date.now()-i,message:"Repository is already archived"};this.results.push(a),this.completed++;return}let n=o instanceof Error?o.message:String(o);R.error(`\u2717 Failed to archive ${e.owner}/${e.repo}: ${n}`);let s={owner:e.owner,repo:e.repo,success:!1,archived:!1,error:n,duration:Date.now()-i,message:`Error: ${n}`};this.results.push(s),this.failed++}}getProgress(){return{completed:this.completed,failed:this.failed,total:this.results.length}}getSummary(){let e=this.results.filter(n=>n.success&&n.archived).length,t=this.results.filter(n=>!n.success).length,i=this.results.filter(n=>n.success&&!n.archived).length,o=Date.now()-this.startTime;return{successful:e,failed:t,skipped:i,totalDuration:o}}};import{Octokit as Q}from"octokit";import{promises as C}from"node:fs";import{join as Z}from"node:path";var _=class{constructor(e=f.APP_DIR){this.configDir=e,this.configFile=Z(e,"config.json")}async loadConfig(){try{let e=process.env.GH_TOKEN,t=await this.loadFileCredentials();return{token:e||t?.token,concurrency:3,timeout:300,logLevel:T,logDir:Z(this.configDir,"logs"),configDir:this.configDir}}catch(e){throw q(`Failed to load configuration: ${e instanceof Error?e.message:String(e)}`)}}async saveConfig(e){try{await this.ensureConfigDir(),await C.writeFile(this.configFile,JSON.stringify(e,null,2),"utf-8")}catch(t){throw O(`Failed to save configuration: ${t instanceof Error?t.message:String(t)}`)}}async ensureConfigDir(){try{await C.mkdir(this.configDir,{recursive:!0})}catch(e){throw O(`Failed to create config directory: ${e instanceof Error?e.message:String(e)}`)}}async getStoredCredentials(){try{let e=await C.readFile(this.configFile,"utf-8");return JSON.parse(e)}catch{return null}}async removeStoredCredentials(){try{await C.unlink(this.configFile)}catch(e){if(e.code!=="ENOENT")throw O(`Failed to remove stored credentials: ${e instanceof Error?e.message:String(e)}`)}}async loadFileCredentials(){try{let e=await C.readFile(this.configFile,"utf-8");return JSON.parse(e)}catch{return null}}};var w=class{constructor(e=f.APP_DIR){this.configManager=new _(e)}async saveToken(e){if(!e||typeof e!="string")throw H("Invalid token format");let t=new Q({auth:e});try{let{data:i}=await t.rest.users.getAuthenticated(),o={token:e,savedAt:new Date().toISOString(),githubUser:i.login};await this.configManager.saveConfig(o)}catch(i){throw H(`Failed to validate token: ${i instanceof Error?i.message:String(i)}`)}}async getToken(){return process.env.GH_TOKEN?process.env.GH_TOKEN:(await this.configManager.getStoredCredentials())?.token}async removeToken(){await this.configManager.removeStoredCredentials()}async validateToken(e){try{let t=new Q({auth:e}),{data:i}=await t.rest.users.getAuthenticated();return{valid:!0,user:i.login}}catch{return{valid:!1}}}getStoredCredentials(){return this.configManager.getStoredCredentials()}ensureConfigDir(){return this.configManager.ensureConfigDir()}loadConfig(){return this.configManager.loadConfig()}};import{Octokit as fe}from"octokit";var g=l(),P=class{constructor(e){this.octokit=new fe({auth:e})}async archiveRepository(e,t){let i=Date.now();g.debug(`Attempting to archive ${e}/${t}`),await this.retryWithBackoff(async()=>{try{await this.octokit.rest.repos.update({owner:e,repo:t,archived:!0});let o=Date.now()-i;g.info(`Successfully archived ${e}/${t} in ${o}ms`)}catch(o){this.handleArchiveError(o,e,t)}})}handleArchiveError(e,t,i){let o=this.parseOctokitError(e);throw g.error(`Failed to archive ${t}/${i}: ${o.message}`,{status:o.status,code:o.code}),o.status===404?K(t,i):o.status===403||o.message.includes("permission")?z(t,i):o.status===422&&o.message.includes("archived")?W(t,i):o.message.includes("API rate limit")?j():o.message.includes("ECONNREFUSED")||o.message.includes("ETIMEDOUT")||o.message.includes("EHOSTUNREACH")||o.message.includes("socket hang up")?M(o.message):e}parseOctokitError(e){let t=e instanceof Error?e.message:String(e),i=e;return{message:t,status:i?.status||0,code:i?.response?.data?.message}}async validateRepository(e,t){try{g.debug(`Validating repository ${e}/${t}`);let{data:i}=await this.octokit.rest.repos.get({owner:e,repo:t}),o={exists:!0,isArchived:i.archived};return g.debug(`Repository ${e}/${t} validated`,o),o}catch(i){if(i instanceof Error&&i.message.includes("404"))return g.debug(`Repository ${e}/${t} does not exist`),{exists:!1,isArchived:!1};throw g.error(`Error validating ${e}/${t}:`,i),i}}async getRateLimitStatus(){try{g.debug("Fetching rate limit status");let{data:e}=await this.octokit.rest.rateLimit.get(),t={remaining:e.rate.remaining,reset:new Date(e.rate.reset*1e3),limit:e.rate.limit};return g.debug("Rate limit status retrieved",{remaining:t.remaining,limit:t.limit,resetAt:t.reset.toISOString()}),t}catch(e){throw g.error("Failed to fetch rate limit status:",e),e}}async validateAuth(){try{g.debug("Validating authentication");let{data:e}=await this.octokit.rest.users.getAuthenticated();return g.info(`Authenticated as user: ${e.login}`),e.login}catch(e){throw g.error("Authentication validation failed:",e),M(e instanceof Error?e.message:"Authentication failed")}}async retryWithBackoff(e,t=3){let i;for(let o=0;o<t;o++)try{return await e()}catch(n){i=n;let s=i.message.toLowerCase();if(!(s.includes("rate limit")||s.includes("timeout")||s.includes("econnrefused")||s.includes("etimedout")||s.includes("ehostunreach")||s.includes("socket hang up"))||o===t-1)throw n;let c=1e3*2**o;g.warn(`Retry attempt ${o+1}/${t} after ${c}ms`,{error:s}),await new Promise(v=>setTimeout(v,c))}throw i||new Error("Unknown error during retry")}};import{promises as Re}from"node:fs";import{createInterface as V}from"node:readline";var k=l(),pe=/^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$/,he=[/^(?:https?:\/\/)?(?:www\.)?github\.com[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:\/)?$/i,/^git@github\.com:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/i,/^([\w.-]+)\/([\w.-]+)$/];function re(r){let e=r.trim();if(!e)throw new u(d.INVALID_URL,"URL cannot be empty");for(let t of he){let i=e.match(t);if(i){let o=i[1],n=i[2];if(!(o&&n))throw new u(d.INVALID_URL,`Invalid GitHub URL format: ${r}`);if(!ee(o))throw new u(d.INVALID_URL,`Invalid owner name: ${o}. Owner names must contain only alphanumeric characters, hyphens, or periods.`);if(!ee(n))throw new u(d.INVALID_URL,`Invalid repository name: ${n}. Repository names must contain only alphanumeric characters, hyphens, underscores, or periods.`);let s=`https://github.com/${o}/${n}`;return k.debug("Parsed repository URL",{original:e,owner:o,repo:n,normalized:s}),{owner:o,repo:n,url:s}}}throw new u(d.INVALID_URL,`Invalid GitHub URL: ${r}`)}function ee(r){return pe.test(r)}function ve(r){let e=[],t=[];for(let[i,o]of r.entries()){let n=i+1,s=o.trim();if(!(!s||s.startsWith("#")))try{let a=re(s);e.push(a),k.debug(`Line ${n}: Valid repository`,{owner:a.owner,repo:a.repo})}catch(a){let c=a instanceof Error?a.message:String(a);t.push({url:s,error:c,line:n}),k.warn(`Line ${n}: Invalid repository`,{url:s,error:c})}}return k.info("Batch parsing complete",{total:r.length,valid:e.length,invalid:t.length,skipped:r.length-e.length-t.length}),{valid:e,invalid:t}}var N={parseRepositoryUrl:re,parseRepositoriesBatch:ve};var p=l(),F=class{getRepositoriesFromInteractive(){return p.info("Starting interactive CLI input for repositories"),new Promise(e=>{let t=V({input:process.stdin,output:process.stdout});console.log(""),console.log("\u{1F4DD} Enter repository URLs one at a time:"),console.log(" (Press CTRL+D to finish, or leave empty and press Enter to skip)"),console.log("");let i=[],o=1,n=!1,s=()=>{t.question(`[${o}] > `,c=>{let v=c.trim();if(v===""){o++,s();return}i.push(v),o++,s()})},a=()=>{if(n)return;n=!0,t.close();let c=N.parseRepositoriesBatch(i);c.invalid.length>0&&p.warn(`Found ${c.invalid.length} invalid repositories`,{errors:c.invalid}),p.info(`Parsed ${c.valid.length} valid repositories`),e({repos:c.valid,errors:c.invalid})};t.on("close",()=>{a()}),s()})}async getRepositoriesFromFile(e){p.info(`Reading repositories from file: ${e}`);try{let i=(await Re.readFile(e,"utf-8")).split(`
2
+ `),o=N.parseRepositoriesBatch(i);return o.invalid.length>0&&p.warn(`Found ${o.invalid.length} invalid entries in file`,{errors:o.invalid}),p.info(`Parsed ${o.valid.length} valid repositories from file`),{repos:o.valid,errors:o.invalid}}catch(t){let i=t instanceof Error?t.message:String(t);throw p.error(`Failed to read file ${e}: ${i}`),new Error(`Failed to read file: ${i}`)}}getRepositoriesFromStdin(){return p.info("Reading repositories from stdin"),new Promise(e=>{let t=[],i=V({input:process.stdin,output:process.stdout});i.on("line",o=>{t.push(o)}),i.on("close",()=>{let o=N.parseRepositoriesBatch(t);o.invalid.length>0&&p.warn(`Found ${o.invalid.length} invalid entries from stdin`,{errors:o.invalid}),p.info(`Parsed ${o.valid.length} valid repositories from stdin`),e({repos:o.valid,errors:o.invalid})})})}promptForConfirmation(e){return new Promise(t=>{let i=V({input:process.stdin,output:process.stdout});i.question(`${e} [y/N]: `,o=>{i.close();let n=o.toLowerCase()==="y";p.info(`User confirmation: ${n}`),t(n)})})}};function ye(r){if(r<1e3)return`${r}ms`;if(r<6e4)return`${(r/1e3).toFixed(1)}s`;let e=Math.floor(r/6e4),t=(r%6e4/1e3).toFixed(0);return`${e}m ${t}s`}function Ee(r){if(r===0)return"0 B";let e=1024,t=["B","KB","MB","GB"],i=Math.floor(Math.log(r)/Math.log(e));return`${(r/e**i).toFixed(2)} ${t[i]}`}function we(r,e){return e===0?"0%":`${(r/e*100).toFixed(1)}%`}function Ae(r,e,t=30){if(e===0)return"[ ]";let i=Math.round(r/e*t),o=t-i,n="=".repeat(i),s=" ".repeat(o),a=(r/e*100).toFixed(0);return`[${n}${s}] ${a}%`}function Ie(r,e){let t=Math.max(0,(e-r.length)/2);return" ".repeat(Math.floor(t))+r}function be(r,e){return r.length<=e?r:`${r.substring(0,e-3)}...`}var $={formatDuration:ye,formatBytes:Ee,formatPercent:we,createProgressBar:Ae,centerText:Ie,truncate:be};var x=class{constructor(){this.lastUpdate=0;this.updateInterval=500;this.startTime=Date.now()}shouldUpdate(){let e=Date.now();return e-this.lastUpdate>=this.updateInterval?(this.lastUpdate=e,!0):!1}getProgressBar(e){let{completed:t,total:i,current:o}=e,n=i>0?t/i*100:0,a=`${$.createProgressBar(t,i,25)} ${t}/${i} (${n.toFixed(0)}%)`;return o&&(a+=` - ${o.owner}/${o.repo}`),a}getSummaryBox(e){let{successful:t,failed:i,skipped:o,totalDuration:n}=e,s=t+i+o,a=$.formatDuration(n),c=(A,U,ne=0)=>{let B=String(U),se=31-(A.length+B.length)+ne;return`\u2551 ${A}${" ".repeat(Math.max(1,se))}${B} \u2551`};return["\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","\u2551 Archive Operation Summary \u2551","\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",c("\u2705 Successful:",String(t),2),c("\u26A0\uFE0F Skipped:",String(o),3),c("\u274C Failed:",String(i),2),"\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",c("Total:",String(s),3),c("Duration:",a,3),"\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"].join(`
3
+ `)}getElapsedTime(){let e=Date.now()-this.startTime;return $.formatDuration(e)}getEstimatedTimeRemaining(e,t){if(e===0)return null;let o=(Date.now()-this.startTime)/e,n=(t-e)*o;return n>0?$.formatDuration(n):null}};var y=l();function te(){return new Ce("archive").description("Archive GitHub repositories").option("--file <path>","Read repository URLs from file").option("--stdin","Read repository URLs from standard input").option("--dry-run","Validate without archiving",!1).option("--concurrency <n>","Number of parallel operations","3").option("--timeout <n>","API timeout in seconds","300").option("--verbose","Verbose logging",!1).option("--force","Skip confirmations",!1).action(async r=>{await $e(r)})}async function $e(r){try{y.info("Archive command started",{options:r});let{concurrency:e,timeout:t}=Se(r),i=new w(f.APP_DIR),o=new F,n=new x,s=await Oe(i),{repositories:a,parseErrors:c}=await Te(r,o);c.length>0&&De(c),a.length===0&&(console.error(`\u274C ${m.NO_REPOS_PROVIDED}`),process.exit(1)),Le(a,r.dryRun),await _e(r,o);let v={dryRun:r.dryRun,concurrency:e,timeout:t,force:r.force,verbose:r.verbose};console.log(""),console.log(`${m.ARCHIVING_START} (concurrency: ${e})`),console.log("");let A=new L(s,v),U=await Pe(A,a,v,n);ke(A,U,n)}catch(e){Ne(e)}}function Se(r){let e=Number.parseInt(r.concurrency,10),t=Number.parseInt(r.timeout,10);return(e<1||e>50)&&(console.error("\u274C Concurrency must be between 1 and 50"),process.exit(1)),(t<10||t>3600)&&(console.error("\u274C Timeout must be between 10 and 3600 seconds"),process.exit(1)),{concurrency:e,timeout:t}}async function Oe(r){console.log("\u{1F510} Checking authentication...");let e=await r.getToken();e||(console.error(`\u274C ${m.NO_TOKEN}`),console.error(" Run: github-archiver auth login"),process.exit(1));let t=new P(e);try{let i=await t.validateAuth();return console.log(`\u2705 Authenticated as: ${i}`),t}catch(i){console.error("\u274C Authentication failed"),y.error("Auth validation failed:",i),process.exit(1)}}async function Te(r,e){console.log(""),console.log("\u{1F4DD} Getting repositories...");let t=[],i=[];if(r.file){y.info(`Using file input: ${r.file}`);let o=await e.getRepositoriesFromFile(r.file);t=o.repos,i=o.errors}else if(r.stdin){y.info("Using stdin input"),console.log("Enter repository URLs (one per line, press Ctrl+D to finish):");let o=await e.getRepositoriesFromStdin();t=o.repos,i=o.errors}else{y.info("Using interactive CLI input");let o=await e.getRepositoriesFromInteractive();t=o.repos,i=o.errors}return{repositories:t,parseErrors:i}}function De(r){console.warn(`\u26A0\uFE0F Found ${r.length} invalid repositories:`);for(let e of r)console.warn(` Line ${e.line}: ${e.error}`);console.warn("")}function Le(r,e){console.log(`\u{1F4CB} Will ${e?"validate":"archive"} ${r.length} repositories:`);for(let t=0;t<Math.min(r.length,5);t++){let i=r[t];i&&console.log(` ${t+1}. ${i.owner}/${i.repo}`)}r.length>5&&console.log(` ... and ${r.length-5} more`)}async function _e(r,e){console.log(""),r.force||r.dryRun?r.dryRun&&console.log(`\u2139\uFE0F ${m.DRY_RUN_MODE}`):await e.promptForConfirmation("Are you sure you want to archive these repositories?")||(console.log("\u274C Cancelled"),process.exit(1))}async function Pe(r,e,t,i){let o=await r.archiveRepositories(e,t),n=r.getSummary(),s={completed:n.successful+n.failed+n.skipped,failed:n.failed,total:e.length};return i.shouldUpdate()&&console.log(`\r${i.getProgressBar(s)}`),o}function ke(r,e,t){console.log(""),console.log("");let i=r.getSummary();if(console.log(t.getSummaryBox(i)),i.failed>0){console.log(""),console.log("\u274C Failed repositories:");for(let o of e)o.success||console.log(` ${o.owner}/${o.repo}: ${o.message}`)}if(i.skipped>0){console.log(""),console.log("\u26A0\uFE0F Skipped repositories:");for(let o of e)o.success&&!o.archived&&console.log(` ${o.owner}/${o.repo}: ${o.message}`)}i.failed>0?(y.warn(`Archive command completed with ${i.failed} failures`),process.exit(1)):(console.log(""),console.log("\u2705 All repositories processed successfully!"),y.info("Archive command completed successfully"),process.exit(0))}function Ne(r){let e=r instanceof Error?r.message:String(r);console.error(`\u274C Error: ${e}`),y.error("Archive command failed:",r),Fe(e),process.exit(1)}function Fe(r){let e=r.toLowerCase();if(e.includes("token")||e.includes("authentication")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. Make sure you have a valid GitHub Personal Access Token"),console.log(' 2. The token needs "repo" scope to archive repositories'),console.log(" 3. Run: github-archiver auth login");return}if(e.includes("rate limit")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. GitHub API rate limit has been exceeded"),console.log(" 2. Wait a few minutes and try again"),console.log(" 3. Use lower concurrency: --concurrency 1");return}if(e.includes("permission")||e.includes("403")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. You must be the repository owner or have push access"),console.log(" 2. Check that your GitHub token has correct permissions"),console.log(" 3. Verify you have push access to the repositories");return}if(e.includes("not found")||e.includes("404")){console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. Make sure the repository URL is correct"),console.log(" 2. The repository may have been deleted"),console.log(" 3. Check your GitHub access to the repository");return}(e.includes("network")||e.includes("timeout"))&&(console.log(""),console.log("\u{1F4A1} Troubleshooting:"),console.log(" 1. Check your internet connection"),console.log(" 2. GitHub API may be temporarily unavailable"),console.log(" 3. Try again in a moment"),console.log(" 4. Increase timeout: --timeout 600"))}import{createInterface as oe}from"node:readline";import{Command as S}from"commander";var h=new w(f.APP_DIR);function xe(){return new S("login").description("Set up GitHub authentication with a Personal Access Token").action(async()=>{try{await h.ensureConfigDir();let r=await Ge();r||(console.error("No token provided. Aborting."),process.exit(1)),console.log("Validating token..."),await h.saveToken(r);let e=await h.getStoredCredentials();console.log(`\u2713 ${m.AUTH_SUCCESS}`),console.log(` User: ${e?.githubUser}`),l().info("Token saved successfully",{user:e?.githubUser})}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),l().error("Failed to save token",{error:e}),process.exit(1)}})}function Ue(){return new S("logout").description("Remove stored GitHub authentication token").action(async()=>{try{if(!await h.getStoredCredentials()){console.log("No stored credentials found.");return}if(!await Ve(m.CONFIRM_LOGOUT)){console.log("Cancelled.");return}await h.removeToken(),console.log(`\u2713 ${m.TOKEN_REMOVED}`),l().info("Token removed")}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),l().error("Failed to remove token",{error:e}),process.exit(1)}})}function He(){return new S("status").description("Check authentication status").action(async()=>{try{let r=await h.getToken();if(!r){console.log("\u2717 Not authenticated"),console.log(' Run "github-archiver auth login" to set up authentication');return}console.log("\u2713 Authenticated");let e=await h.validateToken(r);if(e.valid){console.log(` User: ${e.user}`);let t=await h.getStoredCredentials();t&&console.log(` Saved at: ${new Date(t.savedAt).toLocaleString()}`),l().info("Authentication status check passed")}else console.log("\u2717 Token is invalid or expired"),l().warn("Token validation failed")}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),l().error("Failed to check auth status",{error:e}),process.exit(1)}})}function Me(){return new S("validate").description("Validate stored authentication token").action(async()=>{try{let r=await h.getToken();r||(console.log("\u2717 No token found"),process.exit(1)),console.log("Validating token...");let e=await h.validateToken(r);e.valid?(console.log(`\u2713 Token is valid (user: ${e.user})`),l().info("Token validation successful",{user:e.user})):(console.log("\u2717 Token is invalid or expired"),l().warn("Token validation failed"),process.exit(1))}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Error: ${e}`),l().error("Token validation error",{error:e}),process.exit(1)}})}function Ge(){return new Promise(r=>{let e=oe({input:process.stdin,output:process.stdout});e.question(m.ENTER_TOKEN,t=>{e.close(),r(t.trim())})})}function Ve(r){return new Promise(e=>{let t=oe({input:process.stdin,output:process.stdout});t.question(`${r} [y/N]: `,i=>{t.close(),e(i.toLowerCase()==="y")})})}function ie(){return new S("auth").description("Manage GitHub authentication").addCommand(xe()).addCommand(Ue()).addCommand(He()).addCommand(Me())}var Ye="1.0.6",Ke="Archive GitHub repositories via CLI";async function We(){try{await J(f.LOG_DIR);let r=G();X(r);let e=new Be;e.name("github-archiver").description(Ke).version(Ye,"-v, --version","Display version"),e.addCommand(ie()),e.addCommand(te()),e.addHelpCommand(!0),await e.parseAsync(process.argv),process.argv.slice(2).length||e.outputHelp()}catch(r){let e=r instanceof Error?r.message:String(r);console.error(`\u2717 Fatal error: ${e}`),process.exit(1)}}We();
4
4
  //# sourceMappingURL=index.js.map
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\nasync 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\nfunction 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\nasync 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\nasync 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\nfunction 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\nfunction 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\nasync 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\nasync 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\nfunction 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\nfunction 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\nfunction 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 = 0;\n\n constructor(gitHubService: GitHubService, options: ArchiveOptions) {\n this.gitHubService = gitHubService;\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 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 ' (Type \"done\" when finished, or leave empty and press Enter to skip)'\n );\n console.log(\"\");\n\n const urls: string[] = [];\n let lineNumber = 1;\n\n const promptNext = () => {\n rl.question(`[${lineNumber}] > `, (input) => {\n const trimmedInput = input.trim();\n\n if (trimmedInput.toLowerCase() === \"done\") {\n finishInput();\n return;\n }\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 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\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 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 `\u2551 \u2705 Successful: ${String(successful).padEnd(20)} \u2551`,\n `\u2551 \u26A0\uFE0F Skipped: ${String(skipped).padEnd(20)} \u2551`,\n `\u2551 \u274C Failed: ${String(failed).padEnd(20)} \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 `\u2551 Total: ${String(total).padEnd(20)} \u2551`,\n `\u2551 Duration: ${String(duration).padEnd(20)} \u2551`,\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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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
- "mappings": "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",
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", "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", "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"]
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\nasync 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\nfunction 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\nasync 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\nasync 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\nfunction 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\nfunction 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\nasync 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\nasync 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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\nfunction 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
+ "mappings": "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",
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/docs/RELEASE.md CHANGED
@@ -1,123 +1,177 @@
1
1
  # Release Process
2
2
 
3
- This document details the automated release process for GitHub Archiver CLI using semantic-release.
3
+ This project uses **Changesets** for version management and publishing.
4
4
 
5
- ## How Releases Work
5
+ ## Overview
6
6
 
7
- ### 1. Commit Format
7
+ The release process is **two-step and automated**:
8
8
 
9
- Use [Conventional Commits](https://www.conventionalcommits.org/):
9
+ 1. **Developer creates changesets** - Explicit intent for what's being released
10
+ 2. **GitHub Actions automates the rest** - Creates version PR, then publishes
10
11
 
11
- - `feat:` - New features → **minor** version bump (1.0.0 → 1.1.0)
12
- - `fix:` - Bug fixes → **patch** version bump (1.0.0 → 1.0.1)
13
- - `BREAKING CHANGE:` - Breaking changes → **major** version bump (1.0.0 → 2.0.0)
14
- - `chore:`, `docs:`, `test:` - No version bump
12
+ ## For Contributors
15
13
 
16
- ### 2. Automatic Trigger
14
+ ### Creating a Changeset
17
15
 
18
- Push to `main` branch triggers the release workflow:
16
+ When you've made changes that should be included in a release:
19
17
 
20
- 1. Semantic release analyzes commits and determines version bump
21
- 2. Package version is updated, published to npm
22
- 3. GitHub release is created with changelog
23
- 4. Git tag is created automatically
18
+ 1. **Create a changeset file:**
19
+ ```bash
20
+ bun run changeset:add
21
+ ```
24
22
 
25
- ### 3. Trusted Publishing
23
+ 2. **Answer the prompts:**
24
+ - Which packages are affected? → Select `github-archiver`
25
+ - What's the type of change? → Choose:
26
+ - `patch` - bug fixes (1.0.0 → 1.0.1)
27
+ - `minor` - new features (1.0.0 → 1.1.0)
28
+ - `major` - breaking changes (1.0.0 → 2.0.0)
29
+ - Write a description of your change
26
30
 
27
- Uses OpenID Connect (OIDC) for secure, tokenless authentication:
31
+ 3. **Commit the generated changeset file:**
32
+ ```bash
33
+ git add .changeset/*.md
34
+ git commit -m "docs: add changeset for feature X"
35
+ ```
28
36
 
29
- - No npm tokens required - eliminates security risks
30
- - Short-lived, cryptographically-signed tokens for each publish
31
- - Works with personal GitHub accounts
37
+ 4. **Push your branch and open a PR as usual**
32
38
 
33
- ### 4. Node Version Requirements
39
+ ### Automated Release
34
40
 
35
- - Package runs on Node 18+ (see `package.json` engines)
36
- - Release workflow uses Node 22+ (semantic-release requirement)
37
- - CI tests on Node 18 and 22 for maximum compatibility
41
+ Once your PR is merged to `main`:
38
42
 
39
- ### 5. Example Workflow
43
+ 1. GitHub Actions detects the changeset files
44
+ 2. Creates a "Version Packages" PR with:
45
+ - Updated `package.json` version
46
+ - Updated `CHANGELOG.md` with your descriptions
47
+ - GitHub links to commits and PRs
48
+ 3. Merge the "Version Packages" PR
49
+ 4. GitHub Actions automatically:
50
+ - Publishes to npm
51
+ - Creates a GitHub release
52
+ - Tags the commit with the version
40
53
 
41
- ```bash
42
- git checkout main
43
- git pull
44
- # Make your commits with conventional format
45
- git commit -m "feat: add support for custom config file"
46
- git push
47
- # Release happens automatically!
54
+ ### Changeset File Format
55
+
56
+ Example: `.changeset/excited-newts-talk.md`
57
+
58
+ ```markdown
59
+ ---
60
+ "github-archiver": minor
61
+ ---
62
+
63
+ Add support for custom GitHub token configuration in config file
48
64
  ```
49
65
 
50
- ## Trusted Publishing Setup
66
+ **Note:** Changeset filenames are auto-generated with whimsical names (e.g., `silly-cats-dance.md`). Edit the description after generation if needed.
51
67
 
52
- This project uses npm's **Trusted Publishing** feature for secure, tokenless package publishing.
68
+ ## For Maintainers
53
69
 
54
- ### Setup (one-time)
70
+ ### Understanding Releases
55
71
 
56
- 1. Go to https://www.npmjs.com/package/github-archiver/settings
57
- 2. Under **Trusted Publisher**, click **GitHub Actions**
58
- 3. Fill in:
59
- - **Organization or user**: `mynameistito`
60
- - **Repository**: `github-archiver`
61
- - **Workflow filename**: `release.yml`
62
- 4. Click **Set up connection**
63
- 5. (Recommended) Enable **"Require two-factor authentication and disallow tokens"**
72
+ **What triggers a release?**
73
+ - Any changeset file committed to main
74
+ - Automatic via GitHub Actions (no manual intervention needed)
64
75
 
65
- That's it! No tokens to manage, rotate, or worry about.
76
+ **What happens?**
77
+ 1. Action detects changeset files
78
+ 2. Runs validation (tests, lint, typecheck, build)
79
+ 3. Creates "Version Packages" PR with:
80
+ - Version bumps in `package.json`
81
+ - Updated `CHANGELOG.md` with GitHub links
82
+ - Changeset files consumed
83
+ 4. After PR merge: Automatically publishes to npm
66
84
 
67
- ## Commit Message Examples
85
+ ### Merging the "Version Packages" PR
68
86
 
69
- ```bash
70
- # Feature (minor version bump)
71
- git commit -m "feat: add support for custom config file"
87
+ When GitHub Actions creates a "Version Packages" PR:
88
+ - Review the version bump (patch/minor/major)
89
+ - Review the changelog entries with GitHub links
90
+ - Merge to trigger automatic publish
91
+
92
+ ### Release Changelog
72
93
 
73
- # Bug fix (patch version bump)
74
- git commit -m "fix: handle empty repository list gracefully"
94
+ The changelog is auto-generated with GitHub links:
75
95
 
76
- # Breaking change (major version bump)
77
- git commit -m "feat!: change CLI command structure
96
+ ```markdown
97
+ ## 1.1.0
78
98
 
79
- BREAKING CHANGE: The 'auth' subcommand is now required"
99
+ ### Minor Changes
80
100
 
81
- # No release
82
- git commit -m "chore: update dependencies"
83
- git commit -m "docs: clarify installation steps"
84
- git commit -m "test: add unit tests for parser"
101
+ - [abc123d](https://github.com/mynameistito/github-archiver/commit/abc123d) ([#42](https://github.com/mynameistito/github-archiver/pull/42)): Add custom token config support
85
102
  ```
86
103
 
87
- ## Manual Releases
104
+ ### Manual Publishing (if needed)
88
105
 
89
- If you need to publish without merging to main:
106
+ If automation fails, you can publish manually on the commit with updated versions:
90
107
 
91
108
  ```bash
92
- npm run release -- --no-ci
109
+ bun run changeset:publish
93
110
  ```
94
111
 
95
- ## Release Workflow Details
112
+ ### Troubleshooting
113
+
114
+ **Action not creating PR?**
115
+ - Check that changesets exist in `.changeset/` directory
116
+ - Verify `GITHUB_TOKEN` and `NPM_TOKEN` secrets are configured
117
+ - Review workflow logs in GitHub Actions
118
+
119
+ **Publish failed?**
120
+ - Verify `NPM_TOKEN` has publish permissions
121
+ - Check npm registry status
122
+ - Review the error in GitHub Actions logs
123
+
124
+ **Version bump incorrect?**
125
+ - Verify correct changeset type was selected (patch/minor/major)
126
+ - Check that description was provided
127
+ - Ensure changeset file syntax is valid (YAML front matter)
96
128
 
97
- The `.github/workflows/release.yml` file handles:
129
+ ## Versioning Strategy
98
130
 
99
- - Running tests on Node 18 and 22
100
- - Building the project
101
- - Publishing to npm using Trusted Publishing
102
- - Creating GitHub releases with changelog
103
- - Managing version tags automatically
131
+ This project uses **Semantic Versioning**:
104
132
 
105
- ## Troubleshooting
133
+ - **patch**: Bug fixes, minor improvements (1.0.0 → 1.0.1)
134
+ - **minor**: New features, backward compatible (1.0.0 → 1.1.0)
135
+ - **major**: Breaking changes, incompatible updates (1.0.0 → 2.0.0)
106
136
 
107
- ### Release Not Triggered
137
+ Choose the appropriate type when creating your changeset.
108
138
 
109
- - Ensure commits follow conventional commit format
110
- - Check that workflow file is named `release.yml`
111
- - Verify GitHub Actions permissions are enabled
139
+ ## GitHub Releases
112
140
 
113
- ### Publish Failed
141
+ After publishing, a GitHub Release is automatically created with:
142
+ - Release notes generated from CHANGELOG.md
143
+ - Direct link to the npm package
144
+ - Git tag for the release (v1.1.0, etc.)
114
145
 
115
- - Check npm Trusted Publisher configuration
116
- - Ensure package name in package.json matches npm registry
117
- - Verify semantic-release version range is valid
146
+ ## Environment & CI/CD
147
+
148
+ **Required Secrets:**
149
+ - `GITHUB_TOKEN` - Auto-provided by GitHub Actions
150
+ - `NPM_TOKEN` - Set in repository settings with publish permissions
151
+
152
+ **Node Version:** 22.x (tested on 24.x and 25.x in CI)
153
+ **Package Manager:** Bun
154
+
155
+ **Publishing Method:** OIDC trusted publishing with npm
156
+
157
+ ## Trusted Publishing Setup
158
+
159
+ This project uses npm's **Trusted Publishing** feature for secure, tokenless package publishing.
160
+
161
+ ### Setup (one-time)
162
+
163
+ 1. Go to https://www.npmjs.com/package/github-archiver/settings
164
+ 2. Under **Trusted Publisher**, click **GitHub Actions**
165
+ 3. Fill in:
166
+ - **Organization or user**: `mynameistito`
167
+ - **Repository**: `github-archiver`
168
+ - **Workflow filename**: `release.yml`
169
+ 4. Click **Set up connection**
170
+ 5. (Recommended) Enable **"Require two-factor authentication and disallow tokens"**
171
+
172
+ That's it! No tokens to manage, rotate, or worry about.
118
173
 
119
- ### Version Bump Incorrect
174
+ ## See Also
120
175
 
121
- - Review commit history for proper prefixes
122
- - Check that `BREAKING CHANGE:` is in commit body (footer)
123
- - Ensure no duplicate release tags exist
176
+ - [Contributing Guidelines](../CONTRIBUTING.md#release-process)
177
+ - [Changesets Documentation](https://github.com/changesets/changesets)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-archiver",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Archive GitHub repositories via CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,8 @@
8
8
  "github-archiver": "bin/github-archiver.js"
9
9
  },
10
10
  "engines": {
11
- "node": ">=18.0.0"
11
+ "node": ">=22.0.0",
12
+ "bun": ">=1.0.0"
12
13
  },
13
14
  "scripts": {
14
15
  "build": "tsx scripts/build.ts",
@@ -20,7 +21,11 @@
20
21
  "tsc": "tsc --noEmit",
21
22
  "test": "vitest",
22
23
  "test:coverage": "vitest --coverage",
23
- "release": "semantic-release",
24
+ "changeset": "changeset",
25
+ "changeset:add": "changeset add",
26
+ "changeset:version": "changeset version",
27
+ "changeset:publish": "changeset publish",
28
+ "release": "changeset publish",
24
29
  "prepublishOnly": "npm run build && npm run typecheck && npm run lint"
25
30
  },
26
31
  "dependencies": {
@@ -31,11 +36,11 @@
31
36
  },
32
37
  "devDependencies": {
33
38
  "@biomejs/biome": "^2.3.11",
34
- "@semantic-release/changelog": "^6.0.3",
35
- "@semantic-release/git": "^10.0.1",
39
+ "@changesets/changelog-github": "^0.5.2",
40
+ "@changesets/cli": "^2.29.8",
41
+ "@changesets/get-github-info": "^0.7.0",
36
42
  "@types/node": "^25.0.6",
37
43
  "esbuild": "^0.27.2",
38
- "semantic-release": "^25.0.2",
39
44
  "tsx": "^4.21.0",
40
45
  "typescript": "^5.9.3",
41
46
  "ultracite": "^7.0.11",
@@ -52,6 +57,10 @@
52
57
  "license": "MIT",
53
58
  "repository": {
54
59
  "type": "git",
55
- "url": "https://github.com/mynameistito/github-archiver"
60
+ "url": "git+https://github.com/mynameistito/github-archiver.git"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public",
64
+ "registry": "https://registry.npmjs.org/"
56
65
  }
57
66
  }
package/.releaserc.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "branches": ["main"],
3
- "plugins": [
4
- [
5
- "@semantic-release/commit-analyzer",
6
- {
7
- "releaseRules": [{ "type": "*", "release": "patch" }],
8
- "parserOpts": {
9
- "conventionalCommits": false
10
- }
11
- }
12
- ],
13
- "@semantic-release/release-notes-generator",
14
- [
15
- "@semantic-release/changelog",
16
- {
17
- "changelogFile": "CHANGELOG.md"
18
- }
19
- ],
20
- "@semantic-release/npm",
21
- [
22
- "@semantic-release/git",
23
- {
24
- "assets": ["package.json", "CHANGELOG.md", "README.md"],
25
- "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
26
- }
27
- ],
28
- "@semantic-release/github"
29
- ]
30
- }