@devtion/devcli 1.0.5-alpha.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.
Files changed (42) hide show
  1. package/README.md +118 -0
  2. package/dist/index.js +3206 -0
  3. package/dist/types/commands/auth.d.ts +25 -0
  4. package/dist/types/commands/clean.d.ts +6 -0
  5. package/dist/types/commands/contribute.d.ts +139 -0
  6. package/dist/types/commands/finalize.d.ts +51 -0
  7. package/dist/types/commands/index.d.ts +9 -0
  8. package/dist/types/commands/listCeremonies.d.ts +5 -0
  9. package/dist/types/commands/logout.d.ts +6 -0
  10. package/dist/types/commands/observe.d.ts +22 -0
  11. package/dist/types/commands/setup.d.ts +86 -0
  12. package/dist/types/commands/validate.d.ts +8 -0
  13. package/dist/types/index.d.ts +2 -0
  14. package/dist/types/lib/errors.d.ts +60 -0
  15. package/dist/types/lib/files.d.ts +64 -0
  16. package/dist/types/lib/localConfigs.d.ts +110 -0
  17. package/dist/types/lib/prompts.d.ts +104 -0
  18. package/dist/types/lib/services.d.ts +31 -0
  19. package/dist/types/lib/theme.d.ts +42 -0
  20. package/dist/types/lib/utils.d.ts +158 -0
  21. package/dist/types/types/index.d.ts +65 -0
  22. package/package.json +99 -0
  23. package/src/commands/auth.ts +194 -0
  24. package/src/commands/clean.ts +49 -0
  25. package/src/commands/contribute.ts +1090 -0
  26. package/src/commands/finalize.ts +382 -0
  27. package/src/commands/index.ts +9 -0
  28. package/src/commands/listCeremonies.ts +32 -0
  29. package/src/commands/logout.ts +67 -0
  30. package/src/commands/observe.ts +193 -0
  31. package/src/commands/setup.ts +901 -0
  32. package/src/commands/validate.ts +29 -0
  33. package/src/index.ts +66 -0
  34. package/src/lib/errors.ts +77 -0
  35. package/src/lib/files.ts +102 -0
  36. package/src/lib/localConfigs.ts +186 -0
  37. package/src/lib/prompts.ts +748 -0
  38. package/src/lib/services.ts +191 -0
  39. package/src/lib/theme.ts +45 -0
  40. package/src/lib/utils.ts +778 -0
  41. package/src/types/conf.d.ts +16 -0
  42. package/src/types/index.ts +70 -0
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device"
3
+ import { Verification } from "@octokit/auth-oauth-device/dist-types/types.js"
4
+ import clipboard from "clipboardy"
5
+ import dotenv from "dotenv"
6
+ import open from "open"
7
+ import { fileURLToPath } from "url"
8
+ import { dirname } from "path"
9
+ import { GENERIC_ERRORS, showError } from "../lib/errors.js"
10
+ import { checkLocalAccessToken, getLocalAccessToken, setLocalAccessToken } from "../lib/localConfigs.js"
11
+ import { bootstrapCommandExecutionAndServices, signInToFirebase } from "../lib/services.js"
12
+ import theme from "../lib/theme.js"
13
+ import {
14
+ customSpinner,
15
+ exchangeGithubTokenForCredentials,
16
+ getGithubProviderUserId,
17
+ getUserHandleFromProviderUserId,
18
+ sleep,
19
+ terminate
20
+ } from "../lib/utils.js"
21
+
22
+ const packagePath = `${dirname(fileURLToPath(import.meta.url))}`
23
+ dotenv.config({
24
+ path: packagePath.includes(`src/lib`)
25
+ ? `${dirname(fileURLToPath(import.meta.url))}/../../.env`
26
+ : `${dirname(fileURLToPath(import.meta.url))}/.env`
27
+ })
28
+
29
+ /**
30
+ * Custom countdown which throws an error when expires.
31
+ * @param expirationInSeconds <number> - the expiration time in seconds.
32
+ */
33
+ export const expirationCountdownForGithubOAuth = (expirationInSeconds: number) => {
34
+ // Prepare data.
35
+ let secondsCounter = expirationInSeconds <= 60 ? expirationInSeconds : 60
36
+ const interval = 1 // 1s.
37
+
38
+ setInterval(() => {
39
+ if (expirationInSeconds !== 0) {
40
+ // Update time and seconds counter.
41
+ expirationInSeconds -= interval
42
+ secondsCounter -= interval
43
+
44
+ if (secondsCounter % 60 === 0) secondsCounter = 0
45
+
46
+ // Notify user.
47
+ process.stdout.write(
48
+ `${theme.symbols.warning} Expires in ${theme.text.bold(
49
+ theme.colors.magenta(`00:${Math.floor(expirationInSeconds / 60)}:${secondsCounter}`)
50
+ )}\r`
51
+ )
52
+ } else {
53
+ process.stdout.write(`\n\n`) // workaround to \r.
54
+ showError(GENERIC_ERRORS.GENERIC_COUNTDOWN_EXPIRATION, true)
55
+ }
56
+ }, interval * 1000) // ms.
57
+ }
58
+
59
+ /**
60
+ * Callback to manage the data requested for Github OAuth2.0 device flow.
61
+ * @param verification <Verification> - the data from Github OAuth2.0 device flow.
62
+ */
63
+ export const onVerification = async (verification: Verification): Promise<void> => {
64
+ // Copy code to clipboard.
65
+ clipboard.writeSync(verification.user_code)
66
+ clipboard.readSync()
67
+
68
+ // Display data.
69
+ console.log(
70
+ `${theme.symbols.warning} Visit ${theme.text.bold(
71
+ theme.text.underlined(verification.verification_uri)
72
+ )} on this device to generate a new token and authenticate`
73
+ )
74
+ console.log(
75
+ `${theme.symbols.info} Your auth code: ${theme.text.bold(verification.user_code)} (${theme.emojis.clipboard} ${
76
+ theme.symbols.success
77
+ })\n`
78
+ )
79
+
80
+ const spinner = customSpinner(`Redirecting to Github...`, `clock`)
81
+ spinner.start()
82
+
83
+ await sleep(10000) // ~10s to make users able to read the CLI.
84
+
85
+ // Automatically open the page (# Step 2).
86
+ await open(verification.verification_uri)
87
+
88
+ spinner.stop()
89
+
90
+ // Countdown for time expiration.
91
+ expirationCountdownForGithubOAuth(verification.expires_in)
92
+ }
93
+
94
+ /**
95
+ * Return the Github OAuth 2.0 token using manual Device Flow authentication process.
96
+ * @param clientId <string> - the client id for the CLI OAuth app.
97
+ * @returns <string> the Github OAuth 2.0 token.
98
+ */
99
+ export const executeGithubDeviceFlow = async (clientId: string): Promise<string> => {
100
+ /**
101
+ * Github OAuth 2.0 Device Flow.
102
+ * # Step 1: Request device and user verification codes and gets auth verification uri.
103
+ * # Step 2: The app prompts the user to enter a user verification code at https://github.com/login/device.
104
+ * # Step 3: The app polls/asks for the user authentication status.
105
+ */
106
+
107
+ const clientType = "oauth-app"
108
+ const tokenType = "oauth"
109
+
110
+ // # Step 1.
111
+ const auth = createOAuthDeviceAuth({
112
+ clientType,
113
+ clientId,
114
+ scopes: ["gist"],
115
+ onVerification
116
+ })
117
+
118
+ // # Step 3.
119
+ const { token } = await auth({
120
+ type: tokenType
121
+ })
122
+
123
+ return token
124
+ }
125
+
126
+ /**
127
+ * Auth command.
128
+ * @notice The auth command allows a user to make the association of their Github account with the CLI by leveraging OAuth 2.0 as an authentication mechanism.
129
+ * @dev Under the hood, the command handles a manual Device Flow following the guidelines in the Github documentation.
130
+ */
131
+ const auth = async () => {
132
+ const { firebaseApp } = await bootstrapCommandExecutionAndServices()
133
+
134
+ // Console more context for the user.
135
+ console.log(
136
+ `${theme.symbols.info} ${theme.text.bold(
137
+ `You are about to authenticate on this CLI using your Github account (device flow - OAuth 2.0 mechanism).\n${
138
+ theme.symbols.warning
139
+ } Please, note that only read and write permission for ${theme.text.italic(
140
+ `gists`
141
+ )} will be required in order to publish your contribution transcript!`
142
+ )}\n`
143
+ )
144
+
145
+ const spinner = customSpinner(`Checking authentication token...`, `clock`)
146
+ spinner.start()
147
+
148
+ await sleep(5000)
149
+
150
+ // Manage OAuth Github token.
151
+ const isLocalTokenStored = checkLocalAccessToken()
152
+
153
+ if (!isLocalTokenStored) {
154
+ spinner.fail(`No local authentication token found\n`)
155
+
156
+ // Generate a new access token using Github Device Flow (OAuth 2.0).
157
+ const newToken = await executeGithubDeviceFlow(String(process.env.AUTH_GITHUB_CLIENT_ID))
158
+
159
+ // Store the new access token.
160
+ setLocalAccessToken(newToken)
161
+ } else spinner.succeed(`Local authentication token found\n`)
162
+
163
+ // Get access token from local store.
164
+ const token = getLocalAccessToken()
165
+
166
+ // Exchange token for credential.
167
+ const credentials = exchangeGithubTokenForCredentials(String(token))
168
+
169
+ spinner.text = `Authenticating...`
170
+ spinner.start()
171
+
172
+ // Sign-in to Firebase using credentials.
173
+ await signInToFirebase(firebaseApp, credentials)
174
+
175
+ // Get Github handle.
176
+ const providerUserId = await getGithubProviderUserId(String(token))
177
+
178
+ spinner.succeed(
179
+ `You are authenticated as ${theme.text.bold(
180
+ `@${getUserHandleFromProviderUserId(providerUserId)}`
181
+ )} and now able to interact with zk-SNARK Phase2 Trusted Setup ceremonies`
182
+ )
183
+
184
+ // Console more context for the user.
185
+ console.log(
186
+ `\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
187
+ `phase2cli logout`
188
+ )} command`
189
+ )
190
+
191
+ terminate(providerUserId)
192
+ }
193
+
194
+ export default auth
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
4
+ import { showError } from "../lib/errors.js"
5
+ import { askForConfirmation } from "../lib/prompts.js"
6
+ import { customSpinner, sleep } from "../lib/utils.js"
7
+ import theme from "../lib/theme.js"
8
+ import { localPaths } from "../lib/localConfigs.js"
9
+ import { deleteDir, directoryExists } from "../lib/files.js"
10
+
11
+ /**
12
+ * Clean command.
13
+ */
14
+ const clean = async () => {
15
+ try {
16
+ // Initialize services.
17
+ await bootstrapCommandExecutionAndServices()
18
+
19
+ const spinner = customSpinner(`Cleaning up...`, "clock")
20
+
21
+ if (directoryExists(localPaths.output)) {
22
+ console.log(theme.text.bold(`${theme.symbols.warning} Be careful, this action is irreversible!`))
23
+
24
+ const { confirmation } = await askForConfirmation(
25
+ "Are you sure you want to continue with the clean up?",
26
+ "Yes",
27
+ "No"
28
+ )
29
+
30
+ if (confirmation) {
31
+ spinner.start()
32
+
33
+ // Do the clean up.
34
+ deleteDir(localPaths.output)
35
+
36
+ // nb. simulate waiting time for 1s.
37
+ await sleep(1000)
38
+
39
+ spinner.succeed(`Cleanup was successfully completed ${theme.emojis.broom}`)
40
+ }
41
+ } else {
42
+ console.log(`${theme.symbols.info} There is nothing to clean ${theme.emojis.eyes}`)
43
+ }
44
+ } catch (err: any) {
45
+ showError(`Something went wrong: ${err.toString()}`, true)
46
+ }
47
+ }
48
+
49
+ export default clean