@rookdaemon/agora 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/chunk-JUOGKXFN.js +1645 -0
- package/dist/chunk-JUOGKXFN.js.map +1 -0
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +1163 -1137
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1613 -25
- package/dist/index.js +1135 -24
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/config.d.ts +0 -59
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -115
- package/dist/config.js.map +0 -1
- package/dist/discovery/bootstrap.d.ts +0 -32
- package/dist/discovery/bootstrap.d.ts.map +0 -1
- package/dist/discovery/bootstrap.js +0 -36
- package/dist/discovery/bootstrap.js.map +0 -1
- package/dist/discovery/peer-discovery.d.ts +0 -59
- package/dist/discovery/peer-discovery.d.ts.map +0 -1
- package/dist/discovery/peer-discovery.js +0 -108
- package/dist/discovery/peer-discovery.js.map +0 -1
- package/dist/identity/keypair.d.ts +0 -42
- package/dist/identity/keypair.d.ts.map +0 -1
- package/dist/identity/keypair.js +0 -83
- package/dist/identity/keypair.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/message/envelope.d.ts +0 -59
- package/dist/message/envelope.d.ts.map +0 -1
- package/dist/message/envelope.js +0 -83
- package/dist/message/envelope.js.map +0 -1
- package/dist/message/types/paper-discovery.d.ts +0 -28
- package/dist/message/types/paper-discovery.d.ts.map +0 -1
- package/dist/message/types/paper-discovery.js +0 -2
- package/dist/message/types/paper-discovery.js.map +0 -1
- package/dist/message/types/peer-discovery.d.ts +0 -78
- package/dist/message/types/peer-discovery.d.ts.map +0 -1
- package/dist/message/types/peer-discovery.js +0 -90
- package/dist/message/types/peer-discovery.js.map +0 -1
- package/dist/peer/client.d.ts +0 -50
- package/dist/peer/client.d.ts.map +0 -1
- package/dist/peer/client.js +0 -138
- package/dist/peer/client.js.map +0 -1
- package/dist/peer/manager.d.ts +0 -65
- package/dist/peer/manager.d.ts.map +0 -1
- package/dist/peer/manager.js +0 -153
- package/dist/peer/manager.js.map +0 -1
- package/dist/peer/server.d.ts +0 -65
- package/dist/peer/server.d.ts.map +0 -1
- package/dist/peer/server.js +0 -154
- package/dist/peer/server.js.map +0 -1
- package/dist/registry/capability.d.ts +0 -44
- package/dist/registry/capability.d.ts.map +0 -1
- package/dist/registry/capability.js +0 -94
- package/dist/registry/capability.js.map +0 -1
- package/dist/registry/discovery-service.d.ts +0 -64
- package/dist/registry/discovery-service.d.ts.map +0 -1
- package/dist/registry/discovery-service.js +0 -129
- package/dist/registry/discovery-service.js.map +0 -1
- package/dist/registry/messages.d.ts +0 -105
- package/dist/registry/messages.d.ts.map +0 -1
- package/dist/registry/messages.js +0 -2
- package/dist/registry/messages.js.map +0 -1
- package/dist/registry/peer-store.d.ts +0 -57
- package/dist/registry/peer-store.d.ts.map +0 -1
- package/dist/registry/peer-store.js +0 -92
- package/dist/registry/peer-store.js.map +0 -1
- package/dist/registry/peer.d.ts +0 -20
- package/dist/registry/peer.d.ts.map +0 -1
- package/dist/registry/peer.js +0 -2
- package/dist/registry/peer.js.map +0 -1
- package/dist/relay/client.d.ts +0 -112
- package/dist/relay/client.d.ts.map +0 -1
- package/dist/relay/client.js +0 -281
- package/dist/relay/client.js.map +0 -1
- package/dist/relay/server.d.ts +0 -76
- package/dist/relay/server.d.ts.map +0 -1
- package/dist/relay/server.js +0 -338
- package/dist/relay/server.js.map +0 -1
- package/dist/relay/types.d.ts +0 -35
- package/dist/relay/types.d.ts.map +0 -1
- package/dist/relay/types.js +0 -2
- package/dist/relay/types.js.map +0 -1
- package/dist/reputation/commit-reveal.d.ts +0 -45
- package/dist/reputation/commit-reveal.d.ts.map +0 -1
- package/dist/reputation/commit-reveal.js +0 -125
- package/dist/reputation/commit-reveal.js.map +0 -1
- package/dist/reputation/scoring.d.ts +0 -31
- package/dist/reputation/scoring.d.ts.map +0 -1
- package/dist/reputation/scoring.js +0 -105
- package/dist/reputation/scoring.js.map +0 -1
- package/dist/reputation/store.d.ts +0 -83
- package/dist/reputation/store.d.ts.map +0 -1
- package/dist/reputation/store.js +0 -202
- package/dist/reputation/store.js.map +0 -1
- package/dist/reputation/types.d.ts +0 -150
- package/dist/reputation/types.d.ts.map +0 -1
- package/dist/reputation/types.js +0 -113
- package/dist/reputation/types.js.map +0 -1
- package/dist/reputation/verification.d.ts +0 -28
- package/dist/reputation/verification.d.ts.map +0 -1
- package/dist/reputation/verification.js +0 -91
- package/dist/reputation/verification.js.map +0 -1
- package/dist/service.d.ts +0 -90
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js +0 -176
- package/dist/service.js.map +0 -1
- package/dist/transport/http.d.ts +0 -41
- package/dist/transport/http.d.ts.map +0 -1
- package/dist/transport/http.js +0 -103
- package/dist/transport/http.js.map +0 -1
- package/dist/transport/peer-config.d.ts +0 -38
- package/dist/transport/peer-config.d.ts.map +0 -1
- package/dist/transport/peer-config.js +0 -41
- package/dist/transport/peer-config.js.map +0 -1
- package/dist/transport/relay.d.ts +0 -30
- package/dist/transport/relay.d.ts.map +0 -1
- package/dist/transport/relay.js +0 -85
- package/dist/transport/relay.js.map +0 -1
- package/dist/utils.d.ts +0 -40
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -59
- package/dist/utils.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"sources":["../src/registry/capability.ts","../src/registry/peer-store.ts","../src/registry/discovery-service.ts","../src/message/types/peer-discovery.ts","../src/config.ts","../src/relay/message-buffer.ts","../src/relay/jwt-auth.ts","../src/relay/rest-api.ts","../src/relay/run-relay.ts","../src/service.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\n/**\n * A capability describes something an agent can do\n */\nexport interface Capability {\n /** Unique ID (content-addressed hash of name + version + schema) */\n id: string;\n /** Human-readable name: 'code-review', 'summarization', 'translation' */\n name: string;\n /** Semantic version */\n version: string;\n /** What the capability does */\n description: string;\n /** JSON Schema for expected input */\n inputSchema?: object;\n /** JSON Schema for expected output */\n outputSchema?: object;\n /** Discovery tags: ['code', 'typescript', 'review'] */\n tags: string[];\n}\n\n/**\n * Deterministic JSON serialization for capability hashing.\n * Recursively sorts object keys.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || value === undefined) return JSON.stringify(value);\n if (typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n const keys = Object.keys(value as Record<string, unknown>).sort();\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Compute content-addressed ID for a capability based on name, version, and schemas.\n */\nfunction computeCapabilityId(name: string, version: string, inputSchema?: object, outputSchema?: object): string {\n const data = {\n name,\n version,\n ...(inputSchema !== undefined ? { inputSchema } : {}),\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n };\n const canonical = stableStringify(data);\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Creates a capability with a content-addressed ID.\n * \n * @param name - Human-readable capability name\n * @param version - Semantic version string\n * @param description - Description of what the capability does\n * @param options - Optional input/output schemas and tags\n * @returns A Capability object with computed ID\n */\nexport function createCapability(\n name: string,\n version: string,\n description: string,\n options: {\n inputSchema?: object;\n outputSchema?: object;\n tags?: string[];\n } = {}\n): Capability {\n const { inputSchema, outputSchema, tags = [] } = options;\n \n const id = computeCapabilityId(name, version, inputSchema, outputSchema);\n \n return {\n id,\n name,\n version,\n description,\n ...(inputSchema !== undefined ? { inputSchema } : {}),\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n tags,\n };\n}\n\n/**\n * Validates that a capability has all required fields.\n * \n * @param capability - The capability to validate\n * @returns Object with `valid` boolean and optional `errors` array\n */\nexport function validateCapability(capability: unknown): { valid: boolean; errors?: string[] } {\n const errors: string[] = [];\n \n if (!capability || typeof capability !== 'object') {\n return { valid: false, errors: ['Capability must be an object'] };\n }\n \n const cap = capability as Record<string, unknown>;\n \n if (!cap.id || typeof cap.id !== 'string') {\n errors.push('Missing or invalid field: id (must be a string)');\n }\n \n if (!cap.name || typeof cap.name !== 'string') {\n errors.push('Missing or invalid field: name (must be a string)');\n }\n \n if (!cap.version || typeof cap.version !== 'string') {\n errors.push('Missing or invalid field: version (must be a string)');\n }\n \n if (!cap.description || typeof cap.description !== 'string') {\n errors.push('Missing or invalid field: description (must be a string)');\n }\n \n if (!Array.isArray(cap.tags)) {\n errors.push('Missing or invalid field: tags (must be an array)');\n } else if (!cap.tags.every(tag => typeof tag === 'string')) {\n errors.push('Invalid field: tags (all elements must be strings)');\n }\n \n if (cap.inputSchema !== undefined && (typeof cap.inputSchema !== 'object' || cap.inputSchema === null)) {\n errors.push('Invalid field: inputSchema (must be an object)');\n }\n \n if (cap.outputSchema !== undefined && (typeof cap.outputSchema !== 'object' || cap.outputSchema === null)) {\n errors.push('Invalid field: outputSchema (must be an object)');\n }\n \n if (errors.length > 0) {\n return { valid: false, errors };\n }\n \n return { valid: true };\n}\n","import type { Peer } from './peer';\n\n/**\n * In-memory store for known peers on the network\n */\nexport class PeerStore {\n private peers: Map<string, Peer> = new Map();\n\n /**\n * Add or update a peer in the store.\n * If a peer with the same publicKey exists, it will be updated.\n * \n * @param peer - The peer to add or update\n */\n addOrUpdatePeer(peer: Peer): void {\n this.peers.set(peer.publicKey, peer);\n }\n\n /**\n * Remove a peer from the store.\n * \n * @param publicKey - The public key of the peer to remove\n * @returns true if the peer was removed, false if it didn't exist\n */\n removePeer(publicKey: string): boolean {\n return this.peers.delete(publicKey);\n }\n\n /**\n * Get a peer by their public key.\n * \n * @param publicKey - The public key of the peer to retrieve\n * @returns The peer if found, undefined otherwise\n */\n getPeer(publicKey: string): Peer | undefined {\n return this.peers.get(publicKey);\n }\n\n /**\n * Find all peers that offer a specific capability by name.\n * \n * @param name - The capability name to search for\n * @returns Array of peers that have a capability with the given name\n */\n findByCapability(name: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasCapability = peer.capabilities.some(cap => cap.name === name);\n if (hasCapability) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Find all peers that have capabilities with a specific tag.\n * \n * @param tag - The tag to search for\n * @returns Array of peers that have at least one capability with the given tag\n */\n findByTag(tag: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasTag = peer.capabilities.some(cap => cap.tags.includes(tag));\n if (hasTag) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Get all peers in the store.\n * \n * @returns Array of all peers\n */\n allPeers(): Peer[] {\n return Array.from(this.peers.values());\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds. Peers older than this will be removed.\n * @param currentTime - Current timestamp (ms), defaults to Date.now()\n * @returns Number of peers removed\n */\n prune(maxAgeMs: number, currentTime: number = Date.now()): number {\n const cutoff = currentTime - maxAgeMs;\n let removed = 0;\n \n for (const [publicKey, peer] of this.peers.entries()) {\n if (peer.lastSeen < cutoff) {\n this.peers.delete(publicKey);\n removed++;\n }\n }\n \n return removed;\n }\n}\n","import { createEnvelope, type Envelope } from '../message/envelope';\nimport { PeerStore } from './peer-store';\nimport type { Capability } from './capability';\nimport type { Peer } from './peer';\nimport type {\n CapabilityAnnouncePayload,\n CapabilityQueryPayload,\n CapabilityResponsePayload,\n} from './messages';\n\n/**\n * DiscoveryService manages capability-based peer discovery.\n * It maintains a local index of peer capabilities and handles\n * capability announce, query, and response messages.\n */\nexport class DiscoveryService {\n constructor(\n private peerStore: PeerStore,\n private identity: { publicKey: string; privateKey: string }\n ) {}\n\n /**\n * Announce own capabilities to the network.\n * Creates a capability_announce envelope that can be broadcast to peers.\n * \n * @param capabilities - List of capabilities this agent offers\n * @param metadata - Optional metadata about this agent\n * @returns A signed capability_announce envelope\n */\n announce(\n capabilities: Capability[],\n metadata?: { name?: string; version?: string }\n ): Envelope<CapabilityAnnouncePayload> {\n const payload: CapabilityAnnouncePayload = {\n publicKey: this.identity.publicKey,\n capabilities,\n metadata: metadata ? {\n ...metadata,\n lastSeen: Date.now(),\n } : {\n lastSeen: Date.now(),\n },\n };\n\n return createEnvelope(\n 'capability_announce',\n this.identity.publicKey,\n this.identity.privateKey,\n payload\n );\n }\n\n /**\n * Handle an incoming capability_announce message.\n * Updates the peer store with the announced capabilities.\n * \n * @param envelope - The capability_announce envelope to process\n */\n handleAnnounce(envelope: Envelope<CapabilityAnnouncePayload>): void {\n const { payload } = envelope;\n \n const peer: Peer = {\n publicKey: payload.publicKey,\n capabilities: payload.capabilities,\n lastSeen: payload.metadata?.lastSeen || envelope.timestamp,\n metadata: payload.metadata ? {\n name: payload.metadata.name,\n version: payload.metadata.version,\n } : undefined,\n };\n\n this.peerStore.addOrUpdatePeer(peer);\n }\n\n /**\n * Create a capability query payload.\n * \n * @param queryType - Type of query: 'name', 'tag', or 'schema'\n * @param query - The query value (capability name, tag, or schema)\n * @param filters - Optional filters (limit, minTrustScore)\n * @returns A capability_query payload\n */\n query(\n queryType: 'name' | 'tag' | 'schema',\n query: string | object,\n filters?: { limit?: number; minTrustScore?: number }\n ): CapabilityQueryPayload {\n return {\n queryType,\n query,\n filters,\n };\n }\n\n /**\n * Handle an incoming capability_query message.\n * Searches the local peer store and returns matching peers.\n * \n * @param envelope - The capability_query envelope to process\n * @returns A capability_response envelope with matching peers\n */\n handleQuery(\n envelope: Envelope<CapabilityQueryPayload>\n ): Envelope<CapabilityResponsePayload> {\n const { payload } = envelope;\n let peers: Peer[] = [];\n\n // Execute query based on type\n if (payload.queryType === 'name' && typeof payload.query === 'string') {\n peers = this.peerStore.findByCapability(payload.query);\n } else if (payload.queryType === 'tag' && typeof payload.query === 'string') {\n peers = this.peerStore.findByTag(payload.query);\n } else if (payload.queryType === 'schema') {\n // Schema-based matching is deferred to Phase 2b\n // For now, return empty results\n peers = [];\n }\n\n // Apply filters\n const limit = payload.filters?.limit;\n const totalMatches = peers.length;\n \n if (limit !== undefined && limit > 0) {\n peers = peers.slice(0, limit);\n }\n\n // Transform peers to response format\n const responsePeers = peers.map(peer => ({\n publicKey: peer.publicKey,\n capabilities: peer.capabilities,\n metadata: peer.metadata ? {\n name: peer.metadata.name,\n version: peer.metadata.version,\n lastSeen: peer.lastSeen,\n } : {\n lastSeen: peer.lastSeen,\n },\n // Trust score integration deferred to Phase 2b (RFC-001)\n trustScore: undefined,\n }));\n\n const responsePayload: CapabilityResponsePayload = {\n queryId: envelope.id,\n peers: responsePeers,\n totalMatches,\n };\n\n return createEnvelope(\n 'capability_response',\n this.identity.publicKey,\n this.identity.privateKey,\n responsePayload,\n Date.now(),\n envelope.id // inReplyTo\n );\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds\n * @returns Number of peers removed\n */\n pruneStale(maxAgeMs: number, currentTime: number = Date.now()): number {\n return this.peerStore.prune(maxAgeMs, currentTime);\n }\n}\n","/**\n * Peer discovery message types for the Agora network.\n */\n\n/**\n * Request peer list from relay\n */\nexport interface PeerListRequestPayload {\n /** Optional filters */\n filters?: {\n /** Only peers seen in last N ms */\n activeWithin?: number;\n /** Maximum peers to return */\n limit?: number;\n };\n}\n\n/**\n * Relay responds with connected peers\n */\nexport interface PeerListResponsePayload {\n /** List of known peers */\n peers: Array<{\n /** Peer's Ed25519 public key */\n publicKey: string;\n /** Optional metadata (if peer announced) */\n metadata?: {\n name?: string;\n version?: string;\n capabilities?: string[];\n };\n /** Last seen timestamp (ms) */\n lastSeen: number;\n }>;\n /** Total peer count (may be > peers.length if limited) */\n totalPeers: number;\n /** Relay's public key (for trust verification) */\n relayPublicKey: string;\n}\n\n/**\n * Agent recommends another agent\n */\nexport interface PeerReferralPayload {\n /** Referred peer's public key */\n publicKey: string;\n /** Optional endpoint (if known) */\n endpoint?: string;\n /** Optional metadata */\n metadata?: {\n name?: string;\n version?: string;\n capabilities?: string[];\n };\n /** Referrer's comment */\n comment?: string;\n /** Trust hint (RFC-001 integration) */\n trustScore?: number;\n}\n\n/**\n * Validate PeerListRequestPayload\n */\nexport function validatePeerListRequest(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (p.filters !== undefined) {\n if (typeof p.filters !== 'object' || p.filters === null) {\n errors.push('filters must be an object');\n } else {\n const filters = p.filters as Record<string, unknown>;\n if (filters.activeWithin !== undefined && typeof filters.activeWithin !== 'number') {\n errors.push('filters.activeWithin must be a number');\n }\n if (filters.limit !== undefined && typeof filters.limit !== 'number') {\n errors.push('filters.limit must be a number');\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Validate PeerListResponsePayload\n */\nexport function validatePeerListResponse(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (!Array.isArray(p.peers)) {\n errors.push('peers must be an array');\n } else {\n p.peers.forEach((peer, index) => {\n if (typeof peer !== 'object' || peer === null) {\n errors.push(`peers[${index}] must be an object`);\n return;\n }\n const peerObj = peer as Record<string, unknown>;\n if (typeof peerObj.publicKey !== 'string') {\n errors.push(`peers[${index}].publicKey must be a string`);\n }\n if (typeof peerObj.lastSeen !== 'number') {\n errors.push(`peers[${index}].lastSeen must be a number`);\n }\n });\n }\n\n if (typeof p.totalPeers !== 'number') {\n errors.push('totalPeers must be a number');\n }\n\n if (typeof p.relayPublicKey !== 'string') {\n errors.push('relayPublicKey must be a string');\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Validate PeerReferralPayload\n */\nexport function validatePeerReferral(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (typeof p.publicKey !== 'string') {\n errors.push('publicKey must be a string');\n }\n\n if (p.endpoint !== undefined && typeof p.endpoint !== 'string') {\n errors.push('endpoint must be a string');\n }\n\n if (p.comment !== undefined && typeof p.comment !== 'string') {\n errors.push('comment must be a string');\n }\n\n if (p.trustScore !== undefined && typeof p.trustScore !== 'number') {\n errors.push('trustScore must be a number');\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { homedir } from 'node:os';\n\n/**\n * Normalized relay configuration (supports both string and object in config file).\n */\nexport interface RelayConfig {\n url: string;\n autoConnect: boolean;\n name?: string;\n reconnectMaxMs?: number;\n}\n\n/**\n * Peer entry in config (webhook URL, token, public key).\n */\nexport interface AgoraPeerConfig {\n publicKey: string;\n /** Webhook URL (undefined for relay-only peers) */\n url?: string;\n /** Webhook auth token (undefined for relay-only peers) */\n token?: string;\n name?: string;\n}\n\n/**\n * Identity with optional display name (e.g. for relay registration).\n */\nexport interface AgoraIdentity {\n publicKey: string;\n privateKey: string;\n name?: string;\n}\n\n/**\n * Canonical Agora configuration shape.\n * Use loadAgoraConfig() to load from file with normalized relay.\n */\nexport interface AgoraConfig {\n identity: AgoraIdentity;\n peers: Record<string, AgoraPeerConfig>;\n relay?: RelayConfig;\n}\n\n/**\n * Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json\n */\nexport function getDefaultConfigPath(): string {\n if (process.env.AGORA_CONFIG) {\n return resolve(process.env.AGORA_CONFIG);\n }\n return resolve(homedir(), '.config', 'agora', 'config.json');\n}\n\n/**\n * Parse and normalize config from a JSON object (shared by sync and async loaders).\n */\nfunction parseConfig(config: Record<string, unknown>): AgoraConfig {\n const rawIdentity = config.identity as Record<string, unknown> | undefined;\n if (!rawIdentity?.publicKey || !rawIdentity?.privateKey) {\n throw new Error('Invalid config: missing identity.publicKey or identity.privateKey');\n }\n const identity: AgoraIdentity = {\n publicKey: rawIdentity.publicKey as string,\n privateKey: rawIdentity.privateKey as string,\n name: typeof rawIdentity.name === 'string' ? rawIdentity.name : undefined,\n };\n\n const peers: Record<string, AgoraPeerConfig> = {};\n if (config.peers && typeof config.peers === 'object') {\n for (const [name, entry] of Object.entries(config.peers)) {\n const peer = entry as Record<string, unknown>;\n if (peer && typeof peer.publicKey === 'string') {\n peers[name] = {\n publicKey: peer.publicKey as string,\n url: typeof peer.url === 'string' ? peer.url : undefined,\n token: typeof peer.token === 'string' ? peer.token : undefined,\n name: typeof peer.name === 'string' ? peer.name : undefined,\n };\n }\n }\n }\n\n let relay: RelayConfig | undefined;\n const rawRelay = config.relay;\n if (typeof rawRelay === 'string') {\n relay = { url: rawRelay, autoConnect: true };\n } else if (rawRelay && typeof rawRelay === 'object') {\n const r = rawRelay as Record<string, unknown>;\n if (typeof r.url === 'string') {\n relay = {\n url: r.url,\n autoConnect: typeof r.autoConnect === 'boolean' ? r.autoConnect : true,\n name: typeof r.name === 'string' ? r.name : undefined,\n reconnectMaxMs: typeof r.reconnectMaxMs === 'number' ? r.reconnectMaxMs : undefined,\n };\n }\n }\n\n return {\n identity,\n peers,\n ...(relay ? { relay } : {}),\n };\n}\n\n/**\n * Load and normalize Agora configuration from a JSON file (sync).\n * Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.\n *\n * @param path - Config file path; defaults to getDefaultConfigPath()\n * @returns Normalized AgoraConfig\n * @throws Error if file doesn't exist or config is invalid\n */\nexport function loadAgoraConfig(path?: string): AgoraConfig {\n const configPath = path ?? getDefaultConfigPath();\n\n if (!existsSync(configPath)) {\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\n }\n\n const content = readFileSync(configPath, 'utf-8');\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file: ${configPath}`);\n }\n\n return parseConfig(config);\n}\n\n/**\n * Load and normalize Agora configuration from a JSON file (async).\n *\n * @param path - Config file path; defaults to getDefaultConfigPath()\n * @returns Normalized AgoraConfig\n * @throws Error if file doesn't exist or config is invalid\n */\nexport async function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig> {\n const configPath = path ?? getDefaultConfigPath();\n\n let content: string;\n try {\n content = await readFile(configPath, 'utf-8');\n } catch (err) {\n const code = err && typeof err === 'object' && 'code' in err ? (err as NodeJS.ErrnoException).code : undefined;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\n }\n throw err;\n }\n\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file: ${configPath}`);\n }\n\n return parseConfig(config);\n}\n","/**\n * message-buffer.ts — In-memory bounded message queue per agent.\n *\n * When messages are delivered to an agent via the relay, they are also\n * stored here so that HTTP polling clients can retrieve them via GET /v1/messages.\n */\n\nexport interface BufferedMessage {\n id: string;\n from: string;\n fromName?: string;\n type: string;\n payload: unknown;\n timestamp: number;\n inReplyTo?: string;\n}\n\nconst MAX_MESSAGES_PER_AGENT = 100;\n\n/**\n * MessageBuffer stores inbound messages per agent public key.\n * FIFO eviction when the buffer is full (max 100 messages).\n */\nexport class MessageBuffer {\n private buffers: Map<string, BufferedMessage[]> = new Map();\n\n /**\n * Add a message to an agent's buffer.\n * Evicts the oldest message if the buffer is full.\n */\n add(publicKey: string, message: BufferedMessage): void {\n let queue = this.buffers.get(publicKey);\n if (!queue) {\n queue = [];\n this.buffers.set(publicKey, queue);\n }\n queue.push(message);\n if (queue.length > MAX_MESSAGES_PER_AGENT) {\n queue.shift(); // FIFO eviction\n }\n }\n\n /**\n * Retrieve messages for an agent, optionally filtering by `since` timestamp.\n * Returns messages with timestamp > since (exclusive).\n */\n get(publicKey: string, since?: number): BufferedMessage[] {\n const queue = this.buffers.get(publicKey) ?? [];\n if (since === undefined) {\n return [...queue];\n }\n return queue.filter((m) => m.timestamp > since);\n }\n\n /**\n * Clear all messages for an agent (after polling without `since`).\n */\n clear(publicKey: string): void {\n this.buffers.set(publicKey, []);\n }\n\n /**\n * Remove all state for a disconnected agent.\n */\n delete(publicKey: string): void {\n this.buffers.delete(publicKey);\n }\n}\n","/**\n * jwt-auth.ts — JWT token creation and validation middleware.\n *\n * Tokens are signed with AGORA_RELAY_JWT_SECRET (required env var).\n * Expiry defaults to 3600 seconds (1 hour), configurable via AGORA_JWT_EXPIRY_SECONDS.\n *\n * Token payload: { publicKey, name }\n */\n\nimport jwt from 'jsonwebtoken';\nimport { randomBytes } from 'node:crypto';\nimport type { Request, Response, NextFunction } from 'express';\n\nexport interface JwtPayload {\n publicKey: string;\n name?: string;\n}\n\n/**\n * Augment Express Request to carry decoded JWT payload.\n */\nexport interface AuthenticatedRequest extends Request {\n agent?: JwtPayload;\n}\n\n/**\n * Revocation set for invalidated tokens (populated by DELETE /v1/disconnect).\n * Stored as a Map of JWT `jti` → expiry timestamp (ms).\n * Entries are automatically removed once their JWT would have expired anyway,\n * preventing unbounded memory growth.\n */\nconst revokedJtis: Map<string, number> = new Map();\n\n/**\n * Remove revoked JTI entries whose token expiry has already passed.\n * These tokens can no longer be used regardless, so no need to keep them.\n */\nfunction pruneExpiredRevocations(): void {\n const now = Date.now();\n for (const [jti, expiry] of revokedJtis) {\n if (expiry <= now) {\n revokedJtis.delete(jti);\n }\n }\n}\n\nfunction getJwtSecret(): string {\n const secret = process.env.AGORA_RELAY_JWT_SECRET;\n if (!secret) {\n throw new Error(\n 'AGORA_RELAY_JWT_SECRET environment variable is required but not set'\n );\n }\n return secret;\n}\n\nfunction getExpirySeconds(): number {\n const raw = process.env.AGORA_JWT_EXPIRY_SECONDS;\n if (raw) {\n const parsed = parseInt(raw, 10);\n if (!isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n return 3600; // 1 hour default\n}\n\n/**\n * Create a signed JWT for a registered agent.\n * Returns the token string and its expiry timestamp (ms since epoch).\n */\nexport function createToken(payload: JwtPayload): {\n token: string;\n expiresAt: number;\n} {\n const secret = getJwtSecret();\n const expirySeconds = getExpirySeconds();\n const jti = `${Date.now()}-${randomBytes(16).toString('hex')}`;\n\n const token = jwt.sign(\n { publicKey: payload.publicKey, name: payload.name, jti },\n secret,\n { expiresIn: expirySeconds }\n );\n\n const expiresAt = Date.now() + expirySeconds * 1000;\n return { token, expiresAt };\n}\n\n/**\n * Revoke a token by its jti claim so it cannot be used again.\n * The revocation entry is stored with the token's expiry so it can be\n * pruned automatically once the token would no longer be valid anyway.\n */\nexport function revokeToken(token: string): void {\n try {\n const secret = getJwtSecret();\n const decoded = jwt.verify(token, secret) as jwt.JwtPayload & {\n jti?: string;\n exp?: number;\n };\n if (decoded.jti) {\n const expiry = decoded.exp ? decoded.exp * 1000 : Date.now();\n revokedJtis.set(decoded.jti, expiry);\n pruneExpiredRevocations();\n }\n } catch {\n // Token already invalid — nothing to revoke\n }\n}\n\n/**\n * Express middleware that validates the Authorization: Bearer <token> header.\n * Attaches decoded payload to `req.agent` on success.\n * Responds with 401 if missing/invalid/expired/revoked.\n */\nexport function requireAuth(\n req: AuthenticatedRequest,\n res: Response,\n next: NextFunction\n): void {\n const authHeader = req.headers.authorization;\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n res.status(401).json({ error: 'Missing or malformed Authorization header' });\n return;\n }\n\n const token = authHeader.slice(7);\n try {\n const secret = getJwtSecret();\n const decoded = jwt.verify(token, secret) as jwt.JwtPayload & {\n publicKey: string;\n name?: string;\n jti?: string;\n };\n\n if (decoded.jti && revokedJtis.has(decoded.jti)) {\n res.status(401).json({ error: 'Token has been revoked' });\n return;\n }\n\n req.agent = { publicKey: decoded.publicKey, name: decoded.name };\n next();\n } catch (err) {\n if (err instanceof jwt.TokenExpiredError) {\n res.status(401).json({ error: 'Token expired' });\n } else {\n res.status(401).json({ error: 'Invalid token' });\n }\n }\n}\n","/**\n * rest-api.ts — Express router implementing the Agora relay REST API.\n *\n * Endpoints:\n * POST /v1/register — Register agent, obtain JWT session token\n * POST /v1/send — Send message to a peer (requires auth)\n * GET /v1/peers — List online peers (requires auth)\n * GET /v1/messages — Poll for new inbound messages (requires auth)\n * DELETE /v1/disconnect — Invalidate token and disconnect (requires auth)\n */\n\nimport { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { rateLimit } from 'express-rate-limit';\nimport {\n createToken,\n revokeToken,\n requireAuth,\n type AuthenticatedRequest,\n} from './jwt-auth';\nimport { MessageBuffer, type BufferedMessage } from './message-buffer';\n\nconst apiRateLimit = rateLimit({\n windowMs: 60_000,\n limit: 60,\n standardHeaders: 'draft-7',\n legacyHeaders: false,\n message: { error: 'Too many requests — try again later' },\n});\n\n/**\n * A session for a REST-connected agent.\n * privateKey is held only in memory and never logged or persisted.\n */\nexport interface RestSession {\n publicKey: string;\n privateKey: string;\n name?: string;\n metadata?: { version?: string; capabilities?: string[] };\n registeredAt: number;\n expiresAt: number;\n token: string;\n}\n\nfunction pruneExpiredSessions(\n sessions: Map<string, RestSession>,\n buffer: MessageBuffer\n): void {\n const now = Date.now();\n for (const [publicKey, session] of sessions) {\n if (session.expiresAt <= now) {\n sessions.delete(publicKey);\n buffer.delete(publicKey);\n }\n }\n}\n\n/**\n * Minimal interface for the relay server that the REST API depends on.\n */\nexport interface RelayInterface {\n getAgents(): Map<\n string,\n {\n publicKey: string;\n name?: string;\n lastSeen: number;\n metadata?: { version?: string; capabilities?: string[] };\n socket: unknown;\n }\n >;\n on(\n event: 'message-relayed',\n handler: (from: string, to: string, envelope: unknown) => void\n ): void;\n}\n\n/**\n * Envelope creation function interface (matches createEnvelope from message/envelope).\n */\nexport type CreateEnvelopeFn = (\n type: string,\n sender: string,\n privateKey: string,\n payload: unknown,\n timestamp?: number,\n inReplyTo?: string\n) => {\n id: string;\n type: string;\n sender: string;\n timestamp: number;\n payload: unknown;\n signature: string;\n inReplyTo?: string;\n};\n\n/**\n * Envelope verification function interface.\n */\nexport type VerifyEnvelopeFn = (envelope: unknown) => {\n valid: boolean;\n reason?: string;\n};\n\n/**\n * Create the REST API router.\n */\nexport function createRestRouter(\n relay: RelayInterface,\n buffer: MessageBuffer,\n sessions: Map<string, RestSession>,\n createEnv: CreateEnvelopeFn,\n verifyEnv: VerifyEnvelopeFn\n): Router {\n const router = Router();\n router.use(apiRateLimit);\n\n relay.on('message-relayed', (from, to, envelope) => {\n if (!sessions.has(to)) return;\n const agentMap = relay.getAgents();\n const senderAgent = agentMap.get(from);\n const env = envelope as {\n id: string;\n type: string;\n payload: unknown;\n timestamp: number;\n inReplyTo?: string;\n };\n const msg: BufferedMessage = {\n id: env.id,\n from,\n fromName: senderAgent?.name,\n type: env.type,\n payload: env.payload,\n timestamp: env.timestamp,\n inReplyTo: env.inReplyTo,\n };\n buffer.add(to, msg);\n });\n\n router.post('/v1/register', async (req: Request, res: Response) => {\n const { publicKey, privateKey, name, metadata } = req.body as {\n publicKey?: string;\n privateKey?: string;\n name?: string;\n metadata?: { version?: string; capabilities?: string[] };\n };\n\n if (!publicKey || typeof publicKey !== 'string') {\n res.status(400).json({ error: 'publicKey is required' });\n return;\n }\n if (!privateKey || typeof privateKey !== 'string') {\n res.status(400).json({ error: 'privateKey is required' });\n return;\n }\n\n const testEnvelope = createEnv(\n 'announce',\n publicKey,\n privateKey,\n { challenge: 'register' },\n Date.now()\n );\n const verification = verifyEnv(testEnvelope);\n if (!verification.valid) {\n res\n .status(400)\n .json({ error: 'Key pair verification failed: ' + verification.reason });\n return;\n }\n\n const { token, expiresAt } = createToken({ publicKey, name });\n pruneExpiredSessions(sessions, buffer);\n\n const session: RestSession = {\n publicKey,\n privateKey,\n name,\n metadata,\n registeredAt: Date.now(),\n expiresAt,\n token,\n };\n sessions.set(publicKey, session);\n\n const wsAgents = relay.getAgents();\n const peers: Array<{ publicKey: string; name?: string; lastSeen: number }> = [];\n for (const agent of wsAgents.values()) {\n if (agent.publicKey !== publicKey) {\n peers.push({\n publicKey: agent.publicKey,\n name: agent.name,\n lastSeen: agent.lastSeen,\n });\n }\n }\n for (const s of sessions.values()) {\n if (s.publicKey !== publicKey && !wsAgents.has(s.publicKey)) {\n peers.push({\n publicKey: s.publicKey,\n name: s.name,\n lastSeen: s.registeredAt,\n });\n }\n }\n\n res.json({ token, expiresAt, peers });\n });\n\n router.post(\n '/v1/send',\n requireAuth,\n async (req: AuthenticatedRequest, res: Response) => {\n const { to, type, payload, inReplyTo } = req.body as {\n to?: string;\n type?: string;\n payload?: unknown;\n inReplyTo?: string;\n };\n\n if (!to || typeof to !== 'string') {\n res.status(400).json({ error: 'to is required' });\n return;\n }\n if (!type || typeof type !== 'string') {\n res.status(400).json({ error: 'type is required' });\n return;\n }\n if (payload === undefined) {\n res.status(400).json({ error: 'payload is required' });\n return;\n }\n\n const senderPublicKey = req.agent!.publicKey;\n const session = sessions.get(senderPublicKey);\n if (!session) {\n res.status(401).json({ error: 'Session not found — please re-register' });\n return;\n }\n\n const envelope = createEnv(\n type,\n senderPublicKey,\n session.privateKey,\n payload,\n Date.now(),\n inReplyTo\n );\n\n const wsAgents = relay.getAgents();\n const wsRecipient = wsAgents.get(to);\n if (wsRecipient && wsRecipient.socket) {\n const ws = wsRecipient.socket as { readyState: number; send(data: string): void };\n const OPEN = 1;\n if (ws.readyState !== OPEN) {\n res.status(503).json({ error: 'Recipient connection is not open' });\n return;\n }\n try {\n const relayMsg = JSON.stringify({\n type: 'message',\n from: senderPublicKey,\n name: session.name,\n envelope,\n });\n ws.send(relayMsg);\n res.json({ ok: true, envelopeId: envelope.id });\n return;\n } catch (err) {\n res.status(500).json({\n error:\n 'Failed to deliver message: ' +\n (err instanceof Error ? err.message : String(err)),\n });\n return;\n }\n }\n\n const restRecipient = sessions.get(to);\n if (restRecipient) {\n const senderAgent = wsAgents.get(senderPublicKey);\n const msg: BufferedMessage = {\n id: envelope.id,\n from: senderPublicKey,\n fromName: session.name ?? senderAgent?.name,\n type: envelope.type,\n payload: envelope.payload,\n timestamp: envelope.timestamp,\n inReplyTo: envelope.inReplyTo,\n };\n buffer.add(to, msg);\n res.json({ ok: true, envelopeId: envelope.id });\n return;\n }\n\n res.status(404).json({ error: 'Recipient not connected' });\n }\n );\n\n router.get(\n '/v1/peers',\n requireAuth,\n (req: AuthenticatedRequest, res: Response) => {\n const callerPublicKey = req.agent!.publicKey;\n const wsAgents = relay.getAgents();\n const peerList: Array<{\n publicKey: string;\n name?: string;\n lastSeen: number;\n metadata?: { version?: string; capabilities?: string[] };\n }> = [];\n\n for (const agent of wsAgents.values()) {\n if (agent.publicKey !== callerPublicKey) {\n peerList.push({\n publicKey: agent.publicKey,\n name: agent.name,\n lastSeen: agent.lastSeen,\n metadata: agent.metadata,\n });\n }\n }\n\n for (const s of sessions.values()) {\n if (s.publicKey !== callerPublicKey && !wsAgents.has(s.publicKey)) {\n peerList.push({\n publicKey: s.publicKey,\n name: s.name,\n lastSeen: s.registeredAt,\n metadata: s.metadata,\n });\n }\n }\n\n res.json({ peers: peerList });\n }\n );\n\n router.get(\n '/v1/messages',\n requireAuth,\n (req: AuthenticatedRequest, res: Response) => {\n const publicKey = req.agent!.publicKey;\n const sinceRaw = req.query.since as string | undefined;\n const limitRaw = req.query.limit as string | undefined;\n\n const since = sinceRaw ? parseInt(sinceRaw, 10) : undefined;\n const limit = Math.min(limitRaw ? parseInt(limitRaw, 10) : 50, 100);\n\n let messages = buffer.get(publicKey, since);\n const hasMore = messages.length > limit;\n if (hasMore) {\n messages = messages.slice(0, limit);\n }\n\n if (since === undefined) {\n buffer.clear(publicKey);\n }\n\n res.json({ messages, hasMore });\n }\n );\n\n router.delete(\n '/v1/disconnect',\n requireAuth,\n (req: AuthenticatedRequest, res: Response) => {\n const publicKey = req.agent!.publicKey;\n const authHeader = req.headers.authorization!;\n const token = authHeader.slice(7);\n\n revokeToken(token);\n sessions.delete(publicKey);\n buffer.delete(publicKey);\n\n res.json({ ok: true });\n }\n );\n\n return router;\n}\n","/**\n * run-relay.ts — Start Agora relay: WebSocket server and optional REST API.\n *\n * When REST is enabled, starts:\n * 1. WebSocket relay (RelayServer) on wsPort\n * 2. REST API server (Express) on restPort (default wsPort + 1)\n *\n * Environment variables (when REST enabled):\n * AGORA_RELAY_JWT_SECRET — Required for REST (JWT signing)\n * AGORA_JWT_EXPIRY_SECONDS — JWT expiry in seconds (default: 3600)\n * PORT — WebSocket port (default: 3001); REST uses PORT+1\n */\n\nimport http from 'node:http';\nimport express from 'express';\nimport { RelayServer, type RelayServerOptions } from './server';\nimport {\n createEnvelope,\n verifyEnvelope,\n type Envelope,\n type MessageType,\n} from '../message/envelope';\nimport { createRestRouter, type CreateEnvelopeFn } from './rest-api';\nimport { MessageBuffer } from './message-buffer';\nimport type { RestSession } from './rest-api';\n\n/** Wrapper so REST API can pass string type; createEnvelope expects MessageType */\nconst createEnvelopeForRest: CreateEnvelopeFn = (\n type,\n sender,\n privateKey,\n payload,\n timestamp,\n inReplyTo\n) =>\n createEnvelope(\n type as MessageType,\n sender,\n privateKey,\n payload,\n timestamp ?? Date.now(),\n inReplyTo\n );\n\nexport interface RunRelayOptions {\n /** WebSocket port (default from PORT env or 3001) */\n wsPort?: number;\n /** REST API port (default: wsPort + 1). Ignored if enableRest is false. */\n restPort?: number;\n /** Enable REST API (requires AGORA_RELAY_JWT_SECRET). Default: true if AGORA_RELAY_JWT_SECRET is set. */\n enableRest?: boolean;\n /** Relay server options (identity, storagePeers, storageDir) */\n relayOptions?: RelayServerOptions;\n}\n\n/**\n * Start WebSocket relay and optionally REST API.\n * Returns { relay, httpServer } where httpServer is set only when REST is enabled.\n */\nexport async function runRelay(options: RunRelayOptions = {}): Promise<{\n relay: RelayServer;\n httpServer?: http.Server;\n}> {\n const wsPort = options.wsPort ?? parseInt(process.env.PORT ?? '3001', 10);\n const enableRest =\n options.enableRest ??\n (typeof process.env.AGORA_RELAY_JWT_SECRET === 'string' &&\n process.env.AGORA_RELAY_JWT_SECRET.length > 0);\n\n const relay = new RelayServer(options.relayOptions);\n await relay.start(wsPort);\n\n if (!enableRest) {\n return { relay };\n }\n\n if (!process.env.AGORA_RELAY_JWT_SECRET) {\n await relay.stop();\n throw new Error(\n 'AGORA_RELAY_JWT_SECRET environment variable is required when REST API is enabled'\n );\n }\n\n const restPort = options.restPort ?? wsPort + 1;\n const messageBuffer = new MessageBuffer();\n const restSessions = new Map<string, RestSession>();\n\n const app = express();\n app.use(express.json());\n\n const verifyForRest = (envelope: unknown): { valid: boolean; reason?: string } =>\n verifyEnvelope(envelope as Envelope);\n\n const router = createRestRouter(\n relay as Parameters<typeof createRestRouter>[0],\n messageBuffer,\n restSessions,\n createEnvelopeForRest,\n verifyForRest\n );\n app.use(router);\n\n app.use((_req, res) => {\n res.status(404).json({ error: 'Not found' });\n });\n\n const httpServer = http.createServer(app);\n await new Promise<void>((resolve, reject) => {\n httpServer.listen(restPort, () => resolve());\n httpServer.on('error', reject);\n });\n\n return { relay, httpServer };\n}\n","import type { AgoraIdentity, RelayConfig } from './config';\nimport { getDefaultConfigPath, loadAgoraConfigAsync } from './config';\nimport type { Envelope } from './message/envelope';\nimport type { MessageType } from './message/envelope';\nimport { RelayClient } from './relay/client';\nimport type { PeerConfig } from './transport/http';\nimport { decodeInboundEnvelope, sendToPeer } from './transport/http';\nimport { sendViaRelay } from './transport/relay';\nimport { shortKey } from './utils';\n\n/**\n * Service config: identity, peers keyed by name, optional relay.\n */\nexport interface AgoraServiceConfig {\n identity: AgoraIdentity;\n peers: Map<string, PeerConfig>;\n relay?: RelayConfig;\n}\n\nexport interface SendMessageOptions {\n peerName: string;\n type: MessageType;\n payload: unknown;\n inReplyTo?: string;\n}\n\nexport interface SendMessageResult {\n ok: boolean;\n status: number;\n error?: string;\n}\n\nexport interface DecodeInboundResult {\n ok: boolean;\n envelope?: Envelope;\n reason?: string;\n}\n\nexport type RelayMessageHandler = (envelope: Envelope) => void;\nexport type RelayMessageHandlerWithName = (envelope: Envelope, from: string, fromName?: string) => void;\n\nexport interface Logger {\n debug(message: string): void;\n}\n\nexport interface RelayClientLike {\n connect(): Promise<void>;\n disconnect(): void;\n connected(): boolean;\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\n on(event: 'message', handler: (envelope: Envelope, from: string, fromName?: string) => void): void;\n on(event: 'error', handler: (error: Error) => void): void;\n}\n\nexport interface RelayClientFactory {\n (opts: {\n relayUrl: string;\n publicKey: string;\n privateKey: string;\n name?: string;\n pingInterval: number;\n maxReconnectDelay: number;\n }): RelayClientLike;\n}\n\n/**\n * High-level Agora service: send by peer name, decode inbound, relay lifecycle.\n */\nexport class AgoraService {\n private config: AgoraServiceConfig;\n private relayClient: RelayClientLike | null = null;\n private relayMessageHandler: RelayMessageHandler | null = null;\n private relayMessageHandlerWithName: RelayMessageHandlerWithName | null = null;\n private logger: Logger | null;\n private relayClientFactory: RelayClientFactory | null;\n\n constructor(\n config: AgoraServiceConfig,\n logger?: Logger,\n relayClientFactory?: RelayClientFactory\n ) {\n this.config = config;\n this.logger = logger ?? null;\n this.relayClientFactory = relayClientFactory ?? null;\n }\n\n /**\n * Send a signed message to a named peer.\n * Tries HTTP webhook first; falls back to relay if HTTP is unavailable.\n */\n async sendMessage(options: SendMessageOptions): Promise<SendMessageResult> {\n const peer = this.config.peers.get(options.peerName);\n if (!peer) {\n return {\n ok: false,\n status: 0,\n error: `Unknown peer: ${options.peerName}`,\n };\n }\n\n // Try HTTP first (only if peer has a webhook URL)\n if (peer.url) {\n const transportConfig = {\n identity: {\n publicKey: this.config.identity.publicKey,\n privateKey: this.config.identity.privateKey,\n },\n peers: new Map<string, PeerConfig>([[peer.publicKey, peer]]),\n };\n\n const httpResult = await sendToPeer(\n transportConfig,\n peer.publicKey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n if (httpResult.ok) {\n return httpResult;\n }\n\n this.logger?.debug(`HTTP send to ${options.peerName} failed: ${httpResult.error}`);\n }\n\n // Fall back to relay\n if (this.relayClient?.connected() && this.config.relay) {\n const relayResult = await sendViaRelay(\n {\n identity: this.config.identity,\n relayUrl: this.config.relay.url,\n relayClient: this.relayClient,\n },\n peer.publicKey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n return {\n ok: relayResult.ok,\n status: 0,\n error: relayResult.error,\n };\n }\n\n // Both failed\n return {\n ok: false,\n status: 0,\n error: peer.url\n ? `HTTP send failed and relay not available for peer: ${options.peerName}`\n : `No webhook URL and relay not available for peer: ${options.peerName}`,\n };\n }\n\n /**\n * Decode and verify an inbound envelope from a webhook message.\n */\n async decodeInbound(message: string): Promise<DecodeInboundResult> {\n const peersByPubKey = new Map<string, PeerConfig>();\n for (const peer of this.config.peers.values()) {\n peersByPubKey.set(peer.publicKey, peer);\n }\n const result = decodeInboundEnvelope(message, peersByPubKey);\n if (result.ok) {\n return { ok: true, envelope: result.envelope };\n }\n return { ok: false, reason: result.reason };\n }\n\n getPeers(): string[] {\n return Array.from(this.config.peers.keys());\n }\n\n getPeerConfig(name: string): PeerConfig | undefined {\n return this.config.peers.get(name);\n }\n\n /**\n * Connect to the relay server.\n */\n async connectRelay(url: string): Promise<void> {\n if (this.relayClient) {\n return;\n }\n\n const maxReconnectDelay = this.config.relay?.reconnectMaxMs ?? 300000;\n let name = this.config.identity.name ?? this.config.relay?.name;\n // Never use the short key (id) as the relay display name; treat it as no name\n if (name && name === shortKey(this.config.identity.publicKey)) {\n name = undefined;\n }\n const opts = {\n relayUrl: url,\n publicKey: this.config.identity.publicKey,\n privateKey: this.config.identity.privateKey,\n name,\n pingInterval: 30000,\n maxReconnectDelay,\n };\n\n if (this.relayClientFactory) {\n this.relayClient = this.relayClientFactory(opts);\n } else {\n this.relayClient = new RelayClient(opts);\n }\n\n this.relayClient.on('error', (error: Error) => {\n this.logger?.debug(`Agora relay error: ${error.message}`);\n });\n\n this.relayClient.on('message', (envelope: Envelope, from: string, fromName?: string) => {\n if (this.relayMessageHandlerWithName) {\n this.relayMessageHandlerWithName(envelope, from, fromName);\n } else if (this.relayMessageHandler) {\n this.relayMessageHandler(envelope);\n }\n });\n\n try {\n await this.relayClient.connect();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.logger?.debug(`Agora relay connect failed (${url}): ${message}`);\n this.relayClient = null;\n }\n }\n\n setRelayMessageHandler(handler: RelayMessageHandler): void {\n this.relayMessageHandler = handler;\n this.relayMessageHandlerWithName = null;\n }\n\n setRelayMessageHandlerWithName(handler: RelayMessageHandlerWithName): void {\n this.relayMessageHandlerWithName = handler;\n this.relayMessageHandler = null;\n }\n\n async disconnectRelay(): Promise<void> {\n if (this.relayClient) {\n this.relayClient.disconnect();\n this.relayClient = null;\n }\n }\n\n isRelayConnected(): boolean {\n return this.relayClient?.connected() ?? false;\n }\n\n /**\n * Load Agora configuration and return service config (peers as Map).\n */\n static async loadConfig(path?: string): Promise<AgoraServiceConfig> {\n const configPath = path ?? getDefaultConfigPath();\n const loaded = await loadAgoraConfigAsync(configPath);\n\n const peers = new Map<string, PeerConfig>();\n for (const [name, p] of Object.entries(loaded.peers)) {\n peers.set(name, {\n publicKey: p.publicKey,\n url: p.url,\n token: p.token,\n } satisfies PeerConfig);\n }\n\n return {\n identity: loaded.identity,\n peers,\n relay: loaded.relay,\n };\n }\n}\n"],"mappings":"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","names":["resolve"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rookdaemon/agora",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "A coordination network for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
"agora": "./dist/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "
|
|
12
|
+
"build": "tsup",
|
|
13
13
|
"test": "node --import tsx --test test/*.test.ts test/**/*.test.ts",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
14
15
|
"lint": "eslint src test --ext .ts",
|
|
15
16
|
"prepublishOnly": "npm run lint && npm run build && npm test",
|
|
16
17
|
"pretest": "npm run build"
|
|
@@ -29,15 +30,23 @@
|
|
|
29
30
|
"author": "",
|
|
30
31
|
"license": "MIT",
|
|
31
32
|
"devDependencies": {
|
|
33
|
+
"@types/express": "^5.0.0",
|
|
34
|
+
"@types/jsonwebtoken": "^9.0.9",
|
|
32
35
|
"@types/node": "^25.1.0",
|
|
36
|
+
"@types/supertest": "^6.0.2",
|
|
33
37
|
"@types/ws": "^8.18.1",
|
|
34
38
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
35
39
|
"@typescript-eslint/parser": "^8.54.0",
|
|
36
40
|
"eslint": "^9.39.2",
|
|
41
|
+
"supertest": "^7.1.0",
|
|
42
|
+
"tsup": "^8.3.5",
|
|
37
43
|
"tsx": "^4.21.0",
|
|
38
44
|
"typescript": "^5.9.3"
|
|
39
45
|
},
|
|
40
46
|
"dependencies": {
|
|
47
|
+
"express": "^5.2.1",
|
|
48
|
+
"express-rate-limit": "^8.2.1",
|
|
49
|
+
"jsonwebtoken": "^9.0.3",
|
|
41
50
|
"ws": "^8.19.0"
|
|
42
51
|
}
|
|
43
52
|
}
|
package/dist/cli.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/config.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Normalized relay configuration (supports both string and object in config file).
|
|
3
|
-
*/
|
|
4
|
-
export interface RelayConfig {
|
|
5
|
-
url: string;
|
|
6
|
-
autoConnect: boolean;
|
|
7
|
-
name?: string;
|
|
8
|
-
reconnectMaxMs?: number;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Peer entry in config (webhook URL, token, public key).
|
|
12
|
-
*/
|
|
13
|
-
export interface AgoraPeerConfig {
|
|
14
|
-
publicKey: string;
|
|
15
|
-
/** Webhook URL (undefined for relay-only peers) */
|
|
16
|
-
url?: string;
|
|
17
|
-
/** Webhook auth token (undefined for relay-only peers) */
|
|
18
|
-
token?: string;
|
|
19
|
-
name?: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Identity with optional display name (e.g. for relay registration).
|
|
23
|
-
*/
|
|
24
|
-
export interface AgoraIdentity {
|
|
25
|
-
publicKey: string;
|
|
26
|
-
privateKey: string;
|
|
27
|
-
name?: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Canonical Agora configuration shape.
|
|
31
|
-
* Use loadAgoraConfig() to load from file with normalized relay.
|
|
32
|
-
*/
|
|
33
|
-
export interface AgoraConfig {
|
|
34
|
-
identity: AgoraIdentity;
|
|
35
|
-
peers: Record<string, AgoraPeerConfig>;
|
|
36
|
-
relay?: RelayConfig;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json
|
|
40
|
-
*/
|
|
41
|
-
export declare function getDefaultConfigPath(): string;
|
|
42
|
-
/**
|
|
43
|
-
* Load and normalize Agora configuration from a JSON file (sync).
|
|
44
|
-
* Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.
|
|
45
|
-
*
|
|
46
|
-
* @param path - Config file path; defaults to getDefaultConfigPath()
|
|
47
|
-
* @returns Normalized AgoraConfig
|
|
48
|
-
* @throws Error if file doesn't exist or config is invalid
|
|
49
|
-
*/
|
|
50
|
-
export declare function loadAgoraConfig(path?: string): AgoraConfig;
|
|
51
|
-
/**
|
|
52
|
-
* Load and normalize Agora configuration from a JSON file (async).
|
|
53
|
-
*
|
|
54
|
-
* @param path - Config file path; defaults to getDefaultConfigPath()
|
|
55
|
-
* @returns Normalized AgoraConfig
|
|
56
|
-
* @throws Error if file doesn't exist or config is invalid
|
|
57
|
-
*/
|
|
58
|
-
export declare function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig>;
|
|
59
|
-
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C;AAsDD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAgB1D;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAsB9E"}
|
package/dist/config.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
5
|
-
/**
|
|
6
|
-
* Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json
|
|
7
|
-
*/
|
|
8
|
-
export function getDefaultConfigPath() {
|
|
9
|
-
if (process.env.AGORA_CONFIG) {
|
|
10
|
-
return resolve(process.env.AGORA_CONFIG);
|
|
11
|
-
}
|
|
12
|
-
return resolve(homedir(), '.config', 'agora', 'config.json');
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Parse and normalize config from a JSON object (shared by sync and async loaders).
|
|
16
|
-
*/
|
|
17
|
-
function parseConfig(config) {
|
|
18
|
-
const rawIdentity = config.identity;
|
|
19
|
-
if (!rawIdentity?.publicKey || !rawIdentity?.privateKey) {
|
|
20
|
-
throw new Error('Invalid config: missing identity.publicKey or identity.privateKey');
|
|
21
|
-
}
|
|
22
|
-
const identity = {
|
|
23
|
-
publicKey: rawIdentity.publicKey,
|
|
24
|
-
privateKey: rawIdentity.privateKey,
|
|
25
|
-
name: typeof rawIdentity.name === 'string' ? rawIdentity.name : undefined,
|
|
26
|
-
};
|
|
27
|
-
const peers = {};
|
|
28
|
-
if (config.peers && typeof config.peers === 'object') {
|
|
29
|
-
for (const [name, entry] of Object.entries(config.peers)) {
|
|
30
|
-
const peer = entry;
|
|
31
|
-
if (peer && typeof peer.publicKey === 'string') {
|
|
32
|
-
peers[name] = {
|
|
33
|
-
publicKey: peer.publicKey,
|
|
34
|
-
url: typeof peer.url === 'string' ? peer.url : undefined,
|
|
35
|
-
token: typeof peer.token === 'string' ? peer.token : undefined,
|
|
36
|
-
name: typeof peer.name === 'string' ? peer.name : undefined,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
let relay;
|
|
42
|
-
const rawRelay = config.relay;
|
|
43
|
-
if (typeof rawRelay === 'string') {
|
|
44
|
-
relay = { url: rawRelay, autoConnect: true };
|
|
45
|
-
}
|
|
46
|
-
else if (rawRelay && typeof rawRelay === 'object') {
|
|
47
|
-
const r = rawRelay;
|
|
48
|
-
if (typeof r.url === 'string') {
|
|
49
|
-
relay = {
|
|
50
|
-
url: r.url,
|
|
51
|
-
autoConnect: typeof r.autoConnect === 'boolean' ? r.autoConnect : true,
|
|
52
|
-
name: typeof r.name === 'string' ? r.name : undefined,
|
|
53
|
-
reconnectMaxMs: typeof r.reconnectMaxMs === 'number' ? r.reconnectMaxMs : undefined,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
identity,
|
|
59
|
-
peers,
|
|
60
|
-
...(relay ? { relay } : {}),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Load and normalize Agora configuration from a JSON file (sync).
|
|
65
|
-
* Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.
|
|
66
|
-
*
|
|
67
|
-
* @param path - Config file path; defaults to getDefaultConfigPath()
|
|
68
|
-
* @returns Normalized AgoraConfig
|
|
69
|
-
* @throws Error if file doesn't exist or config is invalid
|
|
70
|
-
*/
|
|
71
|
-
export function loadAgoraConfig(path) {
|
|
72
|
-
const configPath = path ?? getDefaultConfigPath();
|
|
73
|
-
if (!existsSync(configPath)) {
|
|
74
|
-
throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);
|
|
75
|
-
}
|
|
76
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
77
|
-
let config;
|
|
78
|
-
try {
|
|
79
|
-
config = JSON.parse(content);
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
throw new Error(`Invalid JSON in config file: ${configPath}`);
|
|
83
|
-
}
|
|
84
|
-
return parseConfig(config);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Load and normalize Agora configuration from a JSON file (async).
|
|
88
|
-
*
|
|
89
|
-
* @param path - Config file path; defaults to getDefaultConfigPath()
|
|
90
|
-
* @returns Normalized AgoraConfig
|
|
91
|
-
* @throws Error if file doesn't exist or config is invalid
|
|
92
|
-
*/
|
|
93
|
-
export async function loadAgoraConfigAsync(path) {
|
|
94
|
-
const configPath = path ?? getDefaultConfigPath();
|
|
95
|
-
let content;
|
|
96
|
-
try {
|
|
97
|
-
content = await readFile(configPath, 'utf-8');
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
const code = err && typeof err === 'object' && 'code' in err ? err.code : undefined;
|
|
101
|
-
if (code === 'ENOENT') {
|
|
102
|
-
throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);
|
|
103
|
-
}
|
|
104
|
-
throw err;
|
|
105
|
-
}
|
|
106
|
-
let config;
|
|
107
|
-
try {
|
|
108
|
-
config = JSON.parse(content);
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
throw new Error(`Invalid JSON in config file: ${configPath}`);
|
|
112
|
-
}
|
|
113
|
-
return parseConfig(config);
|
|
114
|
-
}
|
|
115
|
-
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootstrap configuration for peer discovery on the Agora network.
|
|
3
|
-
* Provides default bootstrap relays for initial network entry.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Default bootstrap relay servers
|
|
7
|
-
* These are well-known relays that serve as initial entry points to the network
|
|
8
|
-
*/
|
|
9
|
-
export declare const DEFAULT_BOOTSTRAP_RELAYS: {
|
|
10
|
-
url: string;
|
|
11
|
-
name: string;
|
|
12
|
-
}[];
|
|
13
|
-
/**
|
|
14
|
-
* Configuration for bootstrap connection
|
|
15
|
-
*/
|
|
16
|
-
export interface BootstrapConfig {
|
|
17
|
-
/** Bootstrap relay URL */
|
|
18
|
-
relayUrl: string;
|
|
19
|
-
/** Optional relay public key (for verification) */
|
|
20
|
-
relayPublicKey?: string;
|
|
21
|
-
/** Connection timeout in ms (default: 10000) */
|
|
22
|
-
timeout?: number;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Get default bootstrap relay configuration
|
|
26
|
-
*/
|
|
27
|
-
export declare function getDefaultBootstrapRelay(): BootstrapConfig;
|
|
28
|
-
/**
|
|
29
|
-
* Parse bootstrap relay URL and optional public key
|
|
30
|
-
*/
|
|
31
|
-
export declare function parseBootstrapRelay(url: string, publicKey?: string): BootstrapConfig;
|
|
32
|
-
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/discovery/bootstrap.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,eAAO,MAAM,wBAAwB;;;GAOpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,eAAe,CAK1D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,CAMpF"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootstrap configuration for peer discovery on the Agora network.
|
|
3
|
-
* Provides default bootstrap relays for initial network entry.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Default bootstrap relay servers
|
|
7
|
-
* These are well-known relays that serve as initial entry points to the network
|
|
8
|
-
*/
|
|
9
|
-
export const DEFAULT_BOOTSTRAP_RELAYS = [
|
|
10
|
-
{
|
|
11
|
-
url: 'wss://agora-relay.lbsa71.net',
|
|
12
|
-
name: 'Primary Bootstrap Relay',
|
|
13
|
-
// Note: Public key would need to be set when the relay is actually deployed
|
|
14
|
-
// For now, this is a placeholder that would be configured when the relay is running
|
|
15
|
-
},
|
|
16
|
-
];
|
|
17
|
-
/**
|
|
18
|
-
* Get default bootstrap relay configuration
|
|
19
|
-
*/
|
|
20
|
-
export function getDefaultBootstrapRelay() {
|
|
21
|
-
return {
|
|
22
|
-
relayUrl: DEFAULT_BOOTSTRAP_RELAYS[0].url,
|
|
23
|
-
timeout: 10000,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Parse bootstrap relay URL and optional public key
|
|
28
|
-
*/
|
|
29
|
-
export function parseBootstrapRelay(url, publicKey) {
|
|
30
|
-
return {
|
|
31
|
-
relayUrl: url,
|
|
32
|
-
relayPublicKey: publicKey,
|
|
33
|
-
timeout: 10000,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/discovery/bootstrap.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC;QACE,GAAG,EAAE,8BAA8B;QACnC,IAAI,EAAE,yBAAyB;QAC/B,4EAA4E;QAC5E,oFAAoF;KACrF;CACF,CAAC;AAcF;;GAEG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;QACL,QAAQ,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,GAAG;QACzC,OAAO,EAAE,KAAK;KACf,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,SAAkB;IACjE,OAAO;QACL,QAAQ,EAAE,GAAG;QACb,cAAc,EAAE,SAAS;QACzB,OAAO,EAAE,KAAK;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
import type { RelayClient } from '../relay/client.js';
|
|
3
|
-
import type { PeerListRequestPayload, PeerListResponsePayload, PeerReferralPayload } from '../message/types/peer-discovery.js';
|
|
4
|
-
/**
|
|
5
|
-
* Configuration for PeerDiscoveryService
|
|
6
|
-
*/
|
|
7
|
-
export interface PeerDiscoveryConfig {
|
|
8
|
-
/** Agent's public key */
|
|
9
|
-
publicKey: string;
|
|
10
|
-
/** Agent's private key for signing */
|
|
11
|
-
privateKey: string;
|
|
12
|
-
/** RelayClient instance for communication */
|
|
13
|
-
relayClient: RelayClient;
|
|
14
|
-
/** Public key of the relay server (for sending peer list requests) */
|
|
15
|
-
relayPublicKey?: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Events emitted by PeerDiscoveryService
|
|
19
|
-
*/
|
|
20
|
-
export interface PeerDiscoveryEvents {
|
|
21
|
-
/** Emitted when peers are discovered */
|
|
22
|
-
'peers-discovered': (peers: PeerListResponsePayload['peers']) => void;
|
|
23
|
-
/** Emitted when a peer referral is received */
|
|
24
|
-
'peer-referral': (referral: PeerReferralPayload, from: string) => void;
|
|
25
|
-
/** Emitted on errors */
|
|
26
|
-
'error': (error: Error) => void;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Service for discovering peers on the Agora network
|
|
30
|
-
*/
|
|
31
|
-
export declare class PeerDiscoveryService extends EventEmitter {
|
|
32
|
-
private config;
|
|
33
|
-
constructor(config: PeerDiscoveryConfig);
|
|
34
|
-
/**
|
|
35
|
-
* Request peer list from relay
|
|
36
|
-
*/
|
|
37
|
-
discoverViaRelay(filters?: PeerListRequestPayload['filters']): Promise<PeerListResponsePayload | null>;
|
|
38
|
-
/**
|
|
39
|
-
* Send peer referral to another agent
|
|
40
|
-
*/
|
|
41
|
-
referPeer(recipientPublicKey: string, referredPublicKey: string, metadata?: {
|
|
42
|
-
name?: string;
|
|
43
|
-
endpoint?: string;
|
|
44
|
-
comment?: string;
|
|
45
|
-
trustScore?: number;
|
|
46
|
-
}): Promise<{
|
|
47
|
-
ok: boolean;
|
|
48
|
-
error?: string;
|
|
49
|
-
}>;
|
|
50
|
-
/**
|
|
51
|
-
* Handle incoming peer referral
|
|
52
|
-
*/
|
|
53
|
-
private handleReferral;
|
|
54
|
-
/**
|
|
55
|
-
* Handle incoming peer list from relay
|
|
56
|
-
*/
|
|
57
|
-
private handlePeerList;
|
|
58
|
-
}
|
|
59
|
-
//# sourceMappingURL=peer-discovery.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"peer-discovery.d.ts","sourceRoot":"","sources":["../../src/discovery/peer-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAE/H;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,WAAW,EAAE,WAAW,CAAC;IACzB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,kBAAkB,EAAE,CAAC,KAAK,EAAE,uBAAuB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,+CAA+C;IAC/C,eAAe,EAAE,CAAC,QAAQ,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvE,wBAAwB;IACxB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,EAAE,mBAAmB;IAcvC;;OAEG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,sBAAsB,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAiD5G;;OAEG;IACG,SAAS,CACb,kBAAkB,EAAE,MAAM,EAC1B,iBAAiB,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GACrF,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAuB3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,cAAc;CAiBvB"}
|