@layr-labs/ecloud-cli 0.1.0-rc.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/VERSION +2 -2
- package/dist/commands/auth/generate.js +184 -46
- package/dist/commands/auth/generate.js.map +1 -1
- package/dist/commands/auth/login.js +234 -93
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.js +170 -30
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/migrate.js +216 -76
- package/dist/commands/auth/migrate.js.map +1 -1
- package/dist/commands/auth/whoami.js +145 -17
- package/dist/commands/auth/whoami.js.map +1 -1
- package/dist/commands/billing/cancel.js +164 -30
- package/dist/commands/billing/cancel.js.map +1 -1
- package/dist/commands/billing/status.js +213 -80
- package/dist/commands/billing/status.js.map +1 -1
- package/dist/commands/billing/subscribe.js +179 -45
- package/dist/commands/billing/subscribe.js.map +1 -1
- package/dist/commands/compute/app/create.js +148 -20
- package/dist/commands/compute/app/create.js.map +1 -1
- package/dist/commands/compute/app/deploy.js +244 -146
- package/dist/commands/compute/app/deploy.js.map +1 -1
- package/dist/commands/compute/app/info.js +2 -1
- package/dist/commands/compute/app/info.js.map +1 -1
- package/dist/commands/compute/app/list.js +194 -111
- package/dist/commands/compute/app/list.js.map +1 -1
- package/dist/commands/compute/app/logs.js +105 -20
- package/dist/commands/compute/app/logs.js.map +1 -1
- package/dist/commands/compute/app/profile/set.js +153 -64
- package/dist/commands/compute/app/profile/set.js.map +1 -1
- package/dist/commands/compute/app/start.js +132 -43
- package/dist/commands/compute/app/start.js.map +1 -1
- package/dist/commands/compute/app/stop.js +132 -43
- package/dist/commands/compute/app/stop.js.map +1 -1
- package/dist/commands/compute/app/terminate.js +131 -44
- package/dist/commands/compute/app/terminate.js.map +1 -1
- package/dist/commands/compute/app/upgrade.js +210 -109
- package/dist/commands/compute/app/upgrade.js.map +1 -1
- package/dist/commands/compute/environment/list.js +104 -12
- package/dist/commands/compute/environment/list.js.map +1 -1
- package/dist/commands/compute/environment/set.js +103 -18
- package/dist/commands/compute/environment/set.js.map +1 -1
- package/dist/commands/compute/environment/show.js +122 -30
- package/dist/commands/compute/environment/show.js.map +1 -1
- package/dist/commands/compute/undelegate.js +113 -13
- package/dist/commands/compute/undelegate.js.map +1 -1
- package/dist/commands/telemetry.js +213 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/commands/upgrade.js +159 -19
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/version.js +163 -23
- package/dist/commands/version.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@ import { Command, Flags as Flags2 } from "@oclif/core";
|
|
|
5
5
|
|
|
6
6
|
// src/client.ts
|
|
7
7
|
import {
|
|
8
|
-
|
|
8
|
+
createComputeModule,
|
|
9
9
|
createBillingModule,
|
|
10
10
|
getEnvironmentConfig as getEnvironmentConfig2,
|
|
11
11
|
requirePrivateKey,
|
|
@@ -52,7 +52,75 @@ import * as path from "path";
|
|
|
52
52
|
import * as os from "os";
|
|
53
53
|
import { load as loadYaml, dump as dumpYaml } from "js-yaml";
|
|
54
54
|
import { getBuildType } from "@layr-labs/ecloud-sdk";
|
|
55
|
+
import * as crypto from "crypto";
|
|
56
|
+
var GLOBAL_CONFIG_FILE = "config.yaml";
|
|
55
57
|
var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
58
|
+
function getGlobalConfigDir() {
|
|
59
|
+
const configHome = process.env.XDG_CONFIG_HOME;
|
|
60
|
+
let baseDir;
|
|
61
|
+
if (configHome && path.isAbsolute(configHome)) {
|
|
62
|
+
baseDir = configHome;
|
|
63
|
+
} else {
|
|
64
|
+
baseDir = path.join(os.homedir(), ".config");
|
|
65
|
+
}
|
|
66
|
+
const buildType = getBuildType();
|
|
67
|
+
const buildSuffix = buildType === "dev" ? "-dev" : "";
|
|
68
|
+
const configDirName = `ecloud${buildSuffix}`;
|
|
69
|
+
return path.join(baseDir, configDirName);
|
|
70
|
+
}
|
|
71
|
+
function getGlobalConfigPath() {
|
|
72
|
+
return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE);
|
|
73
|
+
}
|
|
74
|
+
function loadGlobalConfig() {
|
|
75
|
+
const configPath = getGlobalConfigPath();
|
|
76
|
+
if (!fs.existsSync(configPath)) {
|
|
77
|
+
return {
|
|
78
|
+
first_run: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
83
|
+
const config = loadYaml(content);
|
|
84
|
+
return config || { first_run: true };
|
|
85
|
+
} catch {
|
|
86
|
+
return {
|
|
87
|
+
first_run: true
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function saveGlobalConfig(config) {
|
|
92
|
+
const configPath = getGlobalConfigPath();
|
|
93
|
+
const configDir = path.dirname(configPath);
|
|
94
|
+
fs.mkdirSync(configDir, { recursive: true, mode: 493 });
|
|
95
|
+
const content = dumpYaml(config, { lineWidth: -1 });
|
|
96
|
+
fs.writeFileSync(configPath, content, { mode: 420 });
|
|
97
|
+
}
|
|
98
|
+
function getDefaultEnvironment() {
|
|
99
|
+
const config = loadGlobalConfig();
|
|
100
|
+
return config.default_environment;
|
|
101
|
+
}
|
|
102
|
+
function getGlobalTelemetryPreference() {
|
|
103
|
+
const config = loadGlobalConfig();
|
|
104
|
+
return config.telemetry_enabled;
|
|
105
|
+
}
|
|
106
|
+
function getOrCreateUserUUID() {
|
|
107
|
+
const config = loadGlobalConfig();
|
|
108
|
+
if (config.user_uuid) {
|
|
109
|
+
return config.user_uuid;
|
|
110
|
+
}
|
|
111
|
+
const uuid = generateUUID();
|
|
112
|
+
config.user_uuid = uuid;
|
|
113
|
+
config.first_run = false;
|
|
114
|
+
saveGlobalConfig(config);
|
|
115
|
+
return uuid;
|
|
116
|
+
}
|
|
117
|
+
function generateUUID() {
|
|
118
|
+
const bytes = crypto.randomBytes(16);
|
|
119
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
120
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
121
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
122
|
+
return hex.slice(0, 4).join("") + hex.slice(4, 6).join("") + "-" + hex.slice(6, 8).join("") + "-" + hex.slice(8, 10).join("") + "-" + hex.slice(10, 12).join("") + "-" + hex.slice(12, 16).join("");
|
|
123
|
+
}
|
|
56
124
|
|
|
57
125
|
// src/utils/appNames.ts
|
|
58
126
|
import * as fs2 from "fs";
|
|
@@ -124,12 +192,74 @@ async function createBillingClient(flags) {
|
|
|
124
192
|
const privateKey = await getPrivateKeyInteractive(result?.key);
|
|
125
193
|
return createBillingModule({
|
|
126
194
|
verbose: flags.verbose ?? false,
|
|
127
|
-
privateKey
|
|
195
|
+
privateKey,
|
|
196
|
+
skipTelemetry: true
|
|
197
|
+
// CLI already has telemetry, skip SDK telemetry
|
|
128
198
|
});
|
|
129
199
|
}
|
|
130
200
|
|
|
131
201
|
// src/commands/billing/status.ts
|
|
132
202
|
import chalk from "chalk";
|
|
203
|
+
|
|
204
|
+
// src/telemetry.ts
|
|
205
|
+
import {
|
|
206
|
+
createTelemetryClient,
|
|
207
|
+
createAppEnvironment,
|
|
208
|
+
createMetricsContext,
|
|
209
|
+
addMetric,
|
|
210
|
+
addMetricWithDimensions,
|
|
211
|
+
emitMetrics,
|
|
212
|
+
getBuildType as getBuildType2
|
|
213
|
+
} from "@layr-labs/ecloud-sdk";
|
|
214
|
+
function createCLITelemetryClient() {
|
|
215
|
+
const userUUID = getOrCreateUserUUID();
|
|
216
|
+
const environment = createAppEnvironment(userUUID);
|
|
217
|
+
const telemetryEnabled = getGlobalTelemetryPreference();
|
|
218
|
+
return createTelemetryClient(environment, "ecloud-cli", {
|
|
219
|
+
telemetryEnabled: telemetryEnabled === true
|
|
220
|
+
// Only enabled if explicitly set to true
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async function withTelemetry(command, action) {
|
|
224
|
+
const client = createCLITelemetryClient();
|
|
225
|
+
const metrics = createMetricsContext();
|
|
226
|
+
metrics.properties["source"] = "ecloud-cli";
|
|
227
|
+
metrics.properties["command"] = command.id || command.constructor.name;
|
|
228
|
+
const environment = getDefaultEnvironment() || "sepolia";
|
|
229
|
+
metrics.properties["environment"] = environment;
|
|
230
|
+
const buildType = getBuildType2() || "prod";
|
|
231
|
+
metrics.properties["build_type"] = buildType;
|
|
232
|
+
const cliVersion = command.config.version;
|
|
233
|
+
if (cliVersion) {
|
|
234
|
+
metrics.properties["cli_version"] = cliVersion;
|
|
235
|
+
}
|
|
236
|
+
addMetric(metrics, "Count", 1);
|
|
237
|
+
let actionError;
|
|
238
|
+
let result;
|
|
239
|
+
try {
|
|
240
|
+
result = await action();
|
|
241
|
+
return result;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
actionError = err instanceof Error ? err : new Error(String(err));
|
|
244
|
+
throw err;
|
|
245
|
+
} finally {
|
|
246
|
+
const resultValue = actionError ? "Failure" : "Success";
|
|
247
|
+
const dimensions = {};
|
|
248
|
+
if (actionError) {
|
|
249
|
+
dimensions["error"] = actionError.message;
|
|
250
|
+
}
|
|
251
|
+
addMetricWithDimensions(metrics, resultValue, 1, dimensions);
|
|
252
|
+
const duration = Date.now() - metrics.startTime.getTime();
|
|
253
|
+
addMetric(metrics, "DurationMilliseconds", duration);
|
|
254
|
+
try {
|
|
255
|
+
await emitMetrics(client, metrics);
|
|
256
|
+
await client.close();
|
|
257
|
+
} catch {
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/commands/billing/status.ts
|
|
133
263
|
var BillingStatus = class _BillingStatus extends Command {
|
|
134
264
|
static description = "Show subscription status";
|
|
135
265
|
static flags = {
|
|
@@ -144,92 +274,95 @@ var BillingStatus = class _BillingStatus extends Command {
|
|
|
144
274
|
})
|
|
145
275
|
};
|
|
146
276
|
async run() {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
${chalk.bold("Subscription Status:")}`);
|
|
179
|
-
this.log(` Status: ${formatStatus(result.subscriptionStatus)}`);
|
|
180
|
-
this.log(` Product: ${result.productId}`);
|
|
181
|
-
if (result.currentPeriodStart && result.currentPeriodEnd) {
|
|
182
|
-
const startDate = new Date(result.currentPeriodStart).toLocaleDateString();
|
|
183
|
-
const endDate = new Date(result.currentPeriodEnd).toLocaleDateString();
|
|
184
|
-
this.log(` Current Period: ${startDate} - ${endDate}`);
|
|
185
|
-
}
|
|
186
|
-
if (result.lineItems && result.lineItems.length > 0) {
|
|
277
|
+
return withTelemetry(this, async () => {
|
|
278
|
+
const { flags } = await this.parse(_BillingStatus);
|
|
279
|
+
const billing = await createBillingClient(flags);
|
|
280
|
+
const result = await billing.getStatus({
|
|
281
|
+
productId: flags.product
|
|
282
|
+
});
|
|
283
|
+
const formatExpiry = (timestamp) => timestamp ? ` (expires ${new Date(timestamp * 1e3).toLocaleDateString()})` : "";
|
|
284
|
+
const formatStatus = (status) => {
|
|
285
|
+
switch (status) {
|
|
286
|
+
case "active":
|
|
287
|
+
return `${chalk.green("\u2713 Active")}`;
|
|
288
|
+
case "trialing":
|
|
289
|
+
return `${chalk.green("\u2713 Trial")}`;
|
|
290
|
+
case "past_due":
|
|
291
|
+
return `${chalk.yellow("\u26A0 Past Due")}`;
|
|
292
|
+
case "canceled":
|
|
293
|
+
return `${chalk.red("\u2717 Canceled")}`;
|
|
294
|
+
case "inactive":
|
|
295
|
+
return `${chalk.gray("\u2717 Inactive")}`;
|
|
296
|
+
case "incomplete":
|
|
297
|
+
return `${chalk.yellow("\u26A0 Incomplete")}`;
|
|
298
|
+
case "incomplete_expired":
|
|
299
|
+
return `${chalk.red("\u2717 Expired")}`;
|
|
300
|
+
case "unpaid":
|
|
301
|
+
return `${chalk.yellow("\u26A0 Unpaid")}`;
|
|
302
|
+
case "paused":
|
|
303
|
+
return `${chalk.yellow("\u26A0 Paused")}`;
|
|
304
|
+
default:
|
|
305
|
+
return status;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
187
308
|
this.log(`
|
|
309
|
+
${chalk.bold("Subscription Status:")}`);
|
|
310
|
+
this.log(` Status: ${formatStatus(result.subscriptionStatus)}`);
|
|
311
|
+
this.log(` Product: ${result.productId}`);
|
|
312
|
+
if (result.currentPeriodStart && result.currentPeriodEnd) {
|
|
313
|
+
const startDate = new Date(result.currentPeriodStart).toLocaleDateString();
|
|
314
|
+
const endDate = new Date(result.currentPeriodEnd).toLocaleDateString();
|
|
315
|
+
this.log(` Current Period: ${startDate} - ${endDate}`);
|
|
316
|
+
}
|
|
317
|
+
if (result.lineItems && result.lineItems.length > 0) {
|
|
318
|
+
this.log(`
|
|
188
319
|
${chalk.bold(" Line Items:")}`);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
320
|
+
for (const item of result.lineItems) {
|
|
321
|
+
const product = `${flags.product.charAt(0).toUpperCase()}${flags.product.slice(1)}`;
|
|
322
|
+
const chain = item.description.toLowerCase().includes("sepolia") ? "Sepolia" : "Mainnet";
|
|
323
|
+
this.log(
|
|
324
|
+
` \u2022 ${product} (${chain}): $${item.subtotal.toFixed(2)} (${item.quantity} vCPU hours \xD7 $${item.price.toFixed(3)}/vCPU hour)`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
195
327
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
this.log(`
|
|
328
|
+
if (result.creditsApplied !== void 0 && result.creditsApplied > 0) {
|
|
329
|
+
this.log(`
|
|
199
330
|
${chalk.bold(" Invoice Summary:")}`);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
331
|
+
const subtotal = result.upcomingInvoiceSubtotal ?? result.upcomingInvoiceTotal ?? 0;
|
|
332
|
+
this.log(` Subtotal: $${subtotal.toFixed(2)}`);
|
|
333
|
+
this.log(` Credits Applied: ${chalk.green(`-$${result.creditsApplied.toFixed(2)}`)}`);
|
|
334
|
+
this.log(` ${"\u2500".repeat(21)}`);
|
|
335
|
+
this.log(` Total Due: $${(result.upcomingInvoiceTotal ?? 0).toFixed(2)}`);
|
|
336
|
+
if (result.remainingCredits !== void 0) {
|
|
337
|
+
this.log(
|
|
338
|
+
`
|
|
208
339
|
${chalk.bold("Remaining Credits:")} ${chalk.cyan(`$${result.remainingCredits.toFixed(2)}`)}${formatExpiry(result.nextCreditExpiry)}`
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
} else if (result.upcomingInvoiceTotal !== void 0) {
|
|
343
|
+
this.log(`
|
|
213
344
|
Upcoming Invoice: $${result.upcomingInvoiceTotal.toFixed(2)}`);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
345
|
+
if (result.remainingCredits !== void 0 && result.remainingCredits > 0) {
|
|
346
|
+
this.log(
|
|
347
|
+
` ${chalk.bold("Available Credits:")} ${chalk.cyan(`$${result.remainingCredits.toFixed(2)}`)}${formatExpiry(result.nextCreditExpiry)}`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
218
350
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.log(`
|
|
351
|
+
if (result.cancelAtPeriodEnd) {
|
|
352
|
+
this.log(`
|
|
222
353
|
${chalk.yellow("\u26A0 Subscription will cancel at period end")}`);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
354
|
+
}
|
|
355
|
+
if (result.canceledAt) {
|
|
356
|
+
const cancelDate = new Date(result.canceledAt).toLocaleDateString();
|
|
357
|
+
this.log(` Canceled On: ${cancelDate}`);
|
|
358
|
+
}
|
|
359
|
+
if (result.portalUrl) {
|
|
360
|
+
this.log(`
|
|
361
|
+
${chalk.bold("Payment & Invoices:")}`);
|
|
362
|
+
this.log(` ${chalk.cyan(result.portalUrl)}`);
|
|
363
|
+
}
|
|
364
|
+
this.log();
|
|
365
|
+
});
|
|
233
366
|
}
|
|
234
367
|
};
|
|
235
368
|
export {
|