UNPKG

permamind

Version:

An MCP server that provides an immortal memory layer for AI agents and clients

802 lines (757 loc) 23.3 kB
const AO_TEAL_TYPES = ` -- AO Process Types for Teal local record AO record Message Id: string Target: string From: string Data: string Tags: {string:string} Timestamp: number "Block-Height": number "Hash-Chain": string end record Process Id: string Tags: {string:string} Handlers: {function} Modules: {string} Scheduler: string Owner: string end record Handler Name: string Pattern: function Handle: function end record Tag name: string value: string end record Response Output: string Messages: {Message} Spawns: {Process} Assignments: {string:any} end end -- AO Global Functions declare ao: AO.Process declare Handlers: AO.Handler[] declare Send: function(target: string, data: string, tags: {string:string}?) declare Spawn: function(module: string, data: string?, tags: {string:string}?) declare Assign: function(processes: {string}, message: string) `; const TEAL_TEMPLATES = { dao: ` -- AO DAO Process Template (Teal) local record DAOState Name: string Description: string TokenProcess: string VotingPower: {string:number} Proposals: {Proposal} ProposalCount: number Admin: string VotingPeriod: number QuorumThreshold: number end local record Proposal Id: number Title: string Description: string Proposer: string Votes: {string:boolean} VotesFor: number VotesAgainst: number Created: number Expires: number Executed: boolean Status: string end local State: DAOState = { Name = "{{DAO_NAME}}", Description = "{{DAO_DESCRIPTION}}", TokenProcess = "{{TOKEN_PROCESS}}", VotingPower = {}, Proposals = {}, ProposalCount = 0, Admin = "{{ADMIN_ADDRESS}}", VotingPeriod = 604800, -- 7 days QuorumThreshold = 51 } -- Handler: Create Proposal local function createProposal(msg: AO.Message): AO.Response local title = msg.Tags.Title local description = msg.Tags.Description if not title or not description then return { Output = json.encode({Error = "Title and description required"}), Messages = {}, Spawns = {}, Assignments = {} } end State.ProposalCount = State.ProposalCount + 1 local proposal: Proposal = { Id = State.ProposalCount, Title = title, Description = description, Proposer = msg.From, Votes = {}, VotesFor = 0, VotesAgainst = 0, Created = msg.Timestamp, Expires = msg.Timestamp + State.VotingPeriod, Executed = false, Status = "Active" } State.Proposals[State.ProposalCount] = proposal return { Output = json.encode({ Success = true, ProposalId = State.ProposalCount, Proposal = proposal }), Messages = {}, Spawns = {}, Assignments = {} } end -- Register Handlers Handlers.add("create-proposal", Handlers.utils.hasMatchingTag("Action", "Create-Proposal"), createProposal) `, game: ` -- AO Game Process Template (Teal) local record GameState Name: string Players: {string:Player} MaxPlayers: number Status: string CurrentTurn: string TurnCount: number Winner: string StartTime: number EndTime: number end local record Player Id: string Name: string Score: number Active: boolean JoinedAt: number LastAction: number end local State: GameState = { Name = "{{GAME_NAME}}", Players = {}, MaxPlayers = 10, Status = "Waiting", CurrentTurn = "", TurnCount = 0, Winner = "", StartTime = 0, EndTime = 0 } -- Handler: Join Game local function joinGame(msg: AO.Message): AO.Response local playerName = msg.Tags.Name or msg.From if State.Players[msg.From] then return { Output = json.encode({Error = "Already joined"}), Messages = {}, Spawns = {}, Assignments = {} } end if #State.Players >= State.MaxPlayers then return { Output = json.encode({Error = "Game full"}), Messages = {}, Spawns = {}, Assignments = {} } end State.Players[msg.From] = { Id = msg.From, Name = playerName, Score = 0, Active = true, JoinedAt = msg.Timestamp, LastAction = msg.Timestamp } return { Output = json.encode({ Success = true, PlayerId = msg.From, PlayerCount = #State.Players }), Messages = {}, Spawns = {}, Assignments = {} } end -- Register Handlers Handlers.add("join-game", Handlers.utils.hasMatchingTag("Action", "Join-Game"), joinGame) `, generic: ` -- AO Generic Process Template (Teal) local record ProcessState Name: string Owner: string Data: {string:any} Handlers: {string} Version: string Created: number Updated: number end local State: ProcessState = { Name = "{{PROCESS_NAME}}", Owner = "{{OWNER_ADDRESS}}", Data = {}, Handlers = {}, Version = "1.0.0", Created = 0, Updated = 0 } -- Handler: Get Process Info local function info(msg: AO.Message): AO.Response return { Output = json.encode({ Name = State.Name, Owner = State.Owner, Version = State.Version, Created = State.Created, Updated = State.Updated, Handlers = State.Handlers }), Messages = {}, Spawns = {}, Assignments = {} } end -- Handler: Set Data local function setData(msg: AO.Message): AO.Response local key = msg.Tags.Key local value = msg.Tags.Value if not key or not value then return { Output = json.encode({Error = "Key and value required"}), Messages = {}, Spawns = {}, Assignments = {} } end State.Data[key] = value State.Updated = msg.Timestamp return { Output = json.encode({ Success = true, Key = key, Value = value }), Messages = {}, Spawns = {}, Assignments = {} } end -- Register Handlers Handlers.add("info", Handlers.utils.hasMatchingTag("Action", "Info"), info) Handlers.add("set-data", Handlers.utils.hasMatchingTag("Action", "Set-Data"), setData) `, token: ` -- AO Token Process Template (Teal) local record TokenState Name: string Ticker: string Denomination: number TotalSupply: number Balances: {string:number} Owner: string Transferable: boolean Mintable: boolean Burnable: boolean end local State: TokenState = { Name = "{{TOKEN_NAME}}", Ticker = "{{TOKEN_TICKER}}", Denomination = 12, TotalSupply = 0, Balances = {}, Owner = "{{OWNER_ADDRESS}}", Transferable = true, Mintable = true, Burnable = true } -- Handler: Get Token Information local function info(msg: AO.Message): AO.Response return { Output = json.encode({ Name = State.Name, Ticker = State.Ticker, Denomination = State.Denomination, TotalSupply = State.TotalSupply, Owner = State.Owner, Transferable = State.Transferable, Mintable = State.Mintable, Burnable = State.Burnable }), Messages = {}, Spawns = {}, Assignments = {} } end -- Handler: Get Balance local function balance(msg: AO.Message): AO.Response local target = msg.Tags.Target or msg.From local balance = State.Balances[target] or 0 return { Output = json.encode({ Balance = tostring(balance), Ticker = State.Ticker, Account = target, Data = json.encode({ Balance = balance, Ticker = State.Ticker, Account = target }) }), Messages = {}, Spawns = {}, Assignments = {} } end -- Handler: Transfer Tokens local function transfer(msg: AO.Message): AO.Response local recipient = msg.Tags.Recipient local quantity = tonumber(msg.Tags.Quantity) if not recipient or not quantity then return { Output = json.encode({Error = "Invalid transfer parameters"}), Messages = {}, Spawns = {}, Assignments = {} } end local sender_balance = State.Balances[msg.From] or 0 if sender_balance >= quantity then State.Balances[msg.From] = sender_balance - quantity State.Balances[recipient] = (State.Balances[recipient] or 0) + quantity return { Output = json.encode({ Success = true, From = msg.From, To = recipient, Quantity = quantity }), Messages = {}, Spawns = {}, Assignments = {} } else return { Output = json.encode({Error = "Insufficient balance"}), Messages = {}, Spawns = {}, Assignments = {} } end end -- Register Handlers Handlers.add("info", Handlers.utils.hasMatchingTag("Action", "Info"), info) Handlers.add("balance", Handlers.utils.hasMatchingTag("Action", "Balance"), balance) Handlers.add("transfer", Handlers.utils.hasMatchingTag("Action", "Transfer"), transfer) `, }; const service = (processService, templateService) => { return { compileTealToLua: async (source, options = {}) => { try { // Simulate Teal compilation process // In a real implementation, this would call the Teal compiler const errors = []; const warnings = []; // Basic syntax validation if (!source.includes("local") && !source.includes("function")) { errors.push("No valid Teal code structure found"); } // Check for AO-specific patterns if (source.includes("Handlers.add") && !source.includes("AO.Message")) { warnings.push("Consider using AO.Message type for message handlers"); } // Type checking simulation const typeCheckErrors = validateTealSyntax(source); errors.push(...typeCheckErrors); if (errors.length > 0) { return { errors, success: false, warnings, }; } // Simulate successful compilation const compiledLua = convertTealToLua(source); return { compiledLua, success: true, warnings, }; } catch (error) { return { errors: [ error instanceof Error ? error.message : "Compilation failed", ], success: false, }; } }, createProcessDefinition: async (source, metadata) => { const compileResult = await service(processService, templateService).compileTealToLua(source, metadata.compileOptions); if (!compileResult.success || !compileResult.compiledLua) { throw new Error(`Teal compilation failed: ${compileResult.errors?.join(", ")}`); } const typeDefinitions = await service(processService, templateService).generateTypeDefinitions(["token", "dao", "game"]); return { compiledLua: compileResult.compiledLua, dependencies: extractDependencies(source), id: generateProcessId(), metadata, name: metadata.description || "Teal Process", source, typeDefinitions, version: metadata.version, }; }, createTealTemplate: async (templateType, name, metadata) => { const template = TEAL_TEMPLATES[templateType]; if (!template) { throw new Error(`Unknown template type: ${templateType}`); } // Replace template variables let processedTemplate = template; const replacements = { "{{ADMIN_ADDRESS}}": metadata.author || "ADMIN", "{{DAO_DESCRIPTION}}": metadata.description || "DAO Description", "{{DAO_NAME}}": name, "{{GAME_NAME}}": name, "{{OWNER_ADDRESS}}": metadata.author || "OWNER", "{{PROCESS_NAME}}": name, "{{TOKEN_NAME}}": name, "{{TOKEN_PROCESS}}": "TOKEN_PROCESS_ID", "{{TOKEN_TICKER}}": metadata.version || "TKN", }; for (const [key, value] of Object.entries(replacements)) { processedTemplate = processedTemplate.replace(new RegExp(key, "g"), value); } return { category: templateType, dependencies: ["json"], description: metadata.description || `${templateType} process template`, metadata: { aoVersion: metadata.aoVersion || "2.0.0", author: metadata.author || "Unknown", features: getTemplateFeatures(templateType), version: metadata.version || "1.0.0", }, name, source: processedTemplate, }; }, generateTypeDefinitions: async (aoPatterns) => { const typeDefinitions = []; // Generate basic AO types typeDefinitions.push({ definition: AO_TEAL_TYPES, documentation: "Core AO process types and interfaces", name: "AO", type: "record", }); // Generate pattern-specific types for (const pattern of aoPatterns) { switch (pattern) { case "dao": typeDefinitions.push({ definition: ` local record DAOState Name: string Description: string TokenProcess: string VotingPower: {string:number} Proposals: {Proposal} ProposalCount: number Admin: string VotingPeriod: number QuorumThreshold: number end `, documentation: "DAO process state structure", name: "DAOState", type: "record", }); break; case "game": typeDefinitions.push({ definition: ` local record GameState Name: string Players: {string:Player} MaxPlayers: number Status: string CurrentTurn: string TurnCount: number Winner: string StartTime: number EndTime: number end `, documentation: "Game process state structure", name: "GameState", type: "record", }); break; case "token": typeDefinitions.push({ definition: ` local record TokenState Name: string Ticker: string Denomination: number TotalSupply: number Balances: {string:number} Owner: string Transferable: boolean Mintable: boolean Burnable: boolean end `, documentation: "Token process state structure", name: "TokenState", type: "record", }); break; } } return typeDefinitions; }, integrateWithAOServices: async (compiledLua, processId) => { try { // Validate the compiled Lua is compatible with AO const validationResult = validateAOCompatibility(compiledLua); if (!validationResult.isValid) { throw new Error(`AO compatibility error: ${validationResult.error}`); } // Add AO-specific wrapper and initialization const aoWrappedLua = wrapForAO(compiledLua); // Integrate with existing ProcessCommunicationService patterns const integrationResult = await integrateWithProcessService(aoWrappedLua, processId, processService); return integrationResult; } catch (error) { throw new Error(`AO integration failed: ${error instanceof Error ? error.message : "Unknown error"}`); } }, validateTealTypes: async (source) => { try { const errors = []; const warnings = []; // Validate type definitions const typeErrors = validateTealTypes(source); errors.push(...typeErrors); // Check for AO compatibility if (source.includes("ao.") && !source.includes("declare ao:")) { warnings.push("Consider declaring AO global types"); } return { errors: errors.length > 0 ? errors : undefined, success: errors.length === 0, warnings: warnings.length > 0 ? warnings : undefined, }; } catch (error) { return { errors: [ error instanceof Error ? error.message : "Type validation failed", ], success: false, }; } }, }; }; // Helper functions const validateTealSyntax = (source) => { const errors = []; // Check for basic Teal syntax if (!source.includes("local") && !source.includes("function")) { errors.push("No valid Teal constructs found"); } // Check for proper record definitions const recordMatches = source.match(/local record \w+/g); if (recordMatches) { for (const match of recordMatches) { if (!source.includes(`end -- ${match}`)) { // This is a simple check; real Teal would need proper parsing } } } return errors; }; const validateTealTypes = (source) => { const errors = []; // Check for type annotations const functionMatches = source.match(/function \w+\([^)]*\)/g); if (functionMatches) { for (const match of functionMatches) { if (!match.includes(":")) { errors.push(`Function ${match} missing type annotations`); } } } return errors; }; const convertTealToLua = (source) => { // This is a simplified conversion - real Teal compiler would handle this let lua = source; // Remove type annotations lua = lua.replace(/:\s*\w+(\|\w+)*(\[\])*/g, ""); lua = lua.replace(/local record \w+/g, "-- Record definition"); lua = lua.replace(/end -- Record definition/g, ""); // Convert basic patterns lua = lua.replace(/local (\w+): (\w+) = /g, "local $1 = "); return lua; }; const validateAOCompatibility = (lua) => { // Check for AO-specific patterns if (!lua.includes("Handlers") && !lua.includes("ao.")) { return { error: "No AO handler patterns found", isValid: false }; } // Check for proper message handling if (lua.includes("Handlers.add") && !lua.includes("msg.")) { return { error: "Handler functions must accept message parameter", isValid: false, }; } return { isValid: true }; }; const wrapForAO = (lua) => { const aoWrapper = ` -- AO Process Wrapper (Generated from Teal) local json = require("json") -- Process initialization if not ao then ao = { id = ao.id or "PROCESS_ID", env = ao.env or {}, authorities = ao.authorities or {}, } end -- Initialize handlers if not present if not Handlers then Handlers = { _handlers = {}, add = function(name, pattern, handler) table.insert(Handlers._handlers, { name = name, pattern = pattern, handler = handler }) end, utils = { hasMatchingTag = function(name, value) return function(msg) return msg.Tags[name] == value end end } } end -- User code begins here ${lua} -- AO message processing return function(msg) for _, handler in ipairs(Handlers._handlers) do if handler.pattern(msg) then return handler.handler(msg) end end return { Output = json.encode({Error = "No handler matched"}), Messages = {}, Spawns = {}, Assignments = {} } end `; return aoWrapper; }; const integrateWithProcessService = async (lua, processId, processService) => { // Validate the process service can work with this Lua code try { // Extract handler definitions from the Lua code const handlers = extractHandlers(lua); // Validate each handler is compatible with ProcessCommunicationService for (const handler of handlers) { // Check handler structure if (!handler.name || !handler.pattern || !handler.handler) { throw new Error(`Invalid handler structure: ${handler.name}`); } } // Integration successful return lua; } catch (error) { throw new Error(`Process service integration failed: ${error instanceof Error ? error.message : "Unknown error"}`); } }; const extractHandlers = (lua) => { const handlers = []; // Extract handler definitions using regex const handlerMatches = lua.match(/Handlers\.add\([^)]+\)/g); if (handlerMatches) { for (const match of handlerMatches) { const parts = match.match(/Handlers\.add\("([^"]+)",\s*([^,]+),\s*([^)]+)\)/); if (parts) { handlers.push({ handler: parts[3], name: parts[1], pattern: parts[2], }); } } } return handlers; }; const getTemplateFeatures = (templateType) => { switch (templateType) { case "dao": return ["proposals", "voting", "governance", "treasury"]; case "game": return ["players", "turns", "scoring", "state"]; case "generic": return ["data", "handlers", "info"]; case "token": return ["balance", "transfer", "info", "mint", "burn"]; default: return []; } }; const generateProcessId = () => { return `teal-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; }; const extractDependencies = (source) => { const dependencies = []; // Extract require statements const requireMatches = source.match(/require\(["']([^"']+)["']\)/g); if (requireMatches) { for (const match of requireMatches) { const dep = match.match(/require\(["']([^"']+)["']\)/); if (dep) { dependencies.push(dep[1]); } } } return dependencies; }; export const createTealCompilerService = (processService, templateService) => service(processService, templateService);