@goodz-core/sdk 0.2.0 → 0.3.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/README.md +54 -0
- package/dist/alive/index.d.ts +175 -0
- package/dist/alive/index.js +4 -0
- package/dist/alive/index.js.map +1 -0
- package/dist/chunk-4SU7SU7K.js +227 -0
- package/dist/chunk-4SU7SU7K.js.map +1 -0
- package/dist/chunk-7O6UN2D2.js +102 -0
- package/dist/chunk-7O6UN2D2.js.map +1 -0
- package/dist/chunk-JAVMQXJM.js +27 -0
- package/dist/chunk-JAVMQXJM.js.map +1 -0
- package/dist/chunk-MUZDYQ67.js +56 -0
- package/dist/chunk-MUZDYQ67.js.map +1 -0
- package/dist/chunk-OUKZ2PRD.js +37 -0
- package/dist/chunk-OUKZ2PRD.js.map +1 -0
- package/dist/commerce/index.d.ts +407 -0
- package/dist/commerce/index.js +4 -0
- package/dist/commerce/index.js.map +1 -0
- package/dist/core/index.d.ts +76 -385
- package/dist/core/index.js +5 -1
- package/dist/exchange/index.d.ts +252 -0
- package/dist/exchange/index.js +4 -0
- package/dist/exchange/index.js.map +1 -0
- package/dist/index.d.ts +31 -4
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/transport-BOlScYEv.d.ts +377 -0
- package/package.json +20 -3
- package/dist/chunk-G7NKU6PT.js +0 -183
- package/dist/chunk-G7NKU6PT.js.map +0 -1
package/README.md
CHANGED
|
@@ -279,6 +279,59 @@ Common error codes and recommended handling:
|
|
|
279
279
|
| `NOT_FOUND` | 404 | Resource doesn't exist | Show 404 page or fallback |
|
|
280
280
|
| `CONFLICT` | 409 | Version conflict (optimistic locking) | Retry the operation |
|
|
281
281
|
|
|
282
|
+
## UI Components (React)
|
|
283
|
+
|
|
284
|
+
The SDK includes a React component for displaying GoodZ collectibles with full material effects and tilt interaction — the same experience used on GoodZ.Core itself.
|
|
285
|
+
|
|
286
|
+
### GoodZCardFocus
|
|
287
|
+
|
|
288
|
+
A "tap to focus, then tilt" component that opens a full-screen modal with holographic effects, gyroscope support, and material-accurate rendering for all 10 form factors.
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
import { GoodZCardFocus } from "@goodz-core/sdk/ui";
|
|
292
|
+
|
|
293
|
+
<GoodZCardFocus
|
|
294
|
+
imageUrl="https://goodzcore.manus.space/api/shell/42.png"
|
|
295
|
+
cardName="Luna"
|
|
296
|
+
rarity="SSR" // Controls holographic intensity
|
|
297
|
+
formFactor="trading_card" // Controls material effect
|
|
298
|
+
>
|
|
299
|
+
<img src={thumbnailUrl} alt="Luna" className="w-full rounded-lg" />
|
|
300
|
+
</GoodZCardFocus>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Supported form factors:** `trading_card`, `postcard`, `postcard_portrait`, `polaroid`, `laser_ticket`, `badge_small`, `badge_medium`, `badge_large`, `acrylic_stand_tall`, `acrylic_stand_chibi`.
|
|
304
|
+
|
|
305
|
+
**Material effects by form factor:**
|
|
306
|
+
|
|
307
|
+
| Form Factor | Material Effect |
|
|
308
|
+
|---|---|
|
|
309
|
+
| `trading_card` | Holographic gradient + sparkle (intensity by rarity) |
|
|
310
|
+
| `postcard` / `postcard_portrait` | Paper texture + subtle shadow |
|
|
311
|
+
| `polaroid` | White border (thick bottom) + paper texture |
|
|
312
|
+
| `laser_ticket` | Intense rainbow holographic + shimmer animation |
|
|
313
|
+
| `badge_*` | Circular clip + metal frame + dome highlight |
|
|
314
|
+
| `acrylic_stand_*` | Transparent glass + edge refraction + caustic |
|
|
315
|
+
|
|
316
|
+
**Interaction modes:**
|
|
317
|
+
- Desktop: mouse position controls tilt (lerp-smoothed)
|
|
318
|
+
- Mobile: gyroscope (auto-detected) or touch drag fallback
|
|
319
|
+
- iOS: permission request button for gyroscope access
|
|
320
|
+
|
|
321
|
+
**Requirements:** React 18+ and React DOM 18+ as peer dependencies. No CSS framework required — all styles are inline.
|
|
322
|
+
|
|
323
|
+
### Form Factor Utilities
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
import { FORM_FACTORS, getAspectRatioCSS, getFormFactorSpec } from "@goodz-core/sdk/ui";
|
|
327
|
+
|
|
328
|
+
getAspectRatioCSS("trading_card"); // "5 / 7"
|
|
329
|
+
getAspectRatioCSS("badge_small"); // "1 / 1"
|
|
330
|
+
|
|
331
|
+
const spec = getFormFactorSpec("polaroid");
|
|
332
|
+
// { key: "polaroid", label: "Polaroid", aspectRatio: [3, 4], isCircle: false }
|
|
333
|
+
```
|
|
334
|
+
|
|
282
335
|
## Subpath Imports
|
|
283
336
|
|
|
284
337
|
For tree-shaking, import from specific subpaths:
|
|
@@ -287,6 +340,7 @@ For tree-shaking, import from specific subpaths:
|
|
|
287
340
|
import { createGoodZClient } from "@goodz-core/sdk/core";
|
|
288
341
|
import { TokenManager } from "@goodz-core/sdk/auth";
|
|
289
342
|
import { toHundredths } from "@goodz-core/sdk/zcoin";
|
|
343
|
+
import { GoodZCardFocus } from "@goodz-core/sdk/ui";
|
|
290
344
|
```
|
|
291
345
|
|
|
292
346
|
The root import (`@goodz-core/sdk`) re-exports everything for convenience.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { X as TransportConfig } from '../transport-BOlScYEv.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @goodz-core/sdk — Alive API Type Definitions
|
|
5
|
+
*
|
|
6
|
+
* Hand-crafted types mirroring the GoodZ.Alive MCP tool surface.
|
|
7
|
+
* Covers memory management, intimacy/affection, context building,
|
|
8
|
+
* and intent classification for AI-powered GoodZ companions.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
type MemoryType = "episodic" | "semantic" | "emotional";
|
|
13
|
+
type IntentCategory = "chat" | "gift" | "trade" | "explore" | "care" | "customize" | "unknown";
|
|
14
|
+
type MoodState = "happy" | "neutral" | "sad" | "excited" | "tired" | "curious" | "shy";
|
|
15
|
+
interface AliveStoreMemoryInput {
|
|
16
|
+
/** The GoodZ card instance ID (the "alive" character) */
|
|
17
|
+
instanceId: number;
|
|
18
|
+
/** The user interacting with this character */
|
|
19
|
+
userId: number;
|
|
20
|
+
/** Type of memory to store */
|
|
21
|
+
memoryType: MemoryType;
|
|
22
|
+
/** The memory content */
|
|
23
|
+
content: string;
|
|
24
|
+
/** Importance score (0.0–1.0) — higher means more likely to be recalled */
|
|
25
|
+
importance?: number;
|
|
26
|
+
/** Optional tags for retrieval */
|
|
27
|
+
tags?: string[];
|
|
28
|
+
}
|
|
29
|
+
interface AliveMemory {
|
|
30
|
+
id: number;
|
|
31
|
+
instanceId: number;
|
|
32
|
+
userId: number;
|
|
33
|
+
memoryType: MemoryType;
|
|
34
|
+
content: string;
|
|
35
|
+
importance: number;
|
|
36
|
+
tags: string[];
|
|
37
|
+
createdAt: string;
|
|
38
|
+
lastRecalledAt: string | null;
|
|
39
|
+
}
|
|
40
|
+
interface AliveRecallMemoriesInput {
|
|
41
|
+
instanceId: number;
|
|
42
|
+
userId: number;
|
|
43
|
+
/** Natural language query to search memories */
|
|
44
|
+
query: string;
|
|
45
|
+
/** Max memories to return (default 10) */
|
|
46
|
+
limit?: number;
|
|
47
|
+
/** Filter by memory type */
|
|
48
|
+
memoryType?: MemoryType;
|
|
49
|
+
}
|
|
50
|
+
interface AliveForgetMemoryInput {
|
|
51
|
+
memoryId: number;
|
|
52
|
+
instanceId: number;
|
|
53
|
+
userId: number;
|
|
54
|
+
}
|
|
55
|
+
interface AliveGetIntimacyInput {
|
|
56
|
+
instanceId: number;
|
|
57
|
+
userId: number;
|
|
58
|
+
}
|
|
59
|
+
interface AliveIntimacy {
|
|
60
|
+
instanceId: number;
|
|
61
|
+
userId: number;
|
|
62
|
+
level: number;
|
|
63
|
+
totalInteractions: number;
|
|
64
|
+
lastInteractionAt: string | null;
|
|
65
|
+
mood: MoodState;
|
|
66
|
+
milestones: string[];
|
|
67
|
+
}
|
|
68
|
+
interface AliveUpdateIntimacyInput {
|
|
69
|
+
instanceId: number;
|
|
70
|
+
userId: number;
|
|
71
|
+
/** Delta to add/subtract from intimacy level */
|
|
72
|
+
delta: number;
|
|
73
|
+
/** Reason for the change */
|
|
74
|
+
reason: string;
|
|
75
|
+
/** Update the character's mood */
|
|
76
|
+
newMood?: MoodState;
|
|
77
|
+
}
|
|
78
|
+
interface AliveBuildContextInput {
|
|
79
|
+
instanceId: number;
|
|
80
|
+
userId: number;
|
|
81
|
+
/** The user's current message (used for memory retrieval) */
|
|
82
|
+
currentMessage?: string;
|
|
83
|
+
/** Include recent conversation history */
|
|
84
|
+
includeHistory?: boolean;
|
|
85
|
+
/** Max conversation turns to include (default 20) */
|
|
86
|
+
historyLimit?: number;
|
|
87
|
+
}
|
|
88
|
+
interface AliveContext {
|
|
89
|
+
/** The character's personality/soul from Core */
|
|
90
|
+
characterProfile: {
|
|
91
|
+
name: string;
|
|
92
|
+
personality: string | null;
|
|
93
|
+
worldview: string | null;
|
|
94
|
+
speakingStyle: string | null;
|
|
95
|
+
backstory: string | null;
|
|
96
|
+
};
|
|
97
|
+
/** Current intimacy state */
|
|
98
|
+
intimacy: AliveIntimacy;
|
|
99
|
+
/** Relevant memories retrieved for current context */
|
|
100
|
+
relevantMemories: AliveMemory[];
|
|
101
|
+
/** Recent conversation history */
|
|
102
|
+
recentHistory: Array<{
|
|
103
|
+
role: "user" | "character";
|
|
104
|
+
content: string;
|
|
105
|
+
timestamp: string;
|
|
106
|
+
}>;
|
|
107
|
+
/** System prompt assembled from all context */
|
|
108
|
+
systemPrompt: string;
|
|
109
|
+
}
|
|
110
|
+
interface AliveClassifyIntentInput {
|
|
111
|
+
instanceId: number;
|
|
112
|
+
userId: number;
|
|
113
|
+
/** The user's message to classify */
|
|
114
|
+
message: string;
|
|
115
|
+
}
|
|
116
|
+
interface AliveClassifiedIntent {
|
|
117
|
+
category: IntentCategory;
|
|
118
|
+
confidence: number;
|
|
119
|
+
/** Suggested action based on intent */
|
|
120
|
+
suggestedAction: string | null;
|
|
121
|
+
/** Extracted entities (e.g., card names, prices) */
|
|
122
|
+
entities: Record<string, any>;
|
|
123
|
+
}
|
|
124
|
+
interface AliveSendMessageInput {
|
|
125
|
+
instanceId: number;
|
|
126
|
+
userId: number;
|
|
127
|
+
message: string;
|
|
128
|
+
/** Optional: override the character's mood for this response */
|
|
129
|
+
moodOverride?: MoodState;
|
|
130
|
+
}
|
|
131
|
+
interface AliveChatResponse {
|
|
132
|
+
reply: string;
|
|
133
|
+
mood: MoodState;
|
|
134
|
+
intimacyDelta: number;
|
|
135
|
+
intent: AliveClassifiedIntent;
|
|
136
|
+
/** New memories formed from this interaction */
|
|
137
|
+
newMemories: string[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @goodz-core/sdk — Alive Namespace
|
|
142
|
+
*
|
|
143
|
+
* Provides typed access to GoodZ.Alive MCP tools:
|
|
144
|
+
* memory management, intimacy/affection, context building,
|
|
145
|
+
* intent classification, and chat sessions.
|
|
146
|
+
*
|
|
147
|
+
* GoodZ.Alive gives each GoodZ card a "soul" — an AI-powered
|
|
148
|
+
* companion that remembers, grows, and responds uniquely.
|
|
149
|
+
*
|
|
150
|
+
* @module
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
interface AliveNamespace {
|
|
154
|
+
/** Store a new memory for a character-user pair. */
|
|
155
|
+
storeMemory(input: AliveStoreMemoryInput): Promise<AliveMemory>;
|
|
156
|
+
/** Recall relevant memories using semantic search. */
|
|
157
|
+
recallMemories(input: AliveRecallMemoriesInput): Promise<AliveMemory[]>;
|
|
158
|
+
/** Delete a specific memory. */
|
|
159
|
+
forgetMemory(input: AliveForgetMemoryInput): Promise<any>;
|
|
160
|
+
/** Get the intimacy/affection state between a character and user. */
|
|
161
|
+
getIntimacy(input: AliveGetIntimacyInput): Promise<AliveIntimacy>;
|
|
162
|
+
/** Update intimacy level (e.g., after a positive/negative interaction). */
|
|
163
|
+
updateIntimacy(input: AliveUpdateIntimacyInput): Promise<AliveIntimacy>;
|
|
164
|
+
/** Build a full context object for AI generation (profile + memories + history + system prompt). */
|
|
165
|
+
buildContext(input: AliveBuildContextInput): Promise<AliveContext>;
|
|
166
|
+
/** Classify a user's message into an intent category. */
|
|
167
|
+
classifyIntent(input: AliveClassifyIntentInput): Promise<AliveClassifiedIntent>;
|
|
168
|
+
/** Send a message and get a character response (includes memory, intimacy, and intent processing). */
|
|
169
|
+
sendMessage(input: AliveSendMessageInput): Promise<AliveChatResponse>;
|
|
170
|
+
/** Call a raw MCP tool by name. Escape hatch for tools not yet in the typed API. */
|
|
171
|
+
rawTool<T = any>(toolName: string, args?: Record<string, any>): Promise<T>;
|
|
172
|
+
}
|
|
173
|
+
declare function createAliveNamespace(transport: TransportConfig): AliveNamespace;
|
|
174
|
+
|
|
175
|
+
export { type AliveBuildContextInput, type AliveChatResponse, type AliveClassifiedIntent, type AliveClassifyIntentInput, type AliveContext, type AliveForgetMemoryInput, type AliveGetIntimacyInput, type AliveIntimacy, type AliveMemory, type AliveNamespace, type AliveRecallMemoriesInput, type AliveSendMessageInput, type AliveStoreMemoryInput, type AliveUpdateIntimacyInput, type IntentCategory, type MemoryType, type MoodState, createAliveNamespace };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import superjson from 'superjson';
|
|
2
|
+
|
|
3
|
+
// src/transport.ts
|
|
4
|
+
var GoodZApiError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
httpStatus;
|
|
7
|
+
path;
|
|
8
|
+
zodErrors;
|
|
9
|
+
data;
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
super(opts.message);
|
|
12
|
+
this.name = "GoodZApiError";
|
|
13
|
+
this.code = opts.code;
|
|
14
|
+
this.httpStatus = opts.httpStatus;
|
|
15
|
+
this.path = opts.path;
|
|
16
|
+
this.zodErrors = opts.zodErrors;
|
|
17
|
+
this.data = opts.data;
|
|
18
|
+
}
|
|
19
|
+
/** Human-readable summary including field errors if present. */
|
|
20
|
+
toDetailedString() {
|
|
21
|
+
const parts = [
|
|
22
|
+
`[GoodZApiError] ${this.code} on ${this.path}: ${this.message}`
|
|
23
|
+
];
|
|
24
|
+
if (this.zodErrors?.length) {
|
|
25
|
+
parts.push("Field errors:");
|
|
26
|
+
for (const e of this.zodErrors) {
|
|
27
|
+
parts.push(` - ${e.path.join(".")}: ${e.message} (${e.code})`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return parts.join("\n");
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
async function callQuery(config, path, input) {
|
|
34
|
+
const url = `${config.baseUrl}/api/trpc/${path}`;
|
|
35
|
+
const headers = await config.getHeaders();
|
|
36
|
+
const serialized = input !== void 0 ? superjson.serialize(input) : void 0;
|
|
37
|
+
const queryUrl = serialized ? `${url}?input=${encodeURIComponent(JSON.stringify(serialized))}` : url;
|
|
38
|
+
const res = await fetch(queryUrl, {
|
|
39
|
+
method: "GET",
|
|
40
|
+
headers: {
|
|
41
|
+
...headers,
|
|
42
|
+
"Content-Type": "application/json"
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return parseResponse(res, path);
|
|
46
|
+
}
|
|
47
|
+
async function callMutation(config, path, input) {
|
|
48
|
+
const url = `${config.baseUrl}/api/trpc/${path}`;
|
|
49
|
+
const headers = await config.getHeaders();
|
|
50
|
+
const serialized = input !== void 0 ? superjson.serialize(input) : void 0;
|
|
51
|
+
const res = await fetch(url, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
...headers,
|
|
55
|
+
"Content-Type": "application/json"
|
|
56
|
+
},
|
|
57
|
+
body: serialized ? JSON.stringify(serialized) : void 0
|
|
58
|
+
});
|
|
59
|
+
return parseResponse(res, path);
|
|
60
|
+
}
|
|
61
|
+
async function parseResponse(res, path) {
|
|
62
|
+
const text = await res.text();
|
|
63
|
+
let body;
|
|
64
|
+
try {
|
|
65
|
+
body = JSON.parse(text);
|
|
66
|
+
} catch {
|
|
67
|
+
throw new GoodZApiError({
|
|
68
|
+
message: `Invalid JSON response: ${text.slice(0, 200)}`,
|
|
69
|
+
code: "PARSE_ERROR",
|
|
70
|
+
httpStatus: res.status,
|
|
71
|
+
path
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const envelope = Array.isArray(body) ? body[0] : body;
|
|
75
|
+
if (envelope?.error) {
|
|
76
|
+
const err = envelope.error;
|
|
77
|
+
const errJson = err?.json ?? err;
|
|
78
|
+
const errData = errJson?.data ?? {};
|
|
79
|
+
throw new GoodZApiError({
|
|
80
|
+
message: errJson?.message ?? "Unknown API error",
|
|
81
|
+
code: errData?.code ?? errJson?.code ?? "UNKNOWN",
|
|
82
|
+
httpStatus: errData?.httpStatus ?? res.status,
|
|
83
|
+
path: errData?.path ?? path,
|
|
84
|
+
zodErrors: errData?.zodError?.issues,
|
|
85
|
+
data: errData
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const resultData = envelope?.result?.data;
|
|
89
|
+
if (resultData === void 0) {
|
|
90
|
+
if (res.ok) return void 0;
|
|
91
|
+
throw new GoodZApiError({
|
|
92
|
+
message: `Unexpected response shape from ${path}`,
|
|
93
|
+
code: "UNEXPECTED_RESPONSE",
|
|
94
|
+
httpStatus: res.status,
|
|
95
|
+
path
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (resultData && typeof resultData === "object" && "json" in resultData) {
|
|
99
|
+
return superjson.deserialize(resultData);
|
|
100
|
+
}
|
|
101
|
+
return resultData;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/mcp-transport.ts
|
|
105
|
+
var _mcpRequestId = 0;
|
|
106
|
+
function nextId() {
|
|
107
|
+
return ++_mcpRequestId;
|
|
108
|
+
}
|
|
109
|
+
var _sessionIds = /* @__PURE__ */ new Map();
|
|
110
|
+
async function callMcpTool(config, toolName, args) {
|
|
111
|
+
const url = `${config.baseUrl}/api/mcp`;
|
|
112
|
+
const headers = await config.getHeaders();
|
|
113
|
+
const sessionId = _sessionIds.get(config.baseUrl);
|
|
114
|
+
if (sessionId) {
|
|
115
|
+
headers["mcp-session-id"] = sessionId;
|
|
116
|
+
}
|
|
117
|
+
const body = {
|
|
118
|
+
jsonrpc: "2.0",
|
|
119
|
+
method: "tools/call",
|
|
120
|
+
params: {
|
|
121
|
+
name: toolName,
|
|
122
|
+
arguments: args ?? {}
|
|
123
|
+
},
|
|
124
|
+
id: nextId()
|
|
125
|
+
};
|
|
126
|
+
const res = await fetch(url, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: {
|
|
129
|
+
...headers,
|
|
130
|
+
"Content-Type": "application/json",
|
|
131
|
+
Accept: "application/json, text/event-stream"
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify(body)
|
|
134
|
+
});
|
|
135
|
+
const newSessionId = res.headers.get("mcp-session-id");
|
|
136
|
+
if (newSessionId) {
|
|
137
|
+
_sessionIds.set(config.baseUrl, newSessionId);
|
|
138
|
+
}
|
|
139
|
+
const contentType = res.headers.get("content-type") || "";
|
|
140
|
+
if (contentType.includes("text/event-stream")) {
|
|
141
|
+
return parseSseResponse(res, toolName);
|
|
142
|
+
}
|
|
143
|
+
const text = await res.text();
|
|
144
|
+
return parseJsonRpcResponse(text, res.status, toolName);
|
|
145
|
+
}
|
|
146
|
+
function parseJsonRpcResponse(text, httpStatus, toolName) {
|
|
147
|
+
let body;
|
|
148
|
+
try {
|
|
149
|
+
body = JSON.parse(text);
|
|
150
|
+
} catch {
|
|
151
|
+
throw new GoodZApiError({
|
|
152
|
+
message: `Invalid JSON response from MCP tool ${toolName}: ${text.slice(0, 200)}`,
|
|
153
|
+
code: "PARSE_ERROR",
|
|
154
|
+
httpStatus,
|
|
155
|
+
path: toolName
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (body.error) {
|
|
159
|
+
throw new GoodZApiError({
|
|
160
|
+
message: body.error.message || "MCP tool error",
|
|
161
|
+
code: body.error.code?.toString() || "MCP_ERROR",
|
|
162
|
+
httpStatus,
|
|
163
|
+
path: toolName,
|
|
164
|
+
data: body.error.data
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return extractMcpResult(body.result, toolName, httpStatus);
|
|
168
|
+
}
|
|
169
|
+
async function parseSseResponse(res, toolName) {
|
|
170
|
+
const text = await res.text();
|
|
171
|
+
const lines = text.split("\n");
|
|
172
|
+
let lastData = null;
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
if (line.startsWith("data:")) {
|
|
175
|
+
lastData = line.slice(5).trim();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!lastData) {
|
|
179
|
+
throw new GoodZApiError({
|
|
180
|
+
message: `Empty SSE response from MCP tool ${toolName}`,
|
|
181
|
+
code: "EMPTY_RESPONSE",
|
|
182
|
+
httpStatus: res.status,
|
|
183
|
+
path: toolName
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return parseJsonRpcResponse(lastData, res.status, toolName);
|
|
187
|
+
}
|
|
188
|
+
function extractMcpResult(result, toolName, httpStatus) {
|
|
189
|
+
if (!result) {
|
|
190
|
+
throw new GoodZApiError({
|
|
191
|
+
message: `No result from MCP tool ${toolName}`,
|
|
192
|
+
code: "EMPTY_RESULT",
|
|
193
|
+
httpStatus,
|
|
194
|
+
path: toolName
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const content = result.content;
|
|
198
|
+
if (Array.isArray(content)) {
|
|
199
|
+
const textBlock = content.find((c) => c.type === "text");
|
|
200
|
+
if (textBlock?.text) {
|
|
201
|
+
if (result.isError) {
|
|
202
|
+
throw new GoodZApiError({
|
|
203
|
+
message: textBlock.text,
|
|
204
|
+
code: "MCP_TOOL_ERROR",
|
|
205
|
+
httpStatus,
|
|
206
|
+
path: toolName
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
return JSON.parse(textBlock.text);
|
|
211
|
+
} catch {
|
|
212
|
+
return textBlock.text;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
function createMcpTransportConfig(baseUrl, getHeaders) {
|
|
219
|
+
return {
|
|
220
|
+
baseUrl: baseUrl.replace(/\/$/, ""),
|
|
221
|
+
getHeaders
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { GoodZApiError, callMcpTool, callMutation, callQuery, createMcpTransportConfig };
|
|
226
|
+
//# sourceMappingURL=chunk-4SU7SU7K.js.map
|
|
227
|
+
//# sourceMappingURL=chunk-4SU7SU7K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport.ts","../src/mcp-transport.ts"],"names":[],"mappings":"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","file":"chunk-4SU7SU7K.js","sourcesContent":["/**\n * @goodz-core/sdk — HTTP Transport Layer\n *\n * Speaks the tRPC v11 HTTP wire protocol directly using fetch.\n * Handles superjson serialization, batching, and error parsing.\n *\n * Wire format reference:\n * - Queries: GET /api/trpc/{procedure}?input={superjson-encoded}\n * - Mutations: POST /api/trpc/{procedure} body: {superjson-encoded}\n * - Batch: GET /api/trpc/{p1},{p2}?input={0: ..., 1: ...}\n *\n * @internal\n */\n\nimport superjson from \"superjson\";\nimport type { GoodZApiFieldError } from \"./types\";\n\n// ─── Error class ─────────────────────────────────────────────\n\nexport class GoodZApiError extends Error {\n public readonly code: string;\n public readonly httpStatus: number;\n public readonly path: string;\n public readonly zodErrors?: GoodZApiFieldError[];\n public readonly data?: unknown;\n\n constructor(opts: {\n message: string;\n code: string;\n httpStatus: number;\n path: string;\n zodErrors?: GoodZApiFieldError[];\n data?: unknown;\n }) {\n super(opts.message);\n this.name = \"GoodZApiError\";\n this.code = opts.code;\n this.httpStatus = opts.httpStatus;\n this.path = opts.path;\n this.zodErrors = opts.zodErrors;\n this.data = opts.data;\n }\n\n /** Human-readable summary including field errors if present. */\n toDetailedString(): string {\n const parts = [\n `[GoodZApiError] ${this.code} on ${this.path}: ${this.message}`,\n ];\n if (this.zodErrors?.length) {\n parts.push(\"Field errors:\");\n for (const e of this.zodErrors) {\n parts.push(` - ${e.path.join(\".\")}: ${e.message} (${e.code})`);\n }\n }\n return parts.join(\"\\n\");\n }\n}\n\n// ─── Transport config ────────────────────────────────────────\n\nexport interface TransportConfig {\n baseUrl: string;\n getHeaders: () => Record<string, string> | Promise<Record<string, string>>;\n}\n\n// ─── Core transport functions ────────────────────────────────\n\n/**\n * Call a tRPC query (GET request).\n * Uses POST with method override for compatibility with Commerce/Exchange\n * server-to-server patterns (some proxies strip GET bodies).\n */\nexport async function callQuery<TInput, TOutput>(\n config: TransportConfig,\n path: string,\n input?: TInput,\n): Promise<TOutput> {\n const url = `${config.baseUrl}/api/trpc/${path}`;\n const headers = await config.getHeaders();\n\n // Use GET with query params for queries (standard tRPC)\n const serialized = input !== undefined ? superjson.serialize(input) : undefined;\n const queryUrl = serialized\n ? `${url}?input=${encodeURIComponent(JSON.stringify(serialized))}`\n : url;\n\n const res = await fetch(queryUrl, {\n method: \"GET\",\n headers: {\n ...headers,\n \"Content-Type\": \"application/json\",\n },\n });\n\n return parseResponse<TOutput>(res, path);\n}\n\n/**\n * Call a tRPC mutation (POST request).\n */\nexport async function callMutation<TInput, TOutput>(\n config: TransportConfig,\n path: string,\n input?: TInput,\n): Promise<TOutput> {\n const url = `${config.baseUrl}/api/trpc/${path}`;\n const headers = await config.getHeaders();\n\n const serialized = input !== undefined ? superjson.serialize(input) : undefined;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...headers,\n \"Content-Type\": \"application/json\",\n },\n body: serialized ? JSON.stringify(serialized) : undefined,\n });\n\n return parseResponse<TOutput>(res, path);\n}\n\n// ─── Response parser ─────────────────────────────────────────\n\nasync function parseResponse<T>(res: Response, path: string): Promise<T> {\n const text = await res.text();\n let body: any;\n\n try {\n body = JSON.parse(text);\n } catch {\n throw new GoodZApiError({\n message: `Invalid JSON response: ${text.slice(0, 200)}`,\n code: \"PARSE_ERROR\",\n httpStatus: res.status,\n path,\n });\n }\n\n // tRPC wraps responses in { result: { data: ... } }\n // Batch responses are arrays: [{ result: { data: ... } }]\n const envelope = Array.isArray(body) ? body[0] : body;\n\n // Check for tRPC error envelope\n if (envelope?.error) {\n const err = envelope.error;\n const errJson = err?.json ?? err;\n const errData = errJson?.data ?? {};\n\n throw new GoodZApiError({\n message: errJson?.message ?? \"Unknown API error\",\n code: errData?.code ?? errJson?.code ?? \"UNKNOWN\",\n httpStatus: errData?.httpStatus ?? res.status,\n path: errData?.path ?? path,\n zodErrors: errData?.zodError?.issues,\n data: errData,\n });\n }\n\n // Success path: extract and deserialize the data\n const resultData = envelope?.result?.data;\n if (resultData === undefined) {\n // Some queries return null/undefined legitimately\n if (res.ok) return undefined as T;\n\n throw new GoodZApiError({\n message: `Unexpected response shape from ${path}`,\n code: \"UNEXPECTED_RESPONSE\",\n httpStatus: res.status,\n path,\n });\n }\n\n // superjson wraps data in { json: ..., meta?: ... }\n // Check if it's a superjson envelope\n if (resultData && typeof resultData === \"object\" && \"json\" in resultData) {\n return superjson.deserialize(resultData) as T;\n }\n\n // Plain JSON (shouldn't happen with superjson transformer, but be safe)\n return resultData as T;\n}\n","/**\n * @goodz-core/sdk — MCP Transport Layer\n *\n * Speaks the MCP (Model Context Protocol) JSON-RPC 2.0 wire format\n * used by Commerce, Exchange, and Alive sub-sites.\n *\n * Wire format:\n * POST /api/mcp\n * Content-Type: application/json\n * Authorization: Bearer <token>\n *\n * { \"jsonrpc\": \"2.0\", \"method\": \"tools/call\", \"params\": { \"name\": \"<tool>\", \"arguments\": { ... } }, \"id\": 1 }\n *\n * Response:\n * { \"jsonrpc\": \"2.0\", \"result\": { \"content\": [{ \"type\": \"text\", \"text\": \"<json>\" }] }, \"id\": 1 }\n *\n * @internal\n */\n\nimport { GoodZApiError } from \"./transport\";\nimport type { TransportConfig } from \"./transport\";\n\n// Re-export for convenience\nexport type { TransportConfig };\n\n// ─── MCP request counter ────────────────────────────────────\n\nlet _mcpRequestId = 0;\nfunction nextId(): number {\n return ++_mcpRequestId;\n}\n\n// ─── Session management ─────────────────────────────────────\n\nconst _sessionIds = new Map<string, string>();\n\n// ─── Core MCP call function ─────────────────────────────────\n\n/**\n * Call an MCP tool on a sub-site.\n *\n * Handles:\n * - JSON-RPC 2.0 envelope construction\n * - Session ID management (mcp-session-id header)\n * - Response parsing (extracts JSON from text content)\n * - Error mapping to GoodZApiError\n */\nexport async function callMcpTool<TInput extends Record<string, any>, TOutput>(\n config: TransportConfig,\n toolName: string,\n args?: TInput,\n): Promise<TOutput> {\n const url = `${config.baseUrl}/api/mcp`;\n const headers = await config.getHeaders();\n\n // Add session ID if we have one for this endpoint\n const sessionId = _sessionIds.get(config.baseUrl);\n if (sessionId) {\n headers[\"mcp-session-id\"] = sessionId;\n }\n\n const body = {\n jsonrpc: \"2.0\" as const,\n method: \"tools/call\",\n params: {\n name: toolName,\n arguments: args ?? {},\n },\n id: nextId(),\n };\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...headers,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json, text/event-stream\",\n },\n body: JSON.stringify(body),\n });\n\n // Store session ID from response\n const newSessionId = res.headers.get(\"mcp-session-id\");\n if (newSessionId) {\n _sessionIds.set(config.baseUrl, newSessionId);\n }\n\n // Handle SSE responses (some MCP servers use streaming)\n const contentType = res.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"text/event-stream\")) {\n return parseSseResponse<TOutput>(res, toolName);\n }\n\n // Standard JSON response\n const text = await res.text();\n return parseJsonRpcResponse<TOutput>(text, res.status, toolName);\n}\n\n// ─── Response parsers ───────────────────────────────────────\n\nfunction parseJsonRpcResponse<T>(\n text: string,\n httpStatus: number,\n toolName: string,\n): T {\n let body: any;\n try {\n body = JSON.parse(text);\n } catch {\n throw new GoodZApiError({\n message: `Invalid JSON response from MCP tool ${toolName}: ${text.slice(0, 200)}`,\n code: \"PARSE_ERROR\",\n httpStatus,\n path: toolName,\n });\n }\n\n // JSON-RPC error\n if (body.error) {\n throw new GoodZApiError({\n message: body.error.message || \"MCP tool error\",\n code: body.error.code?.toString() || \"MCP_ERROR\",\n httpStatus,\n path: toolName,\n data: body.error.data,\n });\n }\n\n // Extract result from MCP content array\n return extractMcpResult<T>(body.result, toolName, httpStatus);\n}\n\nasync function parseSseResponse<T>(\n res: Response,\n toolName: string,\n): Promise<T> {\n const text = await res.text();\n const lines = text.split(\"\\n\");\n\n // Find the last \"data:\" line that contains a JSON-RPC response\n let lastData: string | null = null;\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n lastData = line.slice(5).trim();\n }\n }\n\n if (!lastData) {\n throw new GoodZApiError({\n message: `Empty SSE response from MCP tool ${toolName}`,\n code: \"EMPTY_RESPONSE\",\n httpStatus: res.status,\n path: toolName,\n });\n }\n\n return parseJsonRpcResponse<T>(lastData, res.status, toolName);\n}\n\nfunction extractMcpResult<T>(\n result: any,\n toolName: string,\n httpStatus: number,\n): T {\n if (!result) {\n throw new GoodZApiError({\n message: `No result from MCP tool ${toolName}`,\n code: \"EMPTY_RESULT\",\n httpStatus,\n path: toolName,\n });\n }\n\n // MCP tools return { content: [{ type: \"text\", text: \"...\" }] }\n const content = result.content;\n if (Array.isArray(content)) {\n // Find the first text content block\n const textBlock = content.find((c: any) => c.type === \"text\");\n if (textBlock?.text) {\n // Check if the tool returned an error in the text\n if (result.isError) {\n throw new GoodZApiError({\n message: textBlock.text,\n code: \"MCP_TOOL_ERROR\",\n httpStatus,\n path: toolName,\n });\n }\n\n // Try to parse as JSON\n try {\n return JSON.parse(textBlock.text) as T;\n } catch {\n // If not JSON, return the text as-is (some tools return plain text)\n return textBlock.text as T;\n }\n }\n }\n\n // Fallback: return the result directly\n return result as T;\n}\n\n// ─── MCP transport config builder ───────────────────────────\n\n/**\n * Create a TransportConfig for an MCP sub-site endpoint.\n */\nexport function createMcpTransportConfig(\n baseUrl: string,\n getHeaders: () => Record<string, string> | Promise<Record<string, string>>,\n): TransportConfig {\n return {\n baseUrl: baseUrl.replace(/\\/$/, \"\"),\n getHeaders,\n };\n}\n"]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createCommerceNamespace } from './chunk-MUZDYQ67.js';
|
|
2
|
+
import { createExchangeNamespace } from './chunk-OUKZ2PRD.js';
|
|
3
|
+
import { createAliveNamespace } from './chunk-JAVMQXJM.js';
|
|
4
|
+
import { createMcpTransportConfig, callMutation, callQuery } from './chunk-4SU7SU7K.js';
|
|
5
|
+
|
|
6
|
+
// src/core/index.ts
|
|
7
|
+
var DEFAULT_CORE_URL = "https://goodzcore.manus.space";
|
|
8
|
+
var DEFAULT_COMMERCE_URL = "https://goodzcommerce.manus.space";
|
|
9
|
+
var DEFAULT_EXCHANGE_URL = "https://goodzexchange.manus.space";
|
|
10
|
+
var DEFAULT_ALIVE_URL = "https://goodzalive.manus.space";
|
|
11
|
+
function createGoodZClient(config = {}) {
|
|
12
|
+
const {
|
|
13
|
+
coreUrl = DEFAULT_CORE_URL,
|
|
14
|
+
commerceUrl = DEFAULT_COMMERCE_URL,
|
|
15
|
+
exchangeUrl = DEFAULT_EXCHANGE_URL,
|
|
16
|
+
aliveUrl = DEFAULT_ALIVE_URL,
|
|
17
|
+
accessToken,
|
|
18
|
+
getAccessToken,
|
|
19
|
+
headers: customHeaders
|
|
20
|
+
} = config;
|
|
21
|
+
const buildHeaders = async () => {
|
|
22
|
+
const h = { ...customHeaders };
|
|
23
|
+
const token = getAccessToken ? await getAccessToken() : accessToken;
|
|
24
|
+
if (token) {
|
|
25
|
+
h["Authorization"] = `Bearer ${token}`;
|
|
26
|
+
}
|
|
27
|
+
return h;
|
|
28
|
+
};
|
|
29
|
+
const coreTransport = {
|
|
30
|
+
baseUrl: coreUrl.replace(/\/$/, ""),
|
|
31
|
+
getHeaders: buildHeaders
|
|
32
|
+
};
|
|
33
|
+
const commerceTransport = createMcpTransportConfig(commerceUrl, buildHeaders);
|
|
34
|
+
const exchangeTransport = createMcpTransportConfig(exchangeUrl, buildHeaders);
|
|
35
|
+
const aliveTransport = createMcpTransportConfig(aliveUrl, buildHeaders);
|
|
36
|
+
const q = (path) => (input) => callQuery(coreTransport, path, input);
|
|
37
|
+
const m = (path) => (input) => callMutation(coreTransport, path, input);
|
|
38
|
+
return {
|
|
39
|
+
// ── zcoin ──────────────────────────────────────────────
|
|
40
|
+
zcoin: {
|
|
41
|
+
getMyBalance: q("zcoin.getMyBalance"),
|
|
42
|
+
getMyHistory: q("zcoin.getMyHistory"),
|
|
43
|
+
getDepositPackages: q("zcoin.getDepositPackages"),
|
|
44
|
+
createDepositOrder: m("zcoin.createDepositOrder"),
|
|
45
|
+
getDepositStatus: q("zcoin.getDepositStatus"),
|
|
46
|
+
commercialTransfer: m("zcoin.commercialTransfer"),
|
|
47
|
+
mintAndCharge: m("zcoin.mintAndCharge"),
|
|
48
|
+
chargeUser: m("zcoin.chargeUser"),
|
|
49
|
+
createDirectPurchaseOrder: m("zcoin.createDirectPurchaseOrder")
|
|
50
|
+
},
|
|
51
|
+
// ── inventory ──────────────────────────────────────────
|
|
52
|
+
inventory: {
|
|
53
|
+
getUserInventory: q("inventory.getUserInventory"),
|
|
54
|
+
confirmOwnership: q("inventory.confirmOwnership"),
|
|
55
|
+
mint: m("inventory.mint"),
|
|
56
|
+
transfer: m("inventory.transfer"),
|
|
57
|
+
transferByCard: m("inventory.transferByCard"),
|
|
58
|
+
grantMintAuth: m("inventory.grantMintAuth"),
|
|
59
|
+
transferHistory: q("inventory.transferHistory")
|
|
60
|
+
},
|
|
61
|
+
// ── collectible ────────────────────────────────────────
|
|
62
|
+
collectible: {
|
|
63
|
+
getInstanceById: q("collectible.getInstanceById"),
|
|
64
|
+
getPublicInstance: q("collectible.getPublicInstance"),
|
|
65
|
+
getPublicInstancesBatch: q("collectible.getPublicInstancesBatch"),
|
|
66
|
+
getCardProfile: q("collectible.getCardProfile"),
|
|
67
|
+
getShellImageUrl: q("collectible.getShellImageUrl")
|
|
68
|
+
},
|
|
69
|
+
// ── user ───────────────────────────────────────────────
|
|
70
|
+
user: {
|
|
71
|
+
getPublicProfile: q("user.getPublicProfile"),
|
|
72
|
+
getPublicProfileById: q("user.getPublicProfileById")
|
|
73
|
+
},
|
|
74
|
+
// ── auth ───────────────────────────────────────────────
|
|
75
|
+
auth: {
|
|
76
|
+
me: q("auth.me"),
|
|
77
|
+
getOAuthAppInfo: q("auth.getOAuthAppInfo")
|
|
78
|
+
},
|
|
79
|
+
// ── ip (franchise/series/card) ─────────────────────────
|
|
80
|
+
ip: {
|
|
81
|
+
getFranchise: q("franchise.get"),
|
|
82
|
+
getSeries: q("series.get"),
|
|
83
|
+
listSeriesByFranchise: q("series.listByFranchise"),
|
|
84
|
+
getCard: q("card.get"),
|
|
85
|
+
listCardsBySeries: q("card.listBySeries")
|
|
86
|
+
},
|
|
87
|
+
// ── commerce (MCP) ─────────────────────────────────────
|
|
88
|
+
commerce: createCommerceNamespace(commerceTransport),
|
|
89
|
+
// ── exchange (MCP) ─────────────────────────────────────
|
|
90
|
+
exchange: createExchangeNamespace(exchangeTransport),
|
|
91
|
+
// ── alive (MCP) ────────────────────────────────────────
|
|
92
|
+
alive: createAliveNamespace(aliveTransport),
|
|
93
|
+
// ── raw escape hatches (Core tRPC only) ────────────────
|
|
94
|
+
rawQuery: (path, input) => callQuery(coreTransport, path, input),
|
|
95
|
+
rawMutation: (path, input) => callMutation(coreTransport, path, input)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
var createUserClient = createGoodZClient;
|
|
99
|
+
|
|
100
|
+
export { createGoodZClient, createUserClient };
|
|
101
|
+
//# sourceMappingURL=chunk-7O6UN2D2.js.map
|
|
102
|
+
//# sourceMappingURL=chunk-7O6UN2D2.js.map
|