@stamn/agent 0.2.0 → 0.4.0

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 CHANGED
@@ -12,9 +12,8 @@ npm i -g @stamn/agent
12
12
 
13
13
  ```bash
14
14
  # Configure
15
- stamn config set serverUrl https://your-server.example.com
16
- stamn config set agentId <your-agent-uuid>
17
- stamn config set apiKey <your-api-key>
15
+ stamn config set agent-id <your-agent-uuid>
16
+ stamn config set api-key <your-api-key>
18
17
 
19
18
  # Start daemon
20
19
  stamn start
@@ -32,13 +31,12 @@ Start the agent daemon.
32
31
  ```bash
33
32
  stamn start # foreground
34
33
  stamn start -d # background daemon
35
- stamn start --server-url https://... --agent-id <uuid>
34
+ stamn start --agent-id <uuid> --api-key <key>
36
35
  ```
37
36
 
38
37
  | Flag | Env | Description |
39
38
  |------|-----|-------------|
40
39
  | `--daemon, -d` | | Run as background process |
41
- | `--server-url` | `STAMN_SERVER_URL` | Server URL |
42
40
  | `--agent-id` | `STAMN_AGENT_ID` | Agent UUID |
43
41
  | `--api-key` | `STAMN_API_KEY` | API key |
44
42
  | `--log-level` | | `trace\|debug\|info\|warn\|error\|fatal` |
@@ -89,7 +87,6 @@ stamn spend \
89
87
  All flags can also be set via env vars:
90
88
 
91
89
  ```bash
92
- export STAMN_SERVER_URL=https://your-server.example.com
93
90
  export STAMN_AGENT_ID=<uuid>
94
91
  export STAMN_API_KEY=<key>
95
92
  stamn start
