@onlyworlds/sdk 3.1.0 → 4.0.0-alpha.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/AGENTS.md +4 -0
- package/CHANGELOG.md +86 -44
- package/README.md +83 -271
- package/SCHEMA.md +630 -0
- package/dist/index.d.ts +463 -1469
- package/dist/index.js +691 -994
- package/package.json +14 -6
- package/dist/index.d.mts +0 -3517
- package/dist/index.mjs +0 -1576
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# For AI agents using @onlyworlds/sdk
|
|
2
2
|
|
|
3
|
+
**Start with [SCHEMA.md](SCHEMA.md)** (in this package): the full generated schema reference —
|
|
4
|
+
every type, every field with its meaning, link directions, families, icons, display sections.
|
|
5
|
+
It is generated from the same canonical YAML as the types, so it cannot drift.
|
|
6
|
+
|
|
3
7
|
**What this package is**: the canonical typed TypeScript client for the OnlyWorlds v2 API,
|
|
4
8
|
plus the canonical constants (element types, icons, colour families, field schema).
|
|
5
9
|
OnlyWorlds is an open standard for portable world data — 22 element types, UUID-linked.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,44 +1,86 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward (Kael, Assembly);
|
|
4
|
-
earlier history lives in git log only.
|
|
5
|
-
|
|
6
|
-
## [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward (Kael, Assembly);
|
|
4
|
+
earlier history lives in git log only.
|
|
5
|
+
|
|
6
|
+
## [Unreleased — 4.0.0] (branch `v4`)
|
|
7
|
+
|
|
8
|
+
**v2-native only.** See `docs/migrating-3-to-4.md`. 3.x stays published forever for
|
|
9
|
+
pinned consumers.
|
|
10
|
+
|
|
11
|
+
### Removed (BREAKING)
|
|
12
|
+
- The v1 client (`OnlyWorldsClient`), the v1 `ElementType` enum, `icon-utils`.
|
|
13
|
+
`ElementType` at the root is now THE v2 slug union (was `V2ElementType` alias in 3.x).
|
|
14
|
+
- CJS build — the package is ESM-only with a sealed `exports` map (`"type": "module"`,
|
|
15
|
+
`sideEffects: false`).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Metadata tables (`ELEMENT_ICONS`, `ELEMENT_LABELS`, `ELEMENT_SECTIONS`, `FIELD_SCHEMA`,
|
|
19
|
+
`getElementIcon`, `getElementLabel`) ported onto the v2 slug union — same export names,
|
|
20
|
+
same shapes, contents byte-identical EXCEPT: `FIELD_SCHEMA` `type: 'number'` →
|
|
21
|
+
`'integer'` (70 entries; 'number' dropped from `FieldType`). Migration: consumers
|
|
22
|
+
switching on `'number'` switch on `'integer'`.
|
|
23
|
+
- `TokenResource` now takes a structural `TokenTransport` (`{ request<T>(method, path,
|
|
24
|
+
body?) }`) instead of the removed v1 client. Token surface retained pending the
|
|
25
|
+
wire-fate ruling (RFC-001 §5) — may be removed in a later 4.x.
|
|
26
|
+
|
|
27
|
+
### Added (post-alpha.0, same night)
|
|
28
|
+
- **`ELEMENT_ICONS` GENERATED** from keel's `icon:` wrapper key (keel `56c124a`).
|
|
29
|
+
- **`ELEMENT_SECTIONS` DERIVED** from the canonical schema's own document structure
|
|
30
|
+
(Skeld's ruling: sections are the standard tier's property groups; document order is
|
|
31
|
+
display order). Divergence check vs the old hand table found and fixed three fossils:
|
|
32
|
+
creature "Behaviour"→"Behavior", pin's triple-listed generic link → `element`,
|
|
33
|
+
relation "Involves" listing a nonexistent `relations` field. Canonical is truth.
|
|
34
|
+
- **`SCHEMA.md`** — full generated schema reference (every field with its canonical
|
|
35
|
+
description, link directions, families, icons, sections), ships in the tarball;
|
|
36
|
+
covered by the codegen drift guard. AGENTS.md points agents at it first.
|
|
37
|
+
- **`OwV2Client.request()` is public** (typed escape hatch; does not sanitize) and
|
|
38
|
+
structurally satisfies `TokenTransport` — `new TokenResource(client)` just works.
|
|
39
|
+
Token ruling (Skeld): keel keeps `/tokens/*` long-term; ported, not deleted.
|
|
40
|
+
- README rewritten v2-native (v1 sections, branded types, and the encrypted-key
|
|
41
|
+
walkthrough removed; bulk partial-failure and idempotency-key hygiene promoted).
|
|
42
|
+
|
|
43
|
+
### Planned before release (RFC-001 gates)
|
|
44
|
+
- Skeld's live wire-log v1-traffic query (gate 3). Alpha publishes to the `alpha`
|
|
45
|
+
dist-tag only — never `latest`. **No `npm deprecate` on 3.x** (amended per landscape
|
|
46
|
+
research — 3.x stays plainly supported; README framing carries the message).
|
|
47
|
+
|
|
48
|
+
## [3.1.0] — 2026-07-23
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- **Canonical element colour palette** (`src/v2/palette.ts`, exported from root):
|
|
52
|
+
`ELEMENT_FAMILIES` (type → family), `FAMILY_COLORS` (family → `{light, dark}` hex),
|
|
53
|
+
`familyOf(type)`, `elementColor(type, mode)`, `FAMILY_ORDER`, type `ElementFamily`.
|
|
54
|
+
Four semantic families (agents / world / abstract / temporal) ruled 2026-07-22 after
|
|
55
|
+
CVD-validated measurement (Orrery `product/schema/element-palette-measurements.md`).
|
|
56
|
+
Colour carries the FAMILY; `ELEMENT_ICONS` carries the TYPE. Keyed on the v2 slug
|
|
57
|
+
union. First proven live in atlas and council, whose local copies become re-exports.
|
|
58
|
+
**`ELEMENT_FAMILIES` is GENERATED** from the `family:` key in keel's schema YAML
|
|
59
|
+
(keel `c69366b`, a keel presentation-wrapper key — not part of the council-governed
|
|
60
|
+
OnlyWorlds standard); hexes are hand-authored design constants beside it. Membership
|
|
61
|
+
and hex invariants test-gated (`test/palette.test.mjs`); light hexes render-proven on
|
|
62
|
+
the ruled OnlyWorlds light mode (ow-house-light) 2026-07-23.
|
|
63
|
+
- **Codegen drift guard**: `python codegen/generate_types.py --check` fails if
|
|
64
|
+
`types.generated.ts` doesn't match the schema YAMLs (release gate; mirrors keel's
|
|
65
|
+
schema CI job). Codegen also hard-fails on missing/invalid `family:` keys — the
|
|
66
|
+
canary for a canonical-refresh that overwrote keel's wrapper layer.
|
|
67
|
+
- **CI**: GitHub Action building + running the full suite against the compiled bundle.
|
|
68
|
+
- **AGENTS.md** shipped in the tarball — usage guide for AI agents reading the package
|
|
69
|
+
locally, including the SDK-vs-MCP division of labor and the wire gotchas.
|
|
70
|
+
- README: worked create→link→read-back round-trip, schema-verified field by field
|
|
71
|
+
(also fixes the old example patching `'character'` with a location's id); canonical
|
|
72
|
+
colour section; SDK-vs-MCP guidance.
|
|
73
|
+
- This CHANGELOG.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
- `FieldType` union now includes `'number'` — the legacy alias that 70 `FIELD_SCHEMA`
|
|
77
|
+
entries actually carry (consumers switch on it; data normalization deferred to 4.0).
|
|
78
|
+
- `generate_types.py` exits with a clear message when the sibling keel checkout is
|
|
79
|
+
absent instead of a bare traceback.
|
|
80
|
+
|
|
81
|
+
## [3.0.0] — 2026-07-18
|
|
82
|
+
|
|
83
|
+
- v2-native client (`OwV2Client`) absorbed from Assembly's ow-v2-client v0.9.0 (Kael),
|
|
84
|
+
wire-corrected against live staging fixtures (S22). Generated element types from the
|
|
85
|
+
canonical keel schema YAML (`src/v2/types.generated.ts`), validated against all 1,086
|
|
86
|
+
live W11 elements. v1 surface unchanged and frozen.
|
package/README.md
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@onlyworlds/sdk)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
The canonical typed client for the [OnlyWorlds](https://onlyworlds.github.io) v2 API, plus the
|
|
7
|
+
canonical constants (element types, icons, colour families, field schema) — generated from the
|
|
8
|
+
same schema source the server runs on.
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **v1 (`OnlyWorldsClient`)** -- the original resource-style client. Fully served forever; kept below as the legacy section. Existing 2.x code keeps working unchanged -- v2 is purely additive.
|
|
10
|
+
**4.x is v2-native and ESM-only (Node 18+).** If you need the legacy v1 API dialect
|
|
11
|
+
(`OnlyWorldsClient`) or CommonJS `require()`, stay on 3.x — it remains published and the v1 API
|
|
12
|
+
remains served. See [docs/migrating-3-to-4.md](docs/migrating-3-to-4.md).
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
@@ -16,317 +17,128 @@ The SDK speaks two dialects of the same API:
|
|
|
16
17
|
npm install @onlyworlds/sdk
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
## Quick Start
|
|
20
|
+
## Quick Start
|
|
20
21
|
|
|
21
22
|
```typescript
|
|
22
23
|
import { OwV2Client } from '@onlyworlds/sdk';
|
|
23
24
|
|
|
24
|
-
const client = new OwV2Client({
|
|
25
|
-
|
|
26
|
-
apiPin: '1234', // needed for writes on a PIN-protected world
|
|
27
|
-
});
|
|
28
|
-
// baseUrl defaults to https://www.onlyworlds.com/api/v2
|
|
29
|
-
|
|
30
|
-
// Read a page, or walk every page of a type
|
|
31
|
-
const page = await client.list('character'); // { data, has_more, next_cursor }
|
|
32
|
-
for await (const character of client.listAll('character')) {
|
|
33
|
-
// ...
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// A worked round-trip: two elements, linked, read back. Byte-true v2 dialect:
|
|
37
|
-
// link fields use ONE bare name both directions (no _ids suffix — that's v1),
|
|
38
|
-
// and you NEVER send a "world" field (identity comes from the key; sending it 422s).
|
|
39
|
-
const location = await client.create('location', {
|
|
40
|
-
name: 'Dragon Peak',
|
|
41
|
-
description: 'A treacherous mountain peak where dragons nest',
|
|
42
|
-
}); // id minted client-side when omitted (retries stay idempotent)
|
|
43
|
-
|
|
44
|
-
const dragon = await client.create('creature', {
|
|
45
|
-
name: 'Vorrath the Ember-Scaled',
|
|
46
|
-
location: location.id, // single link: UUID (or null)
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const fetched = await client.get('creature', dragon.id);
|
|
50
|
-
// fetched.location === location.id — reads the way it writes
|
|
51
|
-
|
|
52
|
-
// Partial update (PATCH is destructive on sent fields; arrays replace wholesale)
|
|
53
|
-
await client.patch('location', location.id, { supertype: 'Mountain' });
|
|
54
|
-
|
|
55
|
-
// For relationships, prefer the atomic link merge -- returns the full updated element
|
|
56
|
-
const fireBreath = await client.create('ability', { name: 'Ember Breath' });
|
|
57
|
-
await client.editLinks('creature', dragon.id, 'abilities', { add: [fireBreath.id], remove: [] });
|
|
58
|
-
|
|
59
|
-
// Bulk write (up to ~1000; partial success by default, atomic:true for all-or-nothing)
|
|
60
|
-
const res = await client.bulk([
|
|
61
|
-
{ type: 'character', element: { name: 'A' } },
|
|
62
|
-
{ type: 'event', element: { name: 'B' } },
|
|
63
|
-
]);
|
|
64
|
-
// res.items[i].status is the per-slot HTTP status; res.errors flags any failure
|
|
65
|
-
|
|
66
|
-
// Sync: walk the opaque change feed and persist the final cursor
|
|
67
|
-
let cursor;
|
|
68
|
-
for await (const change of client.changesAll(cursor)) {
|
|
69
|
-
// apply change in order
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Canonical element colours
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
import { elementColor, ELEMENT_FAMILIES, FAMILY_COLORS } from '@onlyworlds/sdk';
|
|
77
|
-
|
|
78
|
-
elementColor('character', 'dark'); // '#3987e5' — colour carries the FAMILY
|
|
79
|
-
// (agents / world / abstract / temporal); ELEMENT_ICONS carries the TYPE.
|
|
80
|
-
// CVD-validated: always pair colour with icon + label, never colour alone.
|
|
25
|
+
const client = new OwV2Client({ apiKey: 'ow_r_your_key' }); // read-only key: that's all you need
|
|
26
|
+
const page = await client.list('character'); // { data, has_more, next_cursor }
|
|
81
27
|
```
|
|
82
28
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Division of labor: for **known, deterministic operations** (CRUD, sync, bulk) use this SDK — typed calls, no tool-schema overhead. For **live exploration of a user's world from a chat/agent context**, use the MCP server. Agents: see `AGENTS.md` in this package.
|
|
29
|
+
Key kinds: `ow_w_` write / `ow_r_` read-only (no PIN — the "give this to your players" key) /
|
|
30
|
+
`ow_a_` account Bearer / 10-digit legacy. Writes on a PIN-protected world also pass `apiPin`.
|
|
31
|
+
Demo keys `0000000000`–`0000000009` read real sample worlds.
|
|
88
32
|
|
|
89
|
-
##
|
|
90
|
-
|
|
91
|
-
The v1 resource-style client remains fully supported and served forever. Prefer `OwV2Client` for new code.
|
|
33
|
+
## The full round-trip
|
|
92
34
|
|
|
93
35
|
```typescript
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const client = new OnlyWorldsClient({
|
|
97
|
-
apiKey: 'your-api-key',
|
|
98
|
-
apiPin: '1234'
|
|
99
|
-
});
|
|
100
|
-
// baseUrl defaults to https://www.onlyworlds.com/api/worldapi — only override it
|
|
101
|
-
// when pointing at a different host (it must include the full API path)
|
|
102
|
-
|
|
103
|
-
// Get your world (API keys are world-scoped)
|
|
104
|
-
const world = await client.worlds.get();
|
|
105
|
-
|
|
106
|
-
// Fetch characters (paginated)
|
|
107
|
-
const characters = await client.characters.list();
|
|
36
|
+
const writer = new OwV2Client({ apiKey: 'ow_w_your_key', apiPin: '1234' });
|
|
108
37
|
|
|
109
|
-
//
|
|
110
|
-
|
|
38
|
+
// v2 dialect rules, worth internalizing once:
|
|
39
|
+
// - link fields use ONE bare name both directions (no _ids suffix — that's v1)
|
|
40
|
+
// - NEVER send a "world" field (identity comes from the key; the client strips it anyway)
|
|
41
|
+
const location = await writer.create('location', {
|
|
111
42
|
name: 'Dragon Peak',
|
|
112
43
|
description: 'A treacherous mountain peak where dragons nest',
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Get a specific element
|
|
117
|
-
const character = await client.characters.get('element-id');
|
|
118
|
-
|
|
119
|
-
// Update an element
|
|
120
|
-
await client.characters.update('element-id', {
|
|
121
|
-
name: 'Updated Name'
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Delete an element
|
|
125
|
-
await client.characters.delete('element-id');
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Features
|
|
129
|
-
|
|
130
|
-
- ✅ **Full Type Safety** - Complete TypeScript definitions for all 22 OnlyWorlds element types
|
|
131
|
-
- ✅ **Schema-Aligned Types** - Type definitions track the OnlyWorlds schema
|
|
132
|
-
- ✅ **CRUD Operations** - Create, read, update, and delete operations for all elements
|
|
133
|
-
- ✅ **Token Management** - Built-in support for OnlyWorlds token rating system
|
|
134
|
-
- ✅ **Branded Types** - Compile-time safety for element relationships
|
|
135
|
-
- ✅ **Zero Runtime Overhead** - Type system has no runtime cost
|
|
136
|
-
- ✅ **Modern ESM/CJS** - Supports both ES modules and CommonJS
|
|
137
|
-
|
|
138
|
-
## API Reference
|
|
139
|
-
|
|
140
|
-
### World Endpoint
|
|
141
|
-
|
|
142
|
-
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.
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
// Get your world
|
|
146
|
-
const world = await client.worlds.get();
|
|
44
|
+
}); // id minted client-side when omitted (retries stay idempotent)
|
|
147
45
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
46
|
+
const dragon = await writer.create('creature', {
|
|
47
|
+
name: 'Vorrath the Ember-Scaled',
|
|
48
|
+
location: location.id, // single link: UUID (or null)
|
|
151
49
|
});
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
**Note**: The `worlds` resource only has `get()` and `update()` methods (no `list()`, `create()`, or `delete()`) because API keys are world-scoped.
|
|
155
|
-
|
|
156
|
-
### Element Endpoints
|
|
157
50
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
// List with pagination
|
|
162
|
-
const response = await client.characters.list({
|
|
163
|
-
limit: 10,
|
|
164
|
-
offset: 0,
|
|
165
|
-
ordering: '-created_at',
|
|
166
|
-
search: 'dragon'
|
|
167
|
-
});
|
|
51
|
+
const fetched = await writer.get('creature', dragon.id);
|
|
52
|
+
// fetched.location === location.id — reads the way it writes
|
|
168
53
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
console.log(response.next); // URL for next page
|
|
172
|
-
console.log(response.previous); // URL for previous page
|
|
173
|
-
```
|
|
54
|
+
// PATCH is destructive on sent fields; arrays replace wholesale
|
|
55
|
+
await writer.patch('location', location.id, { supertype: 'Mountain' });
|
|
174
56
|
|
|
175
|
-
|
|
57
|
+
// For relationships, use the atomic link merge — returns the full updated element
|
|
58
|
+
const fireBreath = await writer.create('ability', { name: 'Ember Breath' });
|
|
59
|
+
await writer.editLinks('creature', dragon.id, 'abilities', { add: [fireBreath.id], remove: [] });
|
|
176
60
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
// Branded types ensure you can't mix up element IDs
|
|
181
|
-
const locationId: ElementId<'Location'> = 'some-location-id';
|
|
182
|
-
const character: Character = {
|
|
183
|
-
name: 'Aragorn',
|
|
184
|
-
location: locationId // Type-safe!
|
|
185
|
-
};
|
|
61
|
+
// Walk every page of a type
|
|
62
|
+
for await (const character of writer.listAll('character')) { /* ... */ }
|
|
186
63
|
```
|
|
187
64
|
|
|
188
|
-
##
|
|
189
|
-
|
|
190
|
-
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.
|
|
65
|
+
## Bulk writes — always check `errors`
|
|
191
66
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
See [base-tool/src/llm/token-service.ts](https://github.com/OnlyWorlds/base-tool) for the complete working implementation that this SDK enables.
|
|
195
|
-
|
|
196
|
-
### Check Token Status
|
|
67
|
+
`/bulk` **succeeds partially by default** (HTTP 200 with per-slot statuses). The one mistake to
|
|
68
|
+
never make: treating a 200 as "everything landed."
|
|
197
69
|
|
|
198
70
|
```typescript
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
71
|
+
const res = await writer.bulk(
|
|
72
|
+
[
|
|
73
|
+
{ type: 'character', element: { name: 'A' } },
|
|
74
|
+
{ type: 'event', element: { name: 'B' } },
|
|
75
|
+
],
|
|
76
|
+
{ idempotencyKey: crypto.randomUUID() }, // mint FRESH per attempt — a failed batch is
|
|
77
|
+
); // cached under its key; never reuse across retries
|
|
78
|
+
if (res.errors) {
|
|
79
|
+
for (const slot of res.items.filter((s) => s.status >= 400)) {
|
|
80
|
+
console.warn(slot.error?.code, slot.error?.message, slot.error?.doc_url);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Pass { atomic: true } for all-or-nothing instead. res.wasReplay flags idempotent replays.
|
|
206
84
|
```
|
|
207
85
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
Report token consumption when your tool uses AI features or other token-tracked services:
|
|
86
|
+
## Sync
|
|
211
87
|
|
|
212
88
|
```typescript
|
|
213
|
-
//
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
service: 'my_worldbuilding_tool',
|
|
217
|
-
metadata: {
|
|
218
|
-
feature: 'character_generation',
|
|
219
|
-
model: 'gpt-4',
|
|
220
|
-
prompt_tokens: 300,
|
|
221
|
-
completion_tokens: 200
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
// Check if consumption succeeded
|
|
226
|
-
if (result.error) {
|
|
227
|
-
console.warn('Token warning:', result.error);
|
|
89
|
+
let cursor; // opaque, never expires; persist it
|
|
90
|
+
for await (const change of client.changesAll(cursor)) {
|
|
91
|
+
// ops arrive in (change_seq, id) order — apply in order → convergence
|
|
228
92
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
console.log(`${result.tokens_remaining} tokens remaining`);
|
|
93
|
+
// GOTCHA: world-meta edits (name, calendar, public_read) do NOT enter /changes.
|
|
94
|
+
// Poll client.getWorld() and compare updated_at separately.
|
|
232
95
|
```
|
|
233
96
|
|
|
234
|
-
|
|
97
|
+
## Errors
|
|
235
98
|
|
|
236
|
-
|
|
99
|
+
Every non-2xx throws `OwApiError` carrying the platform envelope: `.status`, `.type`, `.code`,
|
|
100
|
+
`.param` (the exact field that failed), and `.docUrl` — surface `docUrl` in your UX. Transport
|
|
101
|
+
failures throw `OwNetworkError`. `err.isValidationError` is the common branch.
|
|
237
102
|
|
|
238
|
-
|
|
103
|
+
## Canonical constants (all generated or test-gated from schema)
|
|
239
104
|
|
|
240
105
|
```typescript
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
// 4. Report usage with session_id
|
|
253
|
-
|
|
254
|
-
// See base-tool/src/llm/token-service.ts:99-235 for complete example
|
|
106
|
+
import {
|
|
107
|
+
ELEMENT_TYPES, // the 22 slugs (and the ElementType union)
|
|
108
|
+
ELEMENT_ICONS, // Material Symbols icon per type
|
|
109
|
+
ELEMENT_LABELS, // plural display labels
|
|
110
|
+
ELEMENT_SECTIONS, // canonical field grouping + display order
|
|
111
|
+
FIELD_SCHEMA, // per-field type/target metadata
|
|
112
|
+
elementColor, // canonical colour: family carries COLOUR, icon carries TYPE
|
|
113
|
+
} from '@onlyworlds/sdk';
|
|
114
|
+
|
|
115
|
+
elementColor('character', 'dark'); // '#3987e5' — four CVD-validated families
|
|
116
|
+
// Always pair colour with icon + label; colour alone is not accessible.
|
|
255
117
|
```
|
|
256
118
|
|
|
257
|
-
|
|
119
|
+
The full schema with per-field meaning lives in [SCHEMA.md](SCHEMA.md) (generated, ships in this
|
|
120
|
+
package). AI agents: read [AGENTS.md](AGENTS.md) first.
|
|
258
121
|
|
|
259
|
-
|
|
260
|
-
import { fernet } from 'fernet';
|
|
261
|
-
|
|
262
|
-
// Derive decryption key from world ID
|
|
263
|
-
async function deriveKey(worldId: string): Promise<string> {
|
|
264
|
-
const salt = 'onlyworlds-token-api-2024-public-salt';
|
|
265
|
-
const keyMaterial = `${worldId}:${salt}`;
|
|
266
|
-
const encoder = new TextEncoder();
|
|
267
|
-
const data = encoder.encode(keyMaterial);
|
|
268
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
269
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
270
|
-
const base64 = btoa(String.fromCharCode(...hashArray));
|
|
271
|
-
return base64.replace(/\+/g, '-').replace(/\//g, '_');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Decrypt the API key
|
|
275
|
-
async function decryptApiKey(encryptedKey: string, worldId: string): Promise<string> {
|
|
276
|
-
const derivedKey = await deriveKey(worldId);
|
|
277
|
-
const secret = new fernet.Secret(derivedKey);
|
|
278
|
-
const token = new fernet.Token({
|
|
279
|
-
secret: secret,
|
|
280
|
-
token: encryptedKey,
|
|
281
|
-
ttl: 0 // Don't enforce TTL client-side
|
|
282
|
-
});
|
|
283
|
-
return token.decode();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Usage
|
|
287
|
-
const world = await client.worlds.get();
|
|
288
|
-
const access = await client.tokens.getAccessKey();
|
|
289
|
-
const apiKey = await decryptApiKey(access.encrypted_key, world.id);
|
|
290
|
-
|
|
291
|
-
// Use apiKey for OpenAI API calls, then report usage:
|
|
292
|
-
await client.tokens.consume({
|
|
293
|
-
amount: tokensUsed,
|
|
294
|
-
sessionId: access.session_id,
|
|
295
|
-
service: 'direct_openai',
|
|
296
|
-
metadata: { model: 'gpt-4', /* ... */ }
|
|
297
|
-
});
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### Session Management
|
|
122
|
+
## Token rating system
|
|
301
123
|
|
|
302
124
|
```typescript
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// Revoke all sessions (emergency cleanup)
|
|
307
|
-
const result = await client.tokens.revokeAllSessions();
|
|
308
|
-
console.log(`Revoked ${result.sessions_revoked} sessions`);
|
|
125
|
+
import { TokenResource } from '@onlyworlds/sdk';
|
|
126
|
+
const tokens = new TokenResource(writer); // OwV2Client.request() satisfies TokenTransport
|
|
127
|
+
const status = await tokens.getStatus();
|
|
309
128
|
```
|
|
310
129
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
Get public encryption details (no auth required):
|
|
130
|
+
## AI access — when to use which
|
|
314
131
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
console.log('Public salt:', info.salt);
|
|
320
|
-
console.log(info.javascript_example);
|
|
321
|
-
```
|
|
132
|
+
For **known, deterministic operations** (CRUD, sync, bulk) use this SDK — typed calls, no
|
|
133
|
+
tool-schema overhead. For **live exploration of a user's world from a chat/agent context**, use
|
|
134
|
+
the MCP server at `https://www.onlyworlds.com/mcp` (same `API-Key`/`API-Pin` headers, 11 tools;
|
|
135
|
+
unaffected by SDK versioning).
|
|
322
136
|
|
|
323
137
|
## License
|
|
324
138
|
|
|
325
|
-
MIT
|
|
139
|
+
MIT
|
|
326
140
|
|
|
327
141
|
## Links
|
|
328
142
|
|
|
329
|
-
- [OnlyWorlds
|
|
330
|
-
- [
|
|
331
|
-
- [NPM Package](https://www.npmjs.com/package/@onlyworlds/sdk)
|
|
332
|
-
- [Report Issues](https://github.com/OnlyWorlds/sdk/issues)
|
|
143
|
+
- [OnlyWorlds](https://www.onlyworlds.com) · [Docs](https://onlyworlds.github.io) · [API reference](https://www.onlyworlds.com/api/docs)
|
|
144
|
+
- [Issues](https://github.com/OnlyWorlds/sdk/issues) · [CHANGELOG](CHANGELOG.md) · [Migration 3→4](docs/migrating-3-to-4.md)
|