@braingrid/cli 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -15
- package/dist/cli.js +1 -2
- package/dist/cli.js.map +1 -1
- package/dist/handlers/init.handlers.d.ts +0 -1
- package/dist/handlers/init.handlers.d.ts.map +1 -1
- package/dist/handlers/init.handlers.js +477 -39
- package/dist/handlers/init.handlers.js.map +1 -1
- package/dist/handlers/status.handlers.js +6 -6
- package/dist/handlers/status.handlers.js.map +1 -1
- package/dist/types/project.d.ts +1 -0
- package/dist/types/project.d.ts.map +1 -1
- package/dist/utils/cli-tools.d.ts.map +1 -1
- package/dist/utils/cli-tools.js +9 -0
- package/dist/utils/cli-tools.js.map +1 -1
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +18 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/gh-installer.d.ts +31 -0
- package/dist/utils/gh-installer.d.ts.map +1 -0
- package/dist/utils/gh-installer.js +296 -0
- package/dist/utils/gh-installer.js.map +1 -0
- package/dist/utils/git-installer.d.ts +31 -0
- package/dist/utils/git-installer.d.ts.map +1 -0
- package/dist/utils/git-installer.js +290 -0
- package/dist/utils/git-installer.js.map +1 -0
- package/dist/utils/github-repo.d.ts +43 -0
- package/dist/utils/github-repo.d.ts.map +1 -0
- package/dist/utils/github-repo.js +113 -0
- package/dist/utils/github-repo.js.map +1 -0
- package/dist/utils/package-manager-installer.d.ts +36 -0
- package/dist/utils/package-manager-installer.d.ts.map +1 -0
- package/dist/utils/package-manager-installer.js +106 -0
- package/dist/utils/package-manager-installer.js.map +1 -0
- package/dist/utils/repository-access.d.ts +89 -0
- package/dist/utils/repository-access.d.ts.map +1 -0
- package/dist/utils/repository-access.js +132 -0
- package/dist/utils/repository-access.js.map +1 -0
- package/dist/utils/spinner.d.ts +47 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +101 -0
- package/dist/utils/spinner.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Access Utility
|
|
3
|
+
*
|
|
4
|
+
* Helpers for checking GitHub installation and repository access during project initialization.
|
|
5
|
+
* Handles detection of owner-specific installations and repository visibility in BrainGrid.
|
|
6
|
+
*/
|
|
7
|
+
import { GitHubInstallation } from '../types/github.js';
|
|
8
|
+
import { RepositoryService } from '../services/internal/repository-service.js';
|
|
9
|
+
/**
|
|
10
|
+
* Find a GitHub installation for a specific repository owner
|
|
11
|
+
*
|
|
12
|
+
* @param owner - The GitHub repository owner (org or user)
|
|
13
|
+
* @param installations - List of GitHub installations to search
|
|
14
|
+
* @returns The matching installation or null if not found
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const installation = findInstallationForOwner('myorg', installations);
|
|
19
|
+
* if (installation) {
|
|
20
|
+
* console.log(`Found installation for ${installation.account_name}`);
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function findInstallationForOwner(owner: string, installations: GitHubInstallation[]): GitHubInstallation | null;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a repository is accessible in BrainGrid
|
|
27
|
+
*
|
|
28
|
+
* Queries the repository API to see if the specific owner/name combination
|
|
29
|
+
* is visible to the authenticated user's organization.
|
|
30
|
+
*
|
|
31
|
+
* @param repositoryService - Repository service instance
|
|
32
|
+
* @param owner - GitHub repository owner
|
|
33
|
+
* @param name - GitHub repository name
|
|
34
|
+
* @returns True if repository is accessible, false otherwise
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const hasAccess = await checkRepositoryAccess(repoService, 'myorg', 'myrepo');
|
|
39
|
+
* if (hasAccess) {
|
|
40
|
+
* console.log('Repository is accessible in BrainGrid');
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkRepositoryAccess(repositoryService: RepositoryService, owner: string, name: string): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Poll for repository access with timeout
|
|
47
|
+
*
|
|
48
|
+
* Continuously checks if a repository becomes accessible in BrainGrid.
|
|
49
|
+
* Useful when waiting for a user to grant access through the web UI.
|
|
50
|
+
*
|
|
51
|
+
* @param repositoryService - Repository service instance
|
|
52
|
+
* @param owner - GitHub repository owner
|
|
53
|
+
* @param name - GitHub repository name
|
|
54
|
+
* @param intervalMs - Polling interval in milliseconds (default: 3000)
|
|
55
|
+
* @param maxAttempts - Maximum number of polling attempts (default: 60 = 3 minutes at 3s interval)
|
|
56
|
+
* @returns Promise that resolves to true if access is granted, false if timeout
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const granted = await pollForRepositoryAccess(repoService, 'myorg', 'myrepo');
|
|
61
|
+
* if (granted) {
|
|
62
|
+
* console.log('Access granted!');
|
|
63
|
+
* } else {
|
|
64
|
+
* console.log('Timeout - access not granted within 3 minutes');
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function pollForRepositoryAccess(repositoryService: RepositoryService, owner: string, name: string, intervalMs?: number, maxAttempts?: number): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Get repository ID from BrainGrid API
|
|
71
|
+
*
|
|
72
|
+
* Fetches the repository details to get its BrainGrid UUID, which is needed
|
|
73
|
+
* for linking repositories to projects.
|
|
74
|
+
*
|
|
75
|
+
* @param repositoryService - Repository service instance
|
|
76
|
+
* @param owner - GitHub repository owner
|
|
77
|
+
* @param name - GitHub repository name
|
|
78
|
+
* @returns Repository UUID or null if not found
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const repoId = await getRepositoryId(repoService, 'myorg', 'myrepo');
|
|
83
|
+
* if (repoId) {
|
|
84
|
+
* await projectService.createProject({ name: 'My Project', repository_ids: [repoId] });
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function getRepositoryId(repositoryService: RepositoryService, owner: string, name: string): Promise<string | null>;
|
|
89
|
+
//# sourceMappingURL=repository-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository-access.d.ts","sourceRoot":"","sources":["../../src/utils/repository-access.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAE/E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,kBAAkB,EAAE,GACjC,kBAAkB,GAAG,IAAI,CAK3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,qBAAqB,CAC1C,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,uBAAuB,CAC5C,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAa,EACzB,WAAW,GAAE,MAAW,GACtB,OAAO,CAAC,OAAO,CAAC,CAelB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACpC,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBxB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Access Utility
|
|
3
|
+
*
|
|
4
|
+
* Helpers for checking GitHub installation and repository access during project initialization.
|
|
5
|
+
* Handles detection of owner-specific installations and repository visibility in BrainGrid.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Find a GitHub installation for a specific repository owner
|
|
9
|
+
*
|
|
10
|
+
* @param owner - The GitHub repository owner (org or user)
|
|
11
|
+
* @param installations - List of GitHub installations to search
|
|
12
|
+
* @returns The matching installation or null if not found
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const installation = findInstallationForOwner('myorg', installations);
|
|
17
|
+
* if (installation) {
|
|
18
|
+
* console.log(`Found installation for ${installation.account_name}`);
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function findInstallationForOwner(owner, installations) {
|
|
23
|
+
// Case-insensitive match on account_name
|
|
24
|
+
const normalizedOwner = owner.toLowerCase();
|
|
25
|
+
return installations.find(inst => inst.account_name.toLowerCase() === normalizedOwner) || null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if a repository is accessible in BrainGrid
|
|
29
|
+
*
|
|
30
|
+
* Queries the repository API to see if the specific owner/name combination
|
|
31
|
+
* is visible to the authenticated user's organization.
|
|
32
|
+
*
|
|
33
|
+
* @param repositoryService - Repository service instance
|
|
34
|
+
* @param owner - GitHub repository owner
|
|
35
|
+
* @param name - GitHub repository name
|
|
36
|
+
* @returns True if repository is accessible, false otherwise
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const hasAccess = await checkRepositoryAccess(repoService, 'myorg', 'myrepo');
|
|
41
|
+
* if (hasAccess) {
|
|
42
|
+
* console.log('Repository is accessible in BrainGrid');
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function checkRepositoryAccess(repositoryService, owner, name) {
|
|
47
|
+
try {
|
|
48
|
+
const response = await repositoryService.listRepositories({
|
|
49
|
+
owner,
|
|
50
|
+
name,
|
|
51
|
+
limit: 1,
|
|
52
|
+
});
|
|
53
|
+
// If we get results, the repository is accessible
|
|
54
|
+
return response.repositories.length > 0;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// If the API call fails, assume no access
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Poll for repository access with timeout
|
|
63
|
+
*
|
|
64
|
+
* Continuously checks if a repository becomes accessible in BrainGrid.
|
|
65
|
+
* Useful when waiting for a user to grant access through the web UI.
|
|
66
|
+
*
|
|
67
|
+
* @param repositoryService - Repository service instance
|
|
68
|
+
* @param owner - GitHub repository owner
|
|
69
|
+
* @param name - GitHub repository name
|
|
70
|
+
* @param intervalMs - Polling interval in milliseconds (default: 3000)
|
|
71
|
+
* @param maxAttempts - Maximum number of polling attempts (default: 60 = 3 minutes at 3s interval)
|
|
72
|
+
* @returns Promise that resolves to true if access is granted, false if timeout
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const granted = await pollForRepositoryAccess(repoService, 'myorg', 'myrepo');
|
|
77
|
+
* if (granted) {
|
|
78
|
+
* console.log('Access granted!');
|
|
79
|
+
* } else {
|
|
80
|
+
* console.log('Timeout - access not granted within 3 minutes');
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export async function pollForRepositoryAccess(repositoryService, owner, name, intervalMs = 3000, maxAttempts = 60) {
|
|
85
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
86
|
+
const hasAccess = await checkRepositoryAccess(repositoryService, owner, name);
|
|
87
|
+
if (hasAccess) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
// Wait before next attempt (unless this was the last attempt)
|
|
91
|
+
if (attempt < maxAttempts - 1) {
|
|
92
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get repository ID from BrainGrid API
|
|
99
|
+
*
|
|
100
|
+
* Fetches the repository details to get its BrainGrid UUID, which is needed
|
|
101
|
+
* for linking repositories to projects.
|
|
102
|
+
*
|
|
103
|
+
* @param repositoryService - Repository service instance
|
|
104
|
+
* @param owner - GitHub repository owner
|
|
105
|
+
* @param name - GitHub repository name
|
|
106
|
+
* @returns Repository UUID or null if not found
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const repoId = await getRepositoryId(repoService, 'myorg', 'myrepo');
|
|
111
|
+
* if (repoId) {
|
|
112
|
+
* await projectService.createProject({ name: 'My Project', repository_ids: [repoId] });
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export async function getRepositoryId(repositoryService, owner, name) {
|
|
117
|
+
try {
|
|
118
|
+
const response = await repositoryService.listRepositories({
|
|
119
|
+
owner,
|
|
120
|
+
name,
|
|
121
|
+
limit: 1,
|
|
122
|
+
});
|
|
123
|
+
if (response.repositories.length === 0) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return response.repositories[0].id;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=repository-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository-access.js","sourceRoot":"","sources":["../../src/utils/repository-access.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,wBAAwB,CACvC,KAAa,EACb,aAAmC;IAEnC,yCAAyC;IACzC,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAE5C,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,IAAI,IAAI,CAAC;AAChG,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,iBAAoC,EACpC,KAAa,EACb,IAAY;IAEZ,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,gBAAgB,CAAC;YACzD,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;QAEH,kDAAkD;QAClD,OAAO,QAAQ,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,0CAA0C;QAC1C,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,iBAAoC,EACpC,KAAa,EACb,IAAY,EACZ,aAAqB,IAAI,EACzB,cAAsB,EAAE;IAExB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,iBAAiB,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAE9E,IAAI,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACb,CAAC;QAED,8DAA8D;QAC9D,IAAI,OAAO,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;QAC/D,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,iBAAoC,EACpC,KAAa,EACb,IAAY;IAEZ,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,gBAAgB,CAAC;YACzD,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides a simple polling spinner for async operations with user feedback.
|
|
5
|
+
* Shows an animated spinner while waiting for a condition to become true.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Wait for a condition with animated spinner
|
|
9
|
+
*
|
|
10
|
+
* Polls an async check function and displays a spinner while waiting.
|
|
11
|
+
* Clears the spinner on completion or timeout.
|
|
12
|
+
*
|
|
13
|
+
* @param message - Message to display next to spinner
|
|
14
|
+
* @param checkFn - Async function that returns true when condition is met
|
|
15
|
+
* @param intervalMs - Polling interval in milliseconds (default: 3000)
|
|
16
|
+
* @param maxAttempts - Maximum number of attempts (default: 60 = 3 minutes at 3s interval)
|
|
17
|
+
* @returns Promise that resolves to true if condition met, false if timeout
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const success = await waitWithSpinner(
|
|
22
|
+
* 'Waiting for repository access',
|
|
23
|
+
* async () => await checkAccess(),
|
|
24
|
+
* 3000,
|
|
25
|
+
* 60
|
|
26
|
+
* );
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function waitWithSpinner(message: string, checkFn: () => Promise<boolean>, intervalMs?: number, maxAttempts?: number): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Simple one-time spinner display
|
|
32
|
+
*
|
|
33
|
+
* Shows a spinner without polling. Useful for operations that manage their own timing.
|
|
34
|
+
* Returns a cleanup function to clear the spinner.
|
|
35
|
+
*
|
|
36
|
+
* @param message - Message to display
|
|
37
|
+
* @returns Cleanup function to stop and clear the spinner
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const stop = showSpinner('Loading');
|
|
42
|
+
* await doWork();
|
|
43
|
+
* stop();
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function showSpinner(message: string): () => void;
|
|
47
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAC/B,UAAU,GAAE,MAAa,EACzB,WAAW,GAAE,MAAW,GACtB,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,IAAI,CAYvD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides a simple polling spinner for async operations with user feedback.
|
|
5
|
+
* Shows an animated spinner while waiting for a condition to become true.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
9
|
+
/**
|
|
10
|
+
* Wait for a condition with animated spinner
|
|
11
|
+
*
|
|
12
|
+
* Polls an async check function and displays a spinner while waiting.
|
|
13
|
+
* Clears the spinner on completion or timeout.
|
|
14
|
+
*
|
|
15
|
+
* @param message - Message to display next to spinner
|
|
16
|
+
* @param checkFn - Async function that returns true when condition is met
|
|
17
|
+
* @param intervalMs - Polling interval in milliseconds (default: 3000)
|
|
18
|
+
* @param maxAttempts - Maximum number of attempts (default: 60 = 3 minutes at 3s interval)
|
|
19
|
+
* @returns Promise that resolves to true if condition met, false if timeout
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const success = await waitWithSpinner(
|
|
24
|
+
* 'Waiting for repository access',
|
|
25
|
+
* async () => await checkAccess(),
|
|
26
|
+
* 3000,
|
|
27
|
+
* 60
|
|
28
|
+
* );
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export async function waitWithSpinner(message, checkFn, intervalMs = 3000, maxAttempts = 60) {
|
|
32
|
+
let frameIndex = 0;
|
|
33
|
+
let spinnerInterval = null;
|
|
34
|
+
// Function to update spinner
|
|
35
|
+
const updateSpinner = () => {
|
|
36
|
+
const frame = SPINNER_FRAMES[frameIndex];
|
|
37
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
38
|
+
// Clear current line and write new spinner
|
|
39
|
+
process.stdout.write(`\r${chalk.cyan(frame)} ${message}...`);
|
|
40
|
+
};
|
|
41
|
+
// Start spinner animation (update every 100ms for smooth animation)
|
|
42
|
+
spinnerInterval = setInterval(updateSpinner, 100);
|
|
43
|
+
try {
|
|
44
|
+
// Poll with the specified interval
|
|
45
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
46
|
+
const result = await checkFn();
|
|
47
|
+
if (result) {
|
|
48
|
+
// Success! Clear spinner and return
|
|
49
|
+
if (spinnerInterval)
|
|
50
|
+
clearInterval(spinnerInterval);
|
|
51
|
+
process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r'); // Clear line
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// Wait before next attempt (unless this was the last attempt)
|
|
55
|
+
if (attempt < maxAttempts - 1) {
|
|
56
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Timeout - clear spinner and return false
|
|
60
|
+
if (spinnerInterval)
|
|
61
|
+
clearInterval(spinnerInterval);
|
|
62
|
+
process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r'); // Clear line
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// Error during polling - clean up and rethrow
|
|
67
|
+
if (spinnerInterval)
|
|
68
|
+
clearInterval(spinnerInterval);
|
|
69
|
+
process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r'); // Clear line
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Simple one-time spinner display
|
|
75
|
+
*
|
|
76
|
+
* Shows a spinner without polling. Useful for operations that manage their own timing.
|
|
77
|
+
* Returns a cleanup function to clear the spinner.
|
|
78
|
+
*
|
|
79
|
+
* @param message - Message to display
|
|
80
|
+
* @returns Cleanup function to stop and clear the spinner
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* const stop = showSpinner('Loading');
|
|
85
|
+
* await doWork();
|
|
86
|
+
* stop();
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export function showSpinner(message) {
|
|
90
|
+
let frameIndex = 0;
|
|
91
|
+
const interval = setInterval(() => {
|
|
92
|
+
const frame = SPINNER_FRAMES[frameIndex];
|
|
93
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
94
|
+
process.stdout.write(`\r${chalk.cyan(frame)} ${message}...`);
|
|
95
|
+
}, 100);
|
|
96
|
+
return () => {
|
|
97
|
+
clearInterval(interval);
|
|
98
|
+
process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r');
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"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"}
|