Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
},
"devDependencies": {
"@types/form-data": "^2.5.2",
"@types/js-yaml": "^4.0.9",
"@types/node": "^18",
"ts-node": "^10.9.2"
}
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/commands/compute/app/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
confirm,
getPrivateKeyInteractive,
} from "../../../utils/prompts";
import { setAppName } from "../../../utils/appNames";
import chalk from "chalk";

export default class AppDeploy extends Command {
Expand Down Expand Up @@ -169,6 +170,14 @@ export default class AppDeploy extends Command {
logger,
);

// 11. Save the app name mapping locally
try {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be doing this at all anymore (need to sync with go cli). can leave for now, but planning to remove soon

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yea i forgot about it. i can update it. can get it in this PR only

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually we don't even have a profile command here yet. let me add that in that PR. I will merge this

await setAppName(environment, res.appId, appName);
logger.info(`App saved with name: ${appName}`);
} catch (err: any) {
logger.warn(`Failed to save app name: ${err.message}`);
}

this.log(
`\n✅ ${chalk.green(`App deployed successfully ${chalk.bold(`(id: ${res.appId}, ip: ${res.ipAddress})`)}`)}`,
);
Expand Down
7 changes: 2 additions & 5 deletions packages/cli/src/commands/compute/environment/list.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { Command } from "@oclif/core";
import {
getAvailableEnvironments,
getEnvironmentConfig,
getDefaultEnvironment,
} from "@layr-labs/ecloud-sdk";
import { getAvailableEnvironments, getEnvironmentConfig } from "@layr-labs/ecloud-sdk";
import { getDefaultEnvironment } from "../../../utils/globalConfig";
import chalk from "chalk";

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/compute/environment/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
getEnvironmentConfig,
getAvailableEnvironments,
isEnvironmentAvailable,
setDefaultEnvironment,
} from "@layr-labs/ecloud-sdk";
import { setDefaultEnvironment } from "../../../utils/globalConfig";
import { getEnvironmentInteractive } from "../../../utils/prompts";
import { confirm } from "@inquirer/prompts";

Expand Down
7 changes: 2 additions & 5 deletions packages/cli/src/commands/compute/environment/show.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { Command } from "@oclif/core";
import {
getDefaultEnvironment,
getEnvironmentConfig,
getAvailableEnvironments,
} from "@layr-labs/ecloud-sdk";
import { getEnvironmentConfig, getAvailableEnvironments } from "@layr-labs/ecloud-sdk";
import { getDefaultEnvironment } from "../../../utils/globalConfig";
import chalk from "chalk";

export default class EnvironmentShow extends Command {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/flags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getDefaultEnvironment } from "@layr-labs/ecloud-sdk";
import { Flags } from "@oclif/core";
import { getEnvironmentInteractive, getPrivateKeyInteractive } from "./utils/prompts";
import { getDefaultEnvironment } from "./utils/globalConfig";

