@openserv-labs/sdk 1.8.2 → 2.0.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.
package/README.md CHANGED
@@ -6,9 +6,42 @@
6
6
 
7
7
  A powerful TypeScript framework for building non-deterministic AI agents with advanced cognitive capabilities like reasoning, decision-making, and inter-agent collaboration within the OpenServ platform. Built with strong typing, extensible architecture, and a fully autonomous agent runtime.
8
8
 
9
+ ## What's New in v2
10
+
11
+ Version 2.0.0 introduces built-in tunnel support for local development, eliminating the need to deploy your agent to test it with the OpenServ platform.
12
+
13
+ ### Key Changes
14
+
15
+ - **Built-in Tunnel for Local Development** - New `run()` function and `OpenServTunnel` class create a secure WebSocket connection to OpenServ, allowing you to develop and test locally without deploying. No need to configure an Agent Endpoint URL during development.
16
+ - **Automatic Port Fallback** - If your preferred port is busy, the agent automatically finds an available port instead of failing.
17
+ - **Secrets Management** - New `getSecrets()` and `getSecretValue()` methods allow agents to securely access workspace secrets.
18
+ - **Delete File Support** - New `deleteFile()` method for workspace file management.
19
+ - **Increased Request Size Limit** - Body parser limit increased to 10MB for larger payloads.
20
+ - **Enhanced Logging** - Added pino-pretty for more readable log output during development.
21
+
22
+ ### Migration from v1.x
23
+
24
+ The v2 API is backwards compatible. To take advantage of the new tunnel feature for local development, simply replace:
25
+
26
+ ```typescript
27
+ // v1.x - Required deploying to a public URL
28
+ agent.start()
29
+ ```
30
+
31
+ With:
32
+
33
+ ```typescript
34
+ // v2.x - Works locally without deployment
35
+ import { run } from '@openserv-labs/sdk'
36
+ const { stop } = await run(agent)
37
+ ```
38
+
9
39
  ## Table of Contents
10
40
 
