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
25 changes: 25 additions & 0 deletions agent-manager/src/service/ProposalService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export class ProposalProxyService {
private base: string
constructor() {
this.base = process.env.BACKEND_BASE_URL || 'http://host.docker.internal:8000/api'
}
async fetchProposals(page = 1, pageSize = 10, sort = 'createDate', search = '') {
const url = new URL(this.base.replace(/\/+$/, '') + '/proposals')
url.searchParams.set('page', String(page))
url.searchParams.set('pageSize', String(pageSize))
url.searchParams.set('sort', sort)
if (search) url.searchParams.set('search', search)
console.log('[ProposalService] GET', url.toString())
const res = await fetch(url.toString())
console.log('[ProposalService] status', res.status)
if (!res.ok) {
throw new Error(`Proposal Service Error ${res.status}`)
}

const data = await res.json()
console.log('[ProposalService] items', Array.isArray(data?.items) ? data.items.length : 0)
return data
}
}

export const ProposalService = new ProposalProxyService()
8 changes: 8 additions & 0 deletions agent-manager/src/service/RPCTopicHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { saveTriggerHistory, updateAgentDrepRegistration } from '../repository/t
import { ILog } from './Manager/AgentManagerRPC'
import { metaDataService } from './MetadataService'
import { dbSync } from './DbSyncService'
import { ProposalService } from './ProposalService'

export class RPCTopicHandler {
managerWallet
Expand All @@ -19,6 +20,7 @@ export class RPCTopicHandler {
console.error('Unknown event type', eventName, 'received')
return Promise.resolve()
} else {
console.log('[Manager] Dispatching to handler', { eventName, connection_id })
return handler.bind(this)(connection_id, args)
}
}
Expand Down Expand Up @@ -86,4 +88,10 @@ export class RPCTopicHandler {
const [url, hash] = args
return metaDataService.fetchMetadata(url, hash)
}

