Skip to content
Open
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
24 changes: 4 additions & 20 deletions cli/src/api/cvms.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
safeGetCvmList,
safeGetCvmInfo,
safeGetCvmComposeFile,
type Client,
type CvmInfoDetailV20260121,
} from "@phala/cloud";
Expand All @@ -20,7 +19,6 @@ import type {
GetCvmNetworkResponse,
TeepodResponse,
PubkeyResponse,
CvmComposeConfigResponse,
UpgradeResponse,
} from "./types";
import inquirer from "inquirer";
Expand All @@ -45,22 +43,6 @@ export async function getCvmByAppId(
return result.data;
}

/**
* Get CVM compose configuration
*/
export async function getCvmComposeConfig(
cvmId: string,
): Promise<CvmComposeConfigResponse> {
const client = await getClient();
const result = await safeGetCvmComposeFile(client, { id: cvmId });

if (!result.success) {
throw new Error(result.error.message);
}

return result.data as CvmComposeConfigResponse;
}

/**
* Get CVM network information
* @param appId App ID (with or without app_ prefix)
Expand Down Expand Up @@ -168,11 +150,13 @@ export interface ResizeCvmPayload {
}

/**
* Replicate a CVM
* Replicate a CVM instance
* @param appId App ID (with or without app_ prefix)
* @param vmUuid Source CVM UUID (with or without dashes)
*/
export async function replicateCvm(
appId: string,
vmUuid: string,
payload: {
teepod_id?: number;
encrypted_env?: string;
Expand All @@ -181,7 +165,7 @@ export async function replicateCvm(
const client = await getClient();
const cleanAppId = appId.replace(/^app_/, "");
const response = await client.post<ReplicateCvmResponse>(
`cvms/app_${cleanAppId}/replicas`,
`apps/${cleanAppId}/cvms/${vmUuid}/replicas`,
payload,
);
return replicateCvmResponseSchema.parse(response);
Expand Down
28 changes: 11 additions & 17 deletions cli/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,22 +148,22 @@ export const replicateCvmResponseSchema = z.object({
id: z.number(),
name: z.string(),
}),
user_id: z.number().nullable(),
user_id: z.number().nullable().optional(),
app_id: z.string(),
vm_uuid: z.string().nullable(),
instance_id: z.string().nullable(),
app_url: z.string().nullable(),
base_image: z.string().nullable(),
vm_uuid: z.string().nullable().optional(),
instance_id: z.string().nullable().optional(),
app_url: z.string().nullable().optional(),
base_image: z.string().nullable().optional(),
vcpu: z.number(),
memory: z.number(),
disk_size: z.number(),
manifest_version: z.number().nullable(),
version: z.string().nullable(),
runner: z.string().nullable(),
docker_compose_file: z.string().nullable(),
features: z.array(z.string()).nullable(),
manifest_version: z.number().nullable().optional(),
version: z.string().nullable().optional(),
runner: z.string().nullable().optional(),
docker_compose_file: z.string().nullable().optional(),
features: z.array(z.string()).nullable().optional(),
created_at: z.string(),
encrypted_env_pubkey: z.string().nullable(),
encrypted_env_pubkey: z.string().nullable().optional(),
});

export type ReplicateCvmResponse = z.infer<typeof replicateCvmResponseSchema>;
Expand Down Expand Up @@ -309,9 +309,3 @@ export interface AvailableNodesResponse {
nodes: TEEPod[];
kms_list?: KmsListItem[];
}

// CVM Compose Config Response
export interface CvmComposeConfigResponse {
env_pubkey: string;
[key: string]: unknown;
}
47 changes: 4 additions & 43 deletions cli/src/commands/allow-devices/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import chalk from "chalk";
import inquirer from "inquirer";
import {
type Chain,
type PublicClient,
type WalletClient,
createPublicClient,
createWalletClient,
http,
} from "viem";
import { privateKeyToAccount, nonceManager } from "viem/accounts";
import {
safeGetCvmInfo,
safeGetAppDeviceAllowlist,
Expand Down Expand Up @@ -163,24 +154,6 @@ function resolvePrivateKey(input: { privateKey?: string }): `0x${string}` {
return (key.startsWith("0x") ? key : `0x${key}`) as `0x${string}`;
}

function createSharedClients(
chain: Chain,
privateKey: `0x${string}`,
rpcUrl?: string,
) {
const account = privateKeyToAccount(privateKey, { nonceManager });
const publicClient = createPublicClient({
chain,
transport: http(rpcUrl),
}) as unknown as PublicClient;
const walletClient = createWalletClient({
account,
chain,
transport: http(rpcUrl),
}) as unknown as WalletClient;
return { publicClient, walletClient };
}

async function resolveDeviceIdOrNodeName(
deviceInput: string,
): Promise<`0x${string}`> {
Expand Down Expand Up @@ -429,12 +402,6 @@ async function runAdd(
return 1;
}

const { publicClient, walletClient } = createSharedClients(
chain,
privateKey,
input.rpcUrl,
);

const results: {
deviceId: string;
txHash: string;
Expand All @@ -444,10 +411,10 @@ async function runAdd(
for (const deviceId of deviceIds) {
const result = await safeAddDevice({
chain,
rpcUrl: input.rpcUrl,
appAddress: appContractAddress,
deviceId,
walletClient,
publicClient,
privateKey,
skipPrerequisiteChecks: true,
});

Expand Down Expand Up @@ -574,12 +541,6 @@ async function runRemove(
return 1;
}

const { publicClient, walletClient } = createSharedClients(
chain,
privateKey,
input.rpcUrl,
);

const results: {
deviceId: string;
txHash: string;
Expand All @@ -589,10 +550,10 @@ async function runRemove(
for (const deviceId of deviceIds) {
const result = await safeRemoveDevice({
chain,
rpcUrl: input.rpcUrl,
appAddress: appContractAddress,
deviceId,
walletClient,
publicClient,
privateKey,
skipPrerequisiteChecks: true,
});

Expand Down
79 changes: 74 additions & 5 deletions cli/src/commands/cvms/replicate/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ export const cvmsReplicateCommandMeta: CommandMeta = {
arguments: [cvmIdArgument],
options: [
{
name: "teepod-id",
description: "TEEPod ID for replica",
name: "node-id",
description: "Node ID for replica",
type: "string",
target: "teepodId",
target: "nodeId",
},
{
name: "compose-hash",
description:
"Explicit compose hash to replicate. Required when the source app has multiple live instances",
type: "string",
target: "composeHash",
},
{
name: "env-file",
Expand All @@ -21,20 +28,82 @@ export const cvmsReplicateCommandMeta: CommandMeta = {
type: "string",
target: "envFile",
},
{
name: "private-key",
description: "Private key for signing transactions.",
type: "string",
target: "privateKey",
group: "advanced",
},
{
name: "rpc-url",
description: "RPC URL for the blockchain.",
type: "string",
target: "rpcUrl",
group: "advanced",
},
{
name: "prepare-only",
description:
"Only prepare the replica (generate commit token) without performing on-chain operations.",
type: "boolean",
target: "prepareOnly",
group: "advanced",
},
{
name: "commit",
description:
"Commit a previously prepared replica using a commit token. Requires --token and --compose-hash.",
type: "boolean",
target: "commit",
group: "advanced",
},
{
name: "token",
description: "Commit token from a prepare-only replica request",
type: "string",
target: "token",
group: "advanced",
},
{
name: "transaction-hash",
description:
"Transaction hash proving on-chain compose hash registration. Use already-registered for state-only verification.",
type: "string",
target: "transactionHash",
group: "advanced",
},
interactiveOption,
],
examples: [
{
name: "Replicate a CVM",
value: "phala cvms replicate 1234 --teepod-id 5",
value: "phala cvms replicate 1234 --node-id 5",
},
{
name: "Prepare a replica for multisig approval",
value:
"phala cvms replicate 1234 --node-id 5 --compose-hash <hash> --prepare-only",
},
{
name: "Commit a prepared replica",
value:
"phala cvms replicate 1234 --commit --token <token> --compose-hash <hash> --transaction-hash <tx-hash>",
},
],
};

export const cvmsReplicateCommandSchema = z.object({
cvmId: z.string().optional(),
teepodId: z.string().optional(),
nodeId: z.string().optional(),
composeHash: z.string().optional(),
envFile: z.string().optional(),
privateKey: z.string().optional(),
rpcUrl: z.string().optional(),
prepareOnly: z.boolean().default(false),
commit: z.boolean().default(false),
token: z.string().optional(),
transactionHash: z.string().optional(),
interactive: z.boolean().default(false),
});

Expand Down
Loading
Loading