UNPKG

@phala/cloud

Version:
1,445 lines (1,425 loc) 98.2 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AddComposeHashSchema: () => AddComposeHashSchema, ApiErrorSchema: () => ApiErrorSchema, AvailableNodesSchema: () => AvailableNodesSchema, CommitCvmComposeFileUpdateRequestSchema: () => CommitCvmComposeFileUpdateRequestSchema, CommitCvmComposeFileUpdateSchema: () => CommitCvmComposeFileUpdateSchema, CommitCvmProvisionRequestSchema: () => CommitCvmProvisionRequestSchema, CommitCvmProvisionSchema: () => CommitCvmProvisionSchema, CurrentUserSchema: () => CurrentUserSchema, CvmInfoSchema: () => CvmInfoSchema, CvmLegacyDetailSchema: () => CvmLegacyDetailSchema, CvmNetworkUrlsSchema: () => CvmNetworkUrlsSchema, CvmNodeSchema: () => CvmNodeSchema, DeployAppAuthRequestSchema: () => DeployAppAuthRequestSchema, DeployAppAuthSchema: () => DeployAppAuthSchema, GetAppEnvEncryptPubKeySchema: () => GetAppEnvEncryptPubKeySchema, GetCvmListSchema: () => GetCvmListSchema, GetKmsListSchema: () => GetKmsListSchema, InstanceTypeSchema: () => InstanceTypeSchema, KmsInfoSchema: () => KmsInfoSchema, ListWorkspacesSchema: () => ListWorkspacesSchema, ManagedUserSchema: () => ManagedUserSchema, NetworkError: () => NetworkError, PaginatedInstanceTypesSchema: () => PaginatedInstanceTypesSchema, PaginationMetadataSchema: () => PaginationMetadataSchema, ProvisionCvmComposeFileUpdateRequestSchema: () => ProvisionCvmComposeFileUpdateRequestSchema, ProvisionCvmComposeFileUpdateResultSchema: () => ProvisionCvmComposeFileUpdateResultSchema, ProvisionCvmRequestSchema: () => ProvisionCvmRequestSchema, ProvisionCvmSchema: () => ProvisionCvmSchema, RequestError: () => RequestError, SUPPORTED_CHAINS: () => SUPPORTED_CHAINS, TransactionError: () => TransactionError, VmInfoSchema: () => VmInfoSchema, WalletError: () => WalletError, WorkspaceResponseSchema: () => WorkspaceResponseSchema, addComposeHash: () => addComposeHash, addNetwork: () => addNetwork, asHex: () => asHex, autoCreateClients: () => autoCreateClients, checkBalance: () => checkBalance, checkNetworkStatus: () => checkNetworkStatus, commitCvmComposeFileUpdate: () => commitCvmComposeFileUpdate, commitCvmProvision: () => commitCvmProvision, createClient: () => createClient, createClientsFromBrowser: () => createClientsFromBrowser, createClientsFromPrivateKey: () => createClientsFromPrivateKey, createNetworkClients: () => createNetworkClients, createTransactionTracker: () => createTransactionTracker, deployAppAuth: () => deployAppAuth, encryptEnvVars: () => import_encrypt_env_vars2.encryptEnvVars, estimateTransactionGas: () => estimateTransactionGas, executeBatchTransactions: () => executeBatchTransactions, executeTransaction: () => executeTransaction, executeTransactionWithRetry: () => executeTransactionWithRetry, extractNetworkClients: () => extractNetworkClients, getAppEnvEncryptPubKey: () => getAppEnvEncryptPubKey, getAvailableNodes: () => getAvailableNodes, getComposeHash: () => import_get_compose_hash2.getComposeHash, getCurrentUser: () => getCurrentUser, getCvmComposeFile: () => getCvmComposeFile, getCvmInfo: () => getCvmInfo, getCvmList: () => getCvmList, getErrorMessage: () => getErrorMessage, getKmsInfo: () => getKmsInfo, getKmsList: () => getKmsList, getWorkspace: () => getWorkspace, listInstanceTypes: () => listInstanceTypes, listWorkspaces: () => listWorkspaces, parseEnv: () => parseEnv, parseEnvVars: () => parseEnvVars, provisionCvm: () => provisionCvm, provisionCvmComposeFileUpdate: () => provisionCvmComposeFileUpdate, safeAddComposeHash: () => safeAddComposeHash, safeCommitCvmComposeFileUpdate: () => safeCommitCvmComposeFileUpdate, safeCommitCvmProvision: () => safeCommitCvmProvision, safeDeployAppAuth: () => safeDeployAppAuth, safeGetAppEnvEncryptPubKey: () => safeGetAppEnvEncryptPubKey, safeGetAvailableNodes: () => safeGetAvailableNodes, safeGetCurrentUser: () => safeGetCurrentUser, safeGetCvmComposeFile: () => safeGetCvmComposeFile, safeGetCvmInfo: () => safeGetCvmInfo, safeGetCvmList: () => safeGetCvmList, safeGetKmsInfo: () => safeGetKmsInfo, safeGetKmsList: () => safeGetKmsList, safeGetWorkspace: () => safeGetWorkspace, safeListInstanceTypes: () => safeListInstanceTypes, safeListWorkspaces: () => safeListWorkspaces, safeProvisionCvm: () => safeProvisionCvm, safeProvisionCvmComposeFileUpdate: () => safeProvisionCvmComposeFileUpdate, safeValidateActionParameters: () => safeValidateActionParameters, switchToNetwork: () => switchToNetwork, validateActionParameters: () => validateActionParameters, validateNetworkPrerequisites: () => validateNetworkPrerequisites, verifyEnvEncryptPublicKey: () => import_verify_env_encrypt_public_key.verifyEnvEncryptPublicKey, waitForTransactionReceipt: () => waitForTransactionReceipt }); module.exports = __toCommonJS(index_exports); // src/client.ts var import_ofetch = require("ofetch"); var import_debug = __toESM(require("debug")); // src/types/client.ts var import_zod = require("zod"); var ApiErrorSchema = import_zod.z.object({ detail: import_zod.z.union([ import_zod.z.string(), import_zod.z.array( import_zod.z.object({ msg: import_zod.z.string(), type: import_zod.z.string().optional(), ctx: import_zod.z.record(import_zod.z.unknown()).optional() }) ), import_zod.z.record(import_zod.z.unknown()) ]), type: import_zod.z.string().optional(), code: import_zod.z.string().optional() }); var RequestError = class _RequestError extends Error { constructor(message, options) { super(message); this.name = "RequestError"; this.isRequestError = true; this.status = options?.status; this.statusText = options?.statusText; this.data = options?.data; this.request = options?.request; this.response = options?.response; this.detail = options?.detail || message; this.code = options?.code; this.type = options?.type; } /** * Create RequestError from FetchError */ static fromFetchError(error) { const parseResult = ApiErrorSchema.safeParse(error.data); if (parseResult.success) { return new _RequestError(error.message, { status: error.status ?? void 0, statusText: error.statusText ?? void 0, data: error.data, request: error.request ?? void 0, response: error.response ?? void 0, detail: parseResult.data.detail, code: parseResult.data.code ?? void 0, type: parseResult.data.type ?? void 0 }); } return new _RequestError(error.message, { status: error.status ?? void 0, statusText: error.statusText ?? void 0, data: error.data, request: error.request ?? void 0, response: error.response ?? void 0, detail: error.data?.detail || "Unknown API error", code: error.status?.toString() ?? void 0 }); } /** * Create RequestError from generic Error */ static fromError(error, request) { return new _RequestError(error.message, { request: request ?? void 0, detail: error.message }); } }; // src/client.ts var SUPPORTED_API_VERSIONS = ["2025-05-31"]; var logger = (0, import_debug.default)("phala::api-client"); function formatHeaders(headers) { return Object.entries(headers).map(([key, value]) => ` -H "${key}: ${value}"`).join("\n"); } function formatBody(body) { if (!body) return ""; const bodyStr = typeof body === "string" ? body : JSON.stringify(body, null, 2); return ` -d '${bodyStr.replace(/'/g, "\\'")}'`; } function formatResponse(status, statusText, headers, body) { const headerEntries = []; headers.forEach((value, key) => { headerEntries.push(`${key}: ${value}`); }); const headerStr = headerEntries.join("\n"); const bodyStr = typeof body === "string" ? body : JSON.stringify(body, null, 2); return [ `< HTTP/1.1 ${status} ${statusText}`, headerStr ? `< ${headerStr.replace(/\n/g, "\n< ")}` : "", "", bodyStr ].filter(Boolean).join("\n"); } var Client = class { constructor(config = {}) { const resolvedConfig = { ...config, apiKey: config.apiKey || process?.env?.PHALA_CLOUD_API_KEY, baseURL: config.baseURL || process?.env?.PHALA_CLOUD_API_PREFIX || "https://cloud-api.phala.network/api/v1" }; const version = resolvedConfig.version && SUPPORTED_API_VERSIONS.includes(resolvedConfig.version) ? resolvedConfig.version : SUPPORTED_API_VERSIONS[0]; this.config = resolvedConfig; if (!resolvedConfig.useCookieAuth && !resolvedConfig.apiKey) { throw new Error( "API key is required. Provide it via config.apiKey or set PHALA_CLOUD_API_KEY environment variable." ); } const { apiKey, baseURL, timeout, headers, useCookieAuth, onResponseError, ...fetchOptions } = resolvedConfig; const requestHeaders = { "X-Phala-Version": version, "Content-Type": "application/json" }; if (headers && typeof headers === "object") { Object.entries(headers).forEach(([key, value]) => { if (typeof value === "string") { requestHeaders[key] = value; } }); } if (!useCookieAuth && apiKey) { requestHeaders["X-API-Key"] = apiKey; } this.fetchInstance = import_ofetch.ofetch.create({ baseURL, timeout: timeout || 3e4, headers: requestHeaders, ...useCookieAuth ? { credentials: "include" } : {}, ...fetchOptions, // Log request in cURL format onRequest({ request, options }) { if (logger.enabled) { const method = options.method || "GET"; const url = typeof request === "string" ? request : request.url; const fullUrl = url.startsWith("http") ? url : `${baseURL}${url}`; const headerObj = {}; if (options.headers && typeof options.headers === "object") { Object.entries(options.headers).forEach(([key, value]) => { if (typeof value === "string") { headerObj[key] = value; } }); } const curlCommand = [ `> curl -X ${method} "${fullUrl}"`, formatHeaders(headerObj), options.body ? formatBody(options.body) : "" ].filter(Boolean).join("\n"); logger("\n=== REQUEST ===\n%s\n", curlCommand); } }, // Log response in cURL format onResponse({ request, response, options }) { if (logger.enabled) { const method = options.method || "GET"; const url = typeof request === "string" ? request : request.url; logger( "\n=== RESPONSE [%s %s] (%dms) ===\n%s\n", method, url, response.headers.get("x-response-time") || "?", formatResponse(response.status, response.statusText, response.headers, response._data) ); } }, // Generic handlers for response error (similar to request.ts) onResponseError: ({ request, response, options }) => { console.warn(`HTTP ${response.status}: ${response.url}`); if (logger.enabled) { const method = options.method || "GET"; const url = typeof request === "string" ? request : request.url; logger( "\n=== ERROR RESPONSE [%s %s] ===\n%s\n", method, url, formatResponse(response.status, response.statusText, response.headers, response._data) ); } if (onResponseError) { onResponseError({ request, response, options }); } } }); } /** * Get the underlying ofetch instance for advanced usage */ get raw() { return this.fetchInstance; } // ===== Direct methods (throw on error) ===== /** * Perform GET request (throws on error) */ async get(request, options) { return this.fetchInstance(request, { ...options, method: "GET" }); } /** * Perform POST request (throws on error) */ async post(request, body, options) { return this.fetchInstance(request, { ...options, method: "POST", body }); } /** * Perform PUT request (throws on error) */ async put(request, body, options) { return this.fetchInstance(request, { ...options, method: "PUT", body }); } /** * Perform PATCH request (throws on error) */ async patch(request, body, options) { return this.fetchInstance(request, { ...options, method: "PATCH", body }); } /** * Perform DELETE request (throws on error) */ async delete(request, options) { return this.fetchInstance(request, { ...options, method: "DELETE" }); } // ===== Safe methods (return SafeResult) ===== /** * Safe wrapper for any request method (zod-style result) */ async safeRequest(fn) { try { const data = await fn(); return { success: true, data }; } catch (error) { if (error && typeof error === "object" && "data" in error) { const requestError2 = RequestError.fromFetchError(error); return { success: false, error: requestError2 }; } if (error instanceof Error) { const requestError2 = RequestError.fromError(error); return { success: false, error: requestError2 }; } const requestError = new RequestError("Unknown error occurred", { detail: "Unknown error occurred" }); return { success: false, error: requestError }; } } /** * Safe GET request (returns SafeResult) */ async safeGet(request, options) { return this.safeRequest(() => this.get(request, options)); } /** * Safe POST request (returns SafeResult) */ async safePost(request, body, options) { return this.safeRequest(() => this.post(request, body, options)); } /** * Safe PUT request (returns SafeResult) */ async safePut(request, body, options) { return this.safeRequest(() => this.put(request, body, options)); } /** * Safe PATCH request (returns SafeResult) */ async safePatch(request, body, options) { return this.safeRequest(() => this.patch(request, body, options)); } /** * Safe DELETE request (returns SafeResult) */ async safeDelete(request, options) { return this.safeRequest(() => this.delete(request, options)); } }; function createClient(config = {}) { return new Client(config); } // src/types/kms_info.ts var import_zod2 = require("zod"); // src/types/supported_chains.ts var import_chains = require("viem/chains"); var SUPPORTED_CHAINS = { [import_chains.mainnet.id]: import_chains.mainnet, [import_chains.base.id]: import_chains.base, [import_chains.anvil.id]: import_chains.anvil }; // src/types/kms_info.ts var KmsInfoBaseSchema = import_zod2.z.object({ id: import_zod2.z.string(), slug: import_zod2.z.string().nullable(), url: import_zod2.z.string(), version: import_zod2.z.string(), chain_id: import_zod2.z.number().nullable(), kms_contract_address: import_zod2.z.string().nullable().transform((val) => val), gateway_app_id: import_zod2.z.string().nullable().transform((val) => val) }).passthrough(); var KmsInfoSchema = KmsInfoBaseSchema.transform((data) => { if (data.chain_id != null) { const chain = SUPPORTED_CHAINS[data.chain_id]; if (chain) { return { ...data, chain }; } } return data; }); // src/types/cvm_info.ts var import_zod3 = require("zod"); var VmInfoSchema = import_zod3.z.object({ id: import_zod3.z.string(), name: import_zod3.z.string(), status: import_zod3.z.string(), uptime: import_zod3.z.string(), app_url: import_zod3.z.string().nullable(), app_id: import_zod3.z.string(), instance_id: import_zod3.z.string().nullable(), configuration: import_zod3.z.any().optional(), // TODO: add VmConfiguration schema if needed exited_at: import_zod3.z.string().nullable(), boot_progress: import_zod3.z.string().nullable(), boot_error: import_zod3.z.string().nullable(), shutdown_progress: import_zod3.z.string().nullable(), image_version: import_zod3.z.string().nullable() }); var ManagedUserSchema = import_zod3.z.object({ id: import_zod3.z.number(), username: import_zod3.z.string() }); var CvmNodeSchema = import_zod3.z.object({ id: import_zod3.z.number(), name: import_zod3.z.string(), region_identifier: import_zod3.z.string().optional() }); var CvmNetworkUrlsSchema = import_zod3.z.object({ app: import_zod3.z.string(), instance: import_zod3.z.string() }); var CvmInfoSchema = import_zod3.z.object({ hosted: VmInfoSchema, name: import_zod3.z.string(), managed_user: ManagedUserSchema.optional().nullable(), node: CvmNodeSchema.optional().nullable(), listed: import_zod3.z.boolean().default(false), status: import_zod3.z.string(), in_progress: import_zod3.z.boolean().default(false), dapp_dashboard_url: import_zod3.z.string().nullable(), syslog_endpoint: import_zod3.z.string().nullable(), allow_upgrade: import_zod3.z.boolean().default(false), project_id: import_zod3.z.string().nullable(), // HashedId is represented as string in JS project_type: import_zod3.z.string().nullable(), billing_period: import_zod3.z.string().nullable(), kms_info: KmsInfoSchema.nullable(), vcpu: import_zod3.z.number().nullable(), memory: import_zod3.z.number().nullable(), disk_size: import_zod3.z.number().nullable(), gateway_domain: import_zod3.z.string().nullable(), public_urls: import_zod3.z.array(CvmNetworkUrlsSchema) }).partial(); var CvmLegacyDetailSchema = import_zod3.z.object({ id: import_zod3.z.number(), name: import_zod3.z.string(), status: import_zod3.z.string(), in_progress: import_zod3.z.boolean(), teepod_id: import_zod3.z.number().nullable(), teepod: CvmNodeSchema, app_id: import_zod3.z.string(), vm_uuid: import_zod3.z.string().nullable(), instance_id: import_zod3.z.string().nullable(), vcpu: import_zod3.z.number().nullable(), memory: import_zod3.z.number().nullable(), disk_size: import_zod3.z.number().nullable(), base_image: import_zod3.z.string(), encrypted_env_pubkey: import_zod3.z.string().nullable(), listed: import_zod3.z.boolean(), project_id: import_zod3.z.string().nullable(), project_type: import_zod3.z.string().nullable(), public_sysinfo: import_zod3.z.boolean(), public_logs: import_zod3.z.boolean(), dapp_dashboard_url: import_zod3.z.string().nullable(), syslog_endpoint: import_zod3.z.string().nullable(), kms_info: KmsInfoSchema.nullable(), contract_address: import_zod3.z.string().nullable(), deployer_address: import_zod3.z.string().nullable(), scheduled_delete_at: import_zod3.z.string().nullable(), public_urls: import_zod3.z.array(CvmNetworkUrlsSchema), gateway_domain: import_zod3.z.string().nullable() }); // src/actions/get_current_user.ts var import_zod4 = require("zod"); // src/utils/index.ts var import_encrypt_env_vars = require("@phala/dstack-sdk/encrypt-env-vars"); // src/utils/get_error_message.ts function getErrorMessage(error) { if (typeof error.detail === "string") { return error.detail; } if (Array.isArray(error.detail)) { if (error.detail.length > 0) { return error.detail[0]?.msg || "Validation error"; } return "Validation error"; } if (typeof error.detail === "object" && error.detail !== null) { return JSON.stringify(error.detail); } return "Unknown error occurred"; } // src/utils/as-hex.ts var import_viem = require("viem"); function asHex(value) { if (typeof value === "string") { if (value.startsWith("0x") && (0, import_viem.isHex)(value)) { return value; } else if ((0, import_viem.isHex)(`0x${value}`)) { return `0x${value}`; } } throw new Error(`Invalid hex value: ${value}`); } // src/utils/validate-parameters.ts function validateActionParameters(parameters) { if (parameters?.schema !== void 0 && parameters?.schema !== false) { if (typeof parameters.schema !== "object" || parameters.schema === null || !("parse" in parameters.schema) || typeof parameters.schema.parse !== "function") { throw new Error("Invalid schema: must be a Zod schema object, false, or undefined"); } } } function safeValidateActionParameters(parameters) { if (parameters?.schema !== void 0 && parameters?.schema !== false) { if (typeof parameters.schema !== "object" || parameters.schema === null || !("parse" in parameters.schema) || typeof parameters.schema.parse !== "function") { return { success: false, error: { name: "ZodError", message: "Invalid schema: must be a Zod schema object, false, or undefined", issues: [ { code: "invalid_type", expected: "object", received: typeof parameters.schema, path: ["schema"], message: "Invalid schema: must be a Zod schema object, false, or undefined" } ] } }; } } return void 0; } // src/utils/network.ts var NetworkError = class extends Error { constructor(message, code, details) { super(message); this.code = code; this.details = details; this.name = "NetworkError"; } }; var WalletError = class extends Error { constructor(message, code, details) { super(message); this.code = code; this.details = details; this.name = "WalletError"; } }; var TransactionError = class extends Error { constructor(message, hash, details) { super(message); this.hash = hash; this.details = details; this.name = "TransactionError"; } }; function createNetworkClients(publicClient, walletClient, address, chainId) { return { publicClient, walletClient, address, chainId }; } async function checkNetworkStatus(clients, targetChainId) { try { const currentChainId = await clients.walletClient.getChainId(); return { isCorrectNetwork: currentChainId === targetChainId, currentChainId }; } catch (error) { throw new NetworkError( `Failed to check network status: ${error instanceof Error ? error.message : "Unknown error"}`, "NETWORK_CHECK_FAILED", error ); } } async function checkBalance(publicClient, address, minBalance) { try { const balance = await publicClient.getBalance({ address }); return { address, balance, sufficient: minBalance ? balance >= minBalance : true, required: minBalance }; } catch (error) { throw new NetworkError( `Failed to check balance: ${error instanceof Error ? error.message : "Unknown error"}`, "BALANCE_CHECK_FAILED", error ); } } async function waitForTransactionReceipt(publicClient, hash, options = {}) { const { timeout = 6e4, // 60 seconds default pollingInterval = 2e3, // 2 seconds default confirmations = 1 } = options; const startTime = Date.now(); return new Promise((resolve, reject) => { const poll = async () => { try { const receipt = await publicClient.getTransactionReceipt({ hash }); if (receipt) { if (confirmations > 1) { const currentBlock = await publicClient.getBlockNumber(); const confirmationCount = currentBlock - receipt.blockNumber + 1n; if (confirmationCount < BigInt(confirmations)) { const elapsed = Date.now() - startTime; if (elapsed >= timeout) { reject( new TransactionError(`Transaction confirmation timeout after ${timeout}ms`, hash) ); return; } setTimeout(poll, pollingInterval); return; } } resolve(receipt); } else { const elapsed = Date.now() - startTime; if (elapsed >= timeout) { reject(new TransactionError(`Transaction receipt timeout after ${timeout}ms`, hash)); return; } setTimeout(poll, pollingInterval); } } catch (error) { const elapsed = Date.now() - startTime; if (elapsed >= timeout) { reject( new TransactionError(`Transaction receipt timeout after ${timeout}ms`, hash, error) ); return; } setTimeout(poll, pollingInterval); } }; poll(); }); } async function executeTransaction(clients, operation, args, options = {}) { const { timeout = 6e4, confirmations = 1, onSubmitted, onConfirmed, onError } = options; try { const hash = await operation(clients, ...args); onSubmitted?.(hash); const receipt = await waitForTransactionReceipt(clients.publicClient, hash, { timeout, confirmations }); const success = receipt.status === "success"; if (success) { onConfirmed?.(receipt); } else { const error = new TransactionError("Transaction failed on-chain", hash, receipt); onError?.(error, hash); throw error; } return { hash, receipt, success }; } catch (error) { const txError = error instanceof TransactionError ? error : new TransactionError( `Transaction execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, void 0, error ); onError?.(txError, txError.hash); throw txError; } } async function extractNetworkClients(publicClient, walletClient) { try { const address = walletClient.account?.address; if (!address) { throw new WalletError("WalletClient must have an account", "NO_ACCOUNT"); } const chainId = await walletClient.getChainId(); return createNetworkClients(publicClient, walletClient, address, chainId); } catch (error) { throw new WalletError( `Failed to extract network clients: ${error instanceof Error ? error.message : "Unknown error"}`, "EXTRACTION_FAILED", error ); } } async function validateNetworkPrerequisites(clients, requirements) { const { targetChainId, minBalance, requiredAddress } = requirements; const networkStatus = await checkNetworkStatus(clients, targetChainId); const balanceResult = await checkBalance(clients.publicClient, clients.address, minBalance); const addressValid = requiredAddress ? clients.address.toLowerCase() === requiredAddress.toLowerCase() : true; return { networkValid: networkStatus.isCorrectNetwork, balanceValid: balanceResult.sufficient, addressValid, details: { currentChainId: networkStatus.currentChainId, balance: balanceResult.balance, address: clients.address } }; } // src/utils/transaction.ts function createTransactionTracker() { let status = { state: "idle" }; let timeoutHandle; let abortController; const updateStatus = (newStatus) => { status = { ...status, ...newStatus }; }; const reset = () => { if (timeoutHandle) { clearTimeout(timeoutHandle); timeoutHandle = void 0; } if (abortController) { abortController.abort(); abortController = void 0; } status = { state: "idle" }; }; const abort = () => { if (abortController) { abortController.abort(); } if (timeoutHandle) { clearTimeout(timeoutHandle); timeoutHandle = void 0; } updateStatus({ state: "error", aborted: true, error: "Transaction aborted by user" }); }; const execute = async (operation, clients, args, options = {}) => { const { timeout = 6e4, confirmations = 1, onSubmitted, onConfirmed, onError, signal } = options; try { reset(); abortController = new AbortController(); if (signal) { if (signal.aborted) { throw new TransactionError("Operation was aborted before execution"); } signal.addEventListener("abort", () => { abort(); }); } updateStatus({ state: "submitting", startTime: Date.now(), error: void 0, hash: void 0, receipt: void 0, aborted: false }); if (abortController.signal.aborted) { throw new TransactionError("Transaction aborted"); } const hash = await operation(clients, ...args); if (abortController.signal.aborted) { throw new TransactionError("Transaction aborted after submission", hash); } updateStatus({ state: "pending", hash, submitTime: Date.now() }); onSubmitted?.(hash); if (timeout > 0) { timeoutHandle = setTimeout(() => { if (status.state === "pending" && !abortController?.signal.aborted) { updateStatus({ state: "timeout", error: `Transaction timeout after ${timeout}ms` }); } }, timeout); } const receipt = await Promise.race([ waitForTransactionReceipt(clients.publicClient, hash, { timeout, confirmations }), new Promise((_, reject) => { abortController?.signal.addEventListener("abort", () => { reject(new TransactionError("Transaction aborted while waiting for receipt", hash)); }); }) ]); if (timeoutHandle) { clearTimeout(timeoutHandle); timeoutHandle = void 0; } const success = receipt.status === "success"; updateStatus({ state: success ? "success" : "error", receipt, confirmTime: Date.now(), error: success ? void 0 : "Transaction failed on-chain" }); if (success) { onConfirmed?.(receipt); } else { const error = new TransactionError("Transaction failed on-chain", hash, receipt); onError?.(error, hash); throw error; } return { hash, receipt, success }; } catch (error) { const txError = error instanceof TransactionError ? error : new TransactionError( `Transaction execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, status.hash, error ); updateStatus({ state: "error", error: txError.message }); onError?.(txError, status.hash); throw txError; } }; return { get status() { return { ...status }; }, get isIdle() { return status.state === "idle"; }, get isSubmitting() { return status.state === "submitting"; }, get isPending() { return status.state === "pending"; }, get isSuccess() { return status.state === "success"; }, get isError() { return status.state === "error"; }, get isTimeout() { return status.state === "timeout"; }, get isAborted() { return status.aborted === true; }, get isComplete() { return ["success", "error", "timeout"].includes(status.state); }, abort, reset, execute }; } async function executeBatchTransactions(operations, clients, batchOptions) { const { mode, failFast = false, onProgress } = batchOptions; const results = []; if (mode === "sequential") { for (let i = 0; i < operations.length; i++) { const op = operations[i]; if (!op) continue; const { operation, args, options } = op; try { const tracker = createTransactionTracker(); const result = await tracker.execute(operation, clients, args, options); results.push(result); onProgress?.(i + 1, operations.length, results); } catch (error) { const txError = error instanceof Error ? error : new Error(String(error)); results.push(txError); onProgress?.(i + 1, operations.length, results); if (failFast) { for (let j = i + 1; j < operations.length; j++) { results.push(new Error("Cancelled due to previous failure")); } break; } } } } else { const promises = operations.map(async ({ operation, args, options }) => { try { const tracker = createTransactionTracker(); return await tracker.execute(operation, clients, args, options); } catch (error) { return error instanceof Error ? error : new Error(String(error)); } }); const allResults = await Promise.allSettled(promises); results.push(...allResults.map((r) => r.status === "fulfilled" ? r.value : r.reason)); onProgress?.(operations.length, operations.length, results); } const successCount = results.filter((r) => !(r instanceof Error)).length; const errorCount = results.length - successCount; return { results, successCount, errorCount, allSuccessful: errorCount === 0 }; } async function executeTransactionWithRetry(operation, clients, args, options = {}, retryOptions = {}) { const { maxRetries = 3, initialDelay = 1e3, maxDelay = 1e4, backoffFactor = 2, retryCondition = () => true } = retryOptions; let lastError; let delay = initialDelay; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const tracker = createTransactionTracker(); return await tracker.execute(operation, clients, args, options); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt === maxRetries) { break; } if (!retryCondition(lastError)) { break; } await new Promise((resolve) => setTimeout(resolve, delay)); delay = Math.min(delay * backoffFactor, maxDelay); } } throw lastError; } async function estimateTransactionGas(clients, transaction, options = {}) { const { gasLimitMultiplier = 1.2, maxFeePerGasMultiplier = 1.1, priorityFeeMultiplier = 1.1 } = options; try { const estimatedGas = await clients.publicClient.estimateGas(transaction); const gasLimit = BigInt(Math.ceil(Number(estimatedGas) * gasLimitMultiplier)); let maxFeePerGas; let maxPriorityFeePerGas; try { const feeData = await clients.publicClient.estimateFeesPerGas(); if (feeData.maxFeePerGas) { maxFeePerGas = BigInt(Math.ceil(Number(feeData.maxFeePerGas) * maxFeePerGasMultiplier)); } if (feeData.maxPriorityFeePerGas) { maxPriorityFeePerGas = BigInt( Math.ceil(Number(feeData.maxPriorityFeePerGas) * priorityFeeMultiplier) ); } } catch (error) { } return { gasLimit, maxFeePerGas, maxPriorityFeePerGas }; } catch (error) { throw new TransactionError( `Gas estimation failed: ${error instanceof Error ? error.message : "Unknown error"}`, void 0, error ); } } // src/utils/client-factories.ts var import_viem2 = require("viem"); var import_accounts = require("viem/accounts"); function isBrowser() { return typeof window !== "undefined" && typeof window.ethereum !== "undefined"; } function getEthereumProvider() { if (!isBrowser()) return null; const ethereum = window.ethereum; return ethereum || null; } function createClientsFromPrivateKey(chain, privateKey, rpcUrl) { try { const account = (0, import_accounts.privateKeyToAccount)(privateKey); const publicClient = (0, import_viem2.createPublicClient)({ chain, transport: (0, import_viem2.http)(rpcUrl || chain.rpcUrls.default.http[0]) }); const walletClient = (0, import_viem2.createWalletClient)({ account, chain, transport: (0, import_viem2.http)(rpcUrl || chain.rpcUrls.default.http[0]) }); return createNetworkClients(publicClient, walletClient, account.address, chain.id); } catch (error) { throw new WalletError( `Failed to create clients from private key: ${error instanceof Error ? error.message : "Unknown error"}`, "CLIENT_CREATION_FAILED", error ); } } async function createClientsFromBrowser(chain, rpcUrl) { if (!isBrowser()) { throw new WalletError( "Browser wallet connection is only available in browser environment", "NOT_BROWSER_ENVIRONMENT" ); } const provider = getEthereumProvider(); if (!provider) { throw new WalletError( "No Ethereum provider found. Please install a wallet like MetaMask.", "NO_PROVIDER" ); } try { const accounts = await provider.request({ method: "eth_requestAccounts" }); if (!accounts || accounts.length === 0) { throw new WalletError("No accounts available", "NO_ACCOUNTS"); } const address = accounts[0]; const chainId = await provider.request({ method: "eth_chainId" }); const currentChainId = parseInt(chainId, 16); if (currentChainId !== chain.id) { await switchToNetwork(provider, chain.id); } const publicClient = (0, import_viem2.createPublicClient)({ chain, transport: (0, import_viem2.http)(rpcUrl || chain.rpcUrls.default.http[0]) }); const walletClient = (0, import_viem2.createWalletClient)({ account: address, chain, transport: (0, import_viem2.custom)(provider) }); return createNetworkClients(publicClient, walletClient, address, chain.id); } catch (error) { if (error instanceof WalletError || error instanceof NetworkError) { throw error; } throw new WalletError( `Failed to connect browser wallet: ${error instanceof Error ? error.message : "Unknown error"}`, "BROWSER_CONNECTION_FAILED", error ); } } async function switchToNetwork(provider, chainId) { try { await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: `0x${chainId.toString(16)}` }] }); } catch (error) { const errorObj = error; if (errorObj.code === 4902) { throw new NetworkError( `Network ${chainId} not found in wallet. Please add it manually.`, "NETWORK_NOT_FOUND", error ); } throw new NetworkError( `Failed to switch network: ${errorObj.message || "Unknown error"}`, "NETWORK_SWITCH_FAILED", error ); } } async function addNetwork(provider, config) { try { await provider.request({ method: "wallet_addEthereumChain", params: [ { chainId: `0x${config.chainId.toString(16)}`, chainName: config.name, rpcUrls: [config.rpcUrl], blockExplorerUrls: config.blockExplorer ? [config.blockExplorer] : void 0, nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 } } ] }); } catch (error) { const errorObj = error; throw new NetworkError( `Failed to add network: ${errorObj.message || "Unknown error"}`, "NETWORK_ADD_FAILED", error ); } } async function autoCreateClients(chain, options = {}) { const { privateKey, rpcUrl, preferBrowser = false } = options; if (privateKey) { return createClientsFromPrivateKey(chain, privateKey, rpcUrl); } if (isBrowser() && (preferBrowser || !privateKey)) { return createClientsFromBrowser(chain, rpcUrl); } throw new WalletError( "No wallet connection method available. Provide a private key for server-side usage or use in browser environment.", "NO_CONNECTION_METHOD" ); } // src/actions/get_current_user.ts var CurrentUserSchema = import_zod4.z.object({ username: import_zod4.z.string(), email: import_zod4.z.string(), credits: import_zod4.z.number(), granted_credits: import_zod4.z.number(), avatar: import_zod4.z.string(), team_name: import_zod4.z.string(), team_tier: import_zod4.z.string() }).passthrough(); async function getCurrentUser(client, parameters) { validateActionParameters(parameters); const response = await client.get("/auth/me"); if (parameters?.schema === false) { return response; } const schema = parameters?.schema || CurrentUserSchema; return schema.parse(response); } async function safeGetCurrentUser(client, parameters) { const parameterValidationError = safeValidateActionParameters(parameters); if (parameterValidationError) { return parameterValidationError; } const httpResult = await client.safeGet("/auth/me"); if (!httpResult.success) { return httpResult; } if (parameters?.schema === false) { return { success: true, data: httpResult.data }; } const schema = parameters?.schema || CurrentUserSchema; return schema.safeParse(httpResult.data); } // src/actions/get_available_nodes.ts var import_zod5 = require("zod"); var AvailableOSImageSchema = import_zod5.z.object({ name: import_zod5.z.string(), is_dev: import_zod5.z.boolean(), version: import_zod5.z.union([ import_zod5.z.tuple([import_zod5.z.number(), import_zod5.z.number(), import_zod5.z.number()]), import_zod5.z.tuple([import_zod5.z.number(), import_zod5.z.number(), import_zod5.z.number(), import_zod5.z.number()]) ]), os_image_hash: import_zod5.z.string().nullable().optional() }).passthrough(); var TeepodCapacitySchema = import_zod5.z.object({ teepod_id: import_zod5.z.number(), name: import_zod5.z.string(), listed: import_zod5.z.boolean(), resource_score: import_zod5.z.number(), remaining_vcpu: import_zod5.z.number(), remaining_memory: import_zod5.z.number(), remaining_cvm_slots: import_zod5.z.number(), images: import_zod5.z.array(AvailableOSImageSchema), support_onchain_kms: import_zod5.z.boolean().optional(), fmspc: import_zod5.z.string().nullable().optional(), device_id: import_zod5.z.string().nullable().optional(), region_identifier: import_zod5.z.string().nullable().optional(), default_kms: import_zod5.z.string().nullable().optional(), kms_list: import_zod5.z.array(import_zod5.z.string()).default([]) }).passthrough(); var ResourceThresholdSchema = import_zod5.z.object({ max_instances: import_zod5.z.number().nullable().optional(), max_vcpu: import_zod5.z.number().nullable().optional(), max_memory: import_zod5.z.number().nullable().optional(), max_disk: import_zod5.z.number().nullable().optional() }).passthrough(); var AvailableNodesSchema = import_zod5.z.object({ tier: import_zod5.z.string(), // TeamTier is string enum capacity: ResourceThresholdSchema, nodes: import_zod5.z.array(TeepodCapacitySchema), kms_list: import_zod5.z.array(KmsInfoSchema) }).passthrough(); async function getAvailableNodes(client, parameters) { const response = await client.get("/teepods/available"); if (parameters?.schema === false) { return response; } const schema = parameters?.schema || AvailableNodesSchema; return schema.parse(response); } async function safeGetAvailableNodes(client, parameters) { const httpResult = await client.safeGet("/teepods/available"); if (!httpResult.success) { return httpResult; } if (parameters?.schema === false) { return { success: true, data: httpResult.data }; } const schema = parameters?.schema || AvailableNodesSchema; return schema.safeParse(httpResult.data); } // src/actions/provision_cvm.ts var import_zod6 = require("zod"); var ProvisionCvmSchema = import_zod6.z.object({ app_id: import_zod6.z.string().nullable().optional(), app_env_encrypt_pubkey: import_zod6.z.string().nullable().optional(), compose_hash: import_zod6.z.string(), fmspc: import_zod6.z.string().nullable().optional(), device_id: import_zod6.z.string().nullable().optional(), os_image_hash: import_zod6.z.string().nullable().optional(), node_id: import_zod6.z.number().nullable().optional(), // Transformed from teepod_id in response kms_id: import_zod6.z.string().nullable().optional() }).passthrough(); var ProvisionCvmRequestSchema = import_zod6.z.object({ node_id: import_zod6.z.number().optional(), // recommended teepod_id: import_zod6.z.number().optional(), // deprecated, for compatibility name: import_zod6.z.string(), image: import_zod6.z.string(), vcpu: import_zod6.z.number(), memory: import_zod6.z.number(), disk_size: import_zod6.z.number(), compose_file: import_zod6.z.object({ allowed_envs: import_zod6.z.array(import_zod6.z.string()).optional(), pre_launch_script: import_zod6.z.string().optional(), docker_compose_file: import_zod6.z.string().optional(), name: import_zod6.z.string().optional(), kms_enabled: import_zod6.z.boolean().optional(), public_logs: import_zod6.z.boolean().optional(), public_sysinfo: import_zod6.z.boolean().optional(), gateway_enabled: import_zod6.z.boolean().optional(), // recommended tproxy_enabled: import_zod6.z.boolean().optional() // deprecated, for compatibility }), listed: import_zod6.z.boolean().optional(), instance_type: import_zod6.z.string().nullable().optional(), kms_id: import_zod6.z.string().optional(), env_keys: import_zod6.z.array(import_zod6.z.string()).optional() }).passthrough(); function autofillComposeFileName(appCompose) { if (appCompose.compose_file && !appCompose.compose_file.name) { return { ...appCompose, compose_file: { ...appCompose.compose_file, name: appCompose.name } }; } return appCompose; } function handleGatewayCompatibility(appCompose) { if (!appCompose.compose_file) { return appCompose; } const composeFile = { ...appCompose.compose_file }; if (typeof composeFile.gateway_enabled === "boolean" && typeof composeFile.tproxy_enabled === "boolean") { delete composeFile.tproxy_enabled; } else if (typeof composeFile.tproxy_enabled === "boolean" && typeof composeFile.gateway_enabled === "undefined") { composeFile.gateway_enabled = composeFile.tproxy_enabled; delete composeFile.tproxy_enabled; if (typeof window !== "undefined" ? window.console : globalThis.console) { console.warn( "[phala/cloud] tproxy_enabled is deprecated, please use gateway_enabled instead. See docs for migration." ); } } return { ...appCompose, compose_file: composeFile }; } function transformResponse(data, isDefaultSchema) { if (!isDefaultSchema || !data || typeof data !== "object") { return data; } if (data && typeof data === "object" && "teepod_id" in data) { const { teepod_id, ...rest } = data; return { ...rest, node_id: teepod_id }; } return data; } async function provisionCvm(client, appCompose, parameters) { validateActionParameters(parameters); const body = handleGatewayCompatibility(autofillComposeFileName(appCompose)); let requestBody = { ...body }; if (typeof body.node_id === "number") { requestBody = { ...body, teepod_id: body.node_id }; delete requestBody.node_id; } else if (typeof body.teepod_id === "number") { console.warn("[phala/cloud] teepod_id is deprecated, please use node_id instead."); } const response = await client.post("/cvms/provision", requestBody); const isDefaultSchema = parameters?.schema === void 0; const transformedData = transformResponse(response, isDefaultSchema); if (parameters?.schema === false) { return transformedData; } const usedSchema = parameters?.schema || ProvisionCvmSchema; return usedSchema.parse(transformedData); } async function safeProvisionCvm(client, appCompose, parameters) { const parameterValidationError = safeValidateActionParameters(parameters); if (parameterValidationError) { return parameterValidationError; } const schema = parameters?.schema; const body = handleGatewayCompatibility(autofillComposeFileName(appCompose)); let requestBody = { ...body }; if (typeof body.node_id === "number") { requestBody = { ...body, teepod_id: body.node_id }; delete requestBody.node_id; } else if (typeof body.teepod_id === "number") { console.warn("[phala/cloud] teepod_id is deprecated, please use node_id instead."); } const httpResult = await client.safePost("/cvms/provision", requestBody); if (!httpResult.success) { return httpResult; } if (schema === false) { return { success: true, data: httpResult.data }; } const isDefaultSchema = !schema; const usedSchema = schema || ProvisionCvmSchema; const transformResult = usedSchema.safeParse(transformResponse(httpResult.data, isDefaultSchema)); return transformResult; } // src/actions/commit_cvm_provision.ts var import_zod7 = require("zod"); var CommitCvmProvisionSchema = import_zod7.z.object({ id: import_zod7.z.number(), name: import_zod7.z.string(), status: import_zod7.z.string(), teepod_id: import_zod7.z.number(), teepod: import_zod7.z.object({ id: import_zo