fetchProposals(connection_id: string, args: any[]) {
const [page = 1, pageSize = 10, search = '', sort = 'CreatedDate'] = args || []
console.log('[Manager] fetchProposals handler', { connection_id, page, pageSize, search, sort })
return ProposalService.fetchProposals(page, pageSize, sort, search)
}
}
3 changes: 2 additions & 1 deletion agent-node/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
TOKEN=
WS_URL=ws://localhost:3001
GEMINI_API_KEY=
GEMINI_API_KEY=
MCP_HTTP_PORT=
8 changes: 7 additions & 1 deletion agent-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,24 @@
"dependencies": {
"@emurgo/cardano-serialization-lib-asmjs": "^11.5.0",
"@google/genai": "^1.13.0",
"@modelcontextprotocol/sdk": "^1.17.4",
"@types/ws": "^8.5.10",
"axios": "^1.6.8",
"bech32": "^2.0.0",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^5.1.0",
"kuber-client": "^2.0.0",
"libcardano": "1.4.19",
"luxon": "^3.4.4",
"node-cron": "^3.0.3",
"ws": "^8.18.0"
"ws": "^8.18.0",
"zod": "3.25.76"
},
"devDependencies": {
"@eslint/js": "^9.4.0",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/luxon": "^3.4.2",
"@types/node-cron": "^3.0.11",
"@types/websocket": "^1.0.10",
Expand Down
32 changes: 32 additions & 0 deletions agent-node/src/executor/AgentFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'fs'
import path from 'path'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export interface FunctionHolder {
[key: string]: any
Expand Down Expand Up @@ -73,6 +74,37 @@ function loadHandlersSync(directory: string): FunctionGroup {
const relativeDirectoryPath = '../functions'
const directoryPath = path.resolve(__dirname, relativeDirectoryPath)

// discover per-function schema
export function getFunctionSchemas(): Record<string, FunctionSchemaSpec> {
const schmeas: Record<string, FunctionSchemaSpec> = {}
const files = fs.readdirSync(directoryPath) // reads the func dir synchronously -> "../functions"
files.forEach((file) => {
if (!['.js', '.ts'].includes(path.extname(file))) return // filter
const baseFileName = path.basename(file, path.extname(file)) // base file name without extension
const filePath = path.join(directoryPath, baseFileName) // build total path
try {
const module: any = requireModule(filePath) // require wrapper
const schema = module.schema as Partial<FunctionSchemaSpec> | undefined
if (schema && typeof schema === 'object') {
// guarding
const id = schema.id || baseFileName
schmeas[id] = {
id,
name: schema.name || id,
description: schema.description || '',
response: schema.response,
params: Array.isArray(schema.params) ? schema.params : [],
}
}
} catch (e) {
console.error(`Failed to load meta from ${filePath}:`, e)
}
})
return schmeas
}
// Returns a map like:
// schemas = { transferADA: { id: 'transferADA', name: 'Transfer ADA', …, params: [...] } }

// Export the function
export function getHandlers(): FunctionGroup {
return loadHandlersSync(directoryPath)
Expand Down
17 changes: 13 additions & 4 deletions agent-node/src/executor/AgentRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { HdWallet } from 'libcardano'
import { AgentWalletDetails } from '../types/types'
import { globalState } from '../constants/global'
import { EventContext } from './BaseFunction'
import { CallLog } from './Executor'

export class AgentRunner {
executor: Executor
Expand All @@ -18,10 +19,18 @@ export class AgentRunner {
this.executor = new Executor(null, managerInterface, txListener)
}

async invokeFunction(triggerType: TriggerType, instanceIndex: number, method: string, ...args: any) {
this.executor.invokeFunction(method, ...args).then((result) => {
saveTxLog(result, this.managerInterface, triggerType, instanceIndex)
})
async invokeFunction(triggerType: TriggerType, instanceIndex: number, method: string, ...args: any): Promise<any> {
const callLogs: CallLog[] = await this.executor.invokeFunction(method, ...args)
// to prevent mutation on array
const main = callLogs.find((l) => l?.function === method) ?? callLogs[0]
try {
// shallow
saveTxLog([...callLogs], this.managerInterface, triggerType, instanceIndex)
} catch (e) {
console.error('saveTxLog Error:', e)
}
// return result to mcp
return main && (main as any).return !== undefined ? (main as any).return : undefined
}

async invokeFunctionWithEventContext(
Expand Down
27 changes: 27 additions & 0 deletions agent-node/src/functions/createInfoGovAction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'createInfoGovAction',
name: 'Info Action Proposal',
description: 'Create an informational governance action',
params: [
{
name: 'anchor',
schema: {
type: 'object',
description: 'Optional anchor object { url, dataHash } (leave empty to auto-generate)',
optional: true,
},
},
],
response: {
type: 'object',
properties: {
hash: { type: 'string' },
cborHex: { type: 'string' },
description: { type: 'string' },
type: { type: 'string' },
},
response_text: 'Info action submitted: hash: ${result.hash}',
},
}

export default async function handler(context: FunctionContext, anchor: Record<string, any>) {
const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent())
Expand Down
18 changes: 18 additions & 0 deletions agent-node/src/functions/dRepDeRegistration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'dRepDeRegistration',
name: 'DRep De‑registration',
description: 'Deregister the DRep associated with this agent (submit deregistration certificate).',
params: [],
response: {
type: 'object',
properties: {
cborHex: { type: 'string' },
description: { type: 'string' },
hash: { type: 'string' },
type: { type: 'string' },
},
response_text: 'DRep de‑registration submitted: hash: ${result.hash}',
},
}

export default async function handler(context: FunctionContext) {
const req = {
Expand Down
22 changes: 22 additions & 0 deletions agent-node/src/functions/dRepRegistration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'dRepRegistration',
name: 'DRep Registration',
description: 'Register as a Delegated Representative',
params: [
{
name: 'anchor',
schema: {
type: 'object',
description: 'Optional anchor object { url, dataHash } (leave empty to auto-generate)',
optional: true,
},
},
],
response: {
type: 'object',
properties: { hash: { type: 'string' } },
response_text: 'DRep Registration submitted: hash: ${result.hash}',
},
}

export default async function builtin(context: FunctionContext, anchor: any) {
const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateDrepMetadataContent())
Expand Down
26 changes: 26 additions & 0 deletions agent-node/src/functions/delegation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'delegation',
name: 'Delegation',
description: 'Delegate voting power: abstain | no-confidence | Drep/Pool',
params: [
{
name: 'delegation_params',
schema: {
type: 'string',
description: "One of: 'Abstain', 'No Confidence', 'Drep/Pool'",
},
},
],
response: {
type: 'object',
properties: {
cborHex: { type: 'string' },
description: { type: 'string' },
hash: { type: 'string' },
type: { type: 'string' },
},
response_text: 'Delegation submitted: hash: ${result.hash}',
},
}

export default async function handler(context: FunctionContext, delegation: any) {
let drep = ''
Expand Down
23 changes: 23 additions & 0 deletions agent-node/src/functions/noConfidence.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'noConfidence',
name: 'No Confidence',
description: 'Submit a No‑Confidence governance action. Optionally provide an anchor {url,dataHash}.',
params: [
{
name: 'anchor',
schema: { type: 'object', description: 'Optional anchor object { url, dataHash }', optional: true },
},
],
response: {
type: 'object',
properties: {
cborHex: { type: 'string' },
description: { type: 'string' },
hash: { type: 'string' },
type: { type: 'string' },
},
response_text: 'No‑confidence submitted: hash: ${result.hash}',
},
}

export default async function handler(context: FunctionContext, anchor: any) {
const { dataHash, url } = await context.builtins.saveMetadata(context.helpers.generateProposalMetadataContent())
Expand Down
26 changes: 26 additions & 0 deletions agent-node/src/functions/proposalNewConstitution.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

// ...existing code...
export const schema: FunctionSchemaSpec = {
id: 'proposalNewConstitution',
name: 'Constitution',
description: 'Submit a new constitution',
params: [
{ name: 'anchor', schema: { type: 'object', description: 'Anchor { url, dataHash }' } },
{ name: 'newConstitution', schema: { type: 'object', description: 'New constitution { url, dataHash }' } },
{
name: 'guardrailScript',
schema: { type: 'string', description: 'Optional guardrail script CBOR', optional: true },
},
],
response: {
type: 'object',
properties: {
hash: { type: 'string' },
cborHex: { type: 'string' },
description: { type: 'string' },
type: { type: 'string' },
},
response_text: 'Constitution submitted: hash: ${result.hash}',
},
}

export default async function handler(
context: FunctionContext,
Expand Down
17 changes: 17 additions & 0 deletions agent-node/src/functions/registerStake.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'registerStake',
name: 'Register Stake Key',
description: 'Register the agent stake key on-chain (submit registration certificate).',
params: [],
response: {
type: 'object',
properties: {
cborHex: { type: 'string' },
description: { type: 'string' },
hash: { type: 'string' },
type: { type: 'string' },
},
response_text: 'Stake registration submitted: hash: ${result.hash}',
},
}
export default async function builtin(context: FunctionContext) {
const req = {
certificates: [
Expand Down
18 changes: 18 additions & 0 deletions agent-node/src/functions/stakeDeRegistration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
import { FunctionContext } from '../executor/BaseFunction'
import { FunctionSchemaSpec } from '../utils/functionSchema'

export const schema: FunctionSchemaSpec = {
id: 'stakeDeRegistration',
name: 'Stake De-registration',
description: 'Deregister stake key for this agent (submit deregistration certificate).',
params: [],
response: {
type: 'object',
properties: {
cborHex: { type: 'string' },
description: { type: 'string' },
hash: { type: 'string' },
type: { type: 'string' },
},
response_text: 'Stake deregistration submitted: hash: ${result.hash}',
},
}

export default async function handler(context: FunctionContext) {
const req = {
Expand Down
Loading