export type CommonFlags = {
verbose: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ function saveAppRegistry(environment: string, registry: AppRegistry): void {
}

/**
* Resolve app ID or name to app ID
* Resolve app ID or name to app ID (for CLI use)
*/
function resolveAppID(environment: string, appIDOrName: string): string | null {
export function resolveAppIDFromRegistry(environment: string, appIDOrName: string): string | null {
// First check if it's already a valid hex address
if (/^0x[a-fA-F0-9]{40}$/.test(appIDOrName)) {
return appIDOrName;
Expand Down Expand Up @@ -117,7 +117,7 @@ export async function setAppName(
const registry = loadAppRegistry(environment);

// Resolve the target app ID
let targetAppID: string | null = resolveAppID(environment, appIDOrName);
let targetAppID: string | null = resolveAppIDFromRegistry(environment, appIDOrName);
if (!targetAppID) {
// If can't resolve, check if it's a valid app ID
if (/^0x[a-fA-F0-9]{40}$/.test(appIDOrName)) {
Expand Down Expand Up @@ -187,3 +187,34 @@ export function listApps(environment: string): Record<string, string> {

return result;
}

/**
* Check if an app name is available in the given environment
*/
export function isAppNameAvailable(environment: string, name: string): boolean {
const apps = listApps(environment);
return !apps[name];
}

/**
* Find an available app name by appending numbers if needed
*/
export function findAvailableName(environment: string, baseName: string): string {
const apps = listApps(environment);

// Check if base name is available
if (!apps[baseName]) {
return baseName;
}

// Try with incrementing numbers
for (let i = 2; i <= 100; i++) {
const candidate = `${baseName}-${i}`;
if (!apps[candidate]) {
return candidate;
}
}

// Fallback to timestamp if somehow we have 100+ duplicates
return `${baseName}-${Date.now()}`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { load as loadYaml, dump as dumpYaml } from "js-yaml";
import { getBuildType } from "./environment";
import { getBuildType } from "@layr-labs/ecloud-sdk";

const GLOBAL_CONFIG_FILE = "config.yaml";

Expand Down
10 changes: 2 additions & 8 deletions packages/cli/src/utils/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,19 @@ import {
getEnvironmentConfig,
getAvailableEnvironments,
isEnvironmentAvailable,
listApps,
getAllAppsByDeveloper,
getDefaultEnvironment,
getCategoryDescriptions,
fetchTemplateCatalog,
PRIMARY_LANGUAGES,
AppProfile,
} from "@layr-labs/ecloud-sdk";

// Re-export helper functions from SDK for use in validation
import {
validateAppName,
validateImageReference,
validateFilePath,
validatePrivateKeyFormat,
extractAppNameFromImage,
isAppNameAvailable,
findAvailableName,
} from "@layr-labs/ecloud-sdk";
import { getDefaultEnvironment } from "./globalConfig";
import { listApps, isAppNameAvailable, findAvailableName } from "./appNames";

// Helper to add hex prefix
function addHexPrefix(value: string): `0x${string}` {
Expand Down
7 changes: 1 addition & 6 deletions packages/sdk/src/client/common/contract/caller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { addHexPrefix, getChainFromID } from "../utils";

import { EnvironmentConfig, Logger } from "../types";
import { Release } from "../types";
import { getAppName } from "../registry/appNames";

import AppControllerABI from "../abis/AppController.json";
import PermissionControllerABI from "../abis/PermissionController.json";
Expand Down Expand Up @@ -526,11 +525,7 @@ export async function executeUpgradeBatch(
gas: { maxFeePerGas?: bigint; maxPriorityFeePerGas?: bigint } | undefined,
logger: Logger,
): Promise<Hex> {
const appName = getAppName(prepared.environmentConfig.name, prepared.appId);
let pendingMessage = "Upgrading app...";
if (appName !== "") {
pendingMessage = `Upgrading app '${appName}'...`;
}
const pendingMessage = `Upgrading app ${prepared.appId}...`;

const txHash = await executeBatch(
{
Expand Down
77 changes: 19 additions & 58 deletions packages/sdk/src/client/common/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import fs from "fs";
import path from "path";
import { Address, isAddress } from "viem";
import { stripHexPrefix, addHexPrefix } from "./helpers";
import { listApps } from "../registry/appNames";

// ==================== App Name Validation ====================

Expand All @@ -29,37 +28,6 @@ export function validateAppName(name: string): void {
}
}

/**
* Check if an app name is available in the given environment
*/
export function isAppNameAvailable(environment: string, name: string): boolean {
const apps = listApps(environment);
return !apps[name];
}

/**
* Find an available app name by appending numbers if needed
*/
export function findAvailableName(environment: string, baseName: string): string {
const apps = listApps(environment);

// Check if base name is available
if (!apps[baseName]) {
return baseName;
}

// Try with incrementing numbers
for (let i = 2; i <= 100; i++) {
const candidate = `${baseName}-${i}`;
if (!apps[candidate]) {
return candidate;
}
}

// Fallback to timestamp if somehow we have 100+ duplicates
return `${baseName}-${Date.now()}`;
}

// ==================== Image Reference Validation ====================

/**
Expand Down Expand Up @@ -312,38 +280,31 @@ export function validateImagePath(filePath: string): string | undefined {
return undefined;
}

// ==================== App ID Resolution ====================
// ==================== App ID Validation ====================

/**
* Resolve app ID from name or address
* @param appIDOrName - App ID (address) or app name
* @param environment - Environment name
* @returns Resolved app address
* @throws Error if app ID cannot be resolved
* Validate and normalize app ID address
* @param appID - App ID (must be a valid address)
* @returns Normalized app address
* @throws Error if app ID is not a valid address
*
* Note: Name resolution should be handled by CLI before calling SDK functions.
* The SDK only accepts resolved addresses.
*/
export function resolveAppID(appIDOrName: string | Address, environment: string): Address {
if (!appIDOrName) {
throw new Error("App ID or name is required");
export function validateAppID(appID: string | Address): Address {
if (!appID) {
throw new Error("App ID is required");
}

// Normalize the input
const normalized = typeof appIDOrName === "string" ? addHexPrefix(appIDOrName) : appIDOrName;
const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;

// Check if it's a valid address
if (isAddress(normalized)) {
return normalized as Address;
}

// If not a valid address, treat as app name and look it up
const apps = listApps(environment);
const foundAppID = apps[appIDOrName as string];
if (foundAppID) {
// Ensure it has 0x prefix
return addHexPrefix(foundAppID) as Address;
}

// Name not found
throw new Error(`App name '${appIDOrName}' not found in environment '${environment}'`);
throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
}

// ==================== Log Visibility Validation ====================
Expand Down Expand Up @@ -517,13 +478,13 @@ export interface UpgradeParams {
* Validate upgrade parameters
* @throws Error if required parameters are missing or invalid
*/
export function validateUpgradeParams(params: Partial<UpgradeParams>, environment: string): void {
export function validateUpgradeParams(params: Partial<UpgradeParams>): void {
// App ID is required
if (!params.appID) {
throw new Error("App ID is required for upgrade");
}
// Validate app ID can be resolved (throws if not)
resolveAppID(params.appID, environment);
// Validate app ID is a valid address (throws if not)
validateAppID(params.appID);

// Must have either dockerfilePath or imageRef
if (!params.dockerfilePath && !params.imageRef) {
Expand Down Expand Up @@ -595,10 +556,10 @@ export interface LogsParams {
* Validate logs parameters
* @throws Error if required parameters are missing or invalid
*/
export function validateLogsParams(params: Partial<LogsParams>, environment: string): void {
export function validateLogsParams(params: Partial<LogsParams>): void {
if (!params.appID) {
throw new Error("App ID is required for viewing logs");
}
// Validate app ID can be resolved (throws if not)
resolveAppID(params.appID, environment);
// Validate app ID is a valid address (throws if not)
validateAppID(params.appID);
}
16 changes: 0 additions & 16 deletions packages/sdk/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,6 @@ export {
} from "./common/config/environment";
export { isSubscriptionActive } from "./common/utils/billing";

// Export global config functions
export {
loadGlobalConfig,
saveGlobalConfig,
getDefaultEnvironment,
setDefaultEnvironment,
isFirstRun,
markFirstRunComplete,
getGlobalTelemetryPreference,
setGlobalTelemetryPreference,
type GlobalConfig,
} from "./common/config/globalConfig";

// Export auth utilities
export * from "./common/auth";

Expand All @@ -84,9 +71,6 @@ export {
getCategoryDescriptions,
} from "./common/templates/catalog";

// Export registry utilities
export { listApps, getAppName, setAppName } from "./common/registry/appNames";

// Export contract utilities
export {
getAllAppsByDeveloper,
Expand Down
23 changes: 2 additions & 21 deletions packages/sdk/src/client/modules/app/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
} from "../../common/utils/validation";
import { doPreflightChecks, PreflightContext } from "../../common/utils/preflight";
import { UserApiClient } from "../../common/utils/userapi";
import { setAppName } from "../../common/registry/appNames";
import { defaultLogger } from "../../common/utils";

/**
Expand Down Expand Up @@ -196,7 +195,6 @@ export async function deploy(
const appName = options.appName;
const envFilePath = options.envFilePath || "";
const instanceType = options.instanceType;
const environment = preflightCtx.environmentConfig.name;

// 5. Generate random salt
const salt = generateRandomSalt();
Expand Down Expand Up @@ -269,15 +267,7 @@ export async function deploy(
}
}

// 10. Save the app name mapping
try {
await setAppName(environment, deployResult.appId, appName);
logger.info(`App saved with name: ${appName}`);
} catch (err: any) {
logger.warn(`Failed to save app name: ${err.message}`);
}

// 11. Watch until app is running
// 10. Watch until app is running
logger.info("Waiting for app to start...");
const ipAddress = await watchUntilRunning(
{
Expand Down Expand Up @@ -498,16 +488,7 @@ export async function executeDeploy(
}
}

// 3. Save the app name mapping
const environment = prepared.preflightCtx.environmentConfig.name;
try {
await setAppName(environment, appId, prepared.appName);
logger.info(`App saved with name: ${prepared.appName}`);
} catch (err: any) {
logger.warn(`Failed to save app name: ${err.message}`);
}

// 4. Watch until app is running
// 3. Watch until app is running
logger.info("Waiting for app to start...");
const ipAddress = await watchUntilRunning(
{
Expand Down
Loading