@layr-labs/ecloud-cli 0.0.1-dev → 0.0.1-rfc.1
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 +2 -6
- package/dist/commands/app/create.js +29 -0
- package/dist/commands/app/create.js.map +1 -0
- package/dist/commands/app/deploy.js +142 -0
- package/dist/commands/app/deploy.js.map +1 -0
- package/dist/commands/app/logs.js +108 -0
- package/dist/commands/app/logs.js.map +1 -0
- package/dist/commands/app/start.js +121 -0
- package/dist/commands/app/start.js.map +1 -0
- package/dist/commands/app/stop.js +121 -0
- package/dist/commands/app/stop.js.map +1 -0
- package/dist/commands/app/terminate.js +128 -0
- package/dist/commands/app/terminate.js.map +1 -0
- package/dist/commands/app/upgrade.js +142 -0
- package/dist/commands/app/upgrade.js.map +1 -0
- package/dist/commands/auth/generate.js +10 -116
- package/dist/commands/auth/generate.js.map +1 -1
- package/dist/commands/auth/login.js +35 -37
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.js +8 -2
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/migrate.js +37 -32
- package/dist/commands/auth/migrate.js.map +1 -1
- package/dist/commands/auth/whoami.js +21 -53
- package/dist/commands/auth/whoami.js.map +1 -1
- package/dist/commands/billing/cancel.js +22 -83
- package/dist/commands/billing/cancel.js.map +1 -1
- package/dist/commands/billing/status.js +29 -92
- package/dist/commands/billing/status.js.map +1 -1
- package/dist/commands/billing/subscribe.js +31 -86
- package/dist/commands/billing/subscribe.js.map +1 -1
- package/dist/keys/mainnet-alpha/prod/kms-encryption-public-key.pem +14 -0
- package/dist/keys/mainnet-alpha/prod/kms-signing-public-key.pem +4 -0
- package/dist/keys/sepolia/dev/kms-encryption-public-key.pem +14 -0
- package/dist/keys/sepolia/dev/kms-signing-public-key.pem +4 -0
- package/dist/keys/sepolia/prod/kms-encryption-public-key.pem +14 -0
- package/dist/keys/sepolia/prod/kms-signing-public-key.pem +4 -0
- package/dist/templates/Dockerfile.layered.tmpl +58 -0
- package/dist/templates/compute-source-env.sh.tmpl +110 -0
- package/package.json +4 -29
- package/VERSION +0 -2
- package/dist/commands/compute/app/configure/tls.js +0 -150
- package/dist/commands/compute/app/configure/tls.js.map +0 -1
- package/dist/commands/compute/app/create.js +0 -134
- package/dist/commands/compute/app/create.js.map +0 -1
- package/dist/commands/compute/app/deploy.js +0 -1081
- package/dist/commands/compute/app/deploy.js.map +0 -1
- package/dist/commands/compute/app/info.js +0 -809
- package/dist/commands/compute/app/info.js.map +0 -1
- package/dist/commands/compute/app/list.js +0 -570
- package/dist/commands/compute/app/list.js.map +0 -1
- package/dist/commands/compute/app/logs.js +0 -629
- package/dist/commands/compute/app/logs.js.map +0 -1
- package/dist/commands/compute/app/profile/set.js +0 -1072
- package/dist/commands/compute/app/profile/set.js.map +0 -1
- package/dist/commands/compute/app/start.js +0 -665
- package/dist/commands/compute/app/start.js.map +0 -1
- package/dist/commands/compute/app/stop.js +0 -665
- package/dist/commands/compute/app/stop.js.map +0 -1
- package/dist/commands/compute/app/terminate.js +0 -671
- package/dist/commands/compute/app/terminate.js.map +0 -1
- package/dist/commands/compute/app/upgrade.js +0 -1063
- package/dist/commands/compute/app/upgrade.js.map +0 -1
- package/dist/commands/compute/environment/list.js +0 -89
- package/dist/commands/compute/environment/list.js.map +0 -1
- package/dist/commands/compute/environment/set.js +0 -215
- package/dist/commands/compute/environment/set.js.map +0 -1
- package/dist/commands/compute/environment/show.js +0 -96
- package/dist/commands/compute/environment/show.js.map +0 -1
- package/dist/commands/compute/undelegate.js +0 -250
- package/dist/commands/compute/undelegate.js.map +0 -1
- package/dist/commands/upgrade.js +0 -91
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/version.js +0 -65
- package/dist/commands/version.js.map +0 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/app/terminate.ts
|
|
4
|
+
import { Command, Args, Flags as Flags2 } from "@oclif/core";
|
|
5
|
+
|
|
6
|
+
// src/client.ts
|
|
7
|
+
import {
|
|
8
|
+
createAppModule,
|
|
9
|
+
createBillingModule,
|
|
10
|
+
getPrivateKeyInteractive as getPrivateKeyInteractive2,
|
|
11
|
+
getEnvironmentConfig,
|
|
12
|
+
requirePrivateKey,
|
|
13
|
+
getPrivateKeyWithSource
|
|
14
|
+
} from "@layr-labs/ecloud-sdk";
|
|
15
|
+
|
|
16
|
+
// src/flags.ts
|
|
17
|
+
import {
|
|
18
|
+
getEnvironmentInteractive,
|
|
19
|
+
getPrivateKeyInteractive,
|
|
20
|
+
getAvailableEnvironments
|
|
21
|
+
} from "@layr-labs/ecloud-sdk";
|
|
22
|
+
import { Flags } from "@oclif/core";
|
|
23
|
+
var getEnvironmentOptions = () => {
|
|
24
|
+
try {
|
|
25
|
+
return getAvailableEnvironments();
|
|
26
|
+
} catch {
|
|
27
|
+
return ["sepolia", "sepolia-dev", "mainnet-alpha"];
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var commonFlags = {
|
|
31
|
+
environment: Flags.string({
|
|
32
|
+
required: false,
|
|
33
|
+
description: "Deployment environment to use",
|
|
34
|
+
options: getEnvironmentOptions(),
|
|
35
|
+
env: "ECLOUD_ENV"
|
|
36
|
+
}),
|
|
37
|
+
"private-key": Flags.string({
|
|
38
|
+
required: false,
|
|
39
|
+
description: "Private key for signing transactions",
|
|
40
|
+
env: "ECLOUD_PRIVATE_KEY"
|
|
41
|
+
}),
|
|
42
|
+
"rpc-url": Flags.string({
|
|
43
|
+
required: false,
|
|
44
|
+
description: "RPC URL to connect to blockchain",
|
|
45
|
+
env: "ECLOUD_RPC_URL"
|
|
46
|
+
}),
|
|
47
|
+
verbose: Flags.boolean({
|
|
48
|
+
required: false,
|
|
49
|
+
description: "Enable verbose logging (default: false)",
|
|
50
|
+
default: false
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
async function validateCommonFlags(flags) {
|
|
54
|
+
flags["environment"] = await getEnvironmentInteractive(flags["environment"]);
|
|
55
|
+
flags["private-key"] = await getPrivateKeyInteractive(flags["private-key"]);
|
|
56
|
+
return flags;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/client.ts
|
|
60
|
+
async function createAppClient(flags) {
|
|
61
|
+
flags = await validateCommonFlags(flags);
|
|
62
|
+
const environment = flags.environment;
|
|
63
|
+
const environmentConfig = getEnvironmentConfig(environment);
|
|
64
|
+
const rpcUrl = flags["rpc-url"] || environmentConfig.defaultRPCURL;
|
|
65
|
+
const { key: privateKey, source } = await requirePrivateKey({
|
|
66
|
+
privateKey: flags["private-key"]
|
|
67
|
+
});
|
|
68
|
+
if (flags.verbose) {
|
|
69
|
+
console.log(`Using private key from: ${source}`);
|
|
70
|
+
}
|
|
71
|
+
return createAppModule({
|
|
72
|
+
verbose: flags.verbose,
|
|
73
|
+
privateKey,
|
|
74
|
+
rpcUrl,
|
|
75
|
+
environment
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/commands/app/terminate.ts
|
|
80
|
+
import { getEnvironmentConfig as getEnvironmentConfig2, getOrPromptAppID } from "@layr-labs/ecloud-sdk";
|
|
81
|
+
import chalk from "chalk";
|
|
82
|
+
var AppLifecycleTerminate = class _AppLifecycleTerminate extends Command {
|
|
83
|
+
static description = "Terminate app (terminate GCP instance) permanently";
|
|
84
|
+
static args = {
|
|
85
|
+
"app-id": Args.string({
|
|
86
|
+
description: "App ID or name to terminate",
|
|
87
|
+
required: false
|
|
88
|
+
})
|
|
89
|
+
};
|
|
90
|
+
static flags = {
|
|
91
|
+
...commonFlags,
|
|
92
|
+
force: Flags2.boolean({
|
|
93
|
+
required: false,
|
|
94
|
+
description: "Force termination without confirmation",
|
|
95
|
+
default: false
|
|
96
|
+
})
|
|
97
|
+
};
|
|
98
|
+
async run() {
|
|
99
|
+
const { args, flags } = await this.parse(_AppLifecycleTerminate);
|
|
100
|
+
const app = await createAppClient(flags);
|
|
101
|
+
const environment = flags.environment || "sepolia";
|
|
102
|
+
const environmentConfig = getEnvironmentConfig2(environment);
|
|
103
|
+
const rpcUrl = flags.rpcUrl || environmentConfig.defaultRPCURL;
|
|
104
|
+
const appId = await getOrPromptAppID(
|
|
105
|
+
{
|
|
106
|
+
appID: args["app-id"],
|
|
107
|
+
environment: flags["environment"],
|
|
108
|
+
privateKey: flags["private-key"],
|
|
109
|
+
rpcUrl,
|
|
110
|
+
action: "terminate"
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
const res = await app.terminate(appId, {
|
|
114
|
+
force: flags.force
|
|
115
|
+
});
|
|
116
|
+
if (!res.tx) {
|
|
117
|
+
this.log(`
|
|
118
|
+
${chalk.gray(`Termination aborted`)}`);
|
|
119
|
+
} else {
|
|
120
|
+
this.log(`
|
|
121
|
+
\u2705 ${chalk.green(`App terminated successfully`)}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
export {
|
|
126
|
+
AppLifecycleTerminate as default
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=terminate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/app/terminate.ts","../../../src/client.ts","../../../src/flags.ts"],"sourcesContent":["import { Command, Args, Flags } from \"@oclif/core\";\nimport { createAppClient } from \"../../client\";\nimport { commonFlags } from \"../../flags\";\nimport { getEnvironmentConfig, getOrPromptAppID } from \"@layr-labs/ecloud-sdk\";\nimport chalk from \"chalk\";\n\nexport default class AppLifecycleTerminate extends Command {\n static description =\n \"Terminate app (terminate GCP instance) permanently\";\n\n static args = {\n \"app-id\": Args.string({\n description: \"App ID or name to terminate\",\n required: false,\n }),\n };\n\n static flags = {\n ...commonFlags,\n force: Flags.boolean({\n required: false,\n description: \"Force termination without confirmation\",\n default: false,\n }),\n };\n\n async run() {\n const { args, flags } = await this.parse(AppLifecycleTerminate);\n const app = await createAppClient(flags);\n\n // Get environment config\n const environment = flags.environment || \"sepolia\";\n const environmentConfig = getEnvironmentConfig(environment);\n \n // Get RPC URL (needed for contract queries and authentication)\n const rpcUrl = flags.rpcUrl || environmentConfig.defaultRPCURL;\n \n // Resolve app ID (prompt if not provided)\n const appId = await getOrPromptAppID(\n {\n appID: args[\"app-id\"],\n environment: flags[\"environment\"]!,\n privateKey: flags[\"private-key\"],\n rpcUrl,\n action: \"terminate\",\n }\n );\n\n const res = await app.terminate(appId, {\n force: flags.force,\n });\n\n if (!res.tx) {\n this.log(`\\n${chalk.gray(`Termination aborted`)}`);\n } else {\n this.log(`\\n✅ ${chalk.green(`App terminated successfully`)}`);\n }\n }\n}\n\n","import {\n createAppModule,\n createBillingModule,\n getPrivateKeyInteractive,\n getEnvironmentConfig,\n requirePrivateKey,\n getPrivateKeyWithSource,\n} from \"@layr-labs/ecloud-sdk\";\nimport { CommonFlags, validateCommonFlags } from \"./flags\";\nimport { Hex } from \"viem\";\n\nexport async function createAppClient(flags: CommonFlags) {\n flags = await validateCommonFlags(flags);\n\n const environment = flags.environment!;\n const environmentConfig = getEnvironmentConfig(environment);\n const rpcUrl = flags[\"rpc-url\"] || environmentConfig.defaultRPCURL;\n const { key: privateKey, source } = await requirePrivateKey({\n privateKey: flags[\"private-key\"],\n });\n\n if (flags.verbose) {\n console.log(`Using private key from: ${source}`);\n }\n\n return createAppModule({\n verbose: flags.verbose,\n privateKey,\n rpcUrl,\n environment,\n });\n}\n\nexport async function createBillingClient(flags: {\n \"private-key\"?: string;\n verbose?: boolean;\n}) {\n const result = await getPrivateKeyWithSource({\n privateKey: flags[\"private-key\"],\n });\n const privateKey = await getPrivateKeyInteractive(result?.key);\n\n return createBillingModule({\n verbose: flags.verbose ?? false,\n privateKey: privateKey as Hex,\n });\n}\n","import {\n getEnvironmentInteractive,\n getPrivateKeyInteractive,\n getAvailableEnvironments,\n} from \"@layr-labs/ecloud-sdk\";\nimport { Flags } from \"@oclif/core\";\n\nexport type CommonFlags = {\n verbose: boolean;\n environment?: string;\n \"private-key\"?: string;\n \"rpc-url\"?: string;\n};\n\n// Get available environments dynamically from SDK based on build type\nconst getEnvironmentOptions = (): string[] => {\n try {\n return getAvailableEnvironments();\n } catch {\n // Fallback to all environments if SDK not available\n return [\"sepolia\", \"sepolia-dev\", \"mainnet-alpha\"];\n }\n};\n\nexport const commonFlags = {\n environment: Flags.string({\n required: false,\n description: \"Deployment environment to use\",\n options: getEnvironmentOptions(),\n env: \"ECLOUD_ENV\",\n }),\n \"private-key\": Flags.string({\n required: false,\n description: \"Private key for signing transactions\",\n env: \"ECLOUD_PRIVATE_KEY\",\n }),\n \"rpc-url\": Flags.string({\n required: false,\n description: \"RPC URL to connect to blockchain\",\n env: \"ECLOUD_RPC_URL\",\n }),\n verbose: Flags.boolean({\n required: false,\n description: \"Enable verbose logging (default: false)\",\n default: false,\n }),\n};\n\n// Validate or prompt for required common flags\nexport async function validateCommonFlags(flags: CommonFlags) {\n flags[\"environment\"] = await getEnvironmentInteractive(flags[\"environment\"]);\n flags[\"private-key\"] = await getPrivateKeyInteractive(flags[\"private-key\"]);\n\n return flags;\n}\n"],"mappings":"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","names":["Flags","getPrivateKeyInteractive","getEnvironmentConfig","Flags"]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/app/upgrade.ts
|
|
4
|
+
import { Command, Args, Flags as Flags2 } from "@oclif/core";
|
|
5
|
+
|
|
6
|
+
// src/client.ts
|
|
7
|
+
import {
|
|
8
|
+
createAppModule,
|
|
9
|
+
createBillingModule,
|
|
10
|
+
getPrivateKeyInteractive as getPrivateKeyInteractive2,
|
|
11
|
+
getEnvironmentConfig,
|
|
12
|
+
requirePrivateKey,
|
|
13
|
+
getPrivateKeyWithSource
|
|
14
|
+
} from "@layr-labs/ecloud-sdk";
|
|
15
|
+
|
|
16
|
+
// src/flags.ts
|
|
17
|
+
import {
|
|
18
|
+
getEnvironmentInteractive,
|
|
19
|
+
getPrivateKeyInteractive,
|
|
20
|
+
getAvailableEnvironments
|
|
21
|
+
} from "@layr-labs/ecloud-sdk";
|
|
22
|
+
import { Flags } from "@oclif/core";
|
|
23
|
+
var getEnvironmentOptions = () => {
|
|
24
|
+
try {
|
|
25
|
+
return getAvailableEnvironments();
|
|
26
|
+
} catch {
|
|
27
|
+
return ["sepolia", "sepolia-dev", "mainnet-alpha"];
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var commonFlags = {
|
|
31
|
+
environment: Flags.string({
|
|
32
|
+
required: false,
|
|
33
|
+
description: "Deployment environment to use",
|
|
34
|
+
options: getEnvironmentOptions(),
|
|
35
|
+
env: "ECLOUD_ENV"
|
|
36
|
+
}),
|
|
37
|
+
"private-key": Flags.string({
|
|
38
|
+
required: false,
|
|
39
|
+
description: "Private key for signing transactions",
|
|
40
|
+
env: "ECLOUD_PRIVATE_KEY"
|
|
41
|
+
}),
|
|
42
|
+
"rpc-url": Flags.string({
|
|
43
|
+
required: false,
|
|
44
|
+
description: "RPC URL to connect to blockchain",
|
|
45
|
+
env: "ECLOUD_RPC_URL"
|
|
46
|
+
}),
|
|
47
|
+
verbose: Flags.boolean({
|
|
48
|
+
required: false,
|
|
49
|
+
description: "Enable verbose logging (default: false)",
|
|
50
|
+
default: false
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
async function validateCommonFlags(flags) {
|
|
54
|
+
flags["environment"] = await getEnvironmentInteractive(flags["environment"]);
|
|
55
|
+
flags["private-key"] = await getPrivateKeyInteractive(flags["private-key"]);
|
|
56
|
+
return flags;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/client.ts
|
|
60
|
+
async function createAppClient(flags) {
|
|
61
|
+
flags = await validateCommonFlags(flags);
|
|
62
|
+
const environment = flags.environment;
|
|
63
|
+
const environmentConfig = getEnvironmentConfig(environment);
|
|
64
|
+
const rpcUrl = flags["rpc-url"] || environmentConfig.defaultRPCURL;
|
|
65
|
+
const { key: privateKey, source } = await requirePrivateKey({
|
|
66
|
+
privateKey: flags["private-key"]
|
|
67
|
+
});
|
|
68
|
+
if (flags.verbose) {
|
|
69
|
+
console.log(`Using private key from: ${source}`);
|
|
70
|
+
}
|
|
71
|
+
return createAppModule({
|
|
72
|
+
verbose: flags.verbose,
|
|
73
|
+
privateKey,
|
|
74
|
+
rpcUrl,
|
|
75
|
+
environment
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/commands/app/upgrade.ts
|
|
80
|
+
import chalk from "chalk";
|
|
81
|
+
var AppUpgrade = class _AppUpgrade extends Command {
|
|
82
|
+
static description = "Upgrade existing deployment";
|
|
83
|
+
static args = {
|
|
84
|
+
"app-id": Args.string({
|
|
85
|
+
description: "App ID or name to upgrade",
|
|
86
|
+
required: false
|
|
87
|
+
})
|
|
88
|
+
};
|
|
89
|
+
static flags = {
|
|
90
|
+
...commonFlags,
|
|
91
|
+
dockerfile: Flags2.string({
|
|
92
|
+
required: false,
|
|
93
|
+
description: "Path to Dockerfile",
|
|
94
|
+
env: "ECLOUD_DOCKERFILE_PATH"
|
|
95
|
+
}),
|
|
96
|
+
"image-ref": Flags2.string({
|
|
97
|
+
required: false,
|
|
98
|
+
description: "Image reference pointing to registry",
|
|
99
|
+
env: "ECLOUD_IMAGE_REF"
|
|
100
|
+
}),
|
|
101
|
+
"env-file": Flags2.string({
|
|
102
|
+
required: false,
|
|
103
|
+
description: 'Environment file to use (default: ".env")',
|
|
104
|
+
default: ".env",
|
|
105
|
+
env: "ECLOUD_ENVFILE_PATH"
|
|
106
|
+
}),
|
|
107
|
+
"log-visibility": Flags2.string({
|
|
108
|
+
required: false,
|
|
109
|
+
description: "Log visibility setting: public, private, or off",
|
|
110
|
+
options: ["public", "private", "off"],
|
|
111
|
+
env: "ECLOUD_LOG_VISIBILITY"
|
|
112
|
+
}),
|
|
113
|
+
"instance-type": Flags2.string({
|
|
114
|
+
required: false,
|
|
115
|
+
description: "Machine instance type to use e.g. g1-standard-4t, g1-standard-8t",
|
|
116
|
+
env: "ECLOUD_INSTANCE_TYPE"
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
async run() {
|
|
120
|
+
const { args, flags } = await this.parse(_AppUpgrade);
|
|
121
|
+
const app = await createAppClient(flags);
|
|
122
|
+
const res = await app.upgrade(args["app-id"], {
|
|
123
|
+
dockerfile: flags.dockerfile,
|
|
124
|
+
envFile: flags["env-file"],
|
|
125
|
+
imageRef: flags["image-ref"],
|
|
126
|
+
logVisibility: flags["log-visibility"],
|
|
127
|
+
instanceType: flags["instance-type"]
|
|
128
|
+
});
|
|
129
|
+
if (!res.tx) {
|
|
130
|
+
this.log(`
|
|
131
|
+
${chalk.gray(`Upgrade failed`)}`);
|
|
132
|
+
} else {
|
|
133
|
+
this.log(`
|
|
134
|
+
\u2705 ${chalk.green(`App upgraded successfully ${chalk.bold(`(id: ${res.appID}, image: ${res.imageRef})`)}`)}`);
|
|
135
|
+
}
|
|
136
|
+
this.log(JSON.stringify(res, null, 2));
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
export {
|
|
140
|
+
AppUpgrade as default
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/app/upgrade.ts","../../../src/client.ts","../../../src/flags.ts"],"sourcesContent":["import { Command, Args, Flags } from \"@oclif/core\";\nimport { logVisibility } from \"@layr-labs/ecloud-sdk\";\nimport { createAppClient } from \"../../client\";\nimport { commonFlags } from \"../../flags\";\nimport chalk from \"chalk\";\n\nexport default class AppUpgrade extends Command {\n static description = \"Upgrade existing deployment\";\n\n static args = {\n \"app-id\": Args.string({\n description: \"App ID or name to upgrade\",\n required: false,\n }),\n };\n\n static flags = {\n ...commonFlags,\n dockerfile: Flags.string({\n required: false,\n description: \"Path to Dockerfile\",\n env: \"ECLOUD_DOCKERFILE_PATH\",\n }),\n \"image-ref\": Flags.string({\n required: false,\n description: \"Image reference pointing to registry\",\n env: \"ECLOUD_IMAGE_REF\",\n }),\n \"env-file\": Flags.string({\n required: false,\n description: 'Environment file to use (default: \".env\")',\n default: \".env\",\n env: \"ECLOUD_ENVFILE_PATH\",\n }),\n \"log-visibility\": Flags.string({\n required: false,\n description: \"Log visibility setting: public, private, or off\",\n options: [\"public\", \"private\", \"off\"],\n env: \"ECLOUD_LOG_VISIBILITY\",\n }),\n \"instance-type\": Flags.string({\n required: false,\n description:\n \"Machine instance type to use e.g. g1-standard-4t, g1-standard-8t\",\n env: \"ECLOUD_INSTANCE_TYPE\",\n }),\n };\n\n async run() {\n const { args, flags } = await this.parse(AppUpgrade);\n const app = await createAppClient(flags);\n\n const res = await app.upgrade(args[\"app-id\"] as any, {\n dockerfile: flags.dockerfile,\n envFile: flags[\"env-file\"],\n imageRef: flags[\"image-ref\"],\n logVisibility: flags[\"log-visibility\"] as logVisibility,\n instanceType: flags[\"instance-type\"],\n });\n\n if (!res.tx) {\n this.log(`\\n${chalk.gray(`Upgrade failed`)}`);\n } else {\n this.log(`\\n✅ ${chalk.green(`App upgraded successfully ${chalk.bold(`(id: ${res.appID}, image: ${res.imageRef})`)}`)}`);\n }\n this.log(JSON.stringify(res, null, 2));\n }\n}\n\n","import {\n createAppModule,\n createBillingModule,\n getPrivateKeyInteractive,\n getEnvironmentConfig,\n requirePrivateKey,\n getPrivateKeyWithSource,\n} from \"@layr-labs/ecloud-sdk\";\nimport { CommonFlags, validateCommonFlags } from \"./flags\";\nimport { Hex } from \"viem\";\n\nexport async function createAppClient(flags: CommonFlags) {\n flags = await validateCommonFlags(flags);\n\n const environment = flags.environment!;\n const environmentConfig = getEnvironmentConfig(environment);\n const rpcUrl = flags[\"rpc-url\"] || environmentConfig.defaultRPCURL;\n const { key: privateKey, source } = await requirePrivateKey({\n privateKey: flags[\"private-key\"],\n });\n\n if (flags.verbose) {\n console.log(`Using private key from: ${source}`);\n }\n\n return createAppModule({\n verbose: flags.verbose,\n privateKey,\n rpcUrl,\n environment,\n });\n}\n\nexport async function createBillingClient(flags: {\n \"private-key\"?: string;\n verbose?: boolean;\n}) {\n const result = await getPrivateKeyWithSource({\n privateKey: flags[\"private-key\"],\n });\n const privateKey = await getPrivateKeyInteractive(result?.key);\n\n return createBillingModule({\n verbose: flags.verbose ?? false,\n privateKey: privateKey as Hex,\n });\n}\n","import {\n getEnvironmentInteractive,\n getPrivateKeyInteractive,\n getAvailableEnvironments,\n} from \"@layr-labs/ecloud-sdk\";\nimport { Flags } from \"@oclif/core\";\n\nexport type CommonFlags = {\n verbose: boolean;\n environment?: string;\n \"private-key\"?: string;\n \"rpc-url\"?: string;\n};\n\n// Get available environments dynamically from SDK based on build type\nconst getEnvironmentOptions = (): string[] => {\n try {\n return getAvailableEnvironments();\n } catch {\n // Fallback to all environments if SDK not available\n return [\"sepolia\", \"sepolia-dev\", \"mainnet-alpha\"];\n }\n};\n\nexport const commonFlags = {\n environment: Flags.string({\n required: false,\n description: \"Deployment environment to use\",\n options: getEnvironmentOptions(),\n env: \"ECLOUD_ENV\",\n }),\n \"private-key\": Flags.string({\n required: false,\n description: \"Private key for signing transactions\",\n env: \"ECLOUD_PRIVATE_KEY\",\n }),\n \"rpc-url\": Flags.string({\n required: false,\n description: \"RPC URL to connect to blockchain\",\n env: \"ECLOUD_RPC_URL\",\n }),\n verbose: Flags.boolean({\n required: false,\n description: \"Enable verbose logging (default: false)\",\n default: false,\n }),\n};\n\n// Validate or prompt for required common flags\nexport async function validateCommonFlags(flags: CommonFlags) {\n flags[\"environment\"] = await getEnvironmentInteractive(flags[\"environment\"]);\n flags[\"private-key\"] = await getPrivateKeyInteractive(flags[\"private-key\"]);\n\n return flags;\n}\n"],"mappings":"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","names":["Flags","getPrivateKeyInteractive","Flags"]}
|
|
@@ -3,121 +3,13 @@
|
|
|
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
|
-
|
|
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
|
|
6
|
+
import {
|
|
7
|
+
generateNewPrivateKey,
|
|
8
|
+
storePrivateKey,
|
|
9
|
+
keyExists,
|
|
10
|
+
showPrivateKey,
|
|
11
|
+
displayWarning
|
|
12
|
+
} from "@layr-labs/ecloud-sdk";
|
|
121
13
|
var AuthGenerate = class _AuthGenerate extends Command {
|
|
122
14
|
static description = "Generate a new private key";
|
|
123
15
|
static aliases = ["auth:gen", "auth:new"];
|
|
@@ -191,7 +83,9 @@ Press 'q' to exit and continue...
|
|
|
191
83
|
this.log(`
|
|
192
84
|
\u2713 Private key stored in OS keyring`);
|
|
193
85
|
this.log(`\u2713 Address: ${address}`);
|
|
194
|
-
this.log(
|
|
86
|
+
this.log(
|
|
87
|
+
"\nYou can now use ecloud commands without --private-key flag."
|
|
88
|
+
);
|
|
195
89
|
} catch (err) {
|
|
196
90
|
this.error(`Failed to store key: ${err.message}`);
|
|
197
91
|
}
|
|
@@ -1 +1 @@
|
|
|
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":[]}
|
|
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":[]}
|
|
@@ -2,40 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
// src/commands/auth/login.ts
|
|
4
4
|
import { Command } from "@oclif/core";
|
|
5
|
-
import { confirm, select
|
|
5
|
+
import { confirm, select } from "@inquirer/prompts";
|
|
6
6
|
import {
|
|
7
7
|
storePrivateKey,
|
|
8
8
|
keyExists,
|
|
9
|
+
getHiddenInput,
|
|
9
10
|
validatePrivateKey,
|
|
10
11
|
getAddressFromPrivateKey,
|
|
12
|
+
displayWarning,
|
|
11
13
|
getLegacyKeys,
|
|
12
14
|
getLegacyPrivateKey,
|
|
13
15
|
deleteLegacyPrivateKey
|
|
14
16
|
} 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
|
|
39
17
|
var AuthLogin = class extends Command {
|
|
40
18
|
static description = "Store your private key in OS keyring";
|
|
41
19
|
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
@@ -77,16 +55,23 @@ var AuthLogin = class extends Command {
|
|
|
77
55
|
name: `${key.address} (${key.environment} - ${key.source})`,
|
|
78
56
|
value: key
|
|
79
57
|
}));
|
|
80
|
-
selectedKey = await
|
|
58
|
+
selectedKey = await select({
|
|
81
59
|
message: "Select a key to import:",
|
|
82
60
|
choices
|
|
83
61
|
});
|
|
84
|
-
privateKey = await getLegacyPrivateKey(
|
|
62
|
+
privateKey = await getLegacyPrivateKey(
|
|
63
|
+
selectedKey.environment,
|
|
64
|
+
selectedKey.source
|
|
65
|
+
);
|
|
85
66
|
if (!privateKey) {
|
|
86
|
-
this.error(
|
|
67
|
+
this.error(
|
|
68
|
+
`Failed to retrieve legacy key for ${selectedKey.environment}`
|
|
69
|
+
);
|
|
87
70
|
}
|
|
88
|
-
this.log(
|
|
89
|
-
|
|
71
|
+
this.log(
|
|
72
|
+
`
|
|
73
|
+
Importing key from ${selectedKey.source}:${selectedKey.environment}`
|
|
74
|
+
);
|
|
90
75
|
}
|
|
91
76
|
}
|
|
92
77
|
if (!privateKey) {
|
|
@@ -111,7 +96,9 @@ Address: ${address}`);
|
|
|
111
96
|
await storePrivateKey(privateKey);
|
|
112
97
|
this.log("\n\u2713 Private key stored in OS keyring");
|
|
113
98
|
this.log(`\u2713 Address: ${address}`);
|
|
114
|
-
this.log(
|
|
99
|
+
this.log(
|
|
100
|
+
"\nNote: This key will be used for all environments (mainnet, sepolia, etc.)"
|
|
101
|
+
);
|
|
115
102
|
this.log("You can now use ecloud commands without --private-key flag.");
|
|
116
103
|
if (selectedKey) {
|
|
117
104
|
this.log("");
|
|
@@ -120,14 +107,21 @@ Address: ${address}`);
|
|
|
120
107
|
default: false
|
|
121
108
|
});
|
|
122
109
|
if (confirmDelete) {
|
|
123
|
-
const deleted = await deleteLegacyPrivateKey(
|
|
110
|
+
const deleted = await deleteLegacyPrivateKey(
|
|
111
|
+
selectedKey.environment,
|
|
112
|
+
selectedKey.source
|
|
113
|
+
);
|
|
124
114
|
if (deleted) {
|
|
125
115
|
this.log(
|
|
126
116
|
`
|
|
127
117
|
\u2713 Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`
|
|
128
118
|
);
|
|
129
|
-
this.log(
|
|
130
|
-
|
|
119
|
+
this.log(
|
|
120
|
+
"\nNote: The key is now only stored in ecloud. You can still use it with"
|
|
121
|
+
);
|
|
122
|
+
this.log(
|
|
123
|
+
"eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var."
|
|
124
|
+
);
|
|
131
125
|
} else {
|
|
132
126
|
this.log(
|
|
133
127
|
`
|
|
@@ -136,9 +130,13 @@ Address: ${address}`);
|
|
|
136
130
|
this.log("The key may have already been removed.");
|
|
137
131
|
}
|
|
138
132
|
} else {
|
|
139
|
-
this.log(
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
this.log(
|
|
134
|
+
`
|
|
135
|
+
Legacy key kept in ${selectedKey.source}:${selectedKey.environment}`
|
|
136
|
+
);
|
|
137
|
+
this.log(
|
|
138
|
+
"You can delete it later using 'eigenx auth logout' if needed."
|
|
139
|
+
);
|
|
142
140
|
}
|
|
143
141
|
}
|
|
144
142
|
} catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/auth/login.ts","../../../src/utils/security.ts"],"sourcesContent":["/**\n * Auth Login Command\n *\n * Store an existing private key in OS keyring\n */\n\nimport { Command } from \"@oclif/core\";\nimport { confirm, select } from \"@inquirer/prompts\";\nimport {\n storePrivateKey,\n keyExists,\n validatePrivateKey,\n getAddressFromPrivateKey,\n getLegacyKeys,\n getLegacyPrivateKey,\n deleteLegacyPrivateKey,\n type LegacyKey,\n} from \"@layr-labs/ecloud-sdk\";\nimport { getHiddenInput, displayWarning } from \"../../utils/security\";\n\nexport default class AuthLogin extends Command {\n static description = \"Store your private key in OS keyring\";\n\n static examples = [\"<%= config.bin %> <%= command.id %>\"];\n\n async run(): Promise<void> {\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 \"Replacing it will cause PERMANENT DATA LOSS if not backed up.\",\n \"The previous key will be lost forever.\",\n ]);\n\n const confirmReplace = await confirm({\n message: \"Replace existing key?\",\n default: false,\n });\n\n if (!confirmReplace) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n }\n\n // Check for legacy keys from eigenx-cli\n const legacyKeys = await getLegacyKeys();\n let privateKey: string | null = null;\n let selectedKey: LegacyKey | null = null;\n\n if (legacyKeys.length > 0) {\n this.log(\"\\nFound legacy keys from eigenx-cli:\");\n this.log(\"\");\n\n // Display legacy keys\n for (const key of legacyKeys) {\n this.log(` Address: ${key.address}`);\n this.log(` Environment: ${key.environment}`);\n this.log(` Source: ${key.source}`);\n this.log(\"\");\n }\n\n const importLegacy = await confirm({\n message: \"Would you like to import one of these legacy keys?\",\n default: false,\n });\n\n if (importLegacy) {\n // Create choices for selection\n const choices = legacyKeys.map((key) => ({\n name: `${key.address} (${key.environment} - ${key.source})`,\n value: key,\n }));\n\n selectedKey = await select<LegacyKey>({\n message: \"Select a key to import:\",\n choices,\n });\n\n // Retrieve the actual private key\n privateKey = await getLegacyPrivateKey(selectedKey.environment, selectedKey.source);\n\n if (!privateKey) {\n this.error(`Failed to retrieve legacy key for ${selectedKey.environment}`);\n }\n\n this.log(`\\nImporting key from ${selectedKey.source}:${selectedKey.environment}`);\n }\n }\n\n // If no legacy key was selected, prompt for private key input\n if (!privateKey) {\n privateKey = await getHiddenInput(\"Enter your private key:\");\n\n privateKey = privateKey.trim();\n }\n\n if (!validatePrivateKey(privateKey)) {\n this.error(\"Invalid private key format. Please check and try again.\");\n }\n\n // Derive address for confirmation\n const address = getAddressFromPrivateKey(privateKey);\n\n this.log(`\\nAddress: ${address}`);\n\n const confirmStore = await confirm({\n message: \"Store this key in OS keyring?\",\n default: true,\n });\n\n if (!confirmStore) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n\n // Store in keyring\n try {\n await storePrivateKey(privateKey);\n this.log(\"\\n✓ Private key stored in OS keyring\");\n this.log(`✓ Address: ${address}`);\n this.log(\"\\nNote: This key will be used for all environments (mainnet, sepolia, etc.)\");\n this.log(\"You can now use ecloud commands without --private-key flag.\");\n\n // Ask if user wants to delete the legacy key (only if save was successful)\n if (selectedKey) {\n this.log(\"\");\n const confirmDelete = await confirm({\n message: `Delete the legacy key from ${selectedKey.source}:${selectedKey.environment}?`,\n default: false,\n });\n\n if (confirmDelete) {\n const deleted = await deleteLegacyPrivateKey(selectedKey.environment, selectedKey.source);\n\n if (deleted) {\n this.log(\n `\\n✓ Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`,\n );\n this.log(\"\\nNote: The key is now only stored in ecloud. You can still use it with\");\n this.log(\"eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.\");\n } else {\n this.log(\n `\\n⚠️ Failed to delete legacy key from ${selectedKey.source}:${selectedKey.environment}`,\n );\n this.log(\"The key may have already been removed.\");\n }\n } else {\n this.log(`\\nLegacy key kept in ${selectedKey.source}:${selectedKey.environment}`);\n this.log(\"You can delete it later using 'eigenx auth logout' if needed.\");\n }\n }\n } catch (err: any) {\n this.error(`Failed to store key: ${err.message}`);\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":["select","select"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/auth/login.ts"],"sourcesContent":["/**\n * Auth Login Command\n *\n * Store an existing private key in OS keyring\n */\n\nimport { Command } from \"@oclif/core\";\nimport { confirm, select } from \"@inquirer/prompts\";\nimport {\n storePrivateKey,\n keyExists,\n getHiddenInput,\n validatePrivateKey,\n getAddressFromPrivateKey,\n displayWarning,\n getLegacyKeys,\n getLegacyPrivateKey,\n deleteLegacyPrivateKey,\n type LegacyKey,\n} from \"@layr-labs/ecloud-sdk\";\n\nexport default class AuthLogin extends Command {\n static description = \"Store your private key in OS keyring\";\n\n static examples = [\"<%= config.bin %> <%= command.id %>\"];\n\n async run(): Promise<void> {\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 \"Replacing it will cause PERMANENT DATA LOSS if not backed up.\",\n \"The previous key will be lost forever.\",\n ]);\n\n const confirmReplace = await confirm({\n message: \"Replace existing key?\",\n default: false,\n });\n\n if (!confirmReplace) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n }\n\n // Check for legacy keys from eigenx-cli\n const legacyKeys = await getLegacyKeys();\n let privateKey: string | null = null;\n let selectedKey: LegacyKey | null = null;\n\n if (legacyKeys.length > 0) {\n this.log(\"\\nFound legacy keys from eigenx-cli:\");\n this.log(\"\");\n\n // Display legacy keys\n for (const key of legacyKeys) {\n this.log(` Address: ${key.address}`);\n this.log(` Environment: ${key.environment}`);\n this.log(` Source: ${key.source}`);\n this.log(\"\");\n }\n\n const importLegacy = await confirm({\n message: \"Would you like to import one of these legacy keys?\",\n default: false,\n });\n\n if (importLegacy) {\n // Create choices for selection\n const choices = legacyKeys.map((key) => ({\n name: `${key.address} (${key.environment} - ${key.source})`,\n value: key,\n }));\n\n selectedKey = await select<LegacyKey>({\n message: \"Select a key to import:\",\n choices,\n });\n\n // Retrieve the actual private key\n privateKey = await getLegacyPrivateKey(\n selectedKey.environment,\n selectedKey.source\n );\n\n if (!privateKey) {\n this.error(\n `Failed to retrieve legacy key for ${selectedKey.environment}`\n );\n }\n\n this.log(\n `\\nImporting key from ${selectedKey.source}:${selectedKey.environment}`\n );\n }\n }\n\n // If no legacy key was selected, prompt for private key input\n if (!privateKey) {\n privateKey = await getHiddenInput(\"Enter your private key:\");\n\n privateKey = privateKey.trim();\n }\n\n if (!validatePrivateKey(privateKey)) {\n this.error(\"Invalid private key format. Please check and try again.\");\n }\n\n // Derive address for confirmation\n const address = getAddressFromPrivateKey(privateKey);\n\n this.log(`\\nAddress: ${address}`);\n\n const confirmStore = await confirm({\n message: \"Store this key in OS keyring?\",\n default: true,\n });\n\n if (!confirmStore) {\n this.log(\"\\nLogin cancelled.\");\n return;\n }\n\n // Store in keyring\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 \"\\nNote: This key will be used for all environments (mainnet, sepolia, etc.)\"\n );\n this.log(\"You can now use ecloud commands without --private-key flag.\");\n\n // Ask if user wants to delete the legacy key (only if save was successful)\n if (selectedKey) {\n this.log(\"\");\n const confirmDelete = await confirm({\n message: `Delete the legacy key from ${selectedKey.source}:${selectedKey.environment}?`,\n default: false,\n });\n\n if (confirmDelete) {\n const deleted = await deleteLegacyPrivateKey(\n selectedKey.environment,\n selectedKey.source\n );\n\n if (deleted) {\n this.log(\n `\\n✓ Legacy key deleted from ${selectedKey.source}:${selectedKey.environment}`\n );\n this.log(\n \"\\nNote: The key is now only stored in ecloud. You can still use it with\"\n );\n this.log(\n \"eigenx-cli by providing --private-key flag or EIGENX_PRIVATE_KEY env var.\"\n );\n } else {\n this.log(\n `\\n⚠️ Failed to delete legacy key from ${selectedKey.source}:${selectedKey.environment}`\n );\n this.log(\"The key may have already been removed.\");\n }\n } else {\n this.log(\n `\\nLegacy key kept in ${selectedKey.source}:${selectedKey.environment}`\n );\n this.log(\n \"You can delete it later using 'eigenx auth logout' if needed.\"\n );\n }\n }\n } catch (err: any) {\n this.error(`Failed to store key: ${err.message}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
|