11
41
  - [OpenServ TypeScript SDK, Autonomous AI Agent Development Framework](#openserv-typescript-sdk-autonomous-ai-agent-development-framework)
42
+ - [What's New in v2](#whats-new-in-v2)
43
+ - [Key Changes](#key-changes)
44
+ - [Migration from v1.x](#migration-from-v1x)
12
45
  - [Table of Contents](#table-of-contents)
13
46
  - [Features](#features)
14
47
  - [Framework Architecture](#framework-architecture)
@@ -39,10 +72,24 @@ A powerful TypeScript framework for building non-deterministic AI agents with ad
39
72
  - [Workspace Management](#workspace-management)
40
73
  - [Get Files](#get-files)
41
74
  - [Upload File](#upload-file)
75
+ - [Delete File](#delete-file)
76
+ - [Secrets Management](#secrets-management)
77
+ - [Get Secrets](#get-secrets)
78
+ - [Get Secret Value](#get-secret-value)
42
79
  - [Integration Management](#integration-management)
43
80
  - [Call Integration](#call-integration)
44
81
  - [MCP](#mcp)
82
+ - [Configure MCP servers](#configure-mcp-servers)
83
+ - [Local (stdio) transport](#local-stdio-transport)
84
+ - [Server-Sent Events (sse) transport](#server-sent-events-sse-transport)
85
+ - [Using MCP tools](#using-mcp-tools)
45
86
  - [Advanced Usage](#advanced-usage)
87
+ - [Local Development with Tunnel](#local-development-with-tunnel)
88
+ - [How It Works](#how-it-works)
89
+ - [Quick Start](#quick-start-1)
90
+ - [Tunnel vs. Deployed Endpoint](#tunnel-vs-deployed-endpoint)
91
+ - [Configuration Options](#configuration-options)
92
+ - [Using the Tunnel Directly](#using-the-tunnel-directly)
46
93
  - [OpenAI Process Runtime](#openai-process-runtime)
47
94
  - [Error Handling](#error-handling)
48
95
  - [Custom Agents](#custom-agents)
@@ -64,6 +111,7 @@ A powerful TypeScript framework for building non-deterministic AI agents with ad
64
111
  - 📝 Strong TypeScript typing with Zod schemas
65
112
  - 📊 Built-in logging and error handling
66
113
  - 🎯 Three levels of control for different development needs
114
+ - 🚇 Built-in tunnel for local development and testing
67
115
 
68
116
  ## Framework Architecture
69
117
 
@@ -257,15 +305,21 @@ agent.addCapabilities([
257
305
 
258
306
  // Start the agent server
259
307
  agent.start()
308
+
309
+ // Or use run() for local development with automatic tunnel management
310
+ // import { run } from '@openserv-labs/sdk'
311
+ // const { stop } = await run(agent)
260
312
  ```
261
313
 
262
314
  ## Environment Variables
263
315
 
264
- | Variable | Description | Required | Default |
265
- | ------------------ | ------------------------------------- | -------- | ------- |
266
- | `OPENSERV_API_KEY` | Your OpenServ API key | Yes | - |
267
- | `OPENAI_API_KEY` | OpenAI API key (for process() method) | No\* | - |
268
- | `PORT` | Server port | No | 7378 |
316
+ | Variable | Description | Required | Default |
317
+ | --------------------- | ------------------------------------------ | -------- | ---------------------------------- |
318
+ | `OPENSERV_API_KEY` | Your OpenServ API key | Yes | - |
319
+ | `OPENAI_API_KEY` | OpenAI API key (for process() method) | No\* | - |
320
+ | `PORT` | Server port | No | 7378 |
321
+ | `OPENSERV_AUTH_TOKEN` | Token for authenticating incoming requests | No | - |
322
+ | `OPENSERV_PROXY_URL` | Custom proxy URL for tunnel connections | No | `https://agents-proxy.openserv.ai` |
269
323
 
270
324
  \*Required if using OpenAI integration features
271
325
 
@@ -528,6 +582,61 @@ await agent.uploadFile({
528
582
  })
529
583
  ```
530
584
 
585
+ #### Delete File
586
+
587
+ ```typescript
588
+ await agent.deleteFile({
589
+ workspaceId: number | string,
590
+ fileId: number
591
+ })
592
+ ```
593
+
594
+ ### Secrets Management
595
+
596
+ Agents can securely access secrets configured in their workspace. Secrets are managed through the OpenServ platform.
597
+
598
+ #### Get Secrets
599
+
600
+ Returns a list of all secrets available to the agent in a workspace.
601
+
602
+ ```typescript
603
+ const secrets = await agent.getSecrets({
604
+ workspaceId: number | string
605
+ })
606
+ // Returns: { id: number, name: string }[]
607
+ ```
608
+
609
+ #### Get Secret Value
610
+
611
+ Retrieves the actual value of a specific secret.
612
+
613
+ ```typescript
614
+ const value = await agent.getSecretValue({
615
+ workspaceId: number | string,
616
+ secretId: number
617
+ })
618
+ // Returns: string
619
+ ```
620
+
621
+ **Example:**
622
+
623
+ ```typescript
624
+ // Get all available secrets
625
+ const secrets = await agent.getSecrets({ workspaceId: 123 })
626
+
627
+ // Find a specific secret by name
628
+ const apiKeySecret = secrets.find(s => s.name === 'EXTERNAL_API_KEY')
629
+
630
+ if (apiKeySecret) {
631
+ // Retrieve the secret value
632
+ const apiKey = await agent.getSecretValue({
633
+ workspaceId: 123,
634
+ secretId: apiKeySecret.id
635
+ })
636
+ // Use the secret value securely
637
+ }
638
+ ```
639
+
531
640
  ### Integration Management
532
641
 
533
642
  #### Call Integration
@@ -633,6 +742,109 @@ You can also access the raw MCP client via `agent.mcpClients['MCP_SERVER_ID']` t
633
742
 
634
743
  ## Advanced Usage
635
744
 
745
+ ### Local Development with Tunnel
746
+
747
+ The SDK provides a built-in tunnel that connects your locally running agent to the OpenServ platform. This eliminates the need to deploy your agent to a public URL during development.
748
+
749
+ #### How It Works
750
+
751
+ When you use the `run()` function or `OpenServTunnel` class:
752
+
753
+ 1. Your agent starts an HTTP server locally (default port 7378)
754
+ 2. A WebSocket connection is established to OpenServ's proxy server
755
+ 3. The proxy authenticates your agent using your `OPENSERV_API_KEY`
756
+ 4. OpenServ routes incoming tasks through the tunnel to your local machine
757
+
758
+ **No Agent Endpoint URL configuration is needed during local development.** The tunnel connection is identified by your API key, which is already associated with your registered agent in the OpenServ platform.
759
+
760
+ #### Quick Start
761
+
762
+ ```typescript
763
+ import { Agent, run } from '@openserv-labs/sdk'
764
+ import { z } from 'zod'
765
+
766
+ const agent = new Agent({
767
+ systemPrompt: 'You are a helpful assistant.'
768
+ })
769
+
770
+ agent.addCapability({
771
+ name: 'greet',
772
+ description: 'Greet someone',
773
+ schema: z.object({ name: z.string() }),
774
+ async run({ args }) {
775
+ return `Hello, ${args.name}!`
776
+ }
777
+ })
778
+
779
+ // Start the agent with automatic tunnel management
780
+ const { tunnel, stop } = await run(agent)
781
+
782
+ // The agent is now connected to OpenServ and ready to receive tasks
783
+
784
+ // To gracefully stop the agent and tunnel:
785
+ await stop()
786
+ ```
787
+
788
+ The `run()` function automatically:
789
+
790
+ - Starts the agent's HTTP server
791
+ - Creates a WebSocket tunnel to the OpenServ proxy
792
+ - Handles reconnection with exponential backoff (up to 10 retries)
793
+ - Registers signal handlers for graceful shutdown (SIGTERM, SIGINT)
794
+
795
+ #### Tunnel vs. Deployed Endpoint
796
+
797
+ | Aspect | Tunnel (Local Development) | Deployed Endpoint (Production) |
798
+ | ----------------- | -------------------------- | ------------------------------ |
799
+ | Setup | Just run your code | Deploy to cloud/server |
800
+ | URL Configuration | Not needed | Set Agent Endpoint in platform |
801
+ | Connection | WebSocket via proxy | Direct HTTP |
802
+ | Use case | Development & testing | Production |
803
+
804
+ #### Configuration Options
805
+
806
+ ```typescript
807
+ const { tunnel, stop } = await run(agent, {
808
+ // Tunnel-specific options
809
+ tunnel: {
810
+ apiKey: 'your-api-key', // Defaults to OPENSERV_API_KEY env var
811
+ proxyUrl: 'custom-proxy-url' // Defaults to OPENSERV_PROXY_URL env var
812
+ },
813
+ // Disable automatic signal handlers if you want to handle shutdown yourself
814
+ handleSignals: false
815
+ })
816
+ ```
817
+
818
+ #### Using the Tunnel Directly
819
+
820
+ For more advanced control, you can use the `OpenServTunnel` class directly:
821
+
822
+ ```typescript
823
+ import { Agent, OpenServTunnel } from '@openserv-labs/sdk'
824
+
825
+ const agent = new Agent({
826
+ systemPrompt: 'You are a helpful assistant.'
827
+ })
828
+
829
+ await agent.start()
830
+
831
+ const tunnel = new OpenServTunnel({
832
+ apiKey: process.env.OPENSERV_API_KEY,
833
+ onConnected: isReconnect => {
834
+ console.log(isReconnect ? 'Reconnected!' : 'Connected!')
835
+ },
836
+ onError: error => {
837
+ console.error('Tunnel error:', error.message)
838
+ }
839
+ })
840
+
841
+ await tunnel.start(agent.port)
842
+
843
+ // Later, to stop:
844
+ await tunnel.stop()
845
+ await agent.stop()
846
+ ```
847
+
636
848
  ### OpenAI Process Runtime
637
849
 
638
850
  The framework includes built-in OpenAI function calling support through the `process()` method:
package/dist/agent.d.ts CHANGED
@@ -65,9 +65,9 @@ export declare class Agent<M extends string = string> {
65
65
  /**
66
66
  * The port number the server will listen on.
67
67
  * Defaults to DEFAULT_PORT (7378) if not specified in options.
68
- * @private
68
+ * May change if the preferred port is unavailable.
69
69
  */
70
- private port;
70
+ port: number;
71
71
  /**
72
72
  * The system prompt used for OpenAI chat completions.
73
73
  * This defines the base behavior and context for the agent.
@@ -85,7 +85,13 @@ export declare class Agent<M extends string = string> {
85
85
  * Can be provided in options or via OPENSERV_API_KEY environment variable.
86
86
  * @private
87
87
  */
88
- private apiKey;
88
+ private _apiKey;
89
+ /**
90
+ * Gets the OpenServ API key for this agent instance.
91
+ * Used by run() to pass the correct key to the tunnel.
92
+ * @returns The API key for this agent
93
+ */
94
+ get apiKey(): string;
89
95
  /**
90
96
  * Axios instance for making requests to the OpenServ API.
91
97
  * Pre-configured with base URL and authentication headers.
@@ -405,6 +411,7 @@ export declare class Agent<M extends string = string> {
405
411
  private setupRoutes;
406
412
  /**
407
413
  * Starts the agent's HTTP server.
414
+ * If the preferred port is unavailable, it will find an open port.
408
415
  *
409
416
  * @returns {Promise<void>} Resolves when the server has started
410
417
  * @throws {Error} If server fails to start
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAUjD,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,aAAa,EACb,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAEhB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAOlB,YAAY,EACZ,kBAAkB,EAClB,8BAA8B,EAC/B,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EACV,0BAA0B,EAE1B,cAAc,EACf,MAAM,mCAAmC,CAAA;AAI1C,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAGL,KAAK,eAAe,EAEpB,SAAS,EACV,MAAM,OAAO,CAAA;AAOd;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;IAC5C;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IAEnE;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,eAAe,CAAC,CAAA;CACxC;AAoBD,qBAAa,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IA0H9B,OAAO,CAAC,OAAO;IAzH3B;;;;OAIG;IACH,OAAO,CAAC,GAAG,CAAqB;IAEhC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAA2B;IAEzC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAqB;IAEnC;;;;OAIG;IACH,OAAO,CAAC,IAAI,CAAQ;IAEpB;;;;OAIG;IACH,SAAS,CAAC,YAAY,EAAE,MAAM,CAAA;IAE9B;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAK;IAExD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAQ;IAEtB;;;;OAIG;IACH,OAAO,CAAC,SAAS,CAAe;IAEhC;;;;OAIG;IACH,SAAS,CAAC,aAAa,EAAE,aAAa,CAAA;IAEtC;;;;OAIG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IAE1B;;;OAGG;IACI,UAAU,EAAE,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAgC;IAE1E;;;;;OAKG;IACH,OAAO,KAAK,WAAW,GAStB;IAED;;;;;;OAMG;IACH,OAAO,KAAK,MAAM,GAWjB;IAED;;;;;;;OAOG;gBACiB,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;IAgD5C,OAAO,CAAC,oBAAoB;IAgB5B;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,EACpC,IAAI,EACJ,WAAW,EACX,MAAM,EACN,GAAG,EACJ,EAAE;QACD,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;QACnB,MAAM,EAAE,CAAC,CAAA;QACT,GAAG,CACD,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EACd,MAAM,EAAE;YAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC,EAAE,YAAY,CAAA;SAAE,EACnD,QAAQ,EAAE,0BAA0B,EAAE,GACrC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;KAC5B,GAAG,IAAI;IAYR;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE;SACjF,CAAC,IAAI,MAAM,CAAC,GAAG;YACd,IAAI,EAAE,MAAM,CAAA;YACZ,WAAW,EAAE,MAAM,CAAA;YACnB,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;YACZ,GAAG,CACD,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EACd,MAAM,EAAE;gBAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAAC,MAAM,CAAC,EAAE,YAAY,CAAA;aAAE,EACtD,QAAQ,EAAE,0BAA0B,EAAE,GACrC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;SAC5B;KACF,GAAG,IAAI;IAOR;;;;;;OAMG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc;IAOrC;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB;IAOzC;;;;;OAKG;IACG,cAAc,CAAC,MAAM,EAAE,oBAAoB;IAOjD;;;;;;;;;;OAUG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB;IA6BzC;;;;;;;OAOG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB;IAOzC;;;;;;;;OAQG;IACG,iBAAiB,CAAC,MAAM,EAAE,uBAAuB;IAUvD;;;;;;;;OAQG;IACG,YAAY,CAAC,MAAM,EAAE,kBAAkB;IAU7C;;;;;;;;OAQG;IACG,eAAe,CAAC,MAAM,EAAE,qBAAqB;IAUnD;;;;;;;OAOG;IACG,aAAa,CAAC,MAAM,EAAE,mBAAmB;IAO/C;;;;;;OAMG;IACG,SAAS,CAAC,MAAM,EAAE,eAAe;IAOvC;;;;;;OAMG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc;IAOrC;;;;;;;OAOG;IACG,eAAe,CAAC,MAAM,EAAE,qBAAqB;IAOnD;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB;IAezC;;;;;;;;;;OAUG;IACG,YAAY,CAAC,MAAM,EAAE,kBAAkB;IAY7C;;;;;;;;;;OAUG;IACG,sBAAsB,CAAC,MAAM,EAAE,4BAA4B;IA0BjE;;;;;;;;OAQG;IACG,gBAAgB,CAAC,MAAM,EAAE,sBAAsB;IAUrD;;;;;;;OAOG;IACG,OAAO,CAAC,EAAE,QAAQ,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IA4FnE;;;;OAIG;cACa,MAAM,CAAC,MAAM,EAAE,kBAAkB;IA6BjD;;;;OAIG;cACa,aAAa,CAAC,MAAM,EAAE,8BAA8B;IA+BpE;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,GAAG,EAAE;QACzB,MAAM,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;QAC5B,IAAI,EAAE;YACJ,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;YAC5B,MAAM,CAAC,EAAE,YAAY,CAAA;YACrB,QAAQ,CAAC,EAAE,0BAA0B,EAAE,CAAA;SACxC,CAAA;KACF;;;IAyBD;;;;;;;OAOG;IACG,eAAe,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE;IAgB5C;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAuBnB;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6C5B;;;;OAIG;IACG,IAAI;IAQV;;;OAGG;IACH,OAAO,CAAC,WAAW;IAOnB;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,WAAW,EAAE,sBAAsB;IASzD;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;CAsClC"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js CHANGED
@@ -58,7 +58,7 @@ class Agent {
58
58
  /**
59
59
  * The port number the server will listen on.
60
60
  * Defaults to DEFAULT_PORT (7378) if not specified in options.
61
- * @private
61
+ * May change if the preferred port is unavailable.
62
62
  */
63
63
  port;
64
64
  /**
@@ -78,7 +78,15 @@ class Agent {
78
78
  * Can be provided in options or via OPENSERV_API_KEY environment variable.
79
79
  * @private
80
80
  */
81
- apiKey;
81
+ _apiKey;
82
+ /**
83
+ * Gets the OpenServ API key for this agent instance.
84
+ * Used by run() to pass the correct key to the tunnel.
85
+ * @returns The API key for this agent
86
+ */
87
+ get apiKey() {
88
+ return this._apiKey;
89
+ }
82
90
  /**
83
91
  * Axios instance for making requests to the OpenServ API.
84
92
  * Pre-configured with base URL and authentication headers.
@@ -149,8 +157,8 @@ class Agent {
149
157
  this.router = (0, express_async_router_1.AsyncRouter)();
150
158
  this.port = this.options.port || DEFAULT_PORT;
151
159
  this.systemPrompt = this.options.systemPrompt;
152
- this.apiKey = this.options.apiKey || process.env.OPENSERV_API_KEY || '';
153
- if (!this.apiKey) {
160
+ this._apiKey = this.options.apiKey || process.env.OPENSERV_API_KEY || '';
161
+ if (!this._apiKey) {
154
162
  throw new Error('OpenServ API key is required. Please provide it in options or set OPENSERV_API_KEY environment variable.');
155
163
  }
156
164
  // Initialize API client
@@ -169,7 +177,7 @@ class Agent {
169
177
  'x-openserv-key': this.apiKey
170
178
  }
171
179
  });
172
- this.app.use(express_1.default.json());
180
+ this.app.use(express_1.default.json({ limit: '10mb' }));
173
181
  this.app.use(express_1.default.urlencoded({ extended: false }));
174
182
  this.app.use((0, hpp_1.default)());
175
183
  this.app.use((0, helmet_1.default)());
@@ -737,17 +745,53 @@ class Agent {
737
745
  }
738
746
  /**
739
747
  * Starts the agent's HTTP server.
748
+ * If the preferred port is unavailable, it will find an open port.
740
749
  *
741
750
  * @returns {Promise<void>} Resolves when the server has started
742
751
  * @throws {Error} If server fails to start
743
752
  */
744
753
  async start() {
754
+ const preferredPort = this.port;
755
+ // Try the preferred port first, fallback to an available port if it fails
745
756
  await new Promise((resolve, reject) => {
746
- this.server = this.app.listen(this.port, () => {
747
- logger_1.logger.info(`Agent server started on port ${this.port}`);
748
- resolve();
749
- });
750
- this.server.on('error', reject);
757
+ const tryListen = (port, isRetry) => {
758
+ const server = this.app.listen(port);
759
+ const onListening = () => {
760
+ // Remove the startup handlers once listening succeeds
761
+ server.removeListener('error', errorHandler);
762
+ server.removeListener('listening', onListening);
763
+ if (isRetry) {
764
+ const address = server.address();
765
+ if (address && typeof address === 'object') {
766
+ this.port = address.port;
767
+ logger_1.logger.info(`Port ${preferredPort} was unavailable, using port ${this.port} instead`);
768
+ }
769
+ }
770
+ else {
771
+ logger_1.logger.info(`Agent server started on port ${this.port}`);
772
+ }
773
+ this.server = server;
774
+ resolve();
775
+ };
776
+ const errorHandler = (err) => {
777
+ // Clean up the failed server before handling the error
778
+ server.removeListener('error', errorHandler);
779
+ server.removeListener('listening', onListening);
780
+ // Close the failed server to release resources
781
+ server.close();
782
+ if (err.code === 'EADDRINUSE' && !isRetry) {
783
+ logger_1.logger.warn(`Port ${this.port} is in use, finding an available port...`);
784
+ // Let the OS assign an available port
785
+ tryListen(0, true);
786
+ }
787
+ else {
788
+ reject(err);
789
+ }
790
+ };
791
+ server.on('listening', onListening);
792
+ server.on('error', errorHandler);
793
+ };
794
+ tryListen(this.port, false);
751
795
  });
752
796
  const connectionPromises = Object.values(this.mcpClients).map(client => client.connect());
753
797
  const results = await Promise.allSettled(connectionPromises);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  export { Agent } from './agent';
2
2
  export type { AgentOptions } from './agent';
3
3
  export { Capability } from './capability';
4
+ export { OpenServTunnel } from './tunnel';
5
+ export type { OpenServTunnelOptions, RequestData, ResponseData, TunnelState, TunnelEvent } from './tunnel';
6
+ export { run } from './run';
7
+ export type { RunOptions, RunResult } from './run';
4
8
  export type { TaskStatus, GetFilesParams, UploadFileParams, DeleteFileParams, MarkTaskAsErroredParams, CompleteTaskParams, SendChatMessageParams, GetTaskDetailParams, GetAgentsParams, GetTasksParams, CreateTaskParams, AddLogToTaskParams, RequestHumanAssistanceParams, UpdateTaskStatusParams, ProcessParams, CapabilityFuncParams, GetChatMessagesParams } from './types';
5
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,YAAY,EACV,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAC3B,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAElD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Capability = exports.Agent = void 0;
3
+ exports.run = exports.OpenServTunnel = exports.Capability = exports.Agent = void 0;
4
4
  var agent_1 = require("./agent");
5
5
  Object.defineProperty(exports, "Agent", { enumerable: true, get: function () { return agent_1.Agent; } });
6
6
  var capability_1 = require("./capability");
7
7
  Object.defineProperty(exports, "Capability", { enumerable: true, get: function () { return capability_1.Capability; } });
8
+ var tunnel_1 = require("./tunnel");
9
+ Object.defineProperty(exports, "OpenServTunnel", { enumerable: true, get: function () { return tunnel_1.OpenServTunnel; } });
10
+ var run_1 = require("./run");
11
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return run_1.run; } });
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,6CAIrB,CAAA;AAEJ,eAAO,MAAM,MAAM,uCAAiB,CAAA"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,6CAmBxB,CAAA;AAED,eAAO,MAAM,MAAM,uCAAiB,CAAA"}
package/dist/logger.js CHANGED
@@ -5,9 +5,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logger = exports.createLogger = void 0;
7
7
  const pino_1 = __importDefault(require("pino"));
8
- const createLogger = () => (0, pino_1.default)({
9
- name: 'openserv-agent',
10
- level: process.env.LOG_LEVEL || 'info'
11
- });
8
+ const createLogger = () => {
9
+ const isPretty = process.env.LOG_PRETTY === 'true' ||
10
+ (process.env.NODE_ENV !== 'production' && process.stdout.isTTY);
11
+ return (0, pino_1.default)({
12
+ name: 'openserv-agent',
13
+ level: process.env.LOG_LEVEL || 'info',
14
+ transport: isPretty
15
+ ? {
16
+ target: 'pino-pretty',
17
+ options: {
18
+ colorize: true,
19
+ translateTime: 'SYS:standard',
20
+ ignore: 'pid,hostname'
21
+ }
22
+ }
23
+ : undefined
24
+ });
25
+ };
12
26
  exports.createLogger = createLogger;
13
27
  exports.logger = (0, exports.createLogger)();
package/dist/run.d.ts ADDED
@@ -0,0 +1,58 @@
1
+ import type { Agent } from './agent';
2
+ import { OpenServTunnel, type OpenServTunnelOptions } from './tunnel';
3
+ export interface RunOptions {
4
+ /**
5
+ * Options for the OpenServ tunnel.
6
+ * If not provided, defaults will be used (API key from env, default proxy URL).
7
+ */
8
+ tunnel?: Omit<OpenServTunnelOptions, 'onConnected' | 'onRequest' | 'onError'>;
9
+ /**
10
+ * Whether to register signal handlers for graceful shutdown (SIGTERM, SIGINT).
11
+ * Defaults to true.
12
+ */
13
+ handleSignals?: boolean;
14
+ }
15
+ export interface RunResult {
16
+ /**
17
+ * The tunnel instance for advanced control.
18
+ */
19
+ tunnel: OpenServTunnel;
20
+ /**
21
+ * Stop the agent and tunnel.
22
+ */
23
+ stop: () => Promise<void>;
24
+ }
25
+ /**
26
+ * Run an agent with automatic tunnel management.
27
+ *
28
+ * This function handles:
29
+ * 1. Starting the agent's HTTP server
30
+ * 2. Creating a tunnel to connect to the OpenServ proxy
31
+ *
32
+ * @param agent - The Agent instance to run
33
+ * @param options - Optional configuration for the tunnel
34
+ * @returns Run result with tunnel and stop function
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { Agent, run } from '@openserv-labs/sdk'
39
+ *
40
+ * const agent = new Agent({
41
+ * systemPrompt: 'You are a helpful assistant.'
42
+ * })
43
+ *
44
+ * agent.addCapability({
45
+ * name: 'greet',
46
+ * description: 'Greet someone',
47
+ * schema: z.object({ name: z.string() }),
48
+ * run: async ({ args }) => `Hello, ${args.name}!`
49
+ * })
50
+ *
51
+ * const { stop } = await run(agent)
52
+ *
53
+ * // Later, to stop:
54
+ * await stop()
55
+ * ```
56
+ */
57
+ export declare function run(agent: Agent, options?: RunOptions): Promise<RunResult>;
58
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAMrE,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IAE7E;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,MAAM,EAAE,cAAc,CAAA;IAEtB;;OAEG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA8EhF"}
package/dist/run.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = run;
4
+ const logger_1 = require("./logger");
5
+ const tunnel_1 = require("./tunnel");
6
+ // ============================================================================
7
+ // Main Run Function
8
+ // ============================================================================
9
+ /**
10
+ * Run an agent with automatic tunnel management.
11
+ *
12
+ * This function handles:
13
+ * 1. Starting the agent's HTTP server
14
+ * 2. Creating a tunnel to connect to the OpenServ proxy
15
+ *
16
+ * @param agent - The Agent instance to run
17
+ * @param options - Optional configuration for the tunnel
18
+ * @returns Run result with tunnel and stop function
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * import { Agent, run } from '@openserv-labs/sdk'
23
+ *
24
+ * const agent = new Agent({
25
+ * systemPrompt: 'You are a helpful assistant.'
26
+ * })
27
+ *
28
+ * agent.addCapability({
29
+ * name: 'greet',
30
+ * description: 'Greet someone',
31
+ * schema: z.object({ name: z.string() }),
32
+ * run: async ({ args }) => `Hello, ${args.name}!`
33
+ * })
34
+ *
35
+ * const { stop } = await run(agent)
36
+ *
37
+ * // Later, to stop:
38
+ * await stop()
39
+ * ```
40
+ */
41
+ async function run(agent, options) {
42
+ await agent.start();
43
+ const tunnel = new tunnel_1.OpenServTunnel({
44
+ ...options?.tunnel,
45
+ // Always use the agent's API key to ensure tunnel authenticates as the correct agent
46
+ // This prevents issues when running multiple agents with different API keys
47
+ apiKey: agent.apiKey,
48
+ onConnected: isReconnect => {
49
+ if (!isReconnect) {
50
+ logger_1.logger.info('Agent connected to OpenServ proxy');
51
+ }
52
+ },
53
+ onError: error => {
54
+ logger_1.logger.error(`Tunnel error: ${error.message}`);
55
+ }
56
+ });
57
+ try {
58
+ await tunnel.start(agent.port);
59
+ }
60
+ catch (error) {
61
+ // Clean up the agent if tunnel fails to connect
62
+ await agent.stop();
63
+ throw error;
64
+ }
65
+ let shutdownPromise = null;
66
+ let sigtermHandler = null;
67
+ let sigintHandler = null;
68
+ const stop = async () => {
69
+ // Return existing shutdown promise if already in progress
70
+ // This ensures concurrent callers wait for actual completion
71
+ if (shutdownPromise)
72
+ return shutdownPromise;
73
+ shutdownPromise = (async () => {
74
+ // Remove signal handlers to prevent stale references on repeated run() calls
75
+ if (sigtermHandler) {
76
+ process.removeListener('SIGTERM', sigtermHandler);
77
+ sigtermHandler = null;
78
+ }
79
+ if (sigintHandler) {
80
+ process.removeListener('SIGINT', sigintHandler);
81
+ sigintHandler = null;
82
+ }
83
+ await tunnel.stop();
84
+ await agent.stop();
85
+ })();
86
+ return shutdownPromise;
87
+ };
88
+ // Register signal handlers for graceful shutdown (default: true)
89
+ const handleSignals = options?.handleSignals !== false;
90
+ if (handleSignals) {
91
+ const signalHandler = (signal) => {
92
+ logger_1.logger.info(`Received ${signal}, shutting down gracefully...`);
93
+ stop()
94
+ .then(() => {
95
+ process.exit(0);
96
+ })
97
+ .catch(err => {
98
+ logger_1.logger.error(`Error during shutdown: ${err.message}`);
99
+ process.exit(1);
100
+ });
101
+ };
102
+ sigtermHandler = () => signalHandler('SIGTERM');
103
+ sigintHandler = () => signalHandler('SIGINT');
104
+ process.once('SIGTERM', sigtermHandler);
105
+ process.once('SIGINT', sigintHandler);
106
+ }
107
+ return {
108
+ tunnel,
109
+ stop
110
+ };
111
+ }