@olane/o-core 0.8.2 → 0.8.4

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.
Files changed (2) hide show
  1. package/README.md +101 -31
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -35,22 +35,24 @@ This is **NOT** a network framework or API library - it's the abstract operating
35
35
  ## Installation
36
36
 
37
37
  ```bash
38
- npm install @olane/o-core @olane/o-protocol
38
+ pnpm install @olane/o-core @olane/o-protocol
39
39
  ```
40
40
 
41
41
  ## Quick Start
42
42
 
43
43
  ### Creating Your First Tool Node
44
44
 
45
+ > **Note**: `oCore` is an abstract base class. For production use, extend higher-level packages like `oNodeTool` (from `@olane/o-node`), `oLaneTool` (from `@olane/o-lane`), or `McpTool` (from `@olane/o-mcp`). The example below demonstrates the core concepts.
46
+
45
47
  ```typescript
46
48
  import { oCore, oAddress, NodeType, NodeState } from '@olane/o-core';
47
49
  import { oRequest, oResponse } from '@olane/o-core';
48
50
 
49
51
  // Extend oCore to create your tool node
50
52
  class MyToolNode extends oCore {
51
- constructor(address: string) {
53
+ constructor(config: { address: oAddress }) {
52
54
  super({
53
- address: new oAddress(address),
55
+ address: config.address,
54
56
  type: NodeType.AGENT, // Type remains AGENT for now (legacy naming)
55
57
  description: 'My first tool node',
56
58
  methods: {
@@ -68,11 +70,11 @@ class MyToolNode extends oCore {
68
70
  // Implement required abstract methods
69
71
  async execute(request: oRequest): Promise<any> {
70
72
  const { method, params } = request;
71
-
73
+
72
74
  if (method === 'greet') {
73
75
  return { message: `Hello, ${params.name}!` };
74
76
  }
75
-
77
+
76
78
  throw new Error(`Unknown method: ${method}`);
77
79
  }
78
80
 
@@ -103,19 +105,30 @@ class MyToolNode extends oCore {
103
105
  }
104
106
 
105
107
  // Create and start your tool node
106
- const toolNode = new MyToolNode('o://company/customer-service');
108
+ // IMPORTANT: Use simple (non-nested) addresses in constructors.
109
+ // Nested addresses like 'o://company/customer-service' are created
110
+ // automatically at runtime during parent-child registration.
111
+ const toolNode = new MyToolNode({
112
+ address: new oAddress('o://customer-service')
113
+ });
107
114
  await toolNode.start();
108
115
 
109
116
  // AI agents can now use this tool node via its o:// address
110
117
  const response = await toolNode.use(
111
- new oAddress('o://company/customer-service'),
118
+ new oAddress('o://customer-service'),
112
119
  {
113
120
  method: 'greet',
114
121
  params: { name: 'Alice' }
115
122
  }
116
123
  );
117
124
 
118
- console.log(response.result); // { message: "Hello, Alice!" }
125
+ // Response follows the standard wrapping structure:
126
+ // response.result.success - boolean indicating success/failure
127
+ // response.result.data - the returned data on success
128
+ // response.result.error - error details on failure
129
+ if (response.result.success) {
130
+ console.log(response.result.data); // { message: "Hello, Alice!" }
131
+ }
119
132
 
120
133
  // Stop the tool node gracefully
121
134
  await toolNode.stop();
@@ -125,15 +138,16 @@ await toolNode.stop();
125
138
 
126
139
  ```typescript
127
140
  // Tool Node A can communicate with Tool Node B using o:// addresses
128
- const salesTool = new MyToolNode('o://company/sales');
129
- const analyticsTool = new MyToolNode('o://company/analytics');
141
+ // Use simple addresses in constructors; nested addresses are created at runtime
142
+ const salesTool = new MyToolNode({ address: new oAddress('o://sales') });
143
+ const analyticsTool = new MyToolNode({ address: new oAddress('o://analytics') });
130
144
 
131
145
  await salesTool.start();
132
146
  await analyticsTool.start();
133
147
 
134
148
  // Sales tool calls analytics tool (inter-process communication)
135
149
  const result = await salesTool.use(
136
- new oAddress('o://company/analytics'),
150
+ new oAddress('o://analytics'),
137
151
  {
138
152
  method: 'analyze',
139
153
  params: { data: salesData }
@@ -145,19 +159,25 @@ const result = await salesTool.use(
145
159
 
146
160
  ```typescript
147
161
  // Create a parent-child hierarchy of tool nodes
148
- const parent = new MyToolNode('o://company');
149
- const child1 = new MyToolNode('o://company/sales');
150
- const child2 = new MyToolNode('o://company/marketing');
162
+ // Use simple addresses in constructors - nested addresses are created at runtime
163
+ const parent = new MyToolNode({ address: new oAddress('o://company') });
164
+
165
+ // Children use simple addresses with a parent reference
166
+ // During registration, child addresses become nested (e.g., 'o://company/sales')
167
+ const child1 = new MyToolNode({ address: new oAddress('o://sales') });
168
+ const child2 = new MyToolNode({ address: new oAddress('o://marketing') });
151
169
 
152
170
  await parent.start();
153
171
  await child1.start();
154
172
  await child2.start();
155
173
 
156
- // Register children with parent
174
+ // Register children with parent - this creates the nested addresses
157
175
  parent.addChildNode(child1);
158
176
  parent.addChildNode(child2);
159
177
 
160
- // Child tool nodes automatically inherit context from parent
178
+ // After registration:
179
+ // child1.address -> 'o://company/sales'
180
+ // child2.address -> 'o://company/marketing'
161
181
  // Routing happens automatically through the hierarchy
162
182
  ```
163
183
 
@@ -185,7 +205,9 @@ console.log(toolNode.state); // NodeState.STOPPED
185
205
  Addresses in Olane OS follow a hierarchical filesystem-like pattern:
186
206
 
187
207
  ```typescript
188
- // Hierarchical addresses
208
+ // Hierarchical addresses exist at runtime after parent-child registration.
209
+ // They are created by the system, not by constructors directly.
210
+ // For example, after registration a child address may become:
189
211
  const address1 = new oAddress('o://company/finance/accounting');
190
212
  const address2 = new oAddress('o://users/alice/inbox');
191
213
 
@@ -197,6 +219,12 @@ console.log(address1.validate()); // true
197
219
  // Static vs dynamic addresses
198
220
  const staticAddr = address1.toStaticAddress();
199
221
  console.log(staticAddr.toString()); // "o://accounting"
222
+
223
+ // IMPORTANT: When creating nodes in constructors, always use simple
224
+ // (non-nested) addresses. Nested addresses are created automatically
225
+ // during parent-child registration at runtime.
226
+ // ✅ new oAddress('o://my-tool')
227
+ // ❌ new oAddress('o://parent/my-tool') -- violates validateNotNested()
200
228
  ```
201
229
 
202
230
  **📖 For complete details on address resolution, routing algorithms, and custom resolvers, see the [Router System documentation](./src/router/README.md).**
@@ -207,8 +235,9 @@ All inter-process communication (IPC) follows a request/response pattern using J
207
235
 
208
236
  ```typescript
209
237
  // Making a request from one tool node to another
238
+ // Full signature: use(address: oAddress, data?: { method: string, params: any, id?: string }, options?: UseOptions)
210
239
  const response: oResponse = await toolNode.use(
211
- new oAddress('o://target/toolnode'),
240
+ new oAddress('o://target-toolnode'),
212
241
  {
213
242
  method: 'processData',
214
243
  params: { key: 'value' },
@@ -216,19 +245,58 @@ const response: oResponse = await toolNode.use(
216
245
  }
217
246
  );
218
247
 
219
- // Handling errors
248
+ // Always check the response structure
249
+ if (response.result.success) {
250
+ console.log('Data:', response.result.data);
251
+ } else {
252
+ console.error('Error:', response.result.error);
253
+ }
254
+
255
+ // Transport-level errors throw exceptions
220
256
  try {
221
257
  const response = await toolNode.use(targetAddress, requestData);
222
- console.log(response.result);
258
+ // Check response.result.success for application-level errors
223
259
  } catch (error) {
224
260
  if (error instanceof oError) {
225
- console.error(`Error ${error.code}: ${error.message}`);
261
+ console.error(`Transport error ${error.code}: ${error.message}`);
226
262
  }
227
263
  }
228
264
  ```
229
265
 
230
266
  **📖 Learn more about JSON-RPC messaging, request states, and connection lifecycle in the [Connection System documentation](./src/connection/README.md).**
231
267
 
268
+ ### Response Structure
269
+
270
+ When you call `use()`, the response follows a standard wrapping structure created by the `ResponseBuilder`. In higher-level packages (`@olane/o-node`, `@olane/o-tool`, `@olane/o-lane`), responses are wrapped with `success`, `data`, and `error` fields:
271
+
272
+ ```typescript
273
+ const response = await toolNode.use(targetAddress, {
274
+ method: 'processData',
275
+ params: { key: 'value' }
276
+ });
277
+
278
+ // Standard response structure:
279
+ // {
280
+ // jsonrpc: "2.0",
281
+ // id: "request-id",
282
+ // result: {
283
+ // success: boolean, // Whether the operation succeeded
284
+ // data: any, // The returned data (on success)
285
+ // error?: string // Error details (on failure)
286
+ // }
287
+ // }
288
+
289
+ // Always check success before accessing data
290
+ if (response.result.success) {
291
+ const data = response.result.data;
292
+ console.log('Result:', data);
293
+ } else {
294
+ console.error('Error:', response.result.error);
295
+ }
296
+ ```
297
+
298
+ > **Important**: Access data via `response.result.data`, not `response.result` directly. The `result` object contains the wrapping fields (`success`, `data`, `error`), not the raw return value.
299
+
232
300
  ### Metrics and Observability
233
301
 
234
302
  Every tool node tracks metrics automatically:
@@ -252,7 +320,7 @@ toolNode.logger.error('Error message');
252
320
  `oCore` is an abstract base class that provides:
253
321
 
254
322
  - **Lifecycle Management**: `start()`, `stop()`, `initialize()`, `teardown()`
255
- - **Communication**: `use()`, `use()`, `connect()`
323
+ - **Communication**: `use()`, `useDirect()`, `connect()`
256
324
  - **Routing**: `router`, `initializeRouter()`
257
325
  - **Hierarchy**: `addChildNode()`, `removeChildNode()`, `hierarchyManager`
258
326
  - **State Management**: `state`, `NodeState` enum
@@ -401,7 +469,7 @@ async execute(request: oRequest): Promise<any> {
401
469
  ```typescript
402
470
  import { setupGracefulShutdown } from '@olane/o-core';
403
471
 
404
- const toolNode = new MyToolNode('o://my/toolnode');
472
+ const toolNode = new MyToolNode({ address: new oAddress('o://my-toolnode') });
405
473
  await toolNode.start();
406
474
 
407
475
  // Setup graceful shutdown handlers
@@ -430,7 +498,9 @@ Abstract base class for building tool nodes.
430
498
  #### Methods
431
499
  - `async start(): Promise<void>` - Start the tool node
432
500
  - `async stop(): Promise<void>` - Stop the tool node gracefully
433
- - `async use(address, data?): Promise<oResponse>` - Communicate with another tool node (IPC)
501
+ - `async use(address: oAddress, data?: UseDataConfig, options?: UseOptions): Promise<oResponse>` - Communicate with another tool node (IPC). The `options` parameter supports `noRouting`, `isStream`, `onChunk`, `readTimeoutMs`, `drainTimeoutMs`, and `abortSignal`.
502
+ - `async useDirect(address: oAddress, data?: UseDataConfig): Promise<oResponse>` - Send a request without routing (equivalent to `use()` with `{ noRouting: true }`)
503
+ - `async useStream(address: oAddress, data: UseDataConfig, options: UseStreamOptions): Promise<oResponse>` - Send a streaming request to another tool node
434
504
  - `async execute(request): Promise<any>` - Execute a request (abstract - you implement this)
435
505
  - `addChildNode(node): void` - Add a child tool node
436
506
  - `removeChildNode(node): void` - Remove a child tool node
@@ -460,29 +530,29 @@ new oError(code: oErrorCodes, message: string, data?: any)
460
530
 
461
531
  ```bash
462
532
  # Run tests
463
- npm test
533
+ pnpm test
464
534
 
465
535
  # Run tests in Node.js
466
- npm run test:node
536
+ pnpm run test:node
467
537
 
468
538
  # Run tests in browser
469
- npm run test:browser
539
+ pnpm run test:browser
470
540
  ```
471
541
 
472
542
  ## Development
473
543
 
474
544
  ```bash
475
545
  # Install dependencies
476
- npm install
546
+ pnpm install
477
547
 
478
548
  # Build the package
479
- npm run build
549
+ pnpm run build
480
550
 
481
551
  # Run in development mode
482
- npm run dev
552
+ pnpm run dev
483
553
 
484
554
  # Lint the code
485
- npm run lint
555
+ pnpm run lint
486
556
  ```
487
557
 
488
558
  ## Use Cases
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/o-core",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -63,12 +63,12 @@
63
63
  "typescript": "^5.8.3"
64
64
  },
65
65
  "dependencies": {
66
- "@olane/o-protocol": "0.8.2",
66
+ "@olane/o-protocol": "0.8.4",
67
67
  "chalk": "^5.4.1",
68
68
  "debug": "^4.4.1",
69
69
  "dotenv": "^16.5.0",
70
70
  "multiformats": "^13.3.7",
71
71
  "stream-json": "^1.9.1"
72
72
  },
73
- "gitHead": "9e35c874d849d051bcffe483fd2a8c2b3ecf68cc"
73
+ "gitHead": "b53623b1ad4365133911722f80d5597a72b65bf2"
74
74
  }