@stamn/agent 0.8.0 → 0.8.1
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.
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { createRequire } from "module"; const require = createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
SERVER_URL
|
|
4
|
-
} from "./chunk-JVIBUEVN.js";
|
|
5
2
|
|
|
6
3
|
// src/logging/logger.ts
|
|
7
4
|
import pino from "pino";
|
|
8
|
-
function createLogger(
|
|
5
|
+
function createLogger(config) {
|
|
9
6
|
const isDev = process.env.NODE_ENV !== "production";
|
|
10
7
|
return pino({
|
|
11
|
-
level:
|
|
8
|
+
level: config.logLevel,
|
|
12
9
|
transport: isDev ? {
|
|
13
10
|
target: "pino-pretty",
|
|
14
11
|
options: { colorize: true, translateTime: "SYS:HH:MM:ss" }
|
|
@@ -145,7 +142,8 @@ var WSClient = class {
|
|
|
145
142
|
}
|
|
146
143
|
connect() {
|
|
147
144
|
if (this.isShuttingDown) return;
|
|
148
|
-
const
|
|
145
|
+
const config = this.options.config;
|
|
146
|
+
const wsUrl = config.serverUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:");
|
|
149
147
|
const url = `${wsUrl}/ws/agent`;
|
|
150
148
|
this.options.logger.info({ url }, "Connecting to server...");
|
|
151
149
|
this.ws = new WebSocket(url);
|
|
@@ -266,7 +264,7 @@ var WSClient = class {
|
|
|
266
264
|
const payload = {
|
|
267
265
|
agentId: this.options.config.agentId,
|
|
268
266
|
status,
|
|
269
|
-
version: "0.8.
|
|
267
|
+
version: "0.8.1",
|
|
270
268
|
platform: `${process.platform}-${process.arch}`,
|
|
271
269
|
nodeVersion: process.versions.node
|
|
272
270
|
};
|
|
@@ -373,4 +371,4 @@ export {
|
|
|
373
371
|
WSClient,
|
|
374
372
|
SpendClient
|
|
375
373
|
};
|
|
376
|
-
//# sourceMappingURL=chunk-
|
|
374
|
+
//# sourceMappingURL=chunk-5RSB2UIE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logging/logger.ts","../src/ws/ws-client.ts","../src/ws/heartbeat.ts","../src/ws/message-handler.ts","../src/spend/spend-client.ts"],"sourcesContent":["import pino from 'pino';\nimport type { AgentConfig } from '../config/config-schema.js';\n\nexport function createLogger(\n config: Pick<AgentConfig, 'logLevel'>,\n): pino.Logger {\n const isDev = process.env.NODE_ENV !== 'production';\n\n return pino({\n level: config.logLevel,\n transport: isDev\n ? {\n target: 'pino-pretty',\n options: { colorize: true, translateTime: 'SYS:HH:MM:ss' },\n }\n : undefined,\n });\n}\n","import WebSocket from 'ws';\nimport type {\n AuthenticatePayload,\n AuthenticatedPayload,\n AuthErrorPayload,\n StatusReportPayload,\n SpendApprovedPayload,\n SpendDeniedPayload,\n} from '@stamn/types';\n\ndeclare const AGENT_VERSION: string;\nimport type { Logger } from 'pino';\nimport type { AgentConfig } from '../config/config-schema.js';\nimport { Heartbeat, type HeartbeatSender } from './heartbeat.js';\nimport { MessageHandler } from './message-handler.js';\n\nexport type SpendResultCallback = (\n type: 'approved' | 'denied',\n payload: SpendApprovedPayload | SpendDeniedPayload,\n) => void;\n\nexport interface WSClientOptions {\n config: AgentConfig;\n logger: Logger;\n onCommand: (command: string, params?: Record<string, unknown>) => void;\n onDisconnect: () => void;\n onConnected: () => void;\n}\n\nexport class WSClient implements HeartbeatSender {\n private ws: WebSocket | null = null;\n private heartbeat: Heartbeat | null = null;\n private handler: MessageHandler;\n private reconnectAttempt = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private isShuttingDown = false;\n private authenticated = false;\n private readonly startTime = Date.now();\n private spendListeners = new Map<string, SpendResultCallback>();\n\n constructor(private options: WSClientOptions) {\n this.handler = new MessageHandler(options.logger, options.onCommand);\n }\n\n onSpendResult(requestId: string, callback: SpendResultCallback): void {\n this.spendListeners.set(requestId, callback);\n }\n\n removeSpendListener(requestId: string): void {\n this.spendListeners.delete(requestId);\n }\n\n connect(): void {\n if (this.isShuttingDown) return;\n\n const config = this.options.config;\n const wsUrl = config.serverUrl\n .replace(/^http:/, 'ws:')\n .replace(/^https:/, 'wss:');\n const url = `${wsUrl}/ws/agent`;\n\n this.options.logger.info({ url }, 'Connecting to server...');\n\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n this.options.logger.info('WebSocket connected, authenticating...');\n this.reconnectAttempt = 0;\n\n const payload: AuthenticatePayload = {\n agentId: config.agentId!,\n apiKey: config.apiKey ?? '',\n };\n\n this.send('agent:authenticate', payload);\n });\n\n this.ws.on('message', (data: WebSocket.RawData) => {\n const raw = data.toString();\n const result = this.handler.handle(raw);\n if (!result) return;\n\n switch (result.type) {\n case 'server:authenticated': {\n const payload = result.payload as AuthenticatedPayload;\n this.authenticated = true;\n this.options.logger.info(\n { serverVersion: payload.serverVersion },\n `Agent ${payload.agentId} authenticated`,\n );\n\n this.heartbeat = new Heartbeat(\n this,\n config.agentId!,\n config.heartbeatIntervalMs,\n this.startTime,\n );\n this.heartbeat.start();\n\n this.sendStatusReport('online');\n this.options.onConnected();\n break;\n }\n\n case 'server:auth_error': {\n const payload = result.payload as AuthErrorPayload;\n this.options.logger.error(\n { reason: payload.reason },\n 'Authentication failed',\n );\n this.isShuttingDown = true;\n this.ws?.close(4003, 'Auth failed');\n break;\n }\n\n case 'server:heartbeat_ack':\n this.heartbeat?.onAck();\n break;\n\n case 'server:spend_approved': {\n const sp = result.payload as SpendApprovedPayload;\n const cb = this.spendListeners.get(sp.requestId);\n if (cb) {\n cb('approved', sp);\n this.spendListeners.delete(sp.requestId);\n }\n break;\n }\n\n case 'server:spend_denied': {\n const sd = result.payload as SpendDeniedPayload;\n const dcb = this.spendListeners.get(sd.requestId);\n if (dcb) {\n dcb('denied', sd);\n this.spendListeners.delete(sd.requestId);\n }\n break;\n }\n\n case 'server:event':\n this.options.logger.debug({ event: result.payload }, 'Server event');\n break;\n\n case 'server:command':\n // Already handled by MessageHandler → onCommand callback\n break;\n }\n });\n\n this.ws.on('close', (code: number, reason: Buffer) => {\n this.authenticated = false;\n this.heartbeat?.stop();\n this.heartbeat = null;\n\n if (this.isShuttingDown) {\n this.options.logger.info('Connection closed');\n return;\n }\n\n this.options.logger.warn(\n { code, reason: reason.toString() },\n 'Connection lost',\n );\n this.options.onDisconnect();\n this.scheduleReconnect();\n });\n\n this.ws.on('error', (err: Error) => {\n this.options.logger.error({ err: err.message }, 'WebSocket error');\n });\n }\n\n reconnect(): void {\n this.heartbeat?.stop();\n this.heartbeat = null;\n this.ws?.close();\n }\n\n send<T>(type: string, payload: T): void {\n if (this.ws?.readyState !== WebSocket.OPEN) return;\n\n // NestJS @SubscribeMessage expects {event, data} format\n this.ws.send(JSON.stringify({ event: type, data: payload }));\n }\n\n disconnect(): void {\n this.isShuttingDown = true;\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.authenticated) {\n this.sendStatusReport('shutting_down');\n }\n\n this.heartbeat?.stop();\n this.ws?.close(1000, 'Client shutdown');\n }\n\n get isAuthenticated(): boolean {\n return this.authenticated;\n }\n\n private sendStatusReport(status: StatusReportPayload['status']): void {\n const payload: StatusReportPayload = {\n agentId: this.options.config.agentId!,\n status,\n version: AGENT_VERSION,\n platform: `${process.platform}-${process.arch}`,\n nodeVersion: process.versions.node,\n };\n this.send('agent:status_report', payload);\n }\n\n private scheduleReconnect(): void {\n const { wsReconnectBaseMs, wsReconnectMaxMs } = this.options.config;\n const delay = Math.min(\n wsReconnectBaseMs * 2 ** this.reconnectAttempt,\n wsReconnectMaxMs,\n );\n const jitter = Math.random() * delay * 0.1;\n const totalDelay = Math.round(delay + jitter);\n\n this.reconnectAttempt++;\n this.options.logger.info(\n { attempt: this.reconnectAttempt, delayMs: totalDelay },\n 'Reconnecting...',\n );\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, totalDelay);\n }\n}\n","import type { HeartbeatPayload } from '@stamn/types';\n\nexport interface HeartbeatSender {\n send<T>(type: string, payload: T): void;\n reconnect(): void;\n}\n\nexport class Heartbeat {\n private timer: ReturnType<typeof setInterval> | null = null;\n private missedPongs = 0;\n private readonly maxMissedPongs = 3;\n\n constructor(\n private sender: HeartbeatSender,\n private agentId: string,\n private intervalMs: number,\n private startTime: number,\n ) {}\n\n start(): void {\n this.missedPongs = 0;\n this.timer = setInterval(() => {\n if (this.missedPongs >= this.maxMissedPongs) {\n this.sender.reconnect();\n return;\n }\n\n const payload: HeartbeatPayload = {\n agentId: this.agentId,\n uptimeSeconds: Math.floor((Date.now() - this.startTime) / 1000),\n memoryUsageMb: Math.round(process.memoryUsage().rss / 1024 / 1024),\n };\n\n this.sender.send('agent:heartbeat', payload);\n this.missedPongs++;\n }, this.intervalMs);\n }\n\n onAck(): void {\n this.missedPongs = 0;\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n}\n","import type { Logger } from 'pino';\nimport { z } from 'zod';\nimport type { ServerToAgentMessageType, CommandPayload } from '@stamn/types';\n\n// NestJS WS sends {event, data} format\nconst wsMessageSchema = z.object({\n event: z.string(),\n data: z.unknown(),\n});\n\nconst commandPayloadSchema = z.object({\n commandId: z.string(),\n command: z.enum(['pause', 'resume', 'update_config', 'shutdown']),\n params: z.record(z.unknown()).optional(),\n});\n\nexport class MessageHandler {\n constructor(\n private logger: Logger,\n private onCommand: (\n command: string,\n params?: Record<string, unknown>,\n ) => void,\n ) {}\n\n handle(\n raw: string,\n ): { type: ServerToAgentMessageType; payload: unknown } | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.logger.warn({ raw }, 'Invalid JSON received');\n return null;\n }\n\n const result = wsMessageSchema.safeParse(parsed);\n if (!result.success) {\n this.logger.warn({ error: result.error.message }, 'Invalid WS message');\n return null;\n }\n\n const { event, data } = result.data;\n\n switch (event) {\n case 'server:authenticated':\n case 'server:auth_error':\n case 'server:heartbeat_ack':\n case 'server:event':\n case 'server:spend_approved':\n case 'server:spend_denied':\n return {\n type: event as ServerToAgentMessageType,\n payload: data,\n };\n\n case 'server:command': {\n const cmdResult = commandPayloadSchema.safeParse(data);\n if (!cmdResult.success) {\n this.logger.warn(\n { error: cmdResult.error.message },\n 'Invalid command payload',\n );\n return null;\n }\n\n const cmd = cmdResult.data as CommandPayload;\n this.logger.info(\n { command: cmd.command, commandId: cmd.commandId },\n 'Received command',\n );\n this.onCommand(cmd.command, cmd.params);\n return { type: 'server:command', payload: cmd };\n }\n\n default:\n this.logger.debug({ type: event }, 'Unknown message type');\n return null;\n }\n }\n}\n","import { randomUUID } from 'crypto';\nimport type { Logger } from 'pino';\nimport type {\n SpendRequestPayload,\n SpendApprovedPayload,\n SpendDeniedPayload,\n LedgerCategory,\n LedgerRail,\n} from '@stamn/types';\nimport type { WSClient } from '../ws/ws-client.js';\n\nexport type SpendResult =\n | {\n approved: true;\n ledgerEntryId: string;\n transactionHash?: string;\n remainingBalanceCents: number;\n }\n | { approved: false; reason: string; code: string };\n\nexport interface SpendParams {\n amountCents: number;\n category: LedgerCategory;\n rail: LedgerRail;\n vendor?: string;\n description: string;\n recipientAgentId?: string;\n recipientAddress?: string;\n}\n\nconst SPEND_TIMEOUT_MS = 30_000;\n\nexport class SpendClient {\n constructor(\n private wsClient: WSClient,\n private logger: Logger,\n ) {}\n\n async request(params: SpendParams): Promise<SpendResult> {\n const requestId = randomUUID();\n\n const payload: SpendRequestPayload = {\n requestId,\n amountCents: params.amountCents,\n currency: 'USDC',\n category: params.category,\n rail: params.rail,\n vendor: params.vendor,\n description: params.description,\n recipientAgentId: params.recipientAgentId,\n recipientAddress: params.recipientAddress,\n };\n\n this.logger.info(\n {\n requestId,\n amountCents: params.amountCents,\n vendor: params.vendor,\n category: params.category,\n },\n `Requesting spend: ${params.description}`,\n );\n\n return new Promise<SpendResult>((resolve) => {\n const timeout = setTimeout(() => {\n this.wsClient.removeSpendListener(requestId);\n this.logger.error({ requestId }, 'Spend request timed out');\n resolve({\n approved: false,\n reason: 'Request timed out',\n code: 'timeout',\n });\n }, SPEND_TIMEOUT_MS);\n\n this.wsClient.onSpendResult(requestId, (type, result) => {\n clearTimeout(timeout);\n\n if (type === 'approved') {\n const approved = result as SpendApprovedPayload;\n this.logger.info(\n {\n requestId,\n ledgerEntryId: approved.ledgerEntryId,\n remainingBalanceCents: approved.remainingBalanceCents,\n },\n 'Spend approved',\n );\n resolve({\n approved: true,\n ledgerEntryId: approved.ledgerEntryId,\n transactionHash: approved.transactionHash,\n remainingBalanceCents: approved.remainingBalanceCents,\n });\n } else {\n const denied = result as SpendDeniedPayload;\n this.logger.warn(\n { requestId, code: denied.code, reason: denied.reason },\n 'Spend denied',\n );\n resolve({\n approved: false,\n reason: denied.reason,\n code: denied.code,\n });\n }\n });\n\n this.wsClient.send('agent:spend_request', payload);\n });\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/commands/spend.js
CHANGED
package/dist/commands/start.js
CHANGED
package/dist/commands/update.js
CHANGED
|
@@ -6,13 +6,13 @@ import { execSync } from "child_process";
|
|
|
6
6
|
var Update = class extends Command {
|
|
7
7
|
static description = "Update the Stamn agent to the latest version";
|
|
8
8
|
async run() {
|
|
9
|
-
this.log(`Current version: ${"0.8.
|
|
9
|
+
this.log(`Current version: ${"0.8.1"}`);
|
|
10
10
|
this.log("Checking for updates...");
|
|
11
11
|
try {
|
|
12
12
|
const latest = execSync("npm view @stamn/agent version", {
|
|
13
13
|
encoding: "utf-8"
|
|
14
14
|
}).trim();
|
|
15
|
-
if (latest === "0.8.
|
|
15
|
+
if (latest === "0.8.1") {
|
|
16
16
|
this.log("Already on the latest version.");
|
|
17
17
|
return;
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/logging/logger.ts","../src/ws/ws-client.ts","../src/ws/heartbeat.ts","../src/ws/message-handler.ts","../src/spend/spend-client.ts"],"sourcesContent":["import pino from 'pino';\nimport type { AgentConfig } from '../config/config-schema.js';\n\nexport function createLogger(\n config: Pick<AgentConfig, 'logLevel'>,\n): pino.Logger {\n const isDev = process.env.NODE_ENV !== 'production';\n\n return pino({\n level: config.logLevel,\n transport: isDev\n ? {\n target: 'pino-pretty',\n options: { colorize: true, translateTime: 'SYS:HH:MM:ss' },\n }\n : undefined,\n });\n}\n","import WebSocket from 'ws';\nimport type {\n AuthenticatePayload,\n AuthenticatedPayload,\n AuthErrorPayload,\n StatusReportPayload,\n SpendApprovedPayload,\n SpendDeniedPayload,\n} from '@stamn/types';\n\ndeclare const AGENT_VERSION: string;\nimport type { Logger } from 'pino';\nimport type { AgentConfig } from '../config/config-schema.js';\nimport { SERVER_URL } from '../config/config-schema.js';\nimport { Heartbeat, type HeartbeatSender } from './heartbeat.js';\nimport { MessageHandler } from './message-handler.js';\n\nexport type SpendResultCallback = (\n type: 'approved' | 'denied',\n payload: SpendApprovedPayload | SpendDeniedPayload,\n) => void;\n\nexport interface WSClientOptions {\n config: AgentConfig;\n logger: Logger;\n onCommand: (command: string, params?: Record<string, unknown>) => void;\n onDisconnect: () => void;\n onConnected: () => void;\n}\n\nexport class WSClient implements HeartbeatSender {\n private ws: WebSocket | null = null;\n private heartbeat: Heartbeat | null = null;\n private handler: MessageHandler;\n private reconnectAttempt = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private isShuttingDown = false;\n private authenticated = false;\n private readonly startTime = Date.now();\n private spendListeners = new Map<string, SpendResultCallback>();\n\n constructor(private options: WSClientOptions) {\n this.handler = new MessageHandler(options.logger, options.onCommand);\n }\n\n onSpendResult(requestId: string, callback: SpendResultCallback): void {\n this.spendListeners.set(requestId, callback);\n }\n\n removeSpendListener(requestId: string): void {\n this.spendListeners.delete(requestId);\n }\n\n connect(): void {\n if (this.isShuttingDown) return;\n\n const wsUrl = SERVER_URL.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:');\n const url = `${wsUrl}/ws/agent`;\n\n this.options.logger.info({ url }, 'Connecting to server...');\n\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n this.options.logger.info('WebSocket connected, authenticating...');\n this.reconnectAttempt = 0;\n\n const payload: AuthenticatePayload = {\n agentId: config.agentId!,\n apiKey: config.apiKey ?? '',\n };\n\n this.send('agent:authenticate', payload);\n });\n\n this.ws.on('message', (data: WebSocket.RawData) => {\n const raw = data.toString();\n const result = this.handler.handle(raw);\n if (!result) return;\n\n switch (result.type) {\n case 'server:authenticated': {\n const payload = result.payload as AuthenticatedPayload;\n this.authenticated = true;\n this.options.logger.info(\n { serverVersion: payload.serverVersion },\n `Agent ${payload.agentId} authenticated`,\n );\n\n this.heartbeat = new Heartbeat(\n this,\n config.agentId!,\n config.heartbeatIntervalMs,\n this.startTime,\n );\n this.heartbeat.start();\n\n this.sendStatusReport('online');\n this.options.onConnected();\n break;\n }\n\n case 'server:auth_error': {\n const payload = result.payload as AuthErrorPayload;\n this.options.logger.error(\n { reason: payload.reason },\n 'Authentication failed',\n );\n this.isShuttingDown = true;\n this.ws?.close(4003, 'Auth failed');\n break;\n }\n\n case 'server:heartbeat_ack':\n this.heartbeat?.onAck();\n break;\n\n case 'server:spend_approved': {\n const sp = result.payload as SpendApprovedPayload;\n const cb = this.spendListeners.get(sp.requestId);\n if (cb) {\n cb('approved', sp);\n this.spendListeners.delete(sp.requestId);\n }\n break;\n }\n\n case 'server:spend_denied': {\n const sd = result.payload as SpendDeniedPayload;\n const dcb = this.spendListeners.get(sd.requestId);\n if (dcb) {\n dcb('denied', sd);\n this.spendListeners.delete(sd.requestId);\n }\n break;\n }\n\n case 'server:event':\n this.options.logger.debug({ event: result.payload }, 'Server event');\n break;\n\n case 'server:command':\n // Already handled by MessageHandler → onCommand callback\n break;\n }\n });\n\n this.ws.on('close', (code: number, reason: Buffer) => {\n this.authenticated = false;\n this.heartbeat?.stop();\n this.heartbeat = null;\n\n if (this.isShuttingDown) {\n this.options.logger.info('Connection closed');\n return;\n }\n\n this.options.logger.warn(\n { code, reason: reason.toString() },\n 'Connection lost',\n );\n this.options.onDisconnect();\n this.scheduleReconnect();\n });\n\n this.ws.on('error', (err: Error) => {\n this.options.logger.error({ err: err.message }, 'WebSocket error');\n });\n }\n\n reconnect(): void {\n this.heartbeat?.stop();\n this.heartbeat = null;\n this.ws?.close();\n }\n\n send<T>(type: string, payload: T): void {\n if (this.ws?.readyState !== WebSocket.OPEN) return;\n\n // NestJS @SubscribeMessage expects {event, data} format\n this.ws.send(JSON.stringify({ event: type, data: payload }));\n }\n\n disconnect(): void {\n this.isShuttingDown = true;\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.authenticated) {\n this.sendStatusReport('shutting_down');\n }\n\n this.heartbeat?.stop();\n this.ws?.close(1000, 'Client shutdown');\n }\n\n get isAuthenticated(): boolean {\n return this.authenticated;\n }\n\n private sendStatusReport(status: StatusReportPayload['status']): void {\n const payload: StatusReportPayload = {\n agentId: this.options.config.agentId!,\n status,\n version: AGENT_VERSION,\n platform: `${process.platform}-${process.arch}`,\n nodeVersion: process.versions.node,\n };\n this.send('agent:status_report', payload);\n }\n\n private scheduleReconnect(): void {\n const { wsReconnectBaseMs, wsReconnectMaxMs } = this.options.config;\n const delay = Math.min(\n wsReconnectBaseMs * 2 ** this.reconnectAttempt,\n wsReconnectMaxMs,\n );\n const jitter = Math.random() * delay * 0.1;\n const totalDelay = Math.round(delay + jitter);\n\n this.reconnectAttempt++;\n this.options.logger.info(\n { attempt: this.reconnectAttempt, delayMs: totalDelay },\n 'Reconnecting...',\n );\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, totalDelay);\n }\n}\n","import type { HeartbeatPayload } from '@stamn/types';\n\nexport interface HeartbeatSender {\n send<T>(type: string, payload: T): void;\n reconnect(): void;\n}\n\nexport class Heartbeat {\n private timer: ReturnType<typeof setInterval> | null = null;\n private missedPongs = 0;\n private readonly maxMissedPongs = 3;\n\n constructor(\n private sender: HeartbeatSender,\n private agentId: string,\n private intervalMs: number,\n private startTime: number,\n ) {}\n\n start(): void {\n this.missedPongs = 0;\n this.timer = setInterval(() => {\n if (this.missedPongs >= this.maxMissedPongs) {\n this.sender.reconnect();\n return;\n }\n\n const payload: HeartbeatPayload = {\n agentId: this.agentId,\n uptimeSeconds: Math.floor((Date.now() - this.startTime) / 1000),\n memoryUsageMb: Math.round(process.memoryUsage().rss / 1024 / 1024),\n };\n\n this.sender.send('agent:heartbeat', payload);\n this.missedPongs++;\n }, this.intervalMs);\n }\n\n onAck(): void {\n this.missedPongs = 0;\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n}\n","import type { Logger } from 'pino';\nimport { z } from 'zod';\nimport type { ServerToAgentMessageType, CommandPayload } from '@stamn/types';\n\n// NestJS WS sends {event, data} format\nconst wsMessageSchema = z.object({\n event: z.string(),\n data: z.unknown(),\n});\n\nconst commandPayloadSchema = z.object({\n commandId: z.string(),\n command: z.enum(['pause', 'resume', 'update_config', 'shutdown']),\n params: z.record(z.unknown()).optional(),\n});\n\nexport class MessageHandler {\n constructor(\n private logger: Logger,\n private onCommand: (\n command: string,\n params?: Record<string, unknown>,\n ) => void,\n ) {}\n\n handle(\n raw: string,\n ): { type: ServerToAgentMessageType; payload: unknown } | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.logger.warn({ raw }, 'Invalid JSON received');\n return null;\n }\n\n const result = wsMessageSchema.safeParse(parsed);\n if (!result.success) {\n this.logger.warn({ error: result.error.message }, 'Invalid WS message');\n return null;\n }\n\n const { event, data } = result.data;\n\n switch (event) {\n case 'server:authenticated':\n case 'server:auth_error':\n case 'server:heartbeat_ack':\n case 'server:event':\n case 'server:spend_approved':\n case 'server:spend_denied':\n return {\n type: event as ServerToAgentMessageType,\n payload: data,\n };\n\n case 'server:command': {\n const cmdResult = commandPayloadSchema.safeParse(data);\n if (!cmdResult.success) {\n this.logger.warn(\n { error: cmdResult.error.message },\n 'Invalid command payload',\n );\n return null;\n }\n\n const cmd = cmdResult.data as CommandPayload;\n this.logger.info(\n { command: cmd.command, commandId: cmd.commandId },\n 'Received command',\n );\n this.onCommand(cmd.command, cmd.params);\n return { type: 'server:command', payload: cmd };\n }\n\n default:\n this.logger.debug({ type: event }, 'Unknown message type');\n return null;\n }\n }\n}\n","import { randomUUID } from 'crypto';\nimport type { Logger } from 'pino';\nimport type {\n SpendRequestPayload,\n SpendApprovedPayload,\n SpendDeniedPayload,\n LedgerCategory,\n LedgerRail,\n} from '@stamn/types';\nimport type { WSClient } from '../ws/ws-client.js';\n\nexport type SpendResult =\n | {\n approved: true;\n ledgerEntryId: string;\n transactionHash?: string;\n remainingBalanceCents: number;\n }\n | { approved: false; reason: string; code: string };\n\nexport interface SpendParams {\n amountCents: number;\n category: LedgerCategory;\n rail: LedgerRail;\n vendor?: string;\n description: string;\n recipientAgentId?: string;\n recipientAddress?: string;\n}\n\nconst SPEND_TIMEOUT_MS = 30_000;\n\nexport class SpendClient {\n constructor(\n private wsClient: WSClient,\n private logger: Logger,\n ) {}\n\n async request(params: SpendParams): Promise<SpendResult> {\n const requestId = randomUUID();\n\n const payload: SpendRequestPayload = {\n requestId,\n amountCents: params.amountCents,\n currency: 'USDC',\n category: params.category,\n rail: params.rail,\n vendor: params.vendor,\n description: params.description,\n recipientAgentId: params.recipientAgentId,\n recipientAddress: params.recipientAddress,\n };\n\n this.logger.info(\n {\n requestId,\n amountCents: params.amountCents,\n vendor: params.vendor,\n category: params.category,\n },\n `Requesting spend: ${params.description}`,\n );\n\n return new Promise<SpendResult>((resolve) => {\n const timeout = setTimeout(() => {\n this.wsClient.removeSpendListener(requestId);\n this.logger.error({ requestId }, 'Spend request timed out');\n resolve({\n approved: false,\n reason: 'Request timed out',\n code: 'timeout',\n });\n }, SPEND_TIMEOUT_MS);\n\n this.wsClient.onSpendResult(requestId, (type, result) => {\n clearTimeout(timeout);\n\n if (type === 'approved') {\n const approved = result as SpendApprovedPayload;\n this.logger.info(\n {\n requestId,\n ledgerEntryId: approved.ledgerEntryId,\n remainingBalanceCents: approved.remainingBalanceCents,\n },\n 'Spend approved',\n );\n resolve({\n approved: true,\n ledgerEntryId: approved.ledgerEntryId,\n transactionHash: approved.transactionHash,\n remainingBalanceCents: approved.remainingBalanceCents,\n });\n } else {\n const denied = result as SpendDeniedPayload;\n this.logger.warn(\n { requestId, code: denied.code, reason: denied.reason },\n 'Spend denied',\n );\n resolve({\n approved: false,\n reason: denied.reason,\n code: denied.code,\n });\n }\n });\n\n this.wsClient.send('agent:spend_request', payload);\n });\n }\n}\n"],"mappings":"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","names":["config"]}
|