@layr-labs/ecloud-cli 0.0.1-rfc.1 → 0.1.0-dev
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 +264 -48
- package/VERSION +2 -0
- package/dist/commands/auth/generate.js +116 -10
- package/dist/commands/auth/generate.js.map +1 -1
- package/dist/commands/auth/login.js +37 -35
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.js +2 -8
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/migrate.js +32 -37
- package/dist/commands/auth/migrate.js.map +1 -1
- package/dist/commands/auth/whoami.js +53 -21
- package/dist/commands/auth/whoami.js.map +1 -1
- package/dist/commands/billing/cancel.js +83 -22
- package/dist/commands/billing/cancel.js.map +1 -1
- package/dist/commands/billing/status.js +92 -29
- package/dist/commands/billing/status.js.map +1 -1
- package/dist/commands/billing/subscribe.js +86 -31
- package/dist/commands/billing/subscribe.js.map +1 -1
- package/dist/commands/compute/app/configure/tls.js +150 -0
- package/dist/commands/compute/app/configure/tls.js.map +1 -0
- package/dist/commands/compute/app/create.js +134 -0
- package/dist/commands/compute/app/create.js.map +1 -0
- package/dist/commands/compute/app/deploy.js +1101 -0
- package/dist/commands/compute/app/deploy.js.map +1 -0
- package/dist/commands/compute/app/info.js +818 -0
- package/dist/commands/compute/app/info.js.map +1 -0
- package/dist/commands/compute/app/list.js +578 -0
- package/dist/commands/compute/app/list.js.map +1 -0
- package/dist/commands/compute/app/logs.js +639 -0
- package/dist/commands/compute/app/logs.js.map +1 -0
- package/dist/commands/compute/app/profile/set.js +1086 -0
- package/dist/commands/compute/app/profile/set.js.map +1 -0
- package/dist/commands/compute/app/start.js +675 -0
- package/dist/commands/compute/app/start.js.map +1 -0
- package/dist/commands/compute/app/stop.js +675 -0
- package/dist/commands/compute/app/stop.js.map +1 -0
- package/dist/commands/compute/app/terminate.js +681 -0
- package/dist/commands/compute/app/terminate.js.map +1 -0
- package/dist/commands/compute/app/upgrade.js +1072 -0
- package/dist/commands/compute/app/upgrade.js.map +1 -0
- package/dist/commands/compute/environment/list.js +89 -0
- package/dist/commands/compute/environment/list.js.map +1 -0
- package/dist/commands/compute/environment/set.js +215 -0
- package/dist/commands/compute/environment/set.js.map +1 -0
- package/dist/commands/compute/environment/show.js +96 -0
- package/dist/commands/compute/environment/show.js.map +1 -0
- package/dist/commands/compute/undelegate.js +259 -0
- package/dist/commands/compute/undelegate.js.map +1 -0
- package/dist/commands/upgrade.js +91 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/version.js +65 -0
- package/dist/commands/version.js.map +1 -0
- package/package.json +30 -5
- package/dist/commands/app/create.js +0 -29
- package/dist/commands/app/create.js.map +0 -1
- package/dist/commands/app/deploy.js +0 -142
- package/dist/commands/app/deploy.js.map +0 -1
- package/dist/commands/app/logs.js +0 -108
- package/dist/commands/app/logs.js.map +0 -1
- package/dist/commands/app/start.js +0 -121
- package/dist/commands/app/start.js.map +0 -1
- package/dist/commands/app/stop.js +0 -121
- package/dist/commands/app/stop.js.map +0 -1
- package/dist/commands/app/terminate.js +0 -128
- package/dist/commands/app/terminate.js.map +0 -1
- package/dist/commands/app/upgrade.js +0 -142
- package/dist/commands/app/upgrade.js.map +0 -1
- package/dist/keys/mainnet-alpha/prod/kms-encryption-public-key.pem +0 -14
- package/dist/keys/mainnet-alpha/prod/kms-signing-public-key.pem +0 -4
- package/dist/keys/sepolia/dev/kms-encryption-public-key.pem +0 -14
- package/dist/keys/sepolia/dev/kms-signing-public-key.pem +0 -4
- package/dist/keys/sepolia/prod/kms-encryption-public-key.pem +0 -14
- package/dist/keys/sepolia/prod/kms-signing-public-key.pem +0 -4
- package/dist/templates/Dockerfile.layered.tmpl +0 -58
- package/dist/templates/compute-source-env.sh.tmpl +0 -110
package/README.md
CHANGED
|
@@ -1,87 +1,304 @@
|
|
|
1
|
-
# ECloud SDK
|
|
1
|
+
# ECloud SDK and CLI
|
|
2
2
|
|
|
3
|
-
A TypeScript SDK and CLI for deploying and managing applications on
|
|
3
|
+
A TypeScript SDK and CLI for deploying and managing applications on EigenCloud TEE (Trusted Execution Environment). This monorepo provides both programmatic SDK access and a command-line interface for interacting with ecloud's decentralized compute platform.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
ECloud SDK enables developers to:
|
|
7
|
+
ECloud SDK and CLI enables developers to:
|
|
8
|
+
|
|
8
9
|
- Deploy containerized applications to ecloud TEE
|
|
9
10
|
- Manage application lifecycle (start, stop, terminate)
|
|
10
11
|
- Build and push Docker images with encryption
|
|
11
|
-
- Interact with ecloud smart contracts on Ethereum networks
|
|
12
12
|
- Monitor application status and logs
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Prerequsites
|
|
15
|
+
* Docker - To package and publish application images ([Download](https://www.docker.com/get-started/))
|
|
16
|
+
* ETH for gas - For deployment transactions
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
## Mainnet Alpha Limitations
|
|
19
|
+
* Not recommended for customer funds - Mainnet Alpha is intended to enable developers to build, test and ship applications. We do not recommend holding significant customer funds at this stage in Mainnet Alpha.
|
|
20
|
+
* Developer is still trusted - Mainnet Alpha does not enable full verifiable and trustless execution. * A later version will ensure developers can not upgrade code maliciously, and liveness guarantees.
|
|
21
|
+
No SLA - Mainnet Alpha does not have SLAs around support, and uptime of infrastructure.
|
|
17
22
|
|
|
18
|
-
### `@layr-labs/ecloud-sdk`
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
## Quick Start
|
|
25
|
+
### Installation
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
- KMS encryption for secure deployments
|
|
26
|
-
- Smart contract interactions (EIP7702)
|
|
27
|
-
- Environment configuration management
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @layr-labs/ecloud-cli
|
|
29
|
+
```
|
|
28
30
|
|
|
29
|
-
###
|
|
31
|
+
### Initial Setup
|
|
32
|
+
```bash
|
|
33
|
+
# Log in to your Docker registry (required to push images)
|
|
34
|
+
docker login
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
# Log in with an existing private key
|
|
37
|
+
ecloud auth login
|
|
38
|
+
```
|
|
32
39
|
|
|
33
|
-
**
|
|
34
|
-
- Deploy applications from Docker images
|
|
35
|
-
- Manage application lifecycle
|
|
36
|
-
- Environment-aware configuration
|
|
37
|
-
- Support for multiple networks
|
|
40
|
+
**Don't have a private key?** Use `ecloud auth generate --store` instead
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
**Need ETH for gas?** Run `ecloud auth whoami` to see your address. For sepolia, get funds from [Google Cloud](https://cloud.google.com/application/web3/faucet/ethereum/sepolia) or [Alchemy](https://sepoliafaucet.com/)
|
|
40
43
|
|
|
41
|
-
###
|
|
44
|
+
### Get a billing account
|
|
45
|
+
This is required to create apps
|
|
46
|
+
```bash
|
|
47
|
+
ecloud billing subscribe
|
|
48
|
+
```
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
### **Create & Deploy**
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Create your app (choose: typescript | python | golang | rust)
|
|
54
|
+
ecloud compute app create my-app typescript
|
|
55
|
+
cd my-app
|
|
56
|
+
|
|
57
|
+
# Configure environment variables
|
|
58
|
+
cp .env.example .env
|
|
59
|
+
|
|
60
|
+
# Deploy to TEE
|
|
61
|
+
ecloud compute app deploy
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### **Working with Existing Projects**
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
Have an existing project? You don't need `ecloud compute app create` - the CLI works with any Docker-based project:
|
|
48
67
|
|
|
49
68
|
```bash
|
|
50
|
-
|
|
69
|
+
# From your existing project directory
|
|
70
|
+
cd my-existing-project
|
|
71
|
+
|
|
72
|
+
# Ensure you have a Dockerfile and .env file
|
|
73
|
+
# The CLI will prompt for these if not found in standard locations
|
|
74
|
+
|
|
75
|
+
# Deploy directly - the CLI will detect your project
|
|
76
|
+
ecloud compute app deploy
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**What you need:**
|
|
80
|
+
- **Dockerfile** - Must target `linux/amd64` and run as root user
|
|
81
|
+
- **.env file** - For environment variables (optional but recommended)
|
|
82
|
+
|
|
83
|
+
The CLI will automatically prompt for the Dockerfile and .env paths if they're not in the default locations. This means you can use ecloud with any existing containerized application without restructuring your project.
|
|
84
|
+
|
|
85
|
+
**Need TLS/HTTPS?** Run `ecloud compute app configure tls` to add the necessary configuration files for domain setup with private traffic termination in the TEE.
|
|
86
|
+
|
|
87
|
+
### **View Your App**
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# View app information and logs
|
|
91
|
+
ecloud compute app info
|
|
92
|
+
ecloud compute app logs
|
|
93
|
+
|
|
94
|
+
# Add --watch (or -w) to continuously poll for live updates
|
|
95
|
+
ecloud compute app info --watch
|
|
96
|
+
ecloud compute app logs --watch
|
|
51
97
|
```
|
|
52
98
|
|
|
53
|
-
|
|
99
|
+
That's it! Your starter app is now running in a TEE with access to a MNEMONIC that only it can access.
|
|
100
|
+
|
|
101
|
+
**Ready to customize?** Edit your application code, update `.env` with any API keys you need, then run `ecloud compute app upgrade my-app` to deploy your changes
|
|
102
|
+
|
|
103
|
+
## Application Environment
|
|
104
|
+
|
|
105
|
+
Your TEE application runs with these capabilities:
|
|
106
|
+
|
|
107
|
+
1. **Secure Execution** - Your code runs in an Intel TDX instance with hardware-level isolation
|
|
108
|
+
2. **Auto-Generated Wallet** - Access a private mnemonic via `process.env.MNEMONIC`
|
|
109
|
+
- Derive wallet accounts using standard libraries (e.g., viem’s `mnemonicToAccount(process.env.MNEMONIC)`)
|
|
110
|
+
- Only your TEE can decrypt and use this mnemonic
|
|
111
|
+
3. **Environment Variables** - All variables from your `.env` file are available in your container
|
|
112
|
+
- Variables with `_PUBLIC` suffix are visible to users for transparency
|
|
113
|
+
- Standard variables remain private and encrypted within the TEE
|
|
114
|
+
4. **Onchain Management** - Your app's lifecycle is controlled via Ethereum smart contracts
|
|
115
|
+
|
|
116
|
+
### Working with Your App
|
|
54
117
|
|
|
55
118
|
```bash
|
|
56
|
-
|
|
119
|
+
# List all your apps
|
|
120
|
+
ecloud compute app list
|
|
121
|
+
|
|
122
|
+
# Stop/start your app
|
|
123
|
+
ecloud compute app stop my-app
|
|
124
|
+
ecloud compute app start my-app
|
|
125
|
+
|
|
126
|
+
# Terminate your app
|
|
127
|
+
ecloud compute app terminate my-app
|
|
57
128
|
```
|
|
58
129
|
|
|
59
|
-
##
|
|
130
|
+
## Authentication
|
|
131
|
+
|
|
132
|
+
Ecloud CLI needs a private key to sign transactions. Three options:
|
|
133
|
+
|
|
134
|
+
### 1. OS Keyring (Recommended)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
ecloud auth generate --store # Generate new key and store it
|
|
138
|
+
ecloud auth login # Store an existing key securely
|
|
139
|
+
ecloud auth whoami # Check authentication
|
|
140
|
+
ecloud auth logout # Remove key
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 2. Environment Variable
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
export ECLOUD_PRIVATE_KEY=0x1234...
|
|
147
|
+
ecloud compute app deploy
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 3. Command Flag
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
ecloud compute app deploy --private-key 0x1234...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Priority:** Flag → Environment → Keyring
|
|
157
|
+
|
|
158
|
+
## TLS/HTTPS Setup
|
|
159
|
+
|
|
160
|
+
### Enable TLS
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Add TLS configuration to your project
|
|
164
|
+
ecloud compute app configure tls
|
|
60
165
|
|
|
61
|
-
|
|
166
|
+
# Add variables to .env
|
|
167
|
+
cat .env.example.tls >> .env
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Configure
|
|
171
|
+
|
|
172
|
+
Required in `.env`:
|
|
173
|
+
```bash
|
|
174
|
+
DOMAIN=yourdomain.com
|
|
175
|
+
APP_PORT=3000
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Recommended for first deployment:
|
|
179
|
+
```bash
|
|
180
|
+
ENABLE_CADDY_LOGS=true # Debug logs
|
|
181
|
+
ACME_STAGING=true # Test certificates (avoid rate limits)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### DNS Setup
|
|
185
|
+
|
|
186
|
+
Create A record pointing to instance IP:
|
|
187
|
+
- Type: A
|
|
188
|
+
- Name: yourdomain.com
|
|
189
|
+
- Value: `<instance-ip>` (get from `ecloud compute app info`)
|
|
190
|
+
|
|
191
|
+
### Deploy
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
ecloud compute app upgrade
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Production Certificates
|
|
198
|
+
|
|
199
|
+
To switch from staging to production:
|
|
200
|
+
```bash
|
|
201
|
+
# Set in .env:
|
|
202
|
+
ACME_STAGING=false
|
|
203
|
+
ACME_FORCE_ISSUE=true # Only if staging cert exists
|
|
204
|
+
|
|
205
|
+
# Deploy, then set ACME_FORCE_ISSUE=false for future deploys
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Notes:**
|
|
209
|
+
- Let's Encrypt rate limit: 5 certificates/week per domain
|
|
210
|
+
- Test with staging certificates first to avoid rate limits
|
|
211
|
+
- DNS changes may take a few minutes to propagate
|
|
62
212
|
|
|
63
|
-
|
|
213
|
+
## Advanced Usage
|
|
214
|
+
|
|
215
|
+
### Building and Pushing Images Manually
|
|
216
|
+
|
|
217
|
+
If you prefer to build and push Docker images yourself instead of letting the CLI handle it, or already have an existing image:
|
|
64
218
|
|
|
65
219
|
```bash
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
220
|
+
# Build and push your image manually
|
|
221
|
+
docker build --platform linux/amd64 -t myregistry/myapp:v1.0 .
|
|
222
|
+
docker push myregistry/myapp:v1.0
|
|
223
|
+
|
|
224
|
+
# Deploy using the image reference
|
|
225
|
+
ecloud compute app deploy myregistry/myapp:v1.0
|
|
71
226
|
```
|
|
72
227
|
|
|
73
|
-
**
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
228
|
+
**Requirements:**
|
|
229
|
+
|
|
230
|
+
- Image must target `linux/amd64` architecture
|
|
231
|
+
- Application must run as root user (TEE requirement)
|
|
232
|
+
|
|
233
|
+
## Telemetry
|
|
234
|
+
|
|
235
|
+
Ecloud collects anonymous usage data to help us improve the CLI and understand how it's being used. This telemetry is enabled by default but can be easily disabled.
|
|
236
|
+
|
|
237
|
+
### What We Collect
|
|
238
|
+
|
|
239
|
+
- Commands used (e.g., `ecloud compute app create`, `ecloud compute app deploy`)
|
|
240
|
+
- Error counts and types to identify common issues
|
|
241
|
+
- Performance metrics (command execution times)
|
|
242
|
+
- System information (OS, architecture)
|
|
243
|
+
- Deployment environment (e.g., sepolia, mainnet-alpha)
|
|
244
|
+
- User Ethereum address
|
|
245
|
+
|
|
246
|
+
### What We DON'T Collect
|
|
247
|
+
|
|
248
|
+
- Personal information or identifiers
|
|
249
|
+
- Private keys or sensitive credentials
|
|
250
|
+
- Application source code or configurations
|
|
251
|
+
- Specific file paths or project names
|
|
252
|
+
|
|
253
|
+
## Architecture
|
|
254
|
+
|
|
255
|
+
For a detailed understanding of how Ecloud enables verifiable applications with deterministic identities, see our [Architecture Documentation](docs/ECLOUD_ARCHITECTURE.md).
|
|
256
|
+
|
|
257
|
+
### Key Components
|
|
258
|
+
|
|
259
|
+
- **Hardware Isolation** - Intel TDX secure enclaves with memory encryption
|
|
260
|
+
- **Attestation** - Cryptographic proof of exact Docker image integrity
|
|
261
|
+
- **Deterministic Keys** - Apps receive consistent identities via KMS
|
|
262
|
+
- **Smart Contracts** - Onchain configuration and lifecycle management
|
|
263
|
+
|
|
264
|
+
## Development
|
|
265
|
+
|
|
266
|
+
### Prerequisites
|
|
267
|
+
|
|
268
|
+
- Node.js 18+
|
|
269
|
+
- pnpm (recommended) or npm
|
|
270
|
+
- Docker (for building and pushing images)
|
|
271
|
+
|
|
272
|
+
### Build from source
|
|
77
273
|
|
|
78
|
-
**Example:**
|
|
79
274
|
```bash
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
275
|
+
git clone https://github.com/Layr-Labs/ecloud
|
|
276
|
+
cd ecloud
|
|
277
|
+
pnpm install
|
|
278
|
+
pnpm build
|
|
279
|
+
pnpm ecloud version
|
|
83
280
|
```
|
|
84
281
|
|
|
282
|
+
|
|
283
|
+
## SDK Packages
|
|
284
|
+
|
|
285
|
+
This monorepo contains two main packages:
|
|
286
|
+
|
|
287
|
+
### `@layr-labs/ecloud-sdk`
|
|
288
|
+
|
|
289
|
+
The core TypeScript SDK for programmatic access to ecloud services.
|
|
290
|
+
|
|
291
|
+
**Features:**
|
|
292
|
+
|
|
293
|
+
- Type-safe client for ecloud operations
|
|
294
|
+
- Docker image building and pushing
|
|
295
|
+
- KMS encryption for secure deployments
|
|
296
|
+
- Smart contract interactions (EIP7702)
|
|
297
|
+
- Environment configuration management
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
## Usage
|
|
301
|
+
|
|
85
302
|
### SDK Usage
|
|
86
303
|
|
|
87
304
|
```typescript
|
|
@@ -90,7 +307,7 @@ import { createECloudClient } from "@layr-labs/ecloud-sdk";
|
|
|
90
307
|
// Create a client
|
|
91
308
|
const client = createECloudClient({
|
|
92
309
|
privateKey: "0x...",
|
|
93
|
-
environment: "sepolia", // or "sepolia
|
|
310
|
+
environment: "sepolia", // or "sepolia" or "mainnet-alpha"
|
|
94
311
|
rpcUrl: "https://sepolia.infura.io/v3/...",
|
|
95
312
|
});
|
|
96
313
|
|
|
@@ -192,4 +409,3 @@ The deployment process involves several steps:
|
|
|
192
409
|
## Support
|
|
193
410
|
|
|
194
411
|
For issues and questions, please open an issue on GitHub.
|
|
195
|
-
|
package/VERSION
ADDED
|
@@ -3,13 +3,121 @@
|
|
|
3
3
|
// src/commands/auth/generate.ts
|
|
4
4
|
import { Command, Flags } from "@oclif/core";
|
|
5
5
|
import { confirm } from "@inquirer/prompts";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import { generateNewPrivateKey, storePrivateKey, keyExists } from "@layr-labs/ecloud-sdk";
|
|
7
|
+
|
|
8
|
+
// src/utils/security.ts
|
|
9
|
+
import { spawn, execSync } from "child_process";
|
|
10
|
+
import { platform } from "os";
|
|
11
|
+
import { select, password } from "@inquirer/prompts";
|
|
12
|
+
async function showPrivateKey(content) {
|
|
13
|
+
const pager = detectPager();
|
|
14
|
+
if (pager) {
|
|
15
|
+
try {
|
|
16
|
+
await runPager(pager, content);
|
|
17
|
+
return true;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error(`Failed to run pager: ${err}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
console.log("\nNo pager (less/more) found on PATH.");
|
|
23
|
+
console.log("For security, avoid printing private keys to the terminal.");
|
|
24
|
+
console.log("");
|
|
25
|
+
const choice = await select({
|
|
26
|
+
message: "Choose an option:",
|
|
27
|
+
choices: [
|
|
28
|
+
{ name: "Abort (recommended)", value: "abort" },
|
|
29
|
+
{ name: "Print and clear screen", value: "print" }
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
if (choice === "print") {
|
|
33
|
+
console.log(content);
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log("Press Enter after you have securely saved the key.");
|
|
36
|
+
console.log("The screen will be cleared...");
|
|
37
|
+
await password({
|
|
38
|
+
message: "",
|
|
39
|
+
mask: ""
|
|
40
|
+
});
|
|
41
|
+
clearTerminal();
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function detectPager() {
|
|
47
|
+
if (process.env.PAGER) {
|
|
48
|
+
const pagerEnv = process.env.PAGER.trim();
|
|
49
|
+
if (/^[a-zA-Z0-9_-]+$/.test(pagerEnv)) {
|
|
50
|
+
return pagerEnv;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const pagers = ["less", "more"];
|
|
54
|
+
for (const pagerCmd of pagers) {
|
|
55
|
+
if (commandExists(pagerCmd)) {
|
|
56
|
+
return pagerCmd;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function runPager(pager, content) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const child = spawn(pager, [], {
|
|
64
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
65
|
+
});
|
|
66
|
+
child.on("error", reject);
|
|
67
|
+
child.on("exit", (code) => {
|
|
68
|
+
if (code === 0) {
|
|
69
|
+
resolve();
|
|
70
|
+
} else {
|
|
71
|
+
reject(new Error(`Pager exited with code ${code}`));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
const written = child.stdin.write(content);
|
|
76
|
+
if (!written) {
|
|
77
|
+
child.stdin.once("drain", () => {
|
|
78
|
+
try {
|
|
79
|
+
child.stdin.end();
|
|
80
|
+
} catch (err) {
|
|
81
|
+
reject(err);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
child.stdin.end();
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
reject(err);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function commandExists(command) {
|
|
93
|
+
try {
|
|
94
|
+
const cmd = platform() === "win32" ? `where ${command}` : `which ${command}`;
|
|
95
|
+
execSync(cmd, { stdio: "ignore" });
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function clearTerminal() {
|
|
102
|
+
if (platform() === "win32") {
|
|
103
|
+
process.stdout.write("\x1Bc");
|
|
104
|
+
} else {
|
|
105
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function displayWarning(lines) {
|
|
109
|
+
const width = lines.length > 0 ? Math.max(...lines.map((l) => l.length)) + 4 : 4;
|
|
110
|
+
const border = "\u26A0".repeat(width);
|
|
111
|
+
console.log("");
|
|
112
|
+
console.log(border);
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
console.log(`\u26A0 ${line}`);
|
|
115
|
+
}
|
|
116
|
+
console.log(border);
|
|
117
|
+
console.log("");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/commands/auth/generate.ts
|
|
13
121
|
var AuthGenerate = class _AuthGenerate extends Command {
|
|
14
122
|
static description = "Generate a new private key";
|
|
15
123
|
static aliases = ["auth:gen", "auth:new"];
|
|
@@ -83,9 +191,7 @@ Press 'q' to exit and continue...
|
|
|
83
191
|
this.log(`
|
|
84
192
|
\u2713 Private key stored in OS keyring`);
|
|
85
193
|
this.log(`\u2713 Address: ${address}`);
|
|
86
|
-
this.log(
|
|
87
|
-
"\nYou can now use ecloud commands without --private-key flag."
|
|
88
|
-
);
|
|
194
|
+
this.log("\nYou can now use ecloud commands without --private-key flag.");
|
|
89
195
|
} catch (err) {
|
|
90
196
|
this.error(`Failed to store key: ${err.message}`);
|
|
91
197
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/auth/generate.ts"],"sourcesContent":["/**\n * Auth Generate Command\n *\n * Generate a new private key and optionally store it in OS keyring\n */\n\nimport { Command, Flags } from \"@oclif/core\";\nimport { confirm } from \"@inquirer/prompts\";\nimport {\n generateNewPrivateKey,\n storePrivateKey,\n keyExists,\n showPrivateKey,\n displayWarning,\n} from \"@layr-labs/ecloud-sdk\";\n\nexport default class AuthGenerate extends Command {\n static description = \"Generate a new private key\";\n\n static aliases = [\"auth:gen\", \"auth:new\"];\n\n static examples = [\n \"<%= config.bin %> <%= command.id %>\",\n \"<%= config.bin %> <%= command.id %> --store\",\n ];\n\n static flags = {\n store: Flags.boolean({\n description: \"Automatically store in OS keyring\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(AuthGenerate);\n\n // Generate new key\n this.log(\"Generating new private key...\\n\");\n const { privateKey, address } = generateNewPrivateKey();\n\n // Display key securely\n const content = `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nA new private key was generated for you.\n\nIMPORTANT: You MUST backup this key now.\n It will never be shown again.\n\nAddress: ${address}\nPrivate key: ${privateKey}\n\n⚠️ SECURITY WARNING:\n • Anyone with this key can control your account\n • Never share it or commit it to version control\n • Store it in a secure password manager\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nPress 'q' to exit and continue...\n`;\n\n const displayed = await showPrivateKey(content);\n\n if (!displayed) {\n this.log(\"Key generation cancelled.\");\n return;\n }\n\n // Ask about storing\n let shouldStore = flags.store;\n\n if (!shouldStore && displayed) {\n shouldStore = await confirm({\n message: \"Store this key in your OS keyring?\",\n default: true,\n });\n }\n\n if (shouldStore) {\n // Check if key already exists\n const exists = await keyExists();\n\n if (exists) {\n displayWarning([\n `WARNING: A private key for ecloud already exists!`,\n \"If you continue, the existing key will be PERMANENTLY REPLACED.\",\n \"This cannot be undone!\",\n \"\",\n \"The previous key will be lost forever if you haven't backed it up.\",\n ]);\n\n const confirmReplace = await confirm({\n message: `Replace existing key for ecloud?`,\n default: false,\n });\n\n if (!confirmReplace) {\n this.log(\n \"\\nKey not stored. If you did not save your new key when it was displayed, it is now lost and cannot be recovered.\"\n );\n return;\n }\n }\n\n // Store the key\n try {\n await storePrivateKey(privateKey);\n this.log(`\\n✓ Private key stored in OS keyring`);\n this.log(`✓ Address: ${address}`);\n this.log(\n \"\\nYou can now use ecloud commands without --private-key flag.\"\n );\n } catch (err: any) {\n this.error(`Failed to store key: ${err.message}`);\n }\n } else {\n this.log(\"\\nKey not stored in keyring.\");\n this.log(\"Remember to save the key shown above in a secure location.\");\n }\n }\n}\n"],"mappings":";;;AAMA,SAAS,SAAS,aAAa;AAC/B,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,IAAqB,eAArB,MAAqB,sBAAqB,QAAQ;AAAA,EAChD,OAAO,cAAc;AAAA,EAErB,OAAO,UAAU,CAAC,YAAY,UAAU;AAAA,EAExC,OAAO,WAAW;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ;AAAA,IACb,OAAO,MAAM,QAAQ;AAAA,MACnB,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,aAAY;AAG/C,SAAK,IAAI,iCAAiC;AAC1C,UAAM,EAAE,YAAY,QAAQ,IAAI,sBAAsB;AAGtD,UAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAOL,OAAO;AAAA,eACP,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWrB,UAAM,YAAY,MAAM,eAAe,OAAO;AAE9C,QAAI,CAAC,WAAW;AACd,WAAK,IAAI,2BAA2B;AACpC;AAAA,IACF;AAGA,QAAI,cAAc,MAAM;AAExB,QAAI,CAAC,eAAe,WAAW;AAC7B,oBAAc,MAAM,QAAQ;AAAA,QAC1B,SAAS;AAAA,QACT,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,QAAI,aAAa;AAEf,YAAM,SAAS,MAAM,UAAU;AAE/B,UAAI,QAAQ;AACV,uBAAe;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,iBAAiB,MAAM,QAAQ;AAAA,UACnC,SAAS;AAAA,UACT,SAAS;AAAA,QACX,CAAC;AAED,YAAI,CAAC,gBAAgB;AACnB,eAAK;AAAA,YACH;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAGA,UAAI;AACF,cAAM,gBAAgB,UAAU;AAChC,aAAK,IAAI;AAAA,wCAAsC;AAC/C,aAAK,IAAI,mBAAc,OAAO,EAAE;AAChC,aAAK;AAAA,UACH;AAAA,QACF;AAAA,MACF,SAAS,KAAU;AACjB,aAAK,MAAM,wBAAwB,IAAI,OAAO,EAAE;AAAA,MAClD;AAAA,IACF,OAAO;AACL,WAAK,IAAI,8BAA8B;AACvC,WAAK,IAAI,4DAA4D;AAAA,IACvE;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/auth/generate.ts","../../../src/utils/security.ts"],"sourcesContent":["/**\n * Auth Generate Command\n *\n * Generate a new private key and optionally store it in OS keyring\n */\n\nimport { Command, Flags } from \"@oclif/core\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { generateNewPrivateKey, storePrivateKey, keyExists } from \"@layr-labs/ecloud-sdk\";\nimport { showPrivateKey, displayWarning } from \"../../utils/security\";\n\nexport default class AuthGenerate extends Command {\n static description = \"Generate a new private key\";\n\n static aliases = [\"auth:gen\", \"auth:new\"];\n\n static examples = [\n \"<%= config.bin %> <%= command.id %>\",\n \"<%= config.bin %> <%= command.id %> --store\",\n ];\n\n static flags = {\n store: Flags.boolean({\n description: \"Automatically store in OS keyring\",\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(AuthGenerate);\n\n // Generate new key\n this.log(\"Generating new private key...\\n\");\n const { privateKey, address } = generateNewPrivateKey();\n\n // Display key securely\n const content = `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nA new private key was generated for you.\n\nIMPORTANT: You MUST backup this key now.\n It will never be shown again.\n\nAddress: ${address}\nPrivate key: ${privateKey}\n\n⚠️ SECURITY WARNING:\n • Anyone with this key can control your account\n • Never share it or commit it to version control\n • Store it in a secure password manager\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nPress 'q' to exit and continue...\n`;\n\n const displayed = await showPrivateKey(content);\n\n if (!displayed) {\n this.log(\"Key generation cancelled.\");\n return;\n }\n\n // Ask about storing\n let shouldStore = flags.store;\n\n if (!shouldStore && displayed) {\n shouldStore = await confirm({\n message: \"Store this key in your OS keyring?\",\n default: true,\n });\n }\n\n if (shouldStore) {\n // Check if key already exists\n const exists = await keyExists();\n\n if (exists) {\n displayWarning([\n `WARNING: A private key for ecloud already exists!`,\n \"If you continue, the existing key will be PERMANENTLY REPLACED.\",\n \"This cannot be undone!\",\n \"\",\n \"The previous key will be lost forever if you haven't backed it up.\",\n ]);\n\n const confirmReplace = await confirm({\n message: `Replace existing key for ecloud?`,\n default: false,\n });\n\n if (!confirmReplace) {\n this.log(\n \"\\nKey not stored. If you did not save your new key when it was displayed, it is now lost and cannot be recovered.\",\n );\n return;\n }\n }\n\n // Store the key\n try {\n await storePrivateKey(privateKey);\n this.log(`\\n✓ Private key stored in OS keyring`);\n this.log(`✓ Address: ${address}`);\n this.log(\"\\nYou can now use ecloud commands without --private-key flag.\");\n } catch (err: any) {\n this.error(`Failed to store key: ${err.message}`);\n }\n } else {\n this.log(\"\\nKey not stored in keyring.\");\n this.log(\"Remember to save the key shown above in a secure location.\");\n }\n }\n}\n","/**\n * Security utilities for CLI\n *\n * Functions for securely displaying and handling sensitive content\n * like private keys.\n */\n\nimport { spawn, execSync } from \"child_process\";\nimport { platform } from \"os\";\nimport { select, password } from \"@inquirer/prompts\";\n\n/**\n * Display sensitive content using system pager (less/more)\n * Returns true if content was displayed, false if user aborted\n */\nexport async function showPrivateKey(content: string): Promise<boolean> {\n // Try to use system pager\n const pager = detectPager();\n\n if (pager) {\n try {\n await runPager(pager, content);\n return true;\n } catch (err) {\n console.error(`Failed to run pager: ${err}`);\n // Fall through to fallback\n }\n }\n\n // No pager available - give user a choice\n console.log(\"\\nNo pager (less/more) found on PATH.\");\n console.log(\"For security, avoid printing private keys to the terminal.\");\n console.log(\"\");\n\n const choice = await select({\n message: \"Choose an option:\",\n choices: [\n { name: \"Abort (recommended)\", value: \"abort\" },\n { name: \"Print and clear screen\", value: \"print\" },\n ],\n });\n\n if (choice === \"print\") {\n console.log(content);\n console.log(\"\");\n console.log(\"Press Enter after you have securely saved the key.\");\n console.log(\"The screen will be cleared...\");\n\n // Wait for Enter\n await password({\n message: \"\",\n mask: \"\",\n });\n\n clearTerminal();\n return true;\n }\n\n return false; // User aborted\n}\n\n/**\n * Detect system pager (less or more)\n */\nfunction detectPager(): string | null {\n // Check PAGER env var first\n if (process.env.PAGER) {\n const pagerEnv = process.env.PAGER.trim();\n // Only allow simple command names without arguments or special characters\n if (/^[a-zA-Z0-9_-]+$/.test(pagerEnv)) {\n return pagerEnv;\n }\n }\n\n // Try common pagers\n const pagers = [\"less\", \"more\"];\n\n for (const pagerCmd of pagers) {\n if (commandExists(pagerCmd)) {\n return pagerCmd;\n }\n }\n\n return null;\n}\n\n/**\n * Run pager with content\n */\nfunction runPager(pager: string, content: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(pager, [], {\n stdio: [\"pipe\", \"inherit\", \"inherit\"],\n });\n\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Pager exited with code ${code}`));\n }\n });\n\n try {\n const written = child.stdin!.write(content);\n if (!written) {\n child.stdin!.once(\"drain\", () => {\n try {\n child.stdin!.end();\n } catch (err) {\n reject(err);\n }\n });\n } else {\n child.stdin!.end();\n }\n } catch (err) {\n reject(err);\n }\n });\n}\n\n/**\n * Check if command exists\n */\nfunction commandExists(command: string): boolean {\n try {\n const cmd = platform() === \"win32\" ? `where ${command}` : `which ${command}`;\n execSync(cmd, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Clear terminal screen\n */\nexport function clearTerminal(): void {\n if (platform() === \"win32\") {\n process.stdout.write(\"\\x1Bc\");\n } else {\n process.stdout.write(\"\\x1B[2J\\x1B[3J\\x1B[H\");\n }\n}\n\n/**\n * Get hidden input (password-style)\n */\nexport async function getHiddenInput(message: string): Promise<string> {\n return await password({\n message,\n mask: \"*\",\n });\n}\n\n/**\n * Display multi-line warning for destructive operations\n */\nexport function displayWarning(lines: string[]): void {\n const width = lines.length > 0 ? Math.max(...lines.map((l) => l.length)) + 4 : 4;\n const border = \"⚠\".repeat(width);\n\n console.log(\"\");\n console.log(border);\n for (const line of lines) {\n console.log(`⚠ ${line}`);\n }\n console.log(border);\n console.log(\"\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,18 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
// src/commands/auth/login.ts
|
|
4
4
|
import { Command } from "@oclif/core";
|
|
5
|
-
import { confirm, select } from "@inquirer/prompts";
|
|
5
|
+
import { confirm, select as select2 } from "@inquirer/prompts";
|
|
6
6
|
import {
|
|
7
7
|
storePrivateKey,
|
|
8
8
|
keyExists,
|
|
9
|
-
getHiddenInput,
|
|
10
9
|
validatePrivateKey,
|
|
11
10
|
getAddressFromPrivateKey,
|
|
12
|
-
displayWarning,
|
|
13
11
|
getLegacyKeys,
|
|
14
12
|
getLegacyPrivateKey,
|
|
15
13
|
deleteLegacyPrivateKey
|
|
16
14
|
} from "@layr-labs/ecloud-sdk";
|
|
15
|
+
|
|
16
|
+
// src/utils/security.ts
|
|
17
|
+
import { spawn, execSync } from "child_process";
|
|
18
|
+
import { platform } from "os";
|
|
19
|
+
import { select, password } from "@inquirer/prompts";
|
|
20
|
+
async function getHiddenInput(message) {
|
|
21
|
+
return await password({
|
|
22
|
+
message,
|
|
23
|
+
mask: "*"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function displayWarning(lines) {
|
|
27
|
+
const width = lines.length > 0 ? Math.max(...lines.map((l) => l.length)) + 4 : 4;
|
|
28
|
+
const border = "\u26A0".repeat(width);
|
|
29
|
+
console.log("");
|
|
30
|
+
console.log(border);
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
console.log(`\u26A0 ${line}`);
|
|
33
|
+
}
|
|
34
|
+
console.log(border);
|
|
35
|
+
console.log("");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/commands/auth/login.ts
|
|
17
39
|
var AuthLogin = class extends Command {
|
|
18
40
|
static description = "Store your private key in OS keyring";
|
|
19
41
|
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
@@ -55,23 +77,16 @@ var AuthLogin = class extends Command {
|
|
|
55
77
|
name: `${key.address} (${key.environment} - ${key.source})`,
|
|
56
78
|
value: key
|
|
57
79
|
}));
|
|
58
|
-
selectedKey = await
|
|
80
|
+
selectedKey = await select2({
|
|
59
81
|
message: "Select a key to import:",
|
|
60
82
|
choices
|
|
61
83
|
});
|
|
62
|
-
privateKey = await getLegacyPrivateKey(
|
|
63
|
-
selectedKey.environment,
|
|
64
|
-
selectedKey.source
|
|
65
|
-
);
|
|
84
|
+
privateKey = await getLegacyPrivateKey(selectedKey.environment, selectedKey.source);
|
|
66
85
|
if (!privateKey) {
|
|
67
|
-
this.error(
|
|
68
|
-
`Failed to retrieve legacy key for ${selectedKey.environment}`
|
|
69
|
-
);
|
|
86
|
+
this.error(`Failed to retrieve legacy key for ${selectedKey.environment}`);
|
|
70
87
|
}
|
|
71
|
-
this.log(
|
|
72
|
-
|
|
73
|
-
Importing key from ${selectedKey.source}:${selectedKey.environment}`
|
|
74
|
-
);
|
|
88
|
+
this.log(`
|
|
89
|
+
Importing key from ${selectedKey.source}:${selectedKey.environment}`);
|
|
75
90
|
}
|
|
76
91
|
}
|
|
77
92
|
if (!privateKey) {
|
|
@@ -96,9 +111,7 @@ Address: ${address}`);
|
|
|
96
111
|
await storePrivateKey(privateKey);
|
|
97
112
|
this.log("\n\u2713 Private key stored in OS keyring");
|
|
98
113
|
this.log(`\u2713 Address: ${address}`);
|
|
99
|
-
this.log(
|
|
100
|
-
"\nNote: This key will be used for all environments (mainnet, sepolia, etc.)"
|
|
101
|
-
);
|
|
114
|
+
this.log("\nNote: This key will be used for all environments (mainnet, sepolia, etc.)");
|
|
102
115
|
this.log("You can now use ecloud commands without --private-key flag.");
|
|
103
116
|
if (selectedKey) {
|
|
104
117
|
this.log("");
|
|
@@ -107,21 +120,14 @@ Address: ${address}`);
|
|
|
107
120
|
default: false
|
|
108
121
|
});
|
|
109
122
|
if (confirmDelete) {
|
|
110
|
-
const deleted = await deleteLegacyPrivateKey(
|
|
111
|
-
selectedKey.environment,
|
|
112
|
-
selectedKey.source
|
|
113
|
-
);
|
|
123
|
+
const deleted = await deleteLegacyPrivateKey(selectedKey.environment, selectedKey.source);
|
|
114
124
|
if (deleted) {
|
|
115
125
|
this.log(
|
|
116
126
|
`
|
|
117
127
|
\u2713 Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`
|
|
118
128
|
);
|
|
119
|
-
this.log(
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
this.log(
|
|
123
|
-
"eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var."
|
|
124
|
-
);
|
|
129
|
+
this.log("\nNote: The key is now only stored in ecloud. You can still use it with");
|
|
130
|
+
this.log("eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.");
|
|
125
131
|
} else {
|
|
126
132
|
this.log(
|
|
127
133
|
`
|
|
@@ -130,13 +136,9 @@ Address: ${address}`);
|
|
|
130
136
|
this.log("The key may have already been removed.");
|
|
131
137
|
}
|
|
132
138
|
} else {
|
|
133
|
-
this.log(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
);
|
|
137
|
-
this.log(
|
|
138
|
-
"You can delete it later using 'eigenx auth logout' if needed."
|
|
139
|
-
);
|
|
139
|
+
this.log(`
|
|
140
|
+
Legacy key kept in ${selectedKey.source}:${selectedKey.environment}`);
|
|
141
|
+
this.log("You can delete it later using 'eigenx auth logout' if needed.");
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
} catch (err) {
|