@@ -2,8 +2,8 @@ import { createRequire } from "module"; const require = createRequire(import.met
2
2
 
3
3
  // src/config/config-schema.ts
4
4
  import { z } from "zod";
5
+ var SERVER_URL = "https://api.stamn.com";
5
6
  var configSchema = z.object({
6
- serverUrl: z.string().url().default("http://localhost:3001"),
7
7
  apiKey: z.string().min(1).optional(),
8
8
  agentId: z.string().uuid().optional(),
9
9
  logLevel: z.enum(["trace", "debug", "info", "warn", "error", "fatal"]).default("info"),
@@ -42,7 +42,8 @@ var ConfigStore = class {
42
42
  };
43
43
 
44
44
  export {
45
+ SERVER_URL,
45
46
  configSchema,
46
47
  ConfigStore
47
48
  };
48
- //# sourceMappingURL=chunk-2VA3LUCR.js.map
49
+ //# sourceMappingURL=chunk-5UI5E6YF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/config-schema.ts","../src/config/config-store.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const SERVER_URL = 'https://api.stamn.com';\n\nexport const configSchema = z.object({\n apiKey: z.string().min(1).optional(),\n agentId: z.string().uuid().optional(),\n logLevel: z\n .enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n .default('info'),\n heartbeatIntervalMs: z.number().int().positive().default(30_000),\n wsReconnectBaseMs: z.number().int().positive().default(1_000),\n wsReconnectMaxMs: z.number().int().positive().default(30_000),\n});\n\nexport type AgentConfig = z.infer<typeof configSchema>;\n\nexport const CONFIG_DEFAULTS: AgentConfig = configSchema.parse({});\n","import Conf from 'conf';\nimport { configSchema, CONFIG_DEFAULTS, type AgentConfig } from './config-schema.js';\n\nexport class ConfigStore {\n private store: Conf<AgentConfig>;\n\n constructor() {\n this.store = new Conf<AgentConfig>({\n projectName: 'stamn',\n defaults: CONFIG_DEFAULTS,\n });\n }\n\n get<K extends keyof AgentConfig>(key: K): AgentConfig[K] {\n return this.store.get(key);\n }\n\n set<K extends keyof AgentConfig>(key: K, value: AgentConfig[K]): void {\n configSchema.partial().parse({ [key]: value });\n this.store.set(key, value);\n }\n\n getAll(): AgentConfig {\n return configSchema.parse(this.store.store);\n }\n\n clear(): void {\n this.store.clear();\n }\n\n get path(): string {\n return this.store.path;\n }\n}\n"],"mappings":";;;AAAA,SAAS,SAAS;AAEX,IAAM,aAAa;AAEnB,IAAM,eAAe,EAAE,OAAO;AAAA,EACnC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACnC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,UAAU,EACP,KAAK,CAAC,SAAS,SAAS,QAAQ,QAAQ,SAAS,OAAO,CAAC,EACzD,QAAQ,MAAM;AAAA,EACjB,qBAAqB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAM;AAAA,EAC/D,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAK;AAAA,EAC5D,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAM;AAC9D,CAAC;AAIM,IAAM,kBAA+B,aAAa,MAAM,CAAC,CAAC;;;ACjBjE,OAAO,UAAU;AAGV,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EAER,cAAc;AACZ,SAAK,QAAQ,IAAI,KAAkB;AAAA,MACjC,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,IAAiC,KAAwB;AACvD,WAAO,KAAK,MAAM,IAAI,GAAG;AAAA,EAC3B;AAAA,EAEA,IAAiC,KAAQ,OAA6B;AACpE,iBAAa,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC;AAC7C,SAAK,MAAM,IAAI,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEA,SAAsB;AACpB,WAAO,aAAa,MAAM,KAAK,MAAM,KAAK;AAAA,EAC5C;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;","names":[]}
@@ -1,11 +1,14 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
+ import {
3
+ SERVER_URL
4
+ } from "./chunk-5UI5E6YF.js";
2
5
 
3
6
  // src/logging/logger.ts
4
7
  import pino from "pino";
5
- function createLogger(config) {
8
+ function createLogger(config2) {
6
9
  const isDev = process.env.NODE_ENV !== "production";
7
10
  return pino({
8
- level: config.logLevel,
11
+ level: config2.logLevel,
9
12
  transport: isDev ? {
10
13
  target: "pino-pretty",
11
14
  options: { colorize: true, translateTime: "SYS:HH:MM:ss" }
@@ -142,8 +145,7 @@ var WSClient = class {
142
145
  }
143
146
  connect() {
144
147
  if (this.isShuttingDown) return;
145
- const config = this.options.config;
146
- const wsUrl = config.serverUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:");
148
+ const wsUrl = SERVER_URL.replace(/^http:/, "ws:").replace(/^https:/, "wss:");
147
149
  const url = `${wsUrl}/ws/agent`;
148
150
  this.options.logger.info({ url }, "Connecting to server...");
149
151
  this.ws = new WebSocket(url);
@@ -264,7 +266,9 @@ var WSClient = class {
264
266
  const payload = {
265
267
  agentId: this.options.config.agentId,
266
268
  status,
267
- version: "0.0.0"
269
+ version: "0.4.0",
270
+ platform: `${process.platform}-${process.arch}`,
271
+ nodeVersion: process.versions.node
268
272
  };
269
273
  this.send("agent:status_report", payload);
270
274
  }
@@ -369,4 +373,4 @@ export {
369
373
  WSClient,
370
374
  SpendClient
371
375
  };
372
- //# sourceMappingURL=chunk-UBIM2SRL.js.map
376
+ //# sourceMappingURL=chunk-NXQD5NVI.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 { 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"]}
@@ -2,11 +2,22 @@ import { createRequire } from "module"; const require = createRequire(import.met
2
2
  import {
3
3
  ConfigStore,
4
4
  configSchema
5
- } from "../../chunk-2VA3LUCR.js";
5
+ } from "../../chunk-5UI5E6YF.js";
6
6
 
7
7
  // src/commands/config/get.ts
8
8
  import { Args, Command } from "@oclif/core";
9
9
  var VALID_KEYS = Object.keys(configSchema.shape);
10
+ var KEBAB_TO_CAMEL = {
11
+ "api-key": "apiKey",
12
+ "agent-id": "agentId",
13
+ "log-level": "logLevel",
14
+ "heartbeat-interval-ms": "heartbeatIntervalMs",
15
+ "ws-reconnect-base-ms": "wsReconnectBaseMs",
16
+ "ws-reconnect-max-ms": "wsReconnectMaxMs"
17
+ };
18
+ function resolveKey(input) {
19
+ return KEBAB_TO_CAMEL[input] ?? input;
20
+ }
10
21
  var ConfigGet = class _ConfigGet extends Command {
11
22
  static description = "Get a configuration value";
12
23
  static args = {
@@ -14,13 +25,13 @@ var ConfigGet = class _ConfigGet extends Command {
14
25
  };
15
26
  async run() {
16
27
  const { args } = await this.parse(_ConfigGet);
17
- if (!VALID_KEYS.includes(args.key)) {
18
- this.error(
19
- `Invalid key "${args.key}". Valid keys: ${VALID_KEYS.join(", ")}`
20
- );
28
+ const key = resolveKey(args.key);
29
+ if (!VALID_KEYS.includes(key)) {
30
+ const display = Object.keys(KEBAB_TO_CAMEL).join(", ");
31
+ this.error(`Invalid key "${args.key}". Valid keys: ${display}`);
21
32
  }
22
33
  const store = new ConfigStore();
23
- const value = store.get(args.key);
34
+ const value = store.get(key);
24
35
  this.log(value != null ? String(value) : "(not set)");
25
36
  }
26
37
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/config/get.ts"],"sourcesContent":["import { Args, Command } from '@oclif/core';\nimport { ConfigStore } from '../../config/config-store.js';\nimport { configSchema } from '../../config/config-schema.js';\n\nconst VALID_KEYS = Object.keys(configSchema.shape);\n\nexport default class ConfigGet extends Command {\n static override description = 'Get a configuration value';\n\n static override args = {\n key: Args.string({ description: 'Config key', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(ConfigGet);\n\n if (!VALID_KEYS.includes(args.key)) {\n this.error(\n `Invalid key \"${args.key}\". Valid keys: ${VALID_KEYS.join(', ')}`,\n );\n }\n\n const store = new ConfigStore();\n const value = store.get(args.key as keyof typeof configSchema.shape);\n this.log(value != null ? String(value) : '(not set)');\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAS,MAAM,eAAe;AAI9B,IAAM,aAAa,OAAO,KAAK,aAAa,KAAK;AAEjD,IAAqB,YAArB,MAAqB,mBAAkB,QAAQ;AAAA,EAC7C,OAAgB,cAAc;AAAA,EAE9B,OAAgB,OAAO;AAAA,IACrB,KAAK,KAAK,OAAO,EAAE,aAAa,cAAc,UAAU,KAAK,CAAC;AAAA,EAChE;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,UAAS;AAE3C,QAAI,CAAC,WAAW,SAAS,KAAK,GAAG,GAAG;AAClC,WAAK;AAAA,QACH,gBAAgB,KAAK,GAAG,kBAAkB,WAAW,KAAK,IAAI,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,YAAY;AAC9B,UAAM,QAAQ,MAAM,IAAI,KAAK,GAAsC;AACnE,SAAK,IAAI,SAAS,OAAO,OAAO,KAAK,IAAI,WAAW;AAAA,EACtD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/commands/config/get.ts"],"sourcesContent":["import { Args, Command } from '@oclif/core';\nimport { ConfigStore } from '../../config/config-store.js';\nimport { configSchema } from '../../config/config-schema.js';\n\nconst VALID_KEYS = Object.keys(configSchema.shape);\n\nconst KEBAB_TO_CAMEL: Record<string, string> = {\n 'api-key': 'apiKey',\n 'agent-id': 'agentId',\n 'log-level': 'logLevel',\n 'heartbeat-interval-ms': 'heartbeatIntervalMs',\n 'ws-reconnect-base-ms': 'wsReconnectBaseMs',\n 'ws-reconnect-max-ms': 'wsReconnectMaxMs',\n};\n\nfunction resolveKey(input: string): string {\n return KEBAB_TO_CAMEL[input] ?? input;\n}\n\nexport default class ConfigGet extends Command {\n static override description = 'Get a configuration value';\n\n static override args = {\n key: Args.string({ description: 'Config key', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(ConfigGet);\n const key = resolveKey(args.key);\n\n if (!VALID_KEYS.includes(key)) {\n const display = Object.keys(KEBAB_TO_CAMEL).join(', ');\n this.error(`Invalid key \"${args.key}\". Valid keys: ${display}`);\n }\n\n const store = new ConfigStore();\n const value = store.get(key as keyof typeof configSchema.shape);\n this.log(value != null ? String(value) : '(not set)');\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAS,MAAM,eAAe;AAI9B,IAAM,aAAa,OAAO,KAAK,aAAa,KAAK;AAEjD,IAAM,iBAAyC;AAAA,EAC7C,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AACzB;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,eAAe,KAAK,KAAK;AAClC;AAEA,IAAqB,YAArB,MAAqB,mBAAkB,QAAQ;AAAA,EAC7C,OAAgB,cAAc;AAAA,EAE9B,OAAgB,OAAO;AAAA,IACrB,KAAK,KAAK,OAAO,EAAE,aAAa,cAAc,UAAU,KAAK,CAAC;AAAA,EAChE;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,UAAS;AAC3C,UAAM,MAAM,WAAW,KAAK,GAAG;AAE/B,QAAI,CAAC,WAAW,SAAS,GAAG,GAAG;AAC7B,YAAM,UAAU,OAAO,KAAK,cAAc,EAAE,KAAK,IAAI;AACrD,WAAK,MAAM,gBAAgB,KAAK,GAAG,kBAAkB,OAAO,EAAE;AAAA,IAChE;AAEA,UAAM,QAAQ,IAAI,YAAY;AAC9B,UAAM,QAAQ,MAAM,IAAI,GAAsC;AAC9D,SAAK,IAAI,SAAS,OAAO,OAAO,KAAK,IAAI,WAAW;AAAA,EACtD;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  ConfigStore
4
- } from "../../chunk-2VA3LUCR.js";
4
+ } from "../../chunk-5UI5E6YF.js";
5
5
 
6
6
  // src/commands/config/index.ts
7
7
  import { Command } from "@oclif/core";
@@ -2,7 +2,7 @@ import { createRequire } from "module"; const require = createRequire(import.met
2
2
  import {
3
3
  ConfigStore,
4
4
  configSchema
5
- } from "../../chunk-2VA3LUCR.js";
5
+ } from "../../chunk-5UI5E6YF.js";
6
6
 
7
7
  // src/commands/config/set.ts
8
8
  import { Args, Command } from "@oclif/core";
@@ -12,6 +12,17 @@ var NUMERIC_KEYS = /* @__PURE__ */ new Set([
12
12
  "wsReconnectBaseMs",
13
13
  "wsReconnectMaxMs"
14
14
  ]);
15
+ var KEBAB_TO_CAMEL = {
16
+ "api-key": "apiKey",
17
+ "agent-id": "agentId",
18
+ "log-level": "logLevel",
19
+ "heartbeat-interval-ms": "heartbeatIntervalMs",
20
+ "ws-reconnect-base-ms": "wsReconnectBaseMs",
21
+ "ws-reconnect-max-ms": "wsReconnectMaxMs"
22
+ };
23
+ function resolveKey(input) {
24
+ return KEBAB_TO_CAMEL[input] ?? input;
25
+ }
15
26
  var ConfigSet = class _ConfigSet extends Command {
16
27
  static description = "Set a configuration value";
17
28
  static args = {
@@ -20,17 +31,15 @@ var ConfigSet = class _ConfigSet extends Command {
20
31
  };
21
32
  async run() {
22
33
  const { args } = await this.parse(_ConfigSet);
23
- if (!VALID_KEYS.includes(args.key)) {
24
- this.error(
25
- `Invalid key "${args.key}". Valid keys: ${VALID_KEYS.join(", ")}`
26
- );
34
+ const key = resolveKey(args.key);
35
+ if (!VALID_KEYS.includes(key)) {
36
+ const display = Object.keys(KEBAB_TO_CAMEL).join(", ");
37
+ this.error(`Invalid key "${args.key}". Valid keys: ${display}`);
27
38
  }
28
39
  const store = new ConfigStore();
29
- const parsed = NUMERIC_KEYS.has(args.key) ? Number(args.value) : args.value;
30
- store.set(args.key, parsed);
31
- this.log(
32
- `Set ${args.key} = ${args.key === "apiKey" ? "****" : args.value}`
33
- );
40
+ const parsed = NUMERIC_KEYS.has(key) ? Number(args.value) : args.value;
41
+ store.set(key, parsed);
42
+ this.log(`Set ${args.key} = ${key === "apiKey" ? "****" : args.value}`);
34
43
  }
35
44
  };
36
45
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/config/set.ts"],"sourcesContent":["import { Args, Command } from '@oclif/core';\nimport { ConfigStore } from '../../config/config-store.js';\nimport { configSchema } from '../../config/config-schema.js';\n\nconst VALID_KEYS = Object.keys(configSchema.shape);\n\nconst NUMERIC_KEYS = new Set([\n 'heartbeatIntervalMs',\n 'wsReconnectBaseMs',\n 'wsReconnectMaxMs',\n]);\n\nexport default class ConfigSet extends Command {\n static override description = 'Set a configuration value';\n\n static override args = {\n key: Args.string({ description: 'Config key', required: true }),\n value: Args.string({ description: 'Config value', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(ConfigSet);\n\n if (!VALID_KEYS.includes(args.key)) {\n this.error(\n `Invalid key \"${args.key}\". Valid keys: ${VALID_KEYS.join(', ')}`,\n );\n }\n\n const store = new ConfigStore();\n const parsed = NUMERIC_KEYS.has(args.key)\n ? Number(args.value)\n : args.value;\n\n store.set(args.key as keyof typeof configSchema.shape, parsed as never);\n this.log(\n `Set ${args.key} = ${args.key === 'apiKey' ? '****' : args.value}`,\n );\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAS,MAAM,eAAe;AAI9B,IAAM,aAAa,OAAO,KAAK,aAAa,KAAK;AAEjD,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAqB,YAArB,MAAqB,mBAAkB,QAAQ;AAAA,EAC7C,OAAgB,cAAc;AAAA,EAE9B,OAAgB,OAAO;AAAA,IACrB,KAAK,KAAK,OAAO,EAAE,aAAa,cAAc,UAAU,KAAK,CAAC;AAAA,IAC9D,OAAO,KAAK,OAAO,EAAE,aAAa,gBAAgB,UAAU,KAAK,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,UAAS;AAE3C,QAAI,CAAC,WAAW,SAAS,KAAK,GAAG,GAAG;AAClC,WAAK;AAAA,QACH,gBAAgB,KAAK,GAAG,kBAAkB,WAAW,KAAK,IAAI,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,YAAY;AAC9B,UAAM,SAAS,aAAa,IAAI,KAAK,GAAG,IACpC,OAAO,KAAK,KAAK,IACjB,KAAK;AAET,UAAM,IAAI,KAAK,KAAwC,MAAe;AACtE,SAAK;AAAA,MACH,OAAO,KAAK,GAAG,MAAM,KAAK,QAAQ,WAAW,SAAS,KAAK,KAAK;AAAA,IAClE;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/commands/config/set.ts"],"sourcesContent":["import { Args, Command } from '@oclif/core';\nimport { ConfigStore } from '../../config/config-store.js';\nimport { configSchema } from '../../config/config-schema.js';\n\nconst VALID_KEYS = Object.keys(configSchema.shape);\n\nconst NUMERIC_KEYS = new Set([\n 'heartbeatIntervalMs',\n 'wsReconnectBaseMs',\n 'wsReconnectMaxMs',\n]);\n\nconst KEBAB_TO_CAMEL: Record<string, string> = {\n 'api-key': 'apiKey',\n 'agent-id': 'agentId',\n 'log-level': 'logLevel',\n 'heartbeat-interval-ms': 'heartbeatIntervalMs',\n 'ws-reconnect-base-ms': 'wsReconnectBaseMs',\n 'ws-reconnect-max-ms': 'wsReconnectMaxMs',\n};\n\nfunction resolveKey(input: string): string {\n return KEBAB_TO_CAMEL[input] ?? input;\n}\n\nexport default class ConfigSet extends Command {\n static override description = 'Set a configuration value';\n\n static override args = {\n key: Args.string({ description: 'Config key', required: true }),\n value: Args.string({ description: 'Config value', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(ConfigSet);\n const key = resolveKey(args.key);\n\n if (!VALID_KEYS.includes(key)) {\n const display = Object.keys(KEBAB_TO_CAMEL).join(', ');\n this.error(`Invalid key \"${args.key}\". Valid keys: ${display}`);\n }\n\n const store = new ConfigStore();\n const parsed = NUMERIC_KEYS.has(key) ? Number(args.value) : args.value;\n\n store.set(key as keyof typeof configSchema.shape, parsed as never);\n this.log(`Set ${args.key} = ${key === 'apiKey' ? '****' : args.value}`);\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAS,MAAM,eAAe;AAI9B,IAAM,aAAa,OAAO,KAAK,aAAa,KAAK;AAEjD,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,iBAAyC;AAAA,EAC7C,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AACzB;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,eAAe,KAAK,KAAK;AAClC;AAEA,IAAqB,YAArB,MAAqB,mBAAkB,QAAQ;AAAA,EAC7C,OAAgB,cAAc;AAAA,EAE9B,OAAgB,OAAO;AAAA,IACrB,KAAK,KAAK,OAAO,EAAE,aAAa,cAAc,UAAU,KAAK,CAAC;AAAA,IAC9D,OAAO,KAAK,OAAO,EAAE,aAAa,gBAAgB,UAAU,KAAK,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,UAAS;AAC3C,UAAM,MAAM,WAAW,KAAK,GAAG;AAE/B,QAAI,CAAC,WAAW,SAAS,GAAG,GAAG;AAC7B,YAAM,UAAU,OAAO,KAAK,cAAc,EAAE,KAAK,IAAI;AACrD,WAAK,MAAM,gBAAgB,KAAK,GAAG,kBAAkB,OAAO,EAAE;AAAA,IAChE;AAEA,UAAM,QAAQ,IAAI,YAAY;AAC9B,UAAM,SAAS,aAAa,IAAI,GAAG,IAAI,OAAO,KAAK,KAAK,IAAI,KAAK;AAEjE,UAAM,IAAI,KAAwC,MAAe;AACjE,SAAK,IAAI,OAAO,KAAK,GAAG,MAAM,QAAQ,WAAW,SAAS,KAAK,KAAK,EAAE;AAAA,EACxE;AACF;","names":[]}
@@ -11,7 +11,6 @@ declare class Spend extends Command {
11
11
  description: _oclif_core_interfaces.OptionFlag<string, _oclif_core_interfaces.CustomOptions>;
12
12
  'recipient-agent': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
13
13
  'recipient-address': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
14
- 'server-url': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
15
14
  'agent-id': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
16
15
  'api-key': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
17
16
  };
@@ -3,10 +3,10 @@ import {
3
3
  SpendClient,
4
4
  WSClient,
5
5
  createLogger
6
- } from "../chunk-UBIM2SRL.js";
6
+ } from "../chunk-NXQD5NVI.js";
7
7
  import {
8
8
  ConfigStore
9
- } from "../chunk-2VA3LUCR.js";
9
+ } from "../chunk-5UI5E6YF.js";
10
10
 
11
11
  // src/commands/spend.ts
12
12
  import { Command, Flags } from "@oclif/core";
@@ -38,7 +38,6 @@ var Spend = class _Spend extends Command {
38
38
  "recipient-address": Flags.string({
39
39
  description: "Recipient wallet address (for on-chain)"
40
40
  }),
41
- "server-url": Flags.string({ env: "STAMN_SERVER_URL" }),
42
41
  "agent-id": Flags.string({ env: "STAMN_AGENT_ID" }),
43
42
  "api-key": Flags.string({ env: "STAMN_API_KEY" })
44
43
  };
@@ -46,14 +45,13 @@ var Spend = class _Spend extends Command {
46
45
  const { flags } = await this.parse(_Spend);
47
46
  const configStore = new ConfigStore();
48
47
  const config = { ...configStore.getAll() };
49
- if (flags["server-url"]) config.serverUrl = flags["server-url"];
50
48
  if (flags["agent-id"]) config.agentId = flags["agent-id"];
51
49
  if (flags["api-key"]) config.apiKey = flags["api-key"];
52
50
  if (!config.agentId) {
53
- this.error("agentId is required. Run: stamn config set agentId <uuid>");
51
+ this.error("agentId is required. Run: stamn config set agent-id <uuid>");
54
52
  }
55
53
  const logger = createLogger({ logLevel: "info" });
56
- this.log(`Connecting to ${config.serverUrl}...`);
54
+ this.log("Connecting to Stamn...");
57
55
  const client = new WSClient({
58
56
  config,
59
57
  logger,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/spend.ts"],"sourcesContent":["import { Command, Flags } from '@oclif/core';\nimport { ConfigStore } from '../config/config-store.js';\nimport { createLogger } from '../logging/logger.js';\nimport { WSClient } from '../ws/ws-client.js';\nimport { SpendClient } from '../spend/spend-client.js';\nimport type { LedgerCategory, LedgerRail } from '@stamn/types';\n\nexport default class Spend extends Command {\n static override description =\n 'Send a spend request through the connected agent';\n\n static override flags = {\n amount: Flags.integer({\n required: true,\n description: 'Amount in cents',\n }),\n category: Flags.string({\n required: true,\n description: 'Spend category',\n options: ['api', 'compute', 'contractor', 'transfer'],\n }),\n rail: Flags.string({\n required: true,\n description: 'Payment rail',\n options: ['crypto_onchain', 'x402', 'internal'],\n }),\n vendor: Flags.string({ description: 'Vendor name' }),\n description: Flags.string({\n required: true,\n description: 'Spend description',\n }),\n 'recipient-agent': Flags.string({\n description: 'Recipient agent ID (for agent-to-agent)',\n }),\n 'recipient-address': Flags.string({\n description: 'Recipient wallet address (for on-chain)',\n }),\n 'server-url': Flags.string({ env: 'STAMN_SERVER_URL' }),\n 'agent-id': Flags.string({ env: 'STAMN_AGENT_ID' }),\n 'api-key': Flags.string({ env: 'STAMN_API_KEY' }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Spend);\n const configStore = new ConfigStore();\n const config = { ...configStore.getAll() };\n\n if (flags['server-url']) config.serverUrl = flags['server-url'];\n if (flags['agent-id']) config.agentId = flags['agent-id'];\n if (flags['api-key']) config.apiKey = flags['api-key'];\n\n if (!config.agentId) {\n this.error('agentId is required. Run: stamn config set agentId <uuid>');\n }\n\n const logger = createLogger({ logLevel: 'info' });\n\n this.log(`Connecting to ${config.serverUrl}...`);\n\n const client = new WSClient({\n config,\n logger,\n onCommand: () => {},\n onDisconnect: () => {},\n onConnected: async () => {\n this.log('Authenticated. Sending spend request...\\n');\n\n const spendClient = new SpendClient(client, logger);\n const result = await spendClient.request({\n amountCents: flags.amount,\n category: flags.category as LedgerCategory,\n rail: flags.rail as LedgerRail,\n vendor: flags.vendor,\n description: flags.description,\n recipientAgentId: flags['recipient-agent'],\n recipientAddress: flags['recipient-address'],\n });\n\n if (result.approved) {\n this.log('Spend APPROVED');\n this.log(` Ledger Entry: ${result.ledgerEntryId}`);\n if (result.transactionHash) {\n this.log(` Tx Hash: ${result.transactionHash}`);\n }\n this.log(` Remaining: ${result.remainingBalanceCents} cents`);\n } else {\n this.log('Spend DENIED');\n this.log(` Reason: ${result.reason}`);\n this.log(` Code: ${result.code}`);\n }\n\n client.disconnect();\n process.exit(0);\n },\n });\n\n client.connect();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/commands/spend.ts"],"sourcesContent":["import { Command, Flags } from '@oclif/core';\nimport { ConfigStore } from '../config/config-store.js';\nimport { createLogger } from '../logging/logger.js';\nimport { WSClient } from '../ws/ws-client.js';\nimport { SpendClient } from '../spend/spend-client.js';\nimport type { LedgerCategory, LedgerRail } from '@stamn/types';\n\nexport default class Spend extends Command {\n static override description =\n 'Send a spend request through the connected agent';\n\n static override flags = {\n amount: Flags.integer({\n required: true,\n description: 'Amount in cents',\n }),\n category: Flags.string({\n required: true,\n description: 'Spend category',\n options: ['api', 'compute', 'contractor', 'transfer'],\n }),\n rail: Flags.string({\n required: true,\n description: 'Payment rail',\n options: ['crypto_onchain', 'x402', 'internal'],\n }),\n vendor: Flags.string({ description: 'Vendor name' }),\n description: Flags.string({\n required: true,\n description: 'Spend description',\n }),\n 'recipient-agent': Flags.string({\n description: 'Recipient agent ID (for agent-to-agent)',\n }),\n 'recipient-address': Flags.string({\n description: 'Recipient wallet address (for on-chain)',\n }),\n 'agent-id': Flags.string({ env: 'STAMN_AGENT_ID' }),\n 'api-key': Flags.string({ env: 'STAMN_API_KEY' }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Spend);\n const configStore = new ConfigStore();\n const config = { ...configStore.getAll() };\n\n if (flags['agent-id']) config.agentId = flags['agent-id'];\n if (flags['api-key']) config.apiKey = flags['api-key'];\n\n if (!config.agentId) {\n this.error('agentId is required. Run: stamn config set agent-id <uuid>');\n }\n\n const logger = createLogger({ logLevel: 'info' });\n\n this.log('Connecting to Stamn...');\n\n const client = new WSClient({\n config,\n logger,\n onCommand: () => {},\n onDisconnect: () => {},\n onConnected: async () => {\n this.log('Authenticated. Sending spend request...\\n');\n\n const spendClient = new SpendClient(client, logger);\n const result = await spendClient.request({\n amountCents: flags.amount,\n category: flags.category as LedgerCategory,\n rail: flags.rail as LedgerRail,\n vendor: flags.vendor,\n description: flags.description,\n recipientAgentId: flags['recipient-agent'],\n recipientAddress: flags['recipient-address'],\n });\n\n if (result.approved) {\n this.log('Spend APPROVED');\n this.log(` Ledger Entry: ${result.ledgerEntryId}`);\n if (result.transactionHash) {\n this.log(` Tx Hash: ${result.transactionHash}`);\n }\n this.log(` Remaining: ${result.remainingBalanceCents} cents`);\n } else {\n this.log('Spend DENIED');\n this.log(` Reason: ${result.reason}`);\n this.log(` Code: ${result.code}`);\n }\n\n client.disconnect();\n process.exit(0);\n },\n });\n\n client.connect();\n }\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,SAAS,aAAa;AAO/B,IAAqB,QAArB,MAAqB,eAAc,QAAQ;AAAA,EACzC,OAAgB,cACd;AAAA,EAEF,OAAgB,QAAQ;AAAA,IACtB,QAAQ,MAAM,QAAQ;AAAA,MACpB,UAAU;AAAA,MACV,aAAa;AAAA,IACf,CAAC;AAAA,IACD,UAAU,MAAM,OAAO;AAAA,MACrB,UAAU;AAAA,MACV,aAAa;AAAA,MACb,SAAS,CAAC,OAAO,WAAW,cAAc,UAAU;AAAA,IACtD,CAAC;AAAA,IACD,MAAM,MAAM,OAAO;AAAA,MACjB,UAAU;AAAA,MACV,aAAa;AAAA,MACb,SAAS,CAAC,kBAAkB,QAAQ,UAAU;AAAA,IAChD,CAAC;AAAA,IACD,QAAQ,MAAM,OAAO,EAAE,aAAa,cAAc,CAAC;AAAA,IACnD,aAAa,MAAM,OAAO;AAAA,MACxB,UAAU;AAAA,MACV,aAAa;AAAA,IACf,CAAC;AAAA,IACD,mBAAmB,MAAM,OAAO;AAAA,MAC9B,aAAa;AAAA,IACf,CAAC;AAAA,IACD,qBAAqB,MAAM,OAAO;AAAA,MAChC,aAAa;AAAA,IACf,CAAC;AAAA,IACD,YAAY,MAAM,OAAO,EAAE,KAAK,iBAAiB,CAAC;AAAA,IAClD,WAAW,MAAM,OAAO,EAAE,KAAK,gBAAgB,CAAC;AAAA,EAClD;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,MAAK;AACxC,UAAM,cAAc,IAAI,YAAY;AACpC,UAAM,SAAS,EAAE,GAAG,YAAY,OAAO,EAAE;AAEzC,QAAI,MAAM,UAAU,EAAG,QAAO,UAAU,MAAM,UAAU;AACxD,QAAI,MAAM,SAAS,EAAG,QAAO,SAAS,MAAM,SAAS;AAErD,QAAI,CAAC,OAAO,SAAS;AACnB,WAAK,MAAM,4DAA4D;AAAA,IACzE;AAEA,UAAM,SAAS,aAAa,EAAE,UAAU,OAAO,CAAC;AAEhD,SAAK,IAAI,wBAAwB;AAEjC,UAAM,SAAS,IAAI,SAAS;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,WAAW,MAAM;AAAA,MAAC;AAAA,MAClB,cAAc,MAAM;AAAA,MAAC;AAAA,MACrB,aAAa,YAAY;AACvB,aAAK,IAAI,2CAA2C;AAEpD,cAAM,cAAc,IAAI,YAAY,QAAQ,MAAM;AAClD,cAAM,SAAS,MAAM,YAAY,QAAQ;AAAA,UACvC,aAAa,MAAM;AAAA,UACnB,UAAU,MAAM;AAAA,UAChB,MAAM,MAAM;AAAA,UACZ,QAAQ,MAAM;AAAA,UACd,aAAa,MAAM;AAAA,UACnB,kBAAkB,MAAM,iBAAiB;AAAA,UACzC,kBAAkB,MAAM,mBAAmB;AAAA,QAC7C,CAAC;AAED,YAAI,OAAO,UAAU;AACnB,eAAK,IAAI,gBAAgB;AACzB,eAAK,IAAI,mBAAmB,OAAO,aAAa,EAAE;AAClD,cAAI,OAAO,iBAAiB;AAC1B,iBAAK,IAAI,mBAAmB,OAAO,eAAe,EAAE;AAAA,UACtD;AACA,eAAK,IAAI,mBAAmB,OAAO,qBAAqB,QAAQ;AAAA,QAClE,OAAO;AACL,eAAK,IAAI,cAAc;AACvB,eAAK,IAAI,aAAa,OAAO,MAAM,EAAE;AACrC,eAAK,IAAI,aAAa,OAAO,IAAI,EAAE;AAAA,QACrC;AAEA,eAAO,WAAW;AAClB,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,CAAC;AAED,WAAO,QAAQ;AAAA,EACjB;AACF;","names":[]}
@@ -5,7 +5,6 @@ declare class Start extends Command {
5
5
  static description: string;
6
6
  static flags: {
7
7
  daemon: _oclif_core_interfaces.BooleanFlag<boolean>;
8
- 'server-url': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
9
8
  'agent-id': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
10
9
  'api-key': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
11
10
  'log-level': _oclif_core_interfaces.OptionFlag<string | undefined, _oclif_core_interfaces.CustomOptions>;
@@ -6,10 +6,11 @@ import {
6
6
  SpendClient,
7
7
  WSClient,
8
8
  createLogger
9
- } from "../chunk-UBIM2SRL.js";
9
+ } from "../chunk-NXQD5NVI.js";
10
10
  import {
11
- ConfigStore
12
- } from "../chunk-2VA3LUCR.js";
11
+ ConfigStore,
12
+ SERVER_URL
13
+ } from "../chunk-5UI5E6YF.js";
13
14
 
14
15
  // src/commands/start.ts
15
16
  import { Command, Flags } from "@oclif/core";
@@ -21,10 +22,6 @@ var Start = class _Start extends Command {
21
22
  description: "Run as background daemon",
22
23
  default: false
23
24
  }),
24
- "server-url": Flags.string({
25
- description: "Override server URL",
26
- env: "STAMN_SERVER_URL"
27
- }),
28
25
  "agent-id": Flags.string({
29
26
  description: "Override agent ID",
30
27
  env: "STAMN_AGENT_ID"
@@ -42,14 +39,13 @@ var Start = class _Start extends Command {
42
39
  const { flags } = await this.parse(_Start);
43
40
  const configStore = new ConfigStore();
44
41
  const config = { ...configStore.getAll() };
45
- if (flags["server-url"]) config.serverUrl = flags["server-url"];
46
42
  if (flags["agent-id"]) config.agentId = flags["agent-id"];
47
43
  if (flags["api-key"]) config.apiKey = flags["api-key"];
48
44
  if (flags["log-level"])
49
45
  config.logLevel = flags["log-level"];
50
46
  if (!config.agentId) {
51
47
  this.error(
52
- "agentId is required. Run: stamn config set agentId <uuid>"
48
+ "agentId is required. Run: stamn config set agent-id <uuid>"
53
49
  );
54
50
  }
55
51
  const dm = new DaemonManager();
@@ -64,7 +60,7 @@ var Start = class _Start extends Command {
64
60
  dm.writePid(process.pid);
65
61
  const logger = createLogger(config);
66
62
  logger.info(
67
- { agentId: config.agentId, serverUrl: config.serverUrl },
63
+ { agentId: config.agentId, server: SERVER_URL },
68
64
  "Starting Stamn agent"
69
65
  );
70
66
  const client = new WSClient({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/start.ts"],"sourcesContent":["import { Command, Flags } from '@oclif/core';\nimport { ConfigStore } from '../config/config-store.js';\nimport type { AgentConfig } from '../config/config-schema.js';\nimport { createLogger } from '../logging/logger.js';\nimport { WSClient } from '../ws/ws-client.js';\nimport { SpendClient } from '../spend/spend-client.js';\nimport { DaemonManager } from '../daemon/daemon-manager.js';\n\nexport default class Start extends Command {\n static override description = 'Start the Stamn agent daemon';\n\n static override flags = {\n daemon: Flags.boolean({\n char: 'd',\n description: 'Run as background daemon',\n default: false,\n }),\n 'server-url': Flags.string({\n description: 'Override server URL',\n env: 'STAMN_SERVER_URL',\n }),\n 'agent-id': Flags.string({\n description: 'Override agent ID',\n env: 'STAMN_AGENT_ID',\n }),\n 'api-key': Flags.string({\n description: 'Override API key',\n env: 'STAMN_API_KEY',\n }),\n 'log-level': Flags.string({\n description: 'Override log level',\n options: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'],\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Start);\n const configStore = new ConfigStore();\n const config = { ...configStore.getAll() };\n\n // Apply flag overrides\n if (flags['server-url']) config.serverUrl = flags['server-url'];\n if (flags['agent-id']) config.agentId = flags['agent-id'];\n if (flags['api-key']) config.apiKey = flags['api-key'];\n if (flags['log-level'])\n config.logLevel = flags['log-level'] as AgentConfig['logLevel'];\n\n // Validate required fields\n if (!config.agentId) {\n this.error(\n 'agentId is required. Run: stamn config set agentId <uuid>',\n );\n }\n\n // Check for existing daemon\n const dm = new DaemonManager();\n const { running, pid } = dm.isRunning();\n if (running) {\n this.error(`Daemon already running (PID ${pid})`);\n }\n\n // Daemonize if requested\n if (flags.daemon) {\n const { daemonizeProcess } = await import('../daemon/process.js');\n await daemonizeProcess();\n }\n\n // Write PID\n dm.writePid(process.pid);\n\n const logger = createLogger(config);\n logger.info(\n { agentId: config.agentId, serverUrl: config.serverUrl },\n 'Starting Stamn agent',\n );\n\n // Create WebSocket client\n const client = new WSClient({\n config,\n logger,\n onCommand: (command, params) => {\n logger.info({ command, params }, 'Received command');\n if (command === 'shutdown') {\n shutdown();\n }\n },\n onDisconnect: () => {\n logger.warn('Disconnected from server');\n },\n onConnected: () => {\n logger.info('Agent is online and ready — spend capability active');\n },\n });\n\n // Create spend client — available for plugin/task integration\n const spendClient = new SpendClient(client, logger);\n\n // Expose on process for external plugin access\n (globalThis as Record<string, unknown>).__stamnSpendClient = spendClient;\n\n // Graceful shutdown\n const shutdown = () => {\n logger.info('Shutting down...');\n client.disconnect();\n dm.removePid();\n process.exit(0);\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n // Connect\n client.connect();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,SAAS,aAAa;AAQ/B,IAAqB,QAArB,MAAqB,eAAc,QAAQ;AAAA,EACzC,OAAgB,cAAc;AAAA,EAE9B,OAAgB,QAAQ;AAAA,IACtB,QAAQ,MAAM,QAAQ;AAAA,MACpB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,IACD,cAAc,MAAM,OAAO;AAAA,MACzB,aAAa;AAAA,MACb,KAAK;AAAA,IACP,CAAC;AAAA,IACD,YAAY,MAAM,OAAO;AAAA,MACvB,aAAa;AAAA,MACb,KAAK;AAAA,IACP,CAAC;AAAA,IACD,WAAW,MAAM,OAAO;AAAA,MACtB,aAAa;AAAA,MACb,KAAK;AAAA,IACP,CAAC;AAAA,IACD,aAAa,MAAM,OAAO;AAAA,MACxB,aAAa;AAAA,MACb,SAAS,CAAC,SAAS,SAAS,QAAQ,QAAQ,SAAS,OAAO;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,MAAK;AACxC,UAAM,cAAc,IAAI,YAAY;AACpC,UAAM,SAAS,EAAE,GAAG,YAAY,OAAO,EAAE;AAGzC,QAAI,MAAM,YAAY,EAAG,QAAO,YAAY,MAAM,YAAY;AAC9D,QAAI,MAAM,UAAU,EAAG,QAAO,UAAU,MAAM,UAAU;AACxD,QAAI,MAAM,SAAS,EAAG,QAAO,SAAS,MAAM,SAAS;AACrD,QAAI,MAAM,WAAW;AACnB,aAAO,WAAW,MAAM,WAAW;AAGrC,QAAI,CAAC,OAAO,SAAS;AACnB,WAAK;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,UAAM,KAAK,IAAI,cAAc;AAC7B,UAAM,EAAE,SAAS,IAAI,IAAI,GAAG,UAAU;AACtC,QAAI,SAAS;AACX,WAAK,MAAM,+BAA+B,GAAG,GAAG;AAAA,IAClD;AAGA,QAAI,MAAM,QAAQ;AAChB,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAsB;AAChE,YAAM,iBAAiB;AAAA,IACzB;AAGA,OAAG,SAAS,QAAQ,GAAG;AAEvB,UAAM,SAAS,aAAa,MAAM;AAClC,WAAO;AAAA,MACL,EAAE,SAAS,OAAO,SAAS,WAAW,OAAO,UAAU;AAAA,MACvD;AAAA,IACF;AAGA,UAAM,SAAS,IAAI,SAAS;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,WAAW,CAAC,SAAS,WAAW;AAC9B,eAAO,KAAK,EAAE,SAAS,OAAO,GAAG,kBAAkB;AACnD,YAAI,YAAY,YAAY;AAC1B,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,cAAc,MAAM;AAClB,eAAO,KAAK,0BAA0B;AAAA,MACxC;AAAA,MACA,aAAa,MAAM;AACjB,eAAO,KAAK,0DAAqD;AAAA,MACnE;AAAA,IACF,CAAC;AAGD,UAAM,cAAc,IAAI,YAAY,QAAQ,MAAM;AAGlD,IAAC,WAAuC,qBAAqB;AAG7D,UAAM,WAAW,MAAM;AACrB,aAAO,KAAK,kBAAkB;AAC9B,aAAO,WAAW;AAClB,SAAG,UAAU;AACb,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,GAAG,WAAW,QAAQ;AAC9B,YAAQ,GAAG,UAAU,QAAQ;AAG7B,WAAO,QAAQ;AAAA,EACjB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/commands/start.ts"],"sourcesContent":["import { Command, Flags } from '@oclif/core';\nimport { ConfigStore } from '../config/config-store.js';\nimport { SERVER_URL, type AgentConfig } from '../config/config-schema.js';\nimport { createLogger } from '../logging/logger.js';\nimport { WSClient } from '../ws/ws-client.js';\nimport { SpendClient } from '../spend/spend-client.js';\nimport { DaemonManager } from '../daemon/daemon-manager.js';\n\nexport default class Start extends Command {\n static override description = 'Start the Stamn agent daemon';\n\n static override flags = {\n daemon: Flags.boolean({\n char: 'd',\n description: 'Run as background daemon',\n default: false,\n }),\n 'agent-id': Flags.string({\n description: 'Override agent ID',\n env: 'STAMN_AGENT_ID',\n }),\n 'api-key': Flags.string({\n description: 'Override API key',\n env: 'STAMN_API_KEY',\n }),\n 'log-level': Flags.string({\n description: 'Override log level',\n options: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'],\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Start);\n const configStore = new ConfigStore();\n const config = { ...configStore.getAll() };\n\n // Apply flag overrides\n if (flags['agent-id']) config.agentId = flags['agent-id'];\n if (flags['api-key']) config.apiKey = flags['api-key'];\n if (flags['log-level'])\n config.logLevel = flags['log-level'] as AgentConfig['logLevel'];\n\n // Validate required fields\n if (!config.agentId) {\n this.error(\n 'agentId is required. Run: stamn config set agent-id <uuid>',\n );\n }\n\n // Check for existing daemon\n const dm = new DaemonManager();\n const { running, pid } = dm.isRunning();\n if (running) {\n this.error(`Daemon already running (PID ${pid})`);\n }\n\n // Daemonize if requested\n if (flags.daemon) {\n const { daemonizeProcess } = await import('../daemon/process.js');\n await daemonizeProcess();\n }\n\n // Write PID\n dm.writePid(process.pid);\n\n const logger = createLogger(config);\n logger.info(\n { agentId: config.agentId, server: SERVER_URL },\n 'Starting Stamn agent',\n );\n\n // Create WebSocket client\n const client = new WSClient({\n config,\n logger,\n onCommand: (command, params) => {\n logger.info({ command, params }, 'Received command');\n if (command === 'shutdown') {\n shutdown();\n }\n },\n onDisconnect: () => {\n logger.warn('Disconnected from server');\n },\n onConnected: () => {\n logger.info('Agent is online and ready — spend capability active');\n },\n });\n\n // Create spend client — available for plugin/task integration\n const spendClient = new SpendClient(client, logger);\n\n // Expose on process for external plugin access\n (globalThis as Record<string, unknown>).__stamnSpendClient = spendClient;\n\n // Graceful shutdown\n const shutdown = () => {\n logger.info('Shutting down...');\n client.disconnect();\n dm.removePid();\n process.exit(0);\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n // Connect\n client.connect();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,SAAS,aAAa;AAQ/B,IAAqB,QAArB,MAAqB,eAAc,QAAQ;AAAA,EACzC,OAAgB,cAAc;AAAA,EAE9B,OAAgB,QAAQ;AAAA,IACtB,QAAQ,MAAM,QAAQ;AAAA,MACpB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,IACD,YAAY,MAAM,OAAO;AAAA,MACvB,aAAa;AAAA,MACb,KAAK;AAAA,IACP,CAAC;AAAA,IACD,WAAW,MAAM,OAAO;AAAA,MACtB,aAAa;AAAA,MACb,KAAK;AAAA,IACP,CAAC;AAAA,IACD,aAAa,MAAM,OAAO;AAAA,MACxB,aAAa;AAAA,MACb,SAAS,CAAC,SAAS,SAAS,QAAQ,QAAQ,SAAS,OAAO;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,MAAK;AACxC,UAAM,cAAc,IAAI,YAAY;AACpC,UAAM,SAAS,EAAE,GAAG,YAAY,OAAO,EAAE;AAGzC,QAAI,MAAM,UAAU,EAAG,QAAO,UAAU,MAAM,UAAU;AACxD,QAAI,MAAM,SAAS,EAAG,QAAO,SAAS,MAAM,SAAS;AACrD,QAAI,MAAM,WAAW;AACnB,aAAO,WAAW,MAAM,WAAW;AAGrC,QAAI,CAAC,OAAO,SAAS;AACnB,WAAK;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,UAAM,KAAK,IAAI,cAAc;AAC7B,UAAM,EAAE,SAAS,IAAI,IAAI,GAAG,UAAU;AACtC,QAAI,SAAS;AACX,WAAK,MAAM,+BAA+B,GAAG,GAAG;AAAA,IAClD;AAGA,QAAI,MAAM,QAAQ;AAChB,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAsB;AAChE,YAAM,iBAAiB;AAAA,IACzB;AAGA,OAAG,SAAS,QAAQ,GAAG;AAEvB,UAAM,SAAS,aAAa,MAAM;AAClC,WAAO;AAAA,MACL,EAAE,SAAS,OAAO,SAAS,QAAQ,WAAW;AAAA,MAC9C;AAAA,IACF;AAGA,UAAM,SAAS,IAAI,SAAS;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,WAAW,CAAC,SAAS,WAAW;AAC9B,eAAO,KAAK,EAAE,SAAS,OAAO,GAAG,kBAAkB;AACnD,YAAI,YAAY,YAAY;AAC1B,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,cAAc,MAAM;AAClB,eAAO,KAAK,0BAA0B;AAAA,MACxC;AAAA,MACA,aAAa,MAAM;AACjB,eAAO,KAAK,0DAAqD;AAAA,MACnE;AAAA,IACF,CAAC;AAGD,UAAM,cAAc,IAAI,YAAY,QAAQ,MAAM;AAGlD,IAAC,WAAuC,qBAAqB;AAG7D,UAAM,WAAW,MAAM;AACrB,aAAO,KAAK,kBAAkB;AAC9B,aAAO,WAAW;AAClB,SAAG,UAAU;AACb,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,GAAG,WAAW,QAAQ;AAC9B,YAAQ,GAAG,UAAU,QAAQ;AAG7B,WAAO,QAAQ;AAAA,EACjB;AACF;","names":[]}
@@ -3,8 +3,9 @@ import {
3
3
  DaemonManager
4
4
  } from "../chunk-DCE6ICKM.js";
5
5
  import {
6
- ConfigStore
7
- } from "../chunk-2VA3LUCR.js";
6
+ ConfigStore,
7
+ SERVER_URL
8
+ } from "../chunk-5UI5E6YF.js";
8
9
 
9
10
  // src/commands/status.ts
10
11
  import { Command } from "@oclif/core";
@@ -19,13 +20,12 @@ var Status = class extends Command {
19
20
  this.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
20
21
  this.log(`Daemon: ${running ? `running (PID ${pid})` : "stopped"}`);
21
22
  this.log(`Agent ID: ${config.agentId ?? "(not set)"}`);
22
- this.log(`Server URL: ${config.serverUrl}`);
23
23
  this.log(`Log Level: ${config.logLevel}`);
24
24
  this.log(`Config: ${configStore.path}`);
25
25
  this.log(`PID File: ${dm.pidFilePath}`);
26
- if (running && config.serverUrl) {
26
+ if (running) {
27
27
  try {
28
- const res = await fetch(`${config.serverUrl}/v1/health`);
28
+ const res = await fetch(`${SERVER_URL}/v1/health`);
29
29
  const data = await res.json();
30
30
  this.log(`Server: ${data.data?.status ?? "unknown"}`);
31
31
  } catch {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/status.ts"],"sourcesContent":["import { Command } from '@oclif/core';\nimport { DaemonManager } from '../daemon/daemon-manager.js';\nimport { ConfigStore } from '../config/config-store.js';\n\nexport default class Status extends Command {\n static override description = 'Show Stamn agent daemon status';\n\n async run(): Promise<void> {\n const dm = new DaemonManager();\n const configStore = new ConfigStore();\n const config = configStore.getAll();\n const { running, pid } = dm.isRunning();\n\n this.log('Stamn Agent Status');\n this.log('──────────────────');\n this.log(`Daemon: ${running ? `running (PID ${pid})` : 'stopped'}`);\n this.log(`Agent ID: ${config.agentId ?? '(not set)'}`);\n this.log(`Server URL: ${config.serverUrl}`);\n this.log(`Log Level: ${config.logLevel}`);\n this.log(`Config: ${configStore.path}`);\n this.log(`PID File: ${dm.pidFilePath}`);\n\n if (running && config.serverUrl) {\n try {\n const res = await fetch(`${config.serverUrl}/v1/health`);\n const data = (await res.json()) as { data?: { status?: string } };\n this.log(`Server: ${data.data?.status ?? 'unknown'}`);\n } catch {\n this.log('Server: unreachable');\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,eAAe;AAIxB,IAAqB,SAArB,cAAoC,QAAQ;AAAA,EAC1C,OAAgB,cAAc;AAAA,EAE9B,MAAM,MAAqB;AACzB,UAAM,KAAK,IAAI,cAAc;AAC7B,UAAM,cAAc,IAAI,YAAY;AACpC,UAAM,SAAS,YAAY,OAAO;AAClC,UAAM,EAAE,SAAS,IAAI,IAAI,GAAG,UAAU;AAEtC,SAAK,IAAI,oBAAoB;AAC7B,SAAK,IAAI,8GAAoB;AAC7B,SAAK,IAAI,eAAe,UAAU,gBAAgB,GAAG,MAAM,SAAS,EAAE;AACtE,SAAK,IAAI,eAAe,OAAO,WAAW,WAAW,EAAE;AACvD,SAAK,IAAI,eAAe,OAAO,SAAS,EAAE;AAC1C,SAAK,IAAI,eAAe,OAAO,QAAQ,EAAE;AACzC,SAAK,IAAI,eAAe,YAAY,IAAI,EAAE;AAC1C,SAAK,IAAI,eAAe,GAAG,WAAW,EAAE;AAExC,QAAI,WAAW,OAAO,WAAW;AAC/B,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,SAAS,YAAY;AACvD,cAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,aAAK,IAAI,eAAe,KAAK,MAAM,UAAU,SAAS,EAAE;AAAA,MAC1D,QAAQ;AACN,aAAK,IAAI,yBAAyB;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/commands/status.ts"],"sourcesContent":["import { Command } from '@oclif/core';\nimport { DaemonManager } from '../daemon/daemon-manager.js';\nimport { ConfigStore } from '../config/config-store.js';\nimport { SERVER_URL } from '../config/config-schema.js';\n\nexport default class Status extends Command {\n static override description = 'Show Stamn agent daemon status';\n\n async run(): Promise<void> {\n const dm = new DaemonManager();\n const configStore = new ConfigStore();\n const config = configStore.getAll();\n const { running, pid } = dm.isRunning();\n\n this.log('Stamn Agent Status');\n this.log('──────────────────');\n this.log(`Daemon: ${running ? `running (PID ${pid})` : 'stopped'}`);\n this.log(`Agent ID: ${config.agentId ?? '(not set)'}`);\n this.log(`Log Level: ${config.logLevel}`);\n this.log(`Config: ${configStore.path}`);\n this.log(`PID File: ${dm.pidFilePath}`);\n\n if (running) {\n try {\n const res = await fetch(`${SERVER_URL}/v1/health`);\n const data = (await res.json()) as { data?: { status?: string } };\n this.log(`Server: ${data.data?.status ?? 'unknown'}`);\n } catch {\n this.log('Server: unreachable');\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,eAAe;AAKxB,IAAqB,SAArB,cAAoC,QAAQ;AAAA,EAC1C,OAAgB,cAAc;AAAA,EAE9B,MAAM,MAAqB;AACzB,UAAM,KAAK,IAAI,cAAc;AAC7B,UAAM,cAAc,IAAI,YAAY;AACpC,UAAM,SAAS,YAAY,OAAO;AAClC,UAAM,EAAE,SAAS,IAAI,IAAI,GAAG,UAAU;AAEtC,SAAK,IAAI,oBAAoB;AAC7B,SAAK,IAAI,8GAAoB;AAC7B,SAAK,IAAI,eAAe,UAAU,gBAAgB,GAAG,MAAM,SAAS,EAAE;AACtE,SAAK,IAAI,eAAe,OAAO,WAAW,WAAW,EAAE;AACvD,SAAK,IAAI,eAAe,OAAO,QAAQ,EAAE;AACzC,SAAK,IAAI,eAAe,YAAY,IAAI,EAAE;AAC1C,SAAK,IAAI,eAAe,GAAG,WAAW,EAAE;AAExC,QAAI,SAAS;AACX,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,UAAU,YAAY;AACjD,cAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,aAAK,IAAI,eAAe,KAAK,MAAM,UAAU,SAAS,EAAE;AAAA,MAC1D,QAAQ;AACN,aAAK,IAAI,yBAAyB;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamn/agent",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Stamn Agent Daemon CLI",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/config-schema.ts","../src/config/config-store.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const configSchema = z.object({\n serverUrl: z.string().url().default('http://localhost:3001'),\n apiKey: z.string().min(1).optional(),\n agentId: z.string().uuid().optional(),\n logLevel: z\n .enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n .default('info'),\n heartbeatIntervalMs: z.number().int().positive().default(30_000),\n wsReconnectBaseMs: z.number().int().positive().default(1_000),\n wsReconnectMaxMs: z.number().int().positive().default(30_000),\n});\n\nexport type AgentConfig = z.infer<typeof configSchema>;\n\nexport const CONFIG_DEFAULTS: AgentConfig = configSchema.parse({});\n","import Conf from 'conf';\nimport { configSchema, CONFIG_DEFAULTS, type AgentConfig } from './config-schema.js';\n\nexport class ConfigStore {\n private store: Conf<AgentConfig>;\n\n constructor() {\n this.store = new Conf<AgentConfig>({\n projectName: 'stamn',\n defaults: CONFIG_DEFAULTS,\n });\n }\n\n get<K extends keyof AgentConfig>(key: K): AgentConfig[K] {\n return this.store.get(key);\n }\n\n set<K extends keyof AgentConfig>(key: K, value: AgentConfig[K]): void {\n configSchema.partial().parse({ [key]: value });\n this.store.set(key, value);\n }\n\n getAll(): AgentConfig {\n return configSchema.parse(this.store.store);\n }\n\n clear(): void {\n this.store.clear();\n }\n\n get path(): string {\n return this.store.path;\n }\n}\n"],"mappings":";;;AAAA,SAAS,SAAS;AAEX,IAAM,eAAe,EAAE,OAAO;AAAA,EACnC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,uBAAuB;AAAA,EAC3D,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACnC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,UAAU,EACP,KAAK,CAAC,SAAS,SAAS,QAAQ,QAAQ,SAAS,OAAO,CAAC,EACzD,QAAQ,MAAM;AAAA,EACjB,qBAAqB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAM;AAAA,EAC/D,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAK;AAAA,EAC5D,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,GAAM;AAC9D,CAAC;AAIM,IAAM,kBAA+B,aAAa,MAAM,CAAC,CAAC;;;AChBjE,OAAO,UAAU;AAGV,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EAER,cAAc;AACZ,SAAK,QAAQ,IAAI,KAAkB;AAAA,MACjC,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,IAAiC,KAAwB;AACvD,WAAO,KAAK,MAAM,IAAI,GAAG;AAAA,EAC3B;AAAA,EAEA,IAAiC,KAAQ,OAA6B;AACpE,iBAAa,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC;AAC7C,SAAK,MAAM,IAAI,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEA,SAAsB;AACpB,WAAO,aAAa,MAAM,KAAK,MAAM,KAAK;AAAA,EAC5C;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;","names":[]}
@@ -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';\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: '0.0.0',\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":[]}