@lit-protocol/vincent-scaffold-sdk
Version:
Shared build configuration and utilities for Vincent abilities and policies
752 lines (751 loc) • 33.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StateManager = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const crypto_1 = require("crypto");
const ethers_1 = require("ethers");
const STATE_FILE_PATH = path_1.default.join(process.cwd(), ".e2e-state.json");
const STATE_VERSION = "2.0.0";
class StateManager {
constructor(network, testFileName, configHash, abilityIpfsCids, abilityPolicyCids) {
this.network = network;
this.testFileName = testFileName;
this.configHash = configHash;
this.nestedState = {
version: STATE_VERSION,
testFiles: {},
};
this.currentConfigState = null;
// If ability/policy CIDs are provided, generate the real config hash
if (abilityIpfsCids && abilityPolicyCids) {
this.configHash = StateManager.generateConfigHash(abilityIpfsCids, abilityPolicyCids);
}
// Initialize the current configuration state
this.currentConfigState = this.getCurrentConfigState();
console.log(`🏗️ StateManager initialized with nested structure v${STATE_VERSION} for ${testFileName}:${this.configHash}`);
}
/**
* Generate configuration hash from ability and policy IPFS CIDs
*/
static generateConfigHash(abilityIpfsCids, abilityPolicyCids) {
// Combine all IPFS CIDs and create a hash
const flattenedPolicyCids = abilityPolicyCids.flat();
const allCids = [...abilityIpfsCids, ...flattenedPolicyCids];
const sortedCids = allCids.sort();
const cidString = sortedCids.join("");
const hash = (0, crypto_1.createHash)("sha256")
.update(cidString)
.digest("hex")
.substring(0, 8);
return hash;
}
/**
* Auto-detect test filename from call stack
*/
static autoDetectTestFileName() {
const stack = new Error().stack;
if (!stack)
return "unknown-test.ts";
const stackLines = stack.split("\n");
for (const line of stackLines) {
const match = line.match(/\/([^\/\s]+\.ts):/);
if (match && match[1] !== "state-manager.ts" && match[1] !== "init.ts") {
return match[1];
}
}
return "unknown-test.ts";
}
/**
* Get current configuration state, creating if needed
*/
getCurrentConfigState() {
if (!this.nestedState.testFiles[this.testFileName]) {
this.nestedState.testFiles[this.testFileName] = {
accounts: {},
pkps: {},
capacityCredits: {},
configurations: {},
appVersions: {},
};
}
// Ensure all structures exist for this test file
if (!this.nestedState.testFiles[this.testFileName].accounts) {
this.nestedState.testFiles[this.testFileName].accounts = {};
}
if (!this.nestedState.testFiles[this.testFileName].pkps) {
this.nestedState.testFiles[this.testFileName].pkps = {};
}
if (!this.nestedState.testFiles[this.testFileName].capacityCredits) {
this.nestedState.testFiles[this.testFileName].capacityCredits = {};
}
if (!this.nestedState.testFiles[this.testFileName].configurations[this.configHash]) {
this.nestedState.testFiles[this.testFileName].configurations[this.configHash] = {
configHash: this.configHash,
abilityIpfsCids: [],
abilityPolicyCids: [],
lastUsed: new Date().toISOString(),
network: this.network,
state: {},
};
}
return this.nestedState.testFiles[this.testFileName].configurations[this.configHash];
}
/**
* Set the real configuration hash based on ability/policy CIDs
* This should be called before any state operations when CIDs are known
*/
setConfigurationFromCIDs(abilityIpfsCids, abilityPolicyCids) {
const newConfigHash = StateManager.generateConfigHash(abilityIpfsCids, abilityPolicyCids);
if (newConfigHash !== this.configHash) {
console.log(`🔄 Setting configuration from ${this.configHash} to ${newConfigHash}`);
// Update the internal config hash
this.configHash = newConfigHash;
// Reset current config state since we're switching to a new config
this.currentConfigState = null;
// Initialize the new configuration state
this.currentConfigState = this.getCurrentConfigState();
// Update metadata
this.updateConfigurationMetadata(abilityIpfsCids, abilityPolicyCids);
}
else {
// Update metadata even if hash is same (for lastUsed timestamp)
this.updateConfigurationMetadata(abilityIpfsCids, abilityPolicyCids);
}
}
/**
* Update configuration metadata
*/
updateConfigurationMetadata(abilityIpfsCids, abilityPolicyCids) {
const configState = this.getCurrentConfigState();
configState.abilityIpfsCids = abilityIpfsCids;
configState.abilityPolicyCids = abilityPolicyCids;
configState.lastUsed = new Date().toISOString();
}
/**
* Generate app version key from appId and appVersion
*/
static generateAppVersionKey(appId, appVersion) {
return `${appId}-${appVersion}`;
}
// /**
// * Get app version configuration, creating if needed
// */
// private getAppVersionConfiguration(
// appId: string,
// appVersion: string
// ): AppVersionConfiguration {
// const appVersionKey = StateManager.generateAppVersionKey(appId, appVersion);
// if (!this.nestedState.testFiles[this.testFileName]) {
// this.nestedState.testFiles[this.testFileName] = {
// configurations: {},
// appVersions: {},
// };
// }
// if (
// !this.nestedState.testFiles[this.testFileName].appVersions[appVersionKey]
// ) {
// throw new Error(
// `App version configuration ${appVersionKey} not found. It should be created when saving the app.`
// );
// }
// return this.nestedState.testFiles[this.testFileName].appVersions[
// appVersionKey
// ];
// }
/**
* Save app version configuration
*/
saveAppVersionConfiguration(appId, appVersion, vincentApp, abilityIpfsCids, abilityPolicyCids) {
const appVersionKey = StateManager.generateAppVersionKey(appId, appVersion);
if (!this.nestedState.testFiles[this.testFileName]) {
this.nestedState.testFiles[this.testFileName] = {
configurations: {},
appVersions: {},
};
}
this.nestedState.testFiles[this.testFileName].appVersions[appVersionKey] = {
appId,
appVersion,
abilityIpfsCids,
abilityPolicyCids,
lastUsed: new Date().toISOString(),
network: this.network,
state: {
vincentApp,
pkpAppPermissions: [],
},
};
console.log(chalk_1.default.green(`💾 Saved app version configuration: ${appVersionKey}`));
}
/**
* Get or register Vincent app using app versioning approach
*/
async getOrRegisterVincentAppVersioned(delegateeAddress, registerAppFunction, registerNextVersionFunction, abilityIpfsCids, abilityPolicies, policyParams) {
// First, check if we have any existing app for this delegatee
const existingApp = this.getExistingVincentApp(delegateeAddress);
if (existingApp) {
const appId = existingApp.appId;
// Check if current abilities/policies match any existing app version
const matchingAppVersion = this.findMatchingAppVersion(appId, abilityIpfsCids, abilityPolicies);
if (matchingAppVersion) {
console.log(chalk_1.default.blue(`🏢 Using existing app version: ${appId} v${matchingAppVersion}`));
return {
appId,
appVersion: matchingAppVersion,
isNew: false,
isNewVersion: false,
abilityIpfsCids,
abilityPolicies,
policyParams,
};
}
else {
// Register new version for existing app
console.log(chalk_1.default.yellow(`🏢 Registering new version for existing app: ${appId}`));
const { appVersion } = await registerNextVersionFunction(appId);
// Save the new app version
const vincentApp = {
appId,
appVersion,
delegateeAddress,
abilityIpfsCids,
abilityPolicies,
policyParams,
createdAt: new Date().toISOString(),
network: this.network,
};
this.saveAppVersionConfiguration(appId, appVersion, vincentApp, abilityIpfsCids, abilityPolicies);
return {
appId,
appVersion,
isNew: false,
isNewVersion: true,
abilityIpfsCids,
abilityPolicies,
policyParams,
};
}
}
else {
// Register completely new app
console.log(chalk_1.default.yellow(`🏢 Registering new Vincent app...`));
const { appId, appVersion } = await registerAppFunction();
// Save the new app version
const vincentApp = {
appId,
appVersion,
delegateeAddress,
abilityIpfsCids,
abilityPolicies,
policyParams,
createdAt: new Date().toISOString(),
network: this.network,
};
this.saveAppVersionConfiguration(appId, appVersion, vincentApp, abilityIpfsCids, abilityPolicies);
// Also save to current config for backward compatibility
this.saveVincentApp(appId, appVersion, delegateeAddress, abilityIpfsCids, abilityPolicies, policyParams);
return {
appId,
appVersion,
isNew: true,
isNewVersion: false,
abilityIpfsCids,
abilityPolicies,
policyParams,
};
}
}
/**
* Find matching app version based on ability/policy CIDs
*/
findMatchingAppVersion(appId, abilityIpfsCids, abilityPolicies) {
if (!this.nestedState.testFiles[this.testFileName]?.appVersions) {
return undefined;
}
const appVersions = this.nestedState.testFiles[this.testFileName].appVersions;
for (const [appVersionKey, config] of Object.entries(appVersions)) {
if (config.appId === appId && config.network === this.network) {
// Check if ability CIDs match
const abilityCidsMatch = JSON.stringify(config.abilityIpfsCids.sort()) ===
JSON.stringify(abilityIpfsCids.sort());
const policyCidsMatch = JSON.stringify(config.abilityPolicyCids) ===
JSON.stringify(abilityPolicies);
if (abilityCidsMatch && policyCidsMatch) {
console.log(chalk_1.default.blue(`🔍 Found matching app version: ${appVersionKey}`));
return config.appVersion;
}
}
}
console.log(chalk_1.default.yellow(`🔍 No matching app version found for appId ${appId} with current abilities/policies`));
return undefined;
}
async loadState() {
try {
const data = await fs_1.promises.readFile(STATE_FILE_PATH, "utf-8");
const loadedState = JSON.parse(data);
// Validate version compatibility
if (loadedState.version !== STATE_VERSION) {
console.warn(chalk_1.default.yellow(`⚠️ State file version mismatch. Expected ${STATE_VERSION}, got ${loadedState.version}. Creating new state.`));
return;
}
this.nestedState = loadedState;
this.currentConfigState = this.getCurrentConfigState();
console.log(chalk_1.default.blue(`📁 Loaded existing state for ${this.testFileName}:${this.configHash}`));
}
catch (error) {
// File doesn't exist or is invalid - that's ok, we'll create it
console.log(chalk_1.default.blue("📝 No existing state file found. Will create one."));
this.currentConfigState = this.getCurrentConfigState();
}
}
async saveState() {
try {
// Update last used timestamp
if (this.currentConfigState) {
this.currentConfigState.lastUsed = new Date().toISOString();
}
await fs_1.promises.writeFile(STATE_FILE_PATH, JSON.stringify(this.nestedState, null, 2), "utf-8");
console.log(chalk_1.default.gray(`💾 Saved state for ${this.testFileName}:${this.configHash}`));
}
catch (error) {
console.error(chalk_1.default.red("❌ Failed to save state:"), error);
throw error;
}
}
generateAccount() {
const wallet = ethers_1.ethers.Wallet.createRandom();
return {
privateKey: wallet.privateKey,
address: wallet.address,
createdAt: new Date().toISOString(),
network: this.network,
};
}
getOrGenerateAccount(accountType, existingPrivateKey) {
// If private key provided from env, use it
if (existingPrivateKey) {
const wallet = new ethers_1.ethers.Wallet(existingPrivateKey);
return {
privateKey: existingPrivateKey,
address: wallet.address,
isNew: false,
};
}
// Ensure current config state is initialized (which initializes accounts structure)
this.getCurrentConfigState();
// Get accounts for this test file and network
const testFileAccounts = this.nestedState.testFiles[this.testFileName].accounts;
// Ensure network accounts exist for this test file
if (!testFileAccounts[this.network]) {
testFileAccounts[this.network] = {};
}
// Check if we have a saved account for this type in this test file
const savedAccount = testFileAccounts[this.network][accountType];
if (savedAccount && savedAccount.network === this.network) {
console.log(chalk_1.default.blue(`🔑 Using ${accountType} from ${this.testFileName}: ${savedAccount.address}`));
return {
privateKey: savedAccount.privateKey,
address: savedAccount.address,
isNew: false,
};
}
// Check for legacy shared account and migrate if exists
const legacyAccount = this.nestedState.sharedAccounts?.[this.network]?.[accountType];
if (legacyAccount && legacyAccount.network === this.network) {
console.log(chalk_1.default.blue(`🔄 Migrating shared ${accountType} to ${this.testFileName}: ${legacyAccount.address}`));
testFileAccounts[this.network][accountType] = legacyAccount;
return {
privateKey: legacyAccount.privateKey,
address: legacyAccount.address,
isNew: false,
};
}
// Generate new account for this test file
console.log(chalk_1.default.yellow(`🔑 Generating new ${accountType} account for ${this.testFileName}`));
const newAccount = this.generateAccount();
testFileAccounts[this.network][accountType] = newAccount;
return {
privateKey: newAccount.privateKey,
address: newAccount.address,
isNew: true,
};
}
// getGeneratedAccounts(): string[] {
// // Get accounts for this test file
// const testFileAccounts =
// this.nestedState.testFiles[this.testFileName]?.accounts?.[this.network] ||
// {};
// // Also check legacy shared accounts for backward compatibility
// const legacyAccounts =
// this.nestedState.sharedAccounts?.[this.network] || {};
// // Combine both sources
// const allAccounts = { ...legacyAccounts, ...testFileAccounts };
// return Object.entries(allAccounts)
// .filter(([_, account]) => account !== undefined)
// .map(([type, account]) => `${type}: ${account!.address}`);
// }
/**
* Get existing PKP or return undefined if needs minting
*/
getExistingPKP() {
// Ensure current config state is initialized
this.getCurrentConfigState();
// Get PKP for this test file and network
const testFilePkps = this.nestedState.testFiles[this.testFileName].pkps;
const existingPkp = testFilePkps[this.network];
if (existingPkp && existingPkp.network === this.network) {
console.log(chalk_1.default.blue(`🔑 Using PKP from ${this.testFileName}: ${existingPkp.ethAddress}`));
return existingPkp;
}
// Check for legacy shared PKP and migrate if exists
const legacyPkp = this.nestedState.sharedPkps?.[this.network];
if (legacyPkp && legacyPkp.network === this.network) {
console.log(chalk_1.default.blue(`🔄 Migrating shared PKP to ${this.testFileName}: ${legacyPkp.ethAddress}`));
testFilePkps[this.network] = legacyPkp;
return legacyPkp;
}
return undefined;
}
/**
* Save a newly minted PKP to state
*/
savePKP(pkpInfo) {
// Ensure current config state is initialized
this.getCurrentConfigState();
// Save PKP for this test file and network
const testFilePkps = this.nestedState.testFiles[this.testFileName].pkps;
testFilePkps[this.network] = {
...pkpInfo,
createdAt: new Date().toISOString(),
network: this.network,
};
console.log(chalk_1.default.gray(`💾 Saved PKP for ${this.testFileName}: ${pkpInfo.ethAddress}`));
}
/**
* Get or mint the single testing PKP
*/
async getOrMintPKP(mintFunction) {
// Check if we have an existing PKP
const existingPKP = this.getExistingPKP();
if (existingPKP) {
return {
pkp: {
tokenId: existingPKP.tokenId,
publicKey: existingPKP.publicKey,
ethAddress: existingPKP.ethAddress,
},
isNew: false,
};
}
// Mint new PKP
console.log(chalk_1.default.yellow(`🔨 Minting new testing PKP...`));
const newPKP = await mintFunction();
// Save to state
this.savePKP(newPKP);
return {
pkp: newPKP,
isNew: true,
};
}
/**
* Check if capacity credits are still valid (not expired within 1 day)
*/
isCapacityCreditValid(capacityCredit) {
const expirationDate = new Date(capacityCredit.expiresAt);
const now = new Date();
const oneDayFromNow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
// Consider expired if it expires within the next 24 hours
const isValid = expirationDate > oneDayFromNow;
// Add helpful logging when expired/expiring
if (!isValid) {
console.log(chalk_1.default.yellow(` ↳ Capacity credits expire at: ${expirationDate.toISOString()}`));
console.log(chalk_1.default.yellow(` ↳ Current time: ${now.toISOString()}`));
}
return isValid;
}
/**
* Get existing capacity credits or return undefined if needs minting
*/
getExistingCapacityCredits() {
// Ensure current config state is initialized
this.getCurrentConfigState();
// Get capacity credits for this test file and network
const testFileCC = this.nestedState.testFiles[this.testFileName].capacityCredits;
const existingCC = testFileCC[this.network];
if (existingCC && existingCC.network === this.network) {
if (this.isCapacityCreditValid(existingCC)) {
console.log(chalk_1.default.blue(`🎫 Using capacity credits from ${this.testFileName}: ${existingCC.capacityTokenIdStr}`));
console.log(chalk_1.default.blue(` ↳ Expires: ${existingCC.expiresAt}`));
return existingCC;
}
else {
console.log(chalk_1.default.yellow(`⚠️ Capacity credits found but expired/expiring soon for ${this.testFileName}`));
console.log(chalk_1.default.yellow(` ↳ Token ID: ${existingCC.capacityTokenIdStr}`));
}
}
// Check for legacy shared capacity credits and migrate if exists
const legacyCC = this.nestedState.sharedCapacityCredits?.[this.network];
if (legacyCC && legacyCC.network === this.network) {
if (this.isCapacityCreditValid(legacyCC)) {
console.log(chalk_1.default.blue(`🔄 Migrating shared capacity credits to ${this.testFileName}: ${legacyCC.capacityTokenIdStr}`));
testFileCC[this.network] = legacyCC;
return legacyCC;
}
}
return undefined;
}
/**
* Save newly minted capacity credits to state
*/
saveCapacityCredits(capacityCreditInfo) {
// Calculate expiration date using the same logic as mint-cc.ts
const mintedDate = new Date(capacityCreditInfo.mintedAtUtc);
const expirationDate = new Date(Date.UTC(mintedDate.getUTCFullYear(), mintedDate.getUTCMonth(), mintedDate.getUTCDate() +
capacityCreditInfo.daysUntilUTCMidnightExpiration, 0, 0, 0, 0));
// Ensure current config state is initialized
this.getCurrentConfigState();
// Save capacity credits for this test file and network
const testFileCC = this.nestedState.testFiles[this.testFileName].capacityCredits;
testFileCC[this.network] = {
...capacityCreditInfo,
network: this.network,
expiresAt: expirationDate.toISOString(),
};
console.log(chalk_1.default.green(`💾 Saved capacity credits for ${this.testFileName}: ${capacityCreditInfo.capacityTokenIdStr}`));
console.log(chalk_1.default.green(` ↳ Expires: ${expirationDate.toISOString()}`));
}
/**
* Get or mint capacity credits for testing
*/
async getOrMintCapacityCredits(mintFunction) {
// Check if we have existing valid capacity credits
const existingCC = this.getExistingCapacityCredits();
if (existingCC) {
return {
capacityCredits: {
capacityTokenIdStr: existingCC.capacityTokenIdStr,
capacityTokenId: existingCC.capacityTokenId,
requestsPerKilosecond: existingCC.requestsPerKilosecond,
daysUntilUTCMidnightExpiration: existingCC.daysUntilUTCMidnightExpiration,
mintedAtUtc: existingCC.mintedAtUtc,
},
isNew: false,
};
}
// Mint new capacity credits
console.log(chalk_1.default.yellow(`🎫 Minting new capacity credits...`));
const newCC = await mintFunction();
// Save to state
this.saveCapacityCredits(newCC);
return {
capacityCredits: newCC,
isNew: true,
};
}
/**
* Get existing Vincent app or return undefined if needs registration
* First checks current config, then searches all configs for apps with same delegatee
*/
getExistingVincentApp(delegateeAddress) {
const configState = this.getCurrentConfigState();
const existingApp = configState.state.vincentApp;
if (existingApp &&
existingApp.network === this.network &&
existingApp.delegateeAddress === delegateeAddress) {
console.log(chalk_1.default.blue(`🏢 Using saved Vincent app for config ${this.configHash}: ${existingApp.appId}`));
console.log(chalk_1.default.blue(` ↳ Version: ${existingApp.appVersion}`));
console.log(chalk_1.default.blue(` ↳ Delegatee: ${existingApp.delegateeAddress}`));
return existingApp;
}
// If no app in current config, search all configs for apps with same delegatee
// This handles the case where the smart contract prevents the same delegatee from registering multiple apps
if (this.nestedState.testFiles[this.testFileName]) {
const allConfigs = this.nestedState.testFiles[this.testFileName].configurations;
for (const [configHash, config] of Object.entries(allConfigs)) {
if (configHash === this.configHash)
continue; // Skip current config (already checked)
const appInOtherConfig = config.state.vincentApp;
if (appInOtherConfig &&
appInOtherConfig.network === this.network &&
appInOtherConfig.delegateeAddress === delegateeAddress) {
console.log(chalk_1.default.yellow(`🏢 Found existing Vincent app in different config ${configHash}: ${appInOtherConfig.appId}`));
console.log(chalk_1.default.yellow(` ↳ Version: ${appInOtherConfig.appVersion}`));
console.log(chalk_1.default.yellow(` ↳ Delegatee: ${appInOtherConfig.delegateeAddress}`));
console.log(chalk_1.default.yellow(` ↳ Copying app to current config ${this.configHash}`));
// Copy the app to current configuration since same delegatee can't register twice
configState.state.vincentApp = {
...appInOtherConfig,
// Update metadata to reflect current configuration
createdAt: new Date().toISOString(),
};
return appInOtherConfig;
}
}
}
return undefined;
}
/**
* Save a newly registered Vincent app to state
*/
saveVincentApp(appId, appVersion, delegateeAddress, abilityIpfsCids, abilityPolicies, policyParams) {
const configState = this.getCurrentConfigState();
configState.state.vincentApp = {
appId,
appVersion,
delegateeAddress,
abilityIpfsCids,
abilityPolicies,
policyParams,
createdAt: new Date().toISOString(),
network: this.network,
};
console.log(chalk_1.default.green(`💾 Saved Vincent app for config ${this.configHash}: ${appId}`));
console.log(chalk_1.default.green(` ↳ Version: ${appVersion}`));
console.log(chalk_1.default.green(` ↳ Delegatee: ${delegateeAddress}`));
}
// /**
// * Get or register Vincent app for testing
// */
// async getOrRegisterVincentApp(
// delegateeAddress: string,
// registerFunction: () => Promise<{ appId: string; appVersion: string }>,
// abilityIpfsCids: string[],
// abilityPolicies: string[][],
// abilityPolicyParameterNames: string[][],
// abilityPolicyParameterTypes: number[][],
// abilityPolicyParameterValues?: string[][]
// ): Promise<{
// appId: string;
// appVersion: string;
// isNew: boolean;
// abilityIpfsCids: string[];
// abilityPolicies: string[][];
// abilityPolicyParameterNames: string[][];
// abilityPolicyParameterTypes: number[][];
// abilityPolicyParameterValues?: string[][];
// }> {
// // Check if we have an existing Vincent app for this delegatee
// const existingApp = this.getExistingVincentApp(delegateeAddress);
// if (existingApp) {
// // Update the current configuration with the new ability/policy information
// // since we're reusing an existing app but with potentially different abilities
// const configState = this.getCurrentConfigState();
// if (configState.state.vincentApp) {
// console.log(
// chalk.blue(
// `🔄 Updating reused app ${existingApp.appId} with current ability/policy configuration`
// )
// );
// configState.state.vincentApp.abilityIpfsCids = abilityIpfsCids;
// configState.state.vincentApp.abilityPolicies = abilityPolicies;
// configState.state.vincentApp.abilityPolicyParameterNames =
// abilityPolicyParameterNames;
// configState.state.vincentApp.abilityPolicyParameterTypes =
// abilityPolicyParameterTypes;
// configState.state.vincentApp.abilityPolicyParameterValues =
// abilityPolicyParameterValues;
// }
// return {
// appId: existingApp.appId,
// appVersion: existingApp.appVersion,
// isNew: false,
// abilityIpfsCids,
// abilityPolicies,
// abilityPolicyParameterNames,
// abilityPolicyParameterTypes,
// abilityPolicyParameterValues,
// };
// }
// // Register new Vincent app
// console.log(chalk.yellow(`🏢 Registering new Vincent app...`));
// const { appId, appVersion } = await registerFunction();
// // Save to state
// this.saveVincentApp(
// appId,
// appVersion,
// delegateeAddress,
// abilityIpfsCids,
// abilityPolicies,
// abilityPolicyParameterNames,
// abilityPolicyParameterTypes,
// abilityPolicyParameterValues
// );
// return {
// appId,
// appVersion,
// isNew: true,
// abilityIpfsCids,
// abilityPolicies,
// abilityPolicyParameterNames,
// abilityPolicyParameterTypes,
// abilityPolicyParameterValues,
// };
// }
/**
* Update existing Vincent app state with parameter values
*/
updateVincentAppParameterValues(appId, policyParams) {
const configState = this.getCurrentConfigState();
if (configState.state.vincentApp &&
configState.state.vincentApp.appId === appId) {
configState.state.vincentApp.policyParams = policyParams;
console.log(chalk_1.default.green(`💾 Updated Vincent app parameter values for config ${this.configHash}: ${appId}`));
}
}
/**
* Check if PKP is already permitted for the given app version
*/
isPKPPermittedForAppVersion(pkpEthAddress, appId, appVersion) {
const configState = this.getCurrentConfigState();
if (!configState.state.pkpAppPermissions) {
return false;
}
const existingPermission = configState.state.pkpAppPermissions.find((permission) => permission.pkpEthAddress === pkpEthAddress &&
permission.appId === appId &&
permission.appVersion === appVersion &&
permission.network === this.network);
if (existingPermission) {
console.log(chalk_1.default.blue(`🔐 PKP ${pkpEthAddress} already permitted for app ${appId} v${appVersion} in config ${this.configHash}`));
console.log(chalk_1.default.blue(` ↳ Permitted at: ${existingPermission.permittedAt}`));
return true;
}
return false;
}
/**
* Save PKP app permission to state
*/
savePKPAppPermission(pkpEthAddress, appId, appVersion) {
const configState = this.getCurrentConfigState();
if (!configState.state.pkpAppPermissions) {
configState.state.pkpAppPermissions = [];
}
// Check if permission already exists to avoid duplicates
const existingIndex = configState.state.pkpAppPermissions.findIndex((permission) => permission.pkpEthAddress === pkpEthAddress &&
permission.appId === appId &&
permission.appVersion === appVersion &&
permission.network === this.network);
const newPermission = {
pkpEthAddress,
appId,
appVersion,
permittedAt: new Date().toISOString(),
network: this.network,
};
if (existingIndex >= 0) {
// Update existing permission
configState.state.pkpAppPermissions[existingIndex] = newPermission;
console.log(chalk_1.default.gray(`💾 Updated PKP app permission for config ${this.configHash}: PKP ${pkpEthAddress} for app ${appId} v${appVersion}`));
}
else {
// Add new permission
configState.state.pkpAppPermissions.push(newPermission);
console.log(chalk_1.default.gray(`💾 Saved PKP app permission for config ${this.configHash}: PKP ${pkpEthAddress} for app ${appId} v${appVersion}`));
}
}
}
exports.StateManager = StateManager;