@kokimoki/app 2.0.0 → 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/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +3 -0
- package/dist/core/kokimoki-client.d.ts +361 -0
- package/dist/core/kokimoki-client.js +819 -0
- package/dist/core/room-subscription-mode.d.ts +5 -0
- package/dist/core/room-subscription-mode.js +6 -0
- package/dist/core/room-subscription.d.ts +15 -0
- package/dist/core/room-subscription.js +53 -0
- package/dist/index.d.ts +4 -7
- package/dist/index.js +4 -7
- package/dist/kokimoki.min.d.ts +54 -58
- package/dist/kokimoki.min.js +3080 -1794
- package/dist/kokimoki.min.js.map +1 -1
- package/dist/llms.txt +75 -67
- package/dist/protocol/ws-message/index.d.ts +3 -0
- package/dist/protocol/ws-message/index.js +3 -0
- package/dist/protocol/ws-message/reader.d.ts +11 -0
- package/dist/protocol/ws-message/reader.js +36 -0
- package/dist/protocol/ws-message/type.d.ts +11 -0
- package/dist/protocol/ws-message/type.js +12 -0
- package/dist/protocol/ws-message/writer.d.ts +9 -0
- package/dist/protocol/ws-message/writer.js +45 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +3 -0
- package/dist/services/kokimoki-ai.d.ts +153 -0
- package/dist/services/kokimoki-ai.js +164 -0
- package/dist/services/kokimoki-leaderboard.d.ts +175 -0
- package/dist/services/kokimoki-leaderboard.js +203 -0
- package/dist/services/kokimoki-storage.d.ts +155 -0
- package/dist/services/kokimoki-storage.js +208 -0
- package/dist/stores/index.d.ts +3 -0
- package/dist/stores/index.js +3 -0
- package/dist/stores/kokimoki-local-store.d.ts +11 -0
- package/dist/stores/kokimoki-local-store.js +40 -0
- package/dist/stores/kokimoki-store.d.ts +22 -0
- package/dist/stores/kokimoki-store.js +117 -0
- package/dist/stores/kokimoki-transaction.d.ts +18 -0
- package/dist/stores/kokimoki-transaction.js +143 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/utils/valtio.d.ts +7 -0
- package/dist/utils/valtio.js +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -1
- package/package.json +4 -3
package/dist/llms.txt
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Instructions for the Kokimoki SDK
|
|
3
|
-
applyTo: '**/*.tsx,**/*.ts'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Kokimoki SDK
|
|
7
2
|
|
|
8
3
|
The Kokimoki SDK is a comprehensive development toolkit for building real-time collaborative game applications
|
|
@@ -51,7 +46,7 @@ Kokimoki Store powered by `valtio` and `valtio-yjs` for real-time state manageme
|
|
|
51
46
|
**Example: Creating a Store**
|
|
52
47
|
|
|
53
48
|
```typescript
|
|
54
|
-
import { kmClient } from
|
|
49
|
+
import { kmClient } from "@services/km-client";
|
|
55
50
|
|
|
56
51
|
interface State {
|
|
57
52
|
title: string;
|
|
@@ -59,12 +54,12 @@ interface State {
|
|
|
59
54
|
}
|
|
60
55
|
|
|
61
56
|
const initialState: State = {
|
|
62
|
-
title:
|
|
63
|
-
count: 0
|
|
57
|
+
title: "Store",
|
|
58
|
+
count: 0,
|
|
64
59
|
};
|
|
65
60
|
|
|
66
61
|
// Initialize global store with initial state
|
|
67
|
-
export const store = kmClient.store<PlayerState>(
|
|
62
|
+
export const store = kmClient.store<PlayerState>("store-name", initialState);
|
|
68
63
|
```
|
|
69
64
|
|
|
70
65
|
### State management
|
|
@@ -79,11 +74,11 @@ export const store = kmClient.store<PlayerState>('store-name', initialState);
|
|
|
79
74
|
**Example: Updating State**
|
|
80
75
|
|
|
81
76
|
```typescript
|
|
82
|
-
import { store } from
|
|
77
|
+
import { store } from "../store";
|
|
83
78
|
|
|
84
79
|
// Update state
|
|
85
80
|
await kmClient.transact([store], ([state]) => {
|
|
86
|
-
state.title =
|
|
81
|
+
state.title = "New store";
|
|
87
82
|
state.count += 1;
|
|
88
83
|
});
|
|
89
84
|
```
|
|
@@ -98,8 +93,8 @@ await kmClient.transact([store], ([state]) => {
|
|
|
98
93
|
```typescript
|
|
99
94
|
// Update multiple stores in a single transaction
|
|
100
95
|
await kmClient.transact([store1, store2], ([state1, state2]) => {
|
|
101
|
-
state1.name =
|
|
102
|
-
state2.name =
|
|
96
|
+
state1.name = "My Store1";
|
|
97
|
+
state2.name = "My Store2";
|
|
103
98
|
});
|
|
104
99
|
```
|
|
105
100
|
|
|
@@ -111,8 +106,8 @@ await kmClient.transact([store1, store2], ([state1, state2]) => {
|
|
|
111
106
|
**Example: Using State in Components**
|
|
112
107
|
|
|
113
108
|
```tsx
|
|
114
|
-
import { useSnapshot } from
|
|
115
|
-
import { store } from
|
|
109
|
+
import { useSnapshot } from "valtio";
|
|
110
|
+
import { store } from "../store";
|
|
116
111
|
|
|
117
112
|
const Component = () => {
|
|
118
113
|
// Get reactive snapshot of the store state
|
|
@@ -156,9 +151,9 @@ Uploads a file to storage.
|
|
|
156
151
|
**Example:**
|
|
157
152
|
|
|
158
153
|
```typescript
|
|
159
|
-
const upload: Upload = await kmClient.storage.upload(
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
const upload: Upload = await kmClient.storage.upload("filename.jpg", fileBlob, [
|
|
155
|
+
"tag1",
|
|
156
|
+
"tag2",
|
|
162
157
|
]);
|
|
163
158
|
// Use upload.url to access the media file
|
|
164
159
|
```
|
|
@@ -180,7 +175,7 @@ Query uploaded files by filter and pagination
|
|
|
180
175
|
```typescript
|
|
181
176
|
// Query uploads by tag and uploaded by this client
|
|
182
177
|
const { items, total } = await kmClient.storage.listUploads(
|
|
183
|
-
{ clientId: kmClient.id, tags: [
|
|
178
|
+
{ clientId: kmClient.id, tags: ["tag1"] },
|
|
184
179
|
skip,
|
|
185
180
|
limit
|
|
186
181
|
);
|
|
@@ -199,7 +194,7 @@ Replace uploaded file tags with new tags
|
|
|
199
194
|
|
|
200
195
|
```typescript
|
|
201
196
|
const updatedUpload: Upload = await kmClient.storage.updateUpload(upload.id, {
|
|
202
|
-
tags: [
|
|
197
|
+
tags: ["new"],
|
|
203
198
|
});
|
|
204
199
|
```
|
|
205
200
|
|
|
@@ -245,7 +240,9 @@ interface Paginated<T> {
|
|
|
245
240
|
|
|
246
241
|
```typescript
|
|
247
242
|
// Get uploaded files by clientId
|
|
248
|
-
const clientUploads = await kmClient.storage.listUploads({
|
|
243
|
+
const clientUploads = await kmClient.storage.listUploads({
|
|
244
|
+
clientId: kmClient.id,
|
|
245
|
+
});
|
|
249
246
|
```
|
|
250
247
|
|
|
251
248
|
#### Example: Filter by file type
|
|
@@ -253,7 +250,7 @@ const clientUploads = await kmClient.storage.listUploads({ clientId: kmClient.id
|
|
|
253
250
|
```typescript
|
|
254
251
|
// Get only uploaded images
|
|
255
252
|
const images = await kmClient.storage.listUploads({
|
|
256
|
-
mimeTypes: [
|
|
253
|
+
mimeTypes: ["image/jpeg", "image/png"],
|
|
257
254
|
});
|
|
258
255
|
```
|
|
259
256
|
|
|
@@ -261,17 +258,19 @@ const images = await kmClient.storage.listUploads({
|
|
|
261
258
|
|
|
262
259
|
```typescript
|
|
263
260
|
// Upload file with tag
|
|
264
|
-
await kmClient.storage.upload(
|
|
261
|
+
await kmClient.storage.upload("avatar.jpg", blob, ["profile"]);
|
|
265
262
|
|
|
266
263
|
// Query uploads by tag
|
|
267
|
-
const profileUploads = await kmClient.storage.listUploads({
|
|
264
|
+
const profileUploads = await kmClient.storage.listUploads({
|
|
265
|
+
tags: ["profile"],
|
|
266
|
+
});
|
|
268
267
|
```
|
|
269
268
|
|
|
270
269
|
#### Example: Usage in Kokimoki Store
|
|
271
270
|
|
|
272
271
|
```typescript
|
|
273
272
|
// Upload image from Blob
|
|
274
|
-
const upload = await kmClient.storage.upload(
|
|
273
|
+
const upload = await kmClient.storage.upload("file.jpg", blob);
|
|
275
274
|
|
|
276
275
|
await kmClient.transact([store], (state) => {
|
|
277
276
|
// Add image to images array in the store
|
|
@@ -319,11 +318,11 @@ Used to generate text response with AI
|
|
|
319
318
|
```typescript
|
|
320
319
|
// Generate text response
|
|
321
320
|
const { content } = await kmClient.ai.chat({
|
|
322
|
-
model:
|
|
323
|
-
systemPrompt:
|
|
324
|
-
userPrompt:
|
|
321
|
+
model: "gemini-2.5-flash-lite",
|
|
322
|
+
systemPrompt: "You are a sarcastic assistant",
|
|
323
|
+
userPrompt: "Write a story about dragons",
|
|
325
324
|
temperature: 0.7, // moderate creativity
|
|
326
|
-
maxTokens: 500 // limit to 500 tokens
|
|
325
|
+
maxTokens: 500, // limit to 500 tokens
|
|
327
326
|
});
|
|
328
327
|
```
|
|
329
328
|
|
|
@@ -359,13 +358,13 @@ interface Question {
|
|
|
359
358
|
}
|
|
360
359
|
|
|
361
360
|
const questions = await kmClient.ai.generateJson<Question[]>({
|
|
362
|
-
systemPrompt:
|
|
363
|
-
userPrompt:
|
|
364
|
-
temperature: 0.7
|
|
361
|
+
systemPrompt: "Generate quiz questions as JSON array",
|
|
362
|
+
userPrompt: "Create 5 history quiz questions with 4 options each",
|
|
363
|
+
temperature: 0.7,
|
|
365
364
|
});
|
|
366
365
|
|
|
367
366
|
// questions is already parsed and typed
|
|
368
|
-
questions.forEach(q => console.log(q.question));
|
|
367
|
+
questions.forEach((q) => console.log(q.question));
|
|
369
368
|
```
|
|
370
369
|
|
|
371
370
|
```typescript
|
|
@@ -378,8 +377,8 @@ interface Character {
|
|
|
378
377
|
}
|
|
379
378
|
|
|
380
379
|
const character = await kmClient.ai.generateJson<Character>({
|
|
381
|
-
userPrompt:
|
|
382
|
-
temperature: 0.8
|
|
380
|
+
userPrompt: "Create a fantasy warrior character with stats",
|
|
381
|
+
temperature: 0.8,
|
|
383
382
|
});
|
|
384
383
|
|
|
385
384
|
console.log(character.name, character.strength);
|
|
@@ -400,9 +399,9 @@ Used to modify/transform image with AI. The result is stored as [`Upload`](#stor
|
|
|
400
399
|
```typescript
|
|
401
400
|
// Modify image from url
|
|
402
401
|
const upload: Upload = await kmClient.ai.modifyImage(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
[
|
|
402
|
+
"https://static.kokimoki.com/game/image.jpg",
|
|
403
|
+
"Make it look like a painting",
|
|
404
|
+
["art", "ai-generated"]
|
|
406
405
|
);
|
|
407
406
|
```
|
|
408
407
|
|
|
@@ -420,8 +419,8 @@ Each Kokimoki store has a `connections` property that provides real-time presenc
|
|
|
420
419
|
### Example: Track Online Players
|
|
421
420
|
|
|
422
421
|
```tsx
|
|
423
|
-
import { useSnapshot } from
|
|
424
|
-
import { globalStore } from
|
|
422
|
+
import { useSnapshot } from "valtio";
|
|
423
|
+
import { globalStore } from "@/state/stores/global-store";
|
|
425
424
|
|
|
426
425
|
const Component = () => {
|
|
427
426
|
// Get online client IDs from store connections
|
|
@@ -440,8 +439,8 @@ const Component = () => {
|
|
|
440
439
|
### Example: Display Player List with Online Status
|
|
441
440
|
|
|
442
441
|
```tsx
|
|
443
|
-
import { useSnapshot } from
|
|
444
|
-
import { globalStore } from
|
|
442
|
+
import { useSnapshot } from "valtio";
|
|
443
|
+
import { globalStore } from "@/state/stores/global-store";
|
|
445
444
|
|
|
446
445
|
const PlayerList = () => {
|
|
447
446
|
const players = useSnapshot(globalStore.proxy).players;
|
|
@@ -451,14 +450,14 @@ const PlayerList = () => {
|
|
|
451
450
|
clientId,
|
|
452
451
|
name: player.name,
|
|
453
452
|
|
|
454
|
-
isOnline: onlineClientIds.has(clientId)
|
|
453
|
+
isOnline: onlineClientIds.has(clientId),
|
|
455
454
|
}));
|
|
456
455
|
|
|
457
456
|
return (
|
|
458
457
|
<ul>
|
|
459
458
|
{playersList.map((player) => (
|
|
460
459
|
<li key={player.clientId}>
|
|
461
|
-
{player.name} - {player.isOnline ?
|
|
460
|
+
{player.name} - {player.isOnline ? "Online" : "Offline"}
|
|
462
461
|
</li>
|
|
463
462
|
))}
|
|
464
463
|
</ul>
|
|
@@ -483,12 +482,14 @@ Access leaderboard API via `kmClient.leaderboard`.
|
|
|
483
482
|
### When to Use Leaderboard API vs Global Store
|
|
484
483
|
|
|
485
484
|
**Use the Leaderboard API when:**
|
|
485
|
+
|
|
486
486
|
- You have a large number of entries (hundreds to thousands of players)
|
|
487
487
|
- You need efficient ranking and sorting with database indexes
|
|
488
488
|
- You want pagination and optimized queries for top scores
|
|
489
489
|
- Memory and network efficiency are important
|
|
490
490
|
|
|
491
491
|
**Use a Global Store when:**
|
|
492
|
+
|
|
492
493
|
- You have a small number of players (typically under 100)
|
|
493
494
|
- You need real-time updates and live leaderboard changes
|
|
494
495
|
- You want to combine player scores with other game state
|
|
@@ -516,11 +517,11 @@ Add a new entry to a leaderboard. Creates a new entry each time it's called.
|
|
|
516
517
|
|
|
517
518
|
```typescript
|
|
518
519
|
const { rank } = await kmClient.leaderboard.insertEntry(
|
|
519
|
-
|
|
520
|
-
|
|
520
|
+
"high-scores",
|
|
521
|
+
"desc",
|
|
521
522
|
1500,
|
|
522
|
-
{ playerName:
|
|
523
|
-
{ sessionId:
|
|
523
|
+
{ playerName: "Alice", level: 10 },
|
|
524
|
+
{ sessionId: "abc123" }
|
|
524
525
|
);
|
|
525
526
|
console.log(`New rank: ${rank}`);
|
|
526
527
|
```
|
|
@@ -543,11 +544,11 @@ Add or update the latest entry for the current client in a leaderboard. Replaces
|
|
|
543
544
|
|
|
544
545
|
```typescript
|
|
545
546
|
const { rank } = await kmClient.leaderboard.upsertEntry(
|
|
546
|
-
|
|
547
|
-
|
|
547
|
+
"daily-scores",
|
|
548
|
+
"desc",
|
|
548
549
|
2000,
|
|
549
|
-
{ playerName:
|
|
550
|
-
{ deviceId:
|
|
550
|
+
{ playerName: "Bob", completionTime: 120 },
|
|
551
|
+
{ deviceId: "xyz789" }
|
|
551
552
|
);
|
|
552
553
|
console.log(`Updated rank: ${rank}`);
|
|
553
554
|
```
|
|
@@ -569,14 +570,16 @@ List entries in a leaderboard with pagination.
|
|
|
569
570
|
|
|
570
571
|
```typescript
|
|
571
572
|
const { items, total } = await kmClient.leaderboard.listEntries(
|
|
572
|
-
|
|
573
|
-
|
|
573
|
+
"weekly-scores",
|
|
574
|
+
"desc",
|
|
574
575
|
0, // skip
|
|
575
576
|
10 // limit - get top 10
|
|
576
577
|
);
|
|
577
578
|
|
|
578
|
-
items.forEach(entry => {
|
|
579
|
-
console.log(
|
|
579
|
+
items.forEach((entry) => {
|
|
580
|
+
console.log(
|
|
581
|
+
`Rank ${entry.rank}: ${entry.metadata.playerName} - ${entry.score}`
|
|
582
|
+
);
|
|
580
583
|
});
|
|
581
584
|
```
|
|
582
585
|
|
|
@@ -596,14 +599,14 @@ Get the best entry for a specific client in a leaderboard.
|
|
|
596
599
|
|
|
597
600
|
```typescript
|
|
598
601
|
// Get current client's best entry
|
|
599
|
-
const myBest = await kmClient.leaderboard.getBestEntry(
|
|
602
|
+
const myBest = await kmClient.leaderboard.getBestEntry("all-time-high", "desc");
|
|
600
603
|
console.log(`My best: Rank ${myBest.rank}, Score ${myBest.score}`);
|
|
601
604
|
|
|
602
605
|
// Get another player's best entry
|
|
603
606
|
const otherBest = await kmClient.leaderboard.getBestEntry(
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
+
"all-time-high",
|
|
608
|
+
"desc",
|
|
609
|
+
"other-client-id"
|
|
607
610
|
);
|
|
608
611
|
```
|
|
609
612
|
|
|
@@ -614,15 +617,20 @@ const otherBest = await kmClient.leaderboard.getBestEntry(
|
|
|
614
617
|
```typescript
|
|
615
618
|
// Submit a new high score
|
|
616
619
|
await kmClient.leaderboard.upsertEntry(
|
|
617
|
-
|
|
618
|
-
|
|
620
|
+
"high-scores",
|
|
621
|
+
"desc",
|
|
619
622
|
score,
|
|
620
623
|
{ playerName: player.name },
|
|
621
624
|
{ timestamp: Date.now() }
|
|
622
625
|
);
|
|
623
626
|
|
|
624
627
|
// Display top 10
|
|
625
|
-
const { items } = await kmClient.leaderboard.listEntries(
|
|
628
|
+
const { items } = await kmClient.leaderboard.listEntries(
|
|
629
|
+
"high-scores",
|
|
630
|
+
"desc",
|
|
631
|
+
0,
|
|
632
|
+
10
|
|
633
|
+
);
|
|
626
634
|
```
|
|
627
635
|
|
|
628
636
|
#### Example: Track Speed Run Times
|
|
@@ -630,15 +638,15 @@ const { items } = await kmClient.leaderboard.listEntries('high-scores', 'desc',
|
|
|
630
638
|
```typescript
|
|
631
639
|
// Submit completion time (lower is better)
|
|
632
640
|
await kmClient.leaderboard.upsertEntry(
|
|
633
|
-
|
|
634
|
-
|
|
641
|
+
"speed-run",
|
|
642
|
+
"asc",
|
|
635
643
|
completionTimeInSeconds,
|
|
636
|
-
{ playerName: player.name, difficulty:
|
|
644
|
+
{ playerName: player.name, difficulty: "hard" },
|
|
637
645
|
{}
|
|
638
646
|
);
|
|
639
647
|
|
|
640
648
|
// Get personal best
|
|
641
|
-
const myBest = await kmClient.leaderboard.getBestEntry(
|
|
649
|
+
const myBest = await kmClient.leaderboard.getBestEntry("speed-run", "asc");
|
|
642
650
|
```
|
|
643
651
|
|
|
644
652
|
### Key Points
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class WsMessageReader {
|
|
2
|
+
private buffer;
|
|
3
|
+
private _view;
|
|
4
|
+
private _offset;
|
|
5
|
+
constructor(buffer: ArrayBuffer);
|
|
6
|
+
readInt32(): number;
|
|
7
|
+
readUint32(): number;
|
|
8
|
+
readString(): string;
|
|
9
|
+
readUint8Array(): Uint8Array<ArrayBuffer>;
|
|
10
|
+
get end(): boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class WsMessageReader {
|
|
2
|
+
buffer;
|
|
3
|
+
_view;
|
|
4
|
+
_offset = 0;
|
|
5
|
+
constructor(buffer) {
|
|
6
|
+
this.buffer = buffer;
|
|
7
|
+
this._view = new DataView(buffer);
|
|
8
|
+
}
|
|
9
|
+
readInt32() {
|
|
10
|
+
const value = this._view.getInt32(this._offset, true);
|
|
11
|
+
this._offset += 4;
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
readUint32() {
|
|
15
|
+
const value = this._view.getUint32(this._offset, true);
|
|
16
|
+
this._offset += 4;
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
readString() {
|
|
20
|
+
const length = this.readInt32();
|
|
21
|
+
let value = "";
|
|
22
|
+
for (let i = 0; i < length; i++) {
|
|
23
|
+
value += String.fromCharCode(this._view.getUint8(this._offset++));
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
readUint8Array() {
|
|
28
|
+
const length = this.readInt32();
|
|
29
|
+
const value = new Uint8Array(this.buffer, this._offset, length);
|
|
30
|
+
this._offset += length;
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
get end() {
|
|
34
|
+
return this._offset === this.buffer.byteLength;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export var WsMessageType;
|
|
2
|
+
(function (WsMessageType) {
|
|
3
|
+
WsMessageType[WsMessageType["SubscribeReq"] = 1] = "SubscribeReq";
|
|
4
|
+
WsMessageType[WsMessageType["SubscribeRes"] = 2] = "SubscribeRes";
|
|
5
|
+
WsMessageType[WsMessageType["Transaction"] = 3] = "Transaction";
|
|
6
|
+
WsMessageType[WsMessageType["RoomUpdate"] = 4] = "RoomUpdate";
|
|
7
|
+
WsMessageType[WsMessageType["Error"] = 5] = "Error";
|
|
8
|
+
WsMessageType[WsMessageType["Ping"] = 6] = "Ping";
|
|
9
|
+
WsMessageType[WsMessageType["Pong"] = 7] = "Pong";
|
|
10
|
+
WsMessageType[WsMessageType["UnsubscribeReq"] = 8] = "UnsubscribeReq";
|
|
11
|
+
WsMessageType[WsMessageType["UnsubscribeRes"] = 9] = "UnsubscribeRes";
|
|
12
|
+
})(WsMessageType || (WsMessageType = {}));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class WsMessageWriter {
|
|
2
|
+
private _parts;
|
|
3
|
+
writeInt32(value: number): void;
|
|
4
|
+
writeUint32(value: number): void;
|
|
5
|
+
writeString(value: string): void;
|
|
6
|
+
writeChar(value: string): void;
|
|
7
|
+
writeUint8Array(value: Uint8Array): void;
|
|
8
|
+
getBuffer(): ArrayBuffer;
|
|
9
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export class WsMessageWriter {
|
|
2
|
+
_parts = [];
|
|
3
|
+
writeInt32(value) {
|
|
4
|
+
const buffer = new Uint8Array(4);
|
|
5
|
+
const view = new DataView(buffer.buffer);
|
|
6
|
+
view.setInt32(0, value, true);
|
|
7
|
+
this._parts.push(buffer);
|
|
8
|
+
}
|
|
9
|
+
writeUint32(value) {
|
|
10
|
+
const buffer = new Uint8Array(4);
|
|
11
|
+
const view = new DataView(buffer.buffer);
|
|
12
|
+
view.setUint32(0, value, true);
|
|
13
|
+
this._parts.push(buffer);
|
|
14
|
+
}
|
|
15
|
+
writeString(value) {
|
|
16
|
+
// Write length
|
|
17
|
+
this.writeInt32(value.length);
|
|
18
|
+
// Write chars
|
|
19
|
+
const buffer = new Uint8Array(value.length);
|
|
20
|
+
for (let i = 0; i < value.length; i++) {
|
|
21
|
+
buffer[i] = value.charCodeAt(i);
|
|
22
|
+
}
|
|
23
|
+
this._parts.push(buffer);
|
|
24
|
+
}
|
|
25
|
+
writeChar(value) {
|
|
26
|
+
const buffer = new Uint8Array(1);
|
|
27
|
+
buffer[0] = value.charCodeAt(0);
|
|
28
|
+
this._parts.push(buffer);
|
|
29
|
+
}
|
|
30
|
+
writeUint8Array(value) {
|
|
31
|
+
this.writeInt32(value.byteLength);
|
|
32
|
+
this._parts.push(value);
|
|
33
|
+
}
|
|
34
|
+
getBuffer() {
|
|
35
|
+
const size = this._parts.reduce((acc, part) => acc + part.byteLength, 0);
|
|
36
|
+
const buffer = new ArrayBuffer(size);
|
|
37
|
+
let offset = 0;
|
|
38
|
+
for (const part of this._parts) {
|
|
39
|
+
const view = new Uint8Array(buffer, offset);
|
|
40
|
+
view.set(part);
|
|
41
|
+
offset += part.byteLength;
|
|
42
|
+
}
|
|
43
|
+
return buffer;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { KokimokiClient } from "../core";
|
|
2
|
+
import type { Upload } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Kokimoki AI Integration Service
|
|
5
|
+
*
|
|
6
|
+
* Provides built-in AI capabilities for game applications without requiring API keys or setup.
|
|
7
|
+
* Includes text generation, structured JSON output, and image modification.
|
|
8
|
+
*
|
|
9
|
+
* **Key Features:**
|
|
10
|
+
* - Multiple AI models (GPT-4, GPT-5, Gemini variants)
|
|
11
|
+
* - Text generation with configurable creativity (temperature)
|
|
12
|
+
* - Structured JSON output for game data
|
|
13
|
+
* - AI-powered image modifications
|
|
14
|
+
* - No API keys or configuration required
|
|
15
|
+
*
|
|
16
|
+
* **Common Use Cases:**
|
|
17
|
+
* - Generate dynamic game content (quests, dialogues, stories)
|
|
18
|
+
* - Create AI opponents or NPCs with personalities
|
|
19
|
+
* - Generate quiz questions or trivia
|
|
20
|
+
* - Create game assets (character stats, item descriptions)
|
|
21
|
+
* - Transform user-uploaded images
|
|
22
|
+
* - Generate procedural content
|
|
23
|
+
*
|
|
24
|
+
* Access via `kmClient.ai`
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // Generate story text
|
|
29
|
+
* const story = await kmClient.ai.chat({
|
|
30
|
+
* systemPrompt: 'You are a fantasy story writer',
|
|
31
|
+
* userPrompt: 'Write a short quest description',
|
|
32
|
+
* temperature: 0.8
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Generate structured game data
|
|
36
|
+
* interface Enemy {
|
|
37
|
+
* name: string;
|
|
38
|
+
* health: number;
|
|
39
|
+
* attack: number;
|
|
40
|
+
* }
|
|
41
|
+
* const enemy = await kmClient.ai.generateJson<Enemy>({
|
|
42
|
+
* userPrompt: 'Create a level 5 goblin warrior'
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* // Transform image
|
|
46
|
+
* const modified = await kmClient.ai.modifyImage(
|
|
47
|
+
* imageUrl,
|
|
48
|
+
* 'Make it look like pixel art'
|
|
49
|
+
* );
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare class KokimokiAiService {
|
|
53
|
+
private readonly client;
|
|
54
|
+
constructor(client: KokimokiClient);
|
|
55
|
+
/**
|
|
56
|
+
* Generate a chat response from the AI model.
|
|
57
|
+
*
|
|
58
|
+
* Sends a chat request to the AI service and returns the generated response.
|
|
59
|
+
* Supports multiple AI models including GPT and Gemini variants with configurable
|
|
60
|
+
* parameters for fine-tuning the response behavior.
|
|
61
|
+
*
|
|
62
|
+
* @param req The chat request parameters.
|
|
63
|
+
* @param req.model Optional. The AI model to use. Defaults to server-side default if not specified.
|
|
64
|
+
* Available models:
|
|
65
|
+
* - `gpt-4o`: OpenAI GPT-4 Optimized
|
|
66
|
+
* - `gpt-4o-mini`: Smaller, faster GPT-4 variant
|
|
67
|
+
* - `gpt-5`: OpenAI GPT-5 (latest)
|
|
68
|
+
* - `gpt-5-mini`: Smaller GPT-5 variant
|
|
69
|
+
* - `gpt-5-nano`: Smallest GPT-5 variant for lightweight tasks
|
|
70
|
+
* - `gemini-2.5-flash-lite`: Google Gemini lite variant
|
|
71
|
+
* - `gemini-2.5-flash`: Google Gemini fast variant
|
|
72
|
+
* @param req.systemPrompt Optional. The system message that sets the behavior and context for the AI.
|
|
73
|
+
* This helps define the AI's role, personality, and constraints.
|
|
74
|
+
* @param req.userPrompt The user's message or question to send to the AI.
|
|
75
|
+
* @param req.temperature Optional. Controls randomness in the response (0.0 to 1.0).
|
|
76
|
+
* Lower values make output more focused and deterministic,
|
|
77
|
+
* higher values make it more creative and varied.
|
|
78
|
+
* @param req.maxTokens Optional. The maximum number of tokens to generate in the response.
|
|
79
|
+
* Controls the length of the AI's output.
|
|
80
|
+
*
|
|
81
|
+
* @returns A promise that resolves to an object containing the AI-generated response.
|
|
82
|
+
* @returns {string} content The text content of the AI's response.
|
|
83
|
+
*
|
|
84
|
+
* @throws An error object if the API request fails.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const response = await client.ai.chat({
|
|
89
|
+
* model: "gpt-4o",
|
|
90
|
+
* systemPrompt: "You are a helpful coding assistant.",
|
|
91
|
+
* userPrompt: "Explain what TypeScript is in one sentence.",
|
|
92
|
+
* temperature: 0.7,
|
|
93
|
+
* maxTokens: 100
|
|
94
|
+
* });
|
|
95
|
+
* console.log(response.content);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
chat(req: {
|
|
99
|
+
model?: "gpt-4o" | "gpt-4o-mini" | "gpt-5" | "gpt-5-mini" | "gpt-5-nano" | "gemini-2.5-flash-lite" | "gemini-2.5-flash";
|
|
100
|
+
systemPrompt?: string;
|
|
101
|
+
userPrompt: string;
|
|
102
|
+
temperature?: number;
|
|
103
|
+
maxTokens?: number;
|
|
104
|
+
responseMimeType?: string;
|
|
105
|
+
}): Promise<{
|
|
106
|
+
content: string;
|
|
107
|
+
}>;
|
|
108
|
+
/**
|
|
109
|
+
* Generate structured JSON output from the AI model.
|
|
110
|
+
*
|
|
111
|
+
* Sends a chat request to the AI service with the expectation of receiving
|
|
112
|
+
* a JSON-formatted response. This is useful for scenarios where the output
|
|
113
|
+
* needs to be parsed or processed programmatically.
|
|
114
|
+
*
|
|
115
|
+
* @param req The chat request parameters.
|
|
116
|
+
* @param req.model Optional. The AI model to use. Defaults to server-side default if not specified.
|
|
117
|
+
* Available models:
|
|
118
|
+
* - `gpt-4o`: OpenAI GPT-4 Optimized
|
|
119
|
+
* - `gpt-4o-mini`: Smaller, faster GPT-4 variant
|
|
120
|
+
* - `gpt-5`: OpenAI GPT-5 (latest)
|
|
121
|
+
* - `gpt-5-mini`: Smaller GPT-5 variant
|
|
122
|
+
* - `gpt-5-nano`: Smallest GPT-5 variant for lightweight tasks
|
|
123
|
+
* - `gemini-2.5-flash-lite`: Google Gemini lite variant
|
|
124
|
+
* - `gemini-2.5-flash`: Google Gemini fast variant
|
|
125
|
+
* @param req.systemPrompt Optional. The system message that sets the behavior and context for the AI.
|
|
126
|
+
* This helps define the AI's role, personality, and constraints.
|
|
127
|
+
* @param req.userPrompt The user's message or question to send to the AI.
|
|
128
|
+
* @param req.temperature Optional. Controls randomness in the response (0.0 to 1.0).
|
|
129
|
+
* Lower values make output more focused and deterministic,
|
|
130
|
+
* higher values make it more creative and varied.
|
|
131
|
+
* @param req.maxTokens Optional. The maximum number of tokens to generate in the response.
|
|
132
|
+
* Controls the length of the AI's output.
|
|
133
|
+
*
|
|
134
|
+
* @returns A promise that resolves to the parsed JSON object generated by the AI.
|
|
135
|
+
*
|
|
136
|
+
* @throws An error object if the API request fails or if the response is not valid JSON.
|
|
137
|
+
*/
|
|
138
|
+
generateJson<T extends object>(req: {
|
|
139
|
+
model?: "gpt-4o" | "gpt-4o-mini" | "gpt-5" | "gpt-5-mini" | "gpt-5-nano" | "gemini-2.5-flash-lite" | "gemini-2.5-flash";
|
|
140
|
+
systemPrompt?: string;
|
|
141
|
+
userPrompt: string;
|
|
142
|
+
temperature?: number;
|
|
143
|
+
maxTokens?: number;
|
|
144
|
+
}): Promise<T>;
|
|
145
|
+
/**
|
|
146
|
+
* Modify an image using the AI service.
|
|
147
|
+
* @param baseImageUrl The URL of the base image to modify.
|
|
148
|
+
* @param prompt The modification prompt to apply to the image.
|
|
149
|
+
* @param tags Optional. Tags to associate with the image.
|
|
150
|
+
* @returns A promise that resolves to the modified image upload information.
|
|
151
|
+
*/
|
|
152
|
+
modifyImage(baseImageUrl: string, prompt: string, tags?: string[]): Promise<Upload>;
|
|
153
|
+
}
|