@onlyworlds/sdk 2.0.1 → 2.1.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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 OnlyWorlds
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2025 OnlyWorlds
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # OnlyWorlds TypeScript SDK
2
2
 
3
- Type-safe SDK for building applications with the OnlyWorlds API. Access all 22 element types with full TypeScript support.
3
+ [![npm version](https://badge.fury.io/js/@onlyworlds%2Fsdk.svg)](https://www.npmjs.com/package/@onlyworlds/sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A type-safe TypeScript SDK for building world-building applications with the OnlyWorlds API.
4
7
 
5
8
  ## Installation
6
9
 
7
10
  ```bash
8
11
  npm install @onlyworlds/sdk
9
- # or
10
- yarn add @onlyworlds/sdk
11
12
  ```
12
13
 
13
14
  ## Quick Start
@@ -15,248 +16,239 @@ yarn add @onlyworlds/sdk
15
16
  ```typescript
16
17
  import { OnlyWorldsClient } from '@onlyworlds/sdk';
17
18
 
18
- // Initialize the client
19
19
  const client = new OnlyWorldsClient({
20
20
  apiKey: 'your-api-key',
21
- apiPin: 'your-api-pin'
21
+ apiPin: '1234',
22
+ baseUrl: 'https://onlyworlds.com'
22
23
  });
23
24
 
24
- // Create a character
25
- const character = await client.characters.create({
26
- name: 'Aragorn',
27
- description: 'Heir of Isildur',
28
- location_id: 'uuid-of-location',
29
- abilities_ids: ['uuid1', 'uuid2']
30
- });
25
+ // Get your world (API keys are world-scoped)
26
+ const world = await client.worlds.get();
31
27
 
32
- // List locations with filtering
33
- const locations = await client.locations.list({
34
- search: 'tavern',
35
- ordering: '-created_at',
36
- limit: 10
28
+ // Fetch characters (paginated)
29
+ const characters = await client.characters.list();
30
+
31
+ // Create a new location
32
+ const location = await client.locations.create({
33
+ name: 'Dragon Peak',
34
+ description: 'A treacherous mountain peak where dragons nest',
35
+ supertype: 'mountain'
37
36
  });
38
- ```
39
37
 
40
- ## Features
38
+ // Get a specific element
39
+ const character = await client.characters.get('element-id');
41
40
 
42
- - 🎯 **Full TypeScript Support** - Complete type definitions for all 22 element types
43
- - 🔒 **Type Safety** - Separate input/output types handle the `_id`/`_ids` pattern automatically
44
- - 📦 **All Element Types** - Characters, Locations, Objects, Species, Events, and more
45
- - 🔗 **Relationship Handling** - Properly typed single and multi-link relationships
46
- - 🚀 **Simple API** - Intuitive methods for CRUD operations
47
- - 📄 **Auto-completion** - IDE support for all fields and methods
41
+ // Update an element
42
+ await client.characters.update('element-id', {
43
+ name: 'Updated Name'
44
+ });
48
45
 
49
- ## Element Types
46
+ // Delete an element
47
+ await client.characters.delete('element-id');
48
+ ```
50
49
 
51
- The SDK supports all 22 OnlyWorlds element types:
50
+ ## Features
52
51
 
53
- - **Beings**: Character, Creature, Collective, Family, Species
54
- - **Places**: Location, Zone, Map, Pin, Marker
55
- - **Things**: Object, Construct, Phenomenon
56
- - **Concepts**: Ability, Event, Institution, Language, Law, Narrative, Relation, Title, Trait
52
+ - ✅ **Full Type Safety** - Complete TypeScript definitions for all 22 OnlyWorlds element types
53
+ - ✅ **Auto-Generated Types** - Types synchronized with the latest OnlyWorlds schema
54
+ - ✅ **CRUD Operations** - Create, read, update, and delete operations for all elements
55
+ - ✅ **Token Management** - Built-in support for OnlyWorlds token rating system
56
+ - ✅ **Branded Types** - Compile-time safety for element relationships
57
+ - ✅ **Zero Runtime Overhead** - Type system has no runtime cost
58
+ - ✅ **Modern ESM/CJS** - Supports both ES modules and CommonJS
57
59
 
58
60
  ## API Reference
59
61
 
60
- ### Client Initialization
62
+ ### World Endpoint
63
+
64
+ The `worlds` resource is special because API keys are world-scoped (one key = one world). The endpoint returns a single `World` object directly, not a paginated list.
61
65
 
62
66
  ```typescript
63
- import { OnlyWorldsClient } from '@onlyworlds/sdk';
67
+ // Get your world
68
+ const world = await client.worlds.get();
64
69
 
65
- const client = new OnlyWorldsClient({
66
- apiKey: 'your-api-key', // Required
67
- apiPin: 'your-api-pin', // Required
68
- baseUrl: 'custom-url' // Optional, defaults to onlyworlds.com
70
+ // Update your world
71
+ const updated = await client.worlds.update({
72
+ description: 'A dark fantasy realm'
69
73
  });
70
74
  ```
71
75
 
72
- ### CRUD Operations
76
+ **Note**: The `worlds` resource only has `get()` and `update()` methods (no `list()`, `create()`, or `delete()`) because API keys are world-scoped.
77
+
78
+ ### Element Endpoints
73
79
 
74
- All element types support the same operations:
80
+ All other element types return paginated results:
75
81
 
76
82
  ```typescript
77
- // List with filtering
78
- const items = await client.characters.list({
79
- search: 'hero',
80
- ordering: 'name',
81
- limit: 20,
82
- offset: 0
83
+ // List with pagination
84
+ const response = await client.characters.list({
85
+ limit: 10,
86
+ offset: 0,
87
+ ordering: '-created_at',
88
+ search: 'dragon'
83
89
  });
84
90
 
85
- // Get single item
86
- const character = await client.characters.get('uuid');
91
+ console.log(response.count); // Total count
92
+ console.log(response.results); // Array of Characters
93
+ console.log(response.next); // URL for next page
94
+ console.log(response.previous); // URL for previous page
95
+ ```
87
96
 
88
- // Create new item
89
- const newChar = await client.characters.create({
90
- name: 'Gandalf',
91
- description: 'A wizard'
92
- });
97
+ ## Type-Safe Relationships
93
98
 
94
- // Update existing item
95
- const updated = await client.characters.update('uuid', {
96
- description: 'A wizard, also known as Mithrandir'
97
- });
99
+ ```typescript
100
+ import { ElementId, Character, Location } from '@onlyworlds/sdk';
98
101
 
99
- // Delete item
100
- await client.characters.delete('uuid');
102
+ // Branded types ensure you can't mix up element IDs
103
+ const locationId: ElementId<'Location'> = 'some-location-id';
104
+ const character: Character = {
105
+ name: 'Aragorn',
106
+ location: locationId // Type-safe!
107
+ };
101
108
  ```
102
109
 
103
- ### Working with Relationships
104
-
105
- The SDK handles OnlyWorlds' `_id`/`_ids` pattern automatically:
110
+ ## Token Management
106
111
 
107
- ```typescript
108
- // Creating with relationships
109
- const character = await client.characters.create({
110
- name: 'Frodo',
111
- birthplace_id: 'location-uuid', // Single relationship
112
- abilities_ids: ['uuid1', 'uuid2'], // Multi relationship
113
- friends_ids: ['sam-uuid', 'merry-uuid']
114
- });
112
+ OnlyWorlds provides a token rating system for tracking API usage and enabling AI-powered features. Users get a daily token allowance (default: 10,000 tokens) that resets every day.
115
113
 
116
- // The response includes nested objects
117
- console.log(character.birthplace.name); // "The Shire"
118
- console.log(character.abilities[0].name); // "Ring Bearer"
119
- ```
114
+ ### Working Reference Implementation
120
115
 
121
- ### Type Definitions
116
+ See [base-tool/src/llm/token-service.ts](https://github.com/OnlyWorlds/base-tool) for the complete working implementation that this SDK enables.
122
117
 
123
- All types are exported for your use:
118
+ ### Check Token Status
124
119
 
125
120
  ```typescript
126
- import {
127
- Character,
128
- CharacterInput,
129
- Location,
130
- ElementType
131
- } from '@onlyworlds/sdk';
132
-
133
- // Use in your functions
134
- function createHero(data: CharacterInput): Promise<Character> {
135
- return client.characters.create(data);
136
- }
121
+ // Get current token status
122
+ const status = await client.tokens.getStatus();
137
123
 
138
- // Element type enum
139
- console.log(ElementType.Character); // 'character'
124
+ console.log(`Available: ${status.tokens_available_today}/${status.token_rating}`);
125
+ console.log(`Used today: ${status.tokens_used_today}`);
126
+ console.log(`Active sessions: ${status.sessions_active}`);
127
+ console.log(`Last reset: ${status.last_reset}`);
140
128
  ```
141
129
 
142
- ### Advanced Examples
130
+ ### Consume Tokens
143
131
 
144
- #### Batch Operations
132
+ Report token consumption when your tool uses AI features or other token-tracked services:
145
133
 
146
134
  ```typescript
147
- // Create multiple related elements
148
- async function createParty(partyData: any[]) {
149
- const location = await client.locations.create({
150
- name: 'Tavern',
151
- description: 'Where the party meets'
152
- });
153
-
154
- const characters = await Promise.all(
155
- partyData.map(data =>
156
- client.characters.create({
157
- ...data,
158
- location_id: location.id
159
- })
160
- )
161
- );
135
+ // Report token usage
136
+ const result = await client.tokens.consume({
137
+ amount: 500,
138
+ service: 'my_worldbuilding_tool',
139
+ metadata: {
140
+ feature: 'character_generation',
141
+ model: 'gpt-4',
142
+ prompt_tokens: 300,
143
+ completion_tokens: 200
144
+ }
145
+ });
162
146
 
163
- return { location, characters };
147
+ // Check if consumption succeeded
148
+ if (result.error) {
149
+ console.warn('Token warning:', result.error);
164
150
  }
165
- ```
166
151
 
167
- #### Search and Filter
152
+ console.log(`Consumed ${result.tokens_consumed} tokens`);
153
+ console.log(`${result.tokens_remaining} tokens remaining`);
154
+ ```
168
155
 
169
- ```typescript
170
- // Find all wizards in a specific location
171
- const wizards = await client.characters.list({
172
- search: 'wizard',
173
- location: 'location-uuid',
174
- ordering: '-level'
175
- });
156
+ **Important**: The API allows consumption even when exceeding available tokens (tracks as debt), but returns a warning in the `error` field.
176
157
 
177
- // Get recently updated items
178
- const recent = await client.events.list({
179
- ordering: '-updated_at',
180
- limit: 5
181
- });
182
- ```
158
+ ### Advanced: Encrypted API Key Access
183
159
 
184
- #### Error Handling
160
+ For tools that need direct OpenAI API access using OnlyWorlds tokens:
185
161
 
186
162
  ```typescript
187
- try {
188
- const character = await client.characters.get('invalid-uuid');
189
- } catch (error) {
190
- if (error.message.includes('404')) {
191
- console.log('Character not found');
192
- }
193
- }
194
- ```
163
+ // Get encrypted OpenAI key (requires 100+ tokens)
164
+ const access = await client.tokens.getAccessKey();
195
165
 
196
- ## Input vs Output Types
166
+ console.log('Session ID:', access.session_id);
167
+ console.log('Expires:', access.expires_at);
168
+ console.log('Available tokens:', access.tokens_available);
197
169
 
198
- OnlyWorlds uses different formats for requests and responses:
170
+ // Decrypt the key client-side (see base-tool for full implementation)
171
+ // 1. Derive decryption key from world ID using SHA-256
172
+ // 2. Use 'fernet' npm package to decrypt
173
+ // 3. Use decrypted OpenAI key for direct API calls
174
+ // 4. Report usage with session_id
199
175
 
200
- - **Input** (requests): Use `_id` for single relationships, `_ids` for multiple
201
- - **Output** (responses): Nested objects with full data
176
+ // See base-tool/src/llm/token-service.ts:99-235 for complete example
177
+ ```
202
178
 
203
- The SDK provides separate types for each:
179
+ **Full decryption implementation** (based on base-tool):
204
180
 
205
181
  ```typescript
206
- // CharacterInput for creating/updating
207
- interface CharacterInput {
208
- name: string;
209
- location_id?: string; // UUID string
210
- abilities_ids?: string[]; // Array of UUIDs
182
+ import { fernet } from 'fernet';
183
+
184
+ // Derive decryption key from world ID
185
+ async function deriveKey(worldId: string): Promise<string> {
186
+ const salt = 'onlyworlds-token-api-2024-public-salt';
187
+ const keyMaterial = `${worldId}:${salt}`;
188
+ const encoder = new TextEncoder();
189
+ const data = encoder.encode(keyMaterial);
190
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
191
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
192
+ const base64 = btoa(String.fromCharCode(...hashArray));
193
+ return base64.replace(/\+/g, '-').replace(/\//g, '_');
211
194
  }
212
195
 
213
- // Character for responses
214
- interface Character {
215
- name: string;
216
- location?: Location; // Full nested object
217
- abilities?: Ability[]; // Array of full objects
196
+ // Decrypt the API key
197
+ async function decryptApiKey(encryptedKey: string, worldId: string): Promise<string> {
198
+ const derivedKey = await deriveKey(worldId);
199
+ const secret = new fernet.Secret(derivedKey);
200
+ const token = new fernet.Token({
201
+ secret: secret,
202
+ token: encryptedKey,
203
+ ttl: 0 // Don't enforce TTL client-side
204
+ });
205
+ return token.decode();
218
206
  }
219
- ```
220
-
221
- ## Helper Methods
222
207
 
223
- ```typescript
224
- // Convert nested objects to _id/_ids format
225
- const input = OnlyWorldsClient.prepareInput({
226
- name: 'Test',
227
- location: { id: 'uuid', name: 'Place' },
228
- abilities: [{ id: 'uuid1' }, { id: 'uuid2' }]
208
+ // Usage
209
+ const world = await client.worlds.get();
210
+ const access = await client.tokens.getAccessKey();
211
+ const apiKey = await decryptApiKey(access.encrypted_key, world.id);
212
+
213
+ // Use apiKey for OpenAI API calls, then report usage:
214
+ await client.tokens.consume({
215
+ amount: tokensUsed,
216
+ sessionId: access.session_id,
217
+ service: 'direct_openai',
218
+ metadata: { model: 'gpt-4', /* ... */ }
229
219
  });
230
- // Result: { name: 'Test', location_id: 'uuid', abilities_ids: ['uuid1', 'uuid2'] }
231
220
  ```
232
221
 
233
- ## Requirements
222
+ ### Session Management
234
223
 
235
- - Node.js 18+ or modern browser with fetch API
236
- - TypeScript 4.5+ (for TypeScript projects)
224
+ ```typescript
225
+ // Revoke a specific session
226
+ await client.tokens.revokeSession(sessionId);
237
227
 
238
- ## Browser Usage
228
+ // Revoke all sessions (emergency cleanup)
229
+ const result = await client.tokens.revokeAllSessions();
230
+ console.log(`Revoked ${result.sessions_revoked} sessions`);
231
+ ```
239
232
 
240
- This SDK works in browsers, but you'll need to handle CORS:
233
+ ### Encryption Info
241
234
 
242
- ```javascript
243
- // For development, use a proxy or CORS-enabled server
244
- // Never expose API credentials in client-side code!
235
+ Get public encryption details (no auth required):
245
236
 
246
- // Better approach: Use a backend proxy
247
- const response = await fetch('/api/proxy/onlyworlds', {
248
- method: 'POST',
249
- body: JSON.stringify({ /* your request */ })
250
- });
237
+ ```typescript
238
+ const info = await client.tokens.getEncryptionInfo();
239
+ console.log('Algorithm:', info.algorithm);
240
+ console.log('Key derivation:', info.key_derivation);
241
+ console.log('Public salt:', info.salt);
242
+ console.log(info.javascript_example);
251
243
  ```
252
244
 
253
245
  ## License
254
246
 
255
- MIT
247
+ MIT License - see [LICENSE](LICENSE) file for details
256
248
 
257
249
  ## Links
258
250
 
259
- - [OnlyWorlds Documentation](https://onlyworlds.github.io/)
260
- - [API Reference](https://www.onlyworlds.com/api/docs)
251
+ - [OnlyWorlds Website](https://onlyworlds.com)
252
+ - [Documentation](https://onlyworlds.github.io/)
261
253
  - [NPM Package](https://www.npmjs.com/package/@onlyworlds/sdk)
262
- - [GitHub](https://github.com/onlyworlds/sdk)
254
+ - [Report Issues](https://github.com/OnlyWorlds/sdk/issues)