opencloud-platform-sdk 1.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/README.md +247 -0
- package/dist/index.d.mts +99 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +397 -0
- package/dist/index.mjs +371 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @opencloud/sdk
|
|
2
|
+
|
|
3
|
+
Official SDK for **OpenCloud** - The AI App Marketplace with pay-per-use model.
|
|
4
|
+
|
|
5
|
+
Build AI-powered apps that automatically handle payments and AI API calls through OpenCloud's platform.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- ✅ Call 100+ AI models (GPT-4, Claude, Gemini, Llama, etc.) via OpenRouter
|
|
10
|
+
- ✅ Automatic payment processing (70% to creators, 30% to platform)
|
|
11
|
+
- ✅ No API keys needed - OpenCloud handles everything
|
|
12
|
+
- ✅ TypeScript support with full type definitions
|
|
13
|
+
- ✅ Works in browsers and Node.js
|
|
14
|
+
- ✅ Zero dependencies
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @opencloud/sdk
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @opencloud/sdk
|
|
22
|
+
# or
|
|
23
|
+
yarn add @opencloud/sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### For React/Next.js Apps
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { opencloud } from '@opencloud/sdk';
|
|
32
|
+
|
|
33
|
+
// Initialize with your app ID
|
|
34
|
+
opencloud.init({ appId: 'your-app-slug' });
|
|
35
|
+
|
|
36
|
+
// Call AI models
|
|
37
|
+
const response = await opencloud.callAI({
|
|
38
|
+
model: 'openai/gpt-4',
|
|
39
|
+
prompt: 'Write a tagline for my startup'
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.log(response.text); // AI-generated response
|
|
43
|
+
console.log(response.cost); // $0.10 (or your app's price per use)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### For HTML/Vanilla JS
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<script src="https://opencloud.com/sdk/platform-sdk.js"></script>
|
|
50
|
+
|
|
51
|
+
<script>
|
|
52
|
+
PlatformSDK.init({ appId: 'your-app-slug' });
|
|
53
|
+
|
|
54
|
+
async function generate() {
|
|
55
|
+
const result = await PlatformSDK.callAI({
|
|
56
|
+
model: 'anthropic/claude-3-5-sonnet',
|
|
57
|
+
prompt: 'Generate a logo description'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log(result.text);
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API Reference
|
|
66
|
+
|
|
67
|
+
### `opencloud.init(config)`
|
|
68
|
+
|
|
69
|
+
Initialize the SDK with your app configuration.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
opencloud.init({
|
|
73
|
+
appId: 'your-app-slug', // Required: Your app's unique identifier
|
|
74
|
+
apiUrl: 'https://opencloud.com' // Optional: Platform URL (defaults to current origin)
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `opencloud.callAI(params)`
|
|
79
|
+
|
|
80
|
+
Call an AI model through OpenCloud's platform.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const response = await opencloud.callAI({
|
|
84
|
+
model: 'openai/gpt-4', // Required: Model identifier
|
|
85
|
+
prompt: 'Your prompt here', // Required: The prompt to send
|
|
86
|
+
options: { // Optional: Additional model parameters
|
|
87
|
+
temperature: 0.7,
|
|
88
|
+
max_tokens: 1000
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Response structure:
|
|
93
|
+
{
|
|
94
|
+
success: true,
|
|
95
|
+
text: "AI-generated response...",
|
|
96
|
+
usage: {
|
|
97
|
+
promptTokens: 10,
|
|
98
|
+
completionTokens: 50,
|
|
99
|
+
totalTokens: 60
|
|
100
|
+
},
|
|
101
|
+
cost: 0.10, // Amount charged to user
|
|
102
|
+
model: "openai/gpt-4"
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `opencloud.getBalance()`
|
|
107
|
+
|
|
108
|
+
Get the current user's token balance.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const balance = await opencloud.getBalance();
|
|
112
|
+
console.log(`Balance: $${balance}`);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `opencloud.requestTopUp()`
|
|
116
|
+
|
|
117
|
+
Request the user to purchase more tokens (opens payment dialog).
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
try {
|
|
121
|
+
await opencloud.callAI({...});
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error.message.includes('Insufficient balance')) {
|
|
124
|
+
await opencloud.requestTopUp();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Supported AI Models
|
|
130
|
+
|
|
131
|
+
OpenCloud uses OpenRouter, giving you access to 100+ models:
|
|
132
|
+
|
|
133
|
+
**OpenAI:**
|
|
134
|
+
- `openai/gpt-4`
|
|
135
|
+
- `openai/gpt-4-turbo`
|
|
136
|
+
- `openai/gpt-3.5-turbo`
|
|
137
|
+
|
|
138
|
+
**Anthropic:**
|
|
139
|
+
- `anthropic/claude-3-5-sonnet`
|
|
140
|
+
- `anthropic/claude-3-haiku`
|
|
141
|
+
- `anthropic/claude-3-opus`
|
|
142
|
+
|
|
143
|
+
**Google:**
|
|
144
|
+
- `google/gemini-pro`
|
|
145
|
+
- `google/gemini-pro-vision`
|
|
146
|
+
|
|
147
|
+
**Meta:**
|
|
148
|
+
- `meta-llama/llama-3-70b`
|
|
149
|
+
- `meta-llama/llama-3-8b`
|
|
150
|
+
|
|
151
|
+
**And many more!** See [OpenRouter docs](https://openrouter.ai/docs) for full list.
|
|
152
|
+
|
|
153
|
+
## Example Apps
|
|
154
|
+
|
|
155
|
+
### Text Summarizer
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { opencloud } from '@opencloud/sdk';
|
|
159
|
+
|
|
160
|
+
opencloud.init({ appId: 'text-summarizer' });
|
|
161
|
+
|
|
162
|
+
async function summarize(text: string) {
|
|
163
|
+
const response = await opencloud.callAI({
|
|
164
|
+
model: 'anthropic/claude-3-haiku', // Fast and cheap
|
|
165
|
+
prompt: `Summarize this text in 3 bullet points:\n\n${text}`
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return response.text;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Image Description Generator
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { opencloud } from '@opencloud/sdk';
|
|
176
|
+
|
|
177
|
+
opencloud.init({ appId: 'image-describer' });
|
|
178
|
+
|
|
179
|
+
async function describeImage(imageUrl: string) {
|
|
180
|
+
const response = await opencloud.callAI({
|
|
181
|
+
model: 'google/gemini-pro-vision',
|
|
182
|
+
prompt: 'Describe this image in detail',
|
|
183
|
+
options: {
|
|
184
|
+
image: imageUrl
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return response.text;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Revenue Model
|
|
193
|
+
|
|
194
|
+
OpenCloud uses a **70/30 revenue split**:
|
|
195
|
+
|
|
196
|
+
- **70%** goes to you (the app creator)
|
|
197
|
+
- **30%** goes to OpenCloud (platform fee)
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
```
|
|
201
|
+
User pays: $0.10 per use
|
|
202
|
+
AI cost: -$0.03 (OpenAI API)
|
|
203
|
+
Net revenue: $0.07
|
|
204
|
+
├─ You get: $0.049 (70%)
|
|
205
|
+
└─ Platform: $0.021 (30%)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## TypeScript Support
|
|
209
|
+
|
|
210
|
+
Full TypeScript definitions included:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import {
|
|
214
|
+
opencloud,
|
|
215
|
+
OpenCloudSDK,
|
|
216
|
+
AICallParams,
|
|
217
|
+
AIResponse,
|
|
218
|
+
OpenCloudConfig
|
|
219
|
+
} from '@opencloud/sdk';
|
|
220
|
+
|
|
221
|
+
const params: AICallParams = {
|
|
222
|
+
model: 'openai/gpt-4',
|
|
223
|
+
prompt: 'Hello world'
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const response: AIResponse = await opencloud.callAI(params);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Publishing Your App
|
|
230
|
+
|
|
231
|
+
1. Build your app with the SDK
|
|
232
|
+
2. Go to [opencloud.com/publish](https://opencloud.com/publish)
|
|
233
|
+
3. Upload your app or connect GitHub
|
|
234
|
+
4. Set pricing and details
|
|
235
|
+
5. Test with $5 free credits
|
|
236
|
+
6. Publish to marketplace!
|
|
237
|
+
|
|
238
|
+
## Support
|
|
239
|
+
|
|
240
|
+
- 📚 [Documentation](https://opencloud.com/docs)
|
|
241
|
+
- 💬 [Discord Community](https://discord.gg/opencloud)
|
|
242
|
+
- 🐛 [Report Issues](https://github.com/opencloud/sdk/issues)
|
|
243
|
+
- ✉️ support@opencloud.com
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT © OpenCloud
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @opencloud/sdk v1.1.0
|
|
3
|
+
* Official SDK for OpenCloud - AI App Marketplace
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Automatic preview/production mode detection
|
|
7
|
+
* - Works in bolt.diy/WebContainers preview
|
|
8
|
+
* - Seamless transition to production with credits
|
|
9
|
+
*/
|
|
10
|
+
interface OpenCloudConfig {
|
|
11
|
+
appId: string;
|
|
12
|
+
apiUrl?: string;
|
|
13
|
+
previewApiKey?: string;
|
|
14
|
+
}
|
|
15
|
+
interface AICallParams {
|
|
16
|
+
model: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
options?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
interface AIResponse {
|
|
21
|
+
success: boolean;
|
|
22
|
+
text: string;
|
|
23
|
+
usage: {
|
|
24
|
+
promptTokens: number;
|
|
25
|
+
completionTokens: number;
|
|
26
|
+
totalTokens: number;
|
|
27
|
+
};
|
|
28
|
+
cost: number;
|
|
29
|
+
model: string;
|
|
30
|
+
mode: 'preview' | 'production';
|
|
31
|
+
}
|
|
32
|
+
interface UserSession {
|
|
33
|
+
token: string;
|
|
34
|
+
}
|
|
35
|
+
declare class OpenCloudSDK {
|
|
36
|
+
private config;
|
|
37
|
+
private heartbeatInterval?;
|
|
38
|
+
private _isPreviewMode;
|
|
39
|
+
constructor();
|
|
40
|
+
/**
|
|
41
|
+
* Detect if running in preview/development mode
|
|
42
|
+
*/
|
|
43
|
+
private detectPreviewMode;
|
|
44
|
+
/**
|
|
45
|
+
* Check if currently in preview mode
|
|
46
|
+
*/
|
|
47
|
+
isPreview(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Initialize the SDK
|
|
50
|
+
*/
|
|
51
|
+
init(options: OpenCloudConfig): void;
|
|
52
|
+
/**
|
|
53
|
+
* Set API key for preview mode
|
|
54
|
+
*/
|
|
55
|
+
setPreviewApiKey(key: string, provider?: 'openrouter' | 'anthropic' | 'openai'): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get API key for preview mode
|
|
58
|
+
*/
|
|
59
|
+
private getPreviewApiKey;
|
|
60
|
+
/**
|
|
61
|
+
* Call an AI model - automatically uses preview or production mode
|
|
62
|
+
*/
|
|
63
|
+
callAI(params: AICallParams): Promise<AIResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Call AI in preview mode (direct to OpenRouter/provider)
|
|
66
|
+
*/
|
|
67
|
+
private callAIPreview;
|
|
68
|
+
/**
|
|
69
|
+
* Call AI in production mode (through OpenCloud API)
|
|
70
|
+
*/
|
|
71
|
+
private callAIProduction;
|
|
72
|
+
/**
|
|
73
|
+
* Get current user's balance (production only)
|
|
74
|
+
*/
|
|
75
|
+
getBalance(): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Request user to purchase more tokens
|
|
78
|
+
*/
|
|
79
|
+
requestTopUp(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Start sending heartbeats
|
|
82
|
+
*/
|
|
83
|
+
private startHeartbeat;
|
|
84
|
+
/**
|
|
85
|
+
* Send heartbeat to platform
|
|
86
|
+
*/
|
|
87
|
+
private sendHeartbeat;
|
|
88
|
+
/**
|
|
89
|
+
* Get user session token
|
|
90
|
+
*/
|
|
91
|
+
private getUserSession;
|
|
92
|
+
/**
|
|
93
|
+
* Cleanup
|
|
94
|
+
*/
|
|
95
|
+
destroy(): void;
|
|
96
|
+
}
|
|
97
|
+
declare const opencloud: OpenCloudSDK;
|
|
98
|
+
|
|
99
|
+
export { type AICallParams, type AIResponse, type OpenCloudConfig, OpenCloudSDK, type UserSession, opencloud };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @opencloud/sdk v1.1.0
|
|
3
|
+
* Official SDK for OpenCloud - AI App Marketplace
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Automatic preview/production mode detection
|
|
7
|
+
* - Works in bolt.diy/WebContainers preview
|
|
8
|
+
* - Seamless transition to production with credits
|
|
9
|
+
*/
|
|
10
|
+
interface OpenCloudConfig {
|
|
11
|
+
appId: string;
|
|
12
|
+
apiUrl?: string;
|
|
13
|
+
previewApiKey?: string;
|
|
14
|
+
}
|
|
15
|
+
interface AICallParams {
|
|
16
|
+
model: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
options?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
interface AIResponse {
|
|
21
|
+
success: boolean;
|
|
22
|
+
text: string;
|
|
23
|
+
usage: {
|
|
24
|
+
promptTokens: number;
|
|
25
|
+
completionTokens: number;
|
|
26
|
+
totalTokens: number;
|
|
27
|
+
};
|
|
28
|
+
cost: number;
|
|
29
|
+
model: string;
|
|
30
|
+
mode: 'preview' | 'production';
|
|
31
|
+
}
|
|
32
|
+
interface UserSession {
|
|
33
|
+
token: string;
|
|
34
|
+
}
|
|
35
|
+
declare class OpenCloudSDK {
|
|
36
|
+
private config;
|
|
37
|
+
private heartbeatInterval?;
|
|
38
|
+
private _isPreviewMode;
|
|
39
|
+
constructor();
|
|
40
|
+
/**
|
|
41
|
+
* Detect if running in preview/development mode
|
|
42
|
+
*/
|
|
43
|
+
private detectPreviewMode;
|
|
44
|
+
/**
|
|
45
|
+
* Check if currently in preview mode
|
|
46
|
+
*/
|
|
47
|
+
isPreview(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Initialize the SDK
|
|
50
|
+
*/
|
|
51
|
+
init(options: OpenCloudConfig): void;
|
|
52
|
+
/**
|
|
53
|
+
* Set API key for preview mode
|
|
54
|
+
*/
|
|
55
|
+
setPreviewApiKey(key: string, provider?: 'openrouter' | 'anthropic' | 'openai'): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get API key for preview mode
|
|
58
|
+
*/
|
|
59
|
+
private getPreviewApiKey;
|
|
60
|
+
/**
|
|
61
|
+
* Call an AI model - automatically uses preview or production mode
|
|
62
|
+
*/
|
|
63
|
+
callAI(params: AICallParams): Promise<AIResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Call AI in preview mode (direct to OpenRouter/provider)
|
|
66
|
+
*/
|
|
67
|
+
private callAIPreview;
|
|
68
|
+
/**
|
|
69
|
+
* Call AI in production mode (through OpenCloud API)
|
|
70
|
+
*/
|
|
71
|
+
private callAIProduction;
|
|
72
|
+
/**
|
|
73
|
+
* Get current user's balance (production only)
|
|
74
|
+
*/
|
|
75
|
+
getBalance(): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Request user to purchase more tokens
|
|
78
|
+
*/
|
|
79
|
+
requestTopUp(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Start sending heartbeats
|
|
82
|
+
*/
|
|
83
|
+
private startHeartbeat;
|
|
84
|
+
/**
|
|
85
|
+
* Send heartbeat to platform
|
|
86
|
+
*/
|
|
87
|
+
private sendHeartbeat;
|
|
88
|
+
/**
|
|
89
|
+
* Get user session token
|
|
90
|
+
*/
|
|
91
|
+
private getUserSession;
|
|
92
|
+
/**
|
|
93
|
+
* Cleanup
|
|
94
|
+
*/
|
|
95
|
+
destroy(): void;
|
|
96
|
+
}
|
|
97
|
+
declare const opencloud: OpenCloudSDK;
|
|
98
|
+
|
|
99
|
+
export { type AICallParams, type AIResponse, type OpenCloudConfig, OpenCloudSDK, type UserSession, opencloud };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
OpenCloudSDK: () => OpenCloudSDK,
|
|
24
|
+
opencloud: () => opencloud
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var STORAGE_KEYS = {
|
|
28
|
+
OPENROUTER_KEY: "openrouter_api_key",
|
|
29
|
+
ANTHROPIC_KEY: "anthropic_api_key",
|
|
30
|
+
OPENAI_KEY: "openai_api_key",
|
|
31
|
+
PREVIEW_PROVIDER: "opencloud_preview_provider"
|
|
32
|
+
};
|
|
33
|
+
var OpenCloudSDK = class {
|
|
34
|
+
constructor() {
|
|
35
|
+
this._isPreviewMode = false;
|
|
36
|
+
this.config = {
|
|
37
|
+
appId: "",
|
|
38
|
+
apiUrl: typeof window !== "undefined" ? window.location.origin : "http://localhost:3000",
|
|
39
|
+
previewApiKey: void 0
|
|
40
|
+
};
|
|
41
|
+
if (typeof window !== "undefined") {
|
|
42
|
+
this._isPreviewMode = this.detectPreviewMode();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Detect if running in preview/development mode
|
|
47
|
+
*/
|
|
48
|
+
detectPreviewMode() {
|
|
49
|
+
if (typeof window === "undefined") return false;
|
|
50
|
+
const hostname = window.location.hostname;
|
|
51
|
+
return hostname.includes("webcontainer") || hostname.includes("localhost") || hostname.includes("127.0.0.1") || hostname.includes(".local") || hostname.includes("stackblitz") || hostname.includes("codesandbox");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if currently in preview mode
|
|
55
|
+
*/
|
|
56
|
+
isPreview() {
|
|
57
|
+
return this._isPreviewMode;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Initialize the SDK
|
|
61
|
+
*/
|
|
62
|
+
init(options) {
|
|
63
|
+
if (!options.appId) {
|
|
64
|
+
throw new Error("OpenCloudSDK: appId is required");
|
|
65
|
+
}
|
|
66
|
+
this.config = {
|
|
67
|
+
appId: options.appId,
|
|
68
|
+
apiUrl: options.apiUrl || this.config.apiUrl,
|
|
69
|
+
previewApiKey: options.previewApiKey
|
|
70
|
+
};
|
|
71
|
+
if (!this._isPreviewMode) {
|
|
72
|
+
this.startHeartbeat();
|
|
73
|
+
}
|
|
74
|
+
const mode = this._isPreviewMode ? "preview" : "production";
|
|
75
|
+
console.log(`[OpenCloudSDK] Initialized for app: ${options.appId} (${mode} mode)`);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Set API key for preview mode
|
|
79
|
+
*/
|
|
80
|
+
setPreviewApiKey(key, provider = "openrouter") {
|
|
81
|
+
if (typeof window === "undefined") return;
|
|
82
|
+
const storageKey = provider === "openrouter" ? STORAGE_KEYS.OPENROUTER_KEY : provider === "anthropic" ? STORAGE_KEYS.ANTHROPIC_KEY : STORAGE_KEYS.OPENAI_KEY;
|
|
83
|
+
localStorage.setItem(storageKey, key);
|
|
84
|
+
localStorage.setItem(STORAGE_KEYS.PREVIEW_PROVIDER, provider);
|
|
85
|
+
this.config.previewApiKey = key;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get API key for preview mode
|
|
89
|
+
*/
|
|
90
|
+
getPreviewApiKey() {
|
|
91
|
+
if (typeof window === "undefined") return null;
|
|
92
|
+
if (this.config.previewApiKey) {
|
|
93
|
+
return { key: this.config.previewApiKey, provider: "openrouter" };
|
|
94
|
+
}
|
|
95
|
+
const provider = localStorage.getItem(STORAGE_KEYS.PREVIEW_PROVIDER) || "openrouter";
|
|
96
|
+
const storageKey = provider === "openrouter" ? STORAGE_KEYS.OPENROUTER_KEY : provider === "anthropic" ? STORAGE_KEYS.ANTHROPIC_KEY : STORAGE_KEYS.OPENAI_KEY;
|
|
97
|
+
const key = localStorage.getItem(storageKey);
|
|
98
|
+
if (key) {
|
|
99
|
+
return { key, provider };
|
|
100
|
+
}
|
|
101
|
+
for (const [name, storageKey2] of Object.entries(STORAGE_KEYS)) {
|
|
102
|
+
if (name === "PREVIEW_PROVIDER") continue;
|
|
103
|
+
const key2 = localStorage.getItem(storageKey2);
|
|
104
|
+
if (key2) {
|
|
105
|
+
return { key: key2, provider: name.replace("_KEY", "").toLowerCase() };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Call an AI model - automatically uses preview or production mode
|
|
112
|
+
*/
|
|
113
|
+
async callAI(params) {
|
|
114
|
+
if (!this.config.appId) {
|
|
115
|
+
throw new Error("OpenCloudSDK: Must call init() before using callAI()");
|
|
116
|
+
}
|
|
117
|
+
const { model, prompt, options = {} } = params;
|
|
118
|
+
if (!model || !prompt) {
|
|
119
|
+
throw new Error("OpenCloudSDK: model and prompt are required");
|
|
120
|
+
}
|
|
121
|
+
if (this._isPreviewMode) {
|
|
122
|
+
return this.callAIPreview(params);
|
|
123
|
+
} else {
|
|
124
|
+
return this.callAIProduction(params);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Call AI in preview mode (direct to OpenRouter/provider)
|
|
129
|
+
*/
|
|
130
|
+
async callAIPreview(params) {
|
|
131
|
+
const { model, prompt, options = {} } = params;
|
|
132
|
+
const apiKeyInfo = this.getPreviewApiKey();
|
|
133
|
+
if (!apiKeyInfo) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
'[OpenCloudSDK Preview] No API key found. Please set your API key using opencloud.setPreviewApiKey("your-key") or configure it in the builder settings.'
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const { key, provider } = apiKeyInfo;
|
|
139
|
+
try {
|
|
140
|
+
let response;
|
|
141
|
+
let responseData;
|
|
142
|
+
if (provider === "openrouter" || provider === "openrouter_key") {
|
|
143
|
+
response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: {
|
|
146
|
+
"Content-Type": "application/json",
|
|
147
|
+
"Authorization": `Bearer ${key}`,
|
|
148
|
+
"HTTP-Referer": window.location.origin,
|
|
149
|
+
"X-Title": this.config.appId
|
|
150
|
+
},
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
model,
|
|
153
|
+
messages: [{ role: "user", content: prompt }],
|
|
154
|
+
...options
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
responseData = await response.json();
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(responseData.error?.message || `OpenRouter API error: ${response.status}`);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
text: responseData.choices?.[0]?.message?.content || "",
|
|
164
|
+
usage: {
|
|
165
|
+
promptTokens: responseData.usage?.prompt_tokens || 0,
|
|
166
|
+
completionTokens: responseData.usage?.completion_tokens || 0,
|
|
167
|
+
totalTokens: responseData.usage?.total_tokens || 0
|
|
168
|
+
},
|
|
169
|
+
cost: 0,
|
|
170
|
+
// Preview mode - no cost tracking
|
|
171
|
+
model,
|
|
172
|
+
mode: "preview"
|
|
173
|
+
};
|
|
174
|
+
} else if (provider === "anthropic" || provider === "anthropic_key") {
|
|
175
|
+
response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: {
|
|
178
|
+
"Content-Type": "application/json",
|
|
179
|
+
"x-api-key": key,
|
|
180
|
+
"anthropic-version": "2023-06-01",
|
|
181
|
+
"anthropic-dangerous-direct-browser-access": "true"
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
model: model.replace("anthropic/", ""),
|
|
185
|
+
max_tokens: options.max_tokens || 1024,
|
|
186
|
+
messages: [{ role: "user", content: prompt }]
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
responseData = await response.json();
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
throw new Error(responseData.error?.message || `Anthropic API error: ${response.status}`);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
text: responseData.content?.[0]?.text || "",
|
|
196
|
+
usage: {
|
|
197
|
+
promptTokens: responseData.usage?.input_tokens || 0,
|
|
198
|
+
completionTokens: responseData.usage?.output_tokens || 0,
|
|
199
|
+
totalTokens: (responseData.usage?.input_tokens || 0) + (responseData.usage?.output_tokens || 0)
|
|
200
|
+
},
|
|
201
|
+
cost: 0,
|
|
202
|
+
model,
|
|
203
|
+
mode: "preview"
|
|
204
|
+
};
|
|
205
|
+
} else if (provider === "openai" || provider === "openai_key") {
|
|
206
|
+
response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: {
|
|
209
|
+
"Content-Type": "application/json",
|
|
210
|
+
"Authorization": `Bearer ${key}`
|
|
211
|
+
},
|
|
212
|
+
body: JSON.stringify({
|
|
213
|
+
model: model.replace("openai/", ""),
|
|
214
|
+
messages: [{ role: "user", content: prompt }],
|
|
215
|
+
...options
|
|
216
|
+
})
|
|
217
|
+
});
|
|
218
|
+
responseData = await response.json();
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
throw new Error(responseData.error?.message || `OpenAI API error: ${response.status}`);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
success: true,
|
|
224
|
+
text: responseData.choices?.[0]?.message?.content || "",
|
|
225
|
+
usage: {
|
|
226
|
+
promptTokens: responseData.usage?.prompt_tokens || 0,
|
|
227
|
+
completionTokens: responseData.usage?.completion_tokens || 0,
|
|
228
|
+
totalTokens: responseData.usage?.total_tokens || 0
|
|
229
|
+
},
|
|
230
|
+
cost: 0,
|
|
231
|
+
model,
|
|
232
|
+
mode: "preview"
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error("[OpenCloudSDK Preview] AI call failed:", error);
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Call AI in production mode (through OpenCloud API)
|
|
243
|
+
*/
|
|
244
|
+
async callAIProduction(params) {
|
|
245
|
+
const { model, prompt, options = {} } = params;
|
|
246
|
+
try {
|
|
247
|
+
const session = await this.getUserSession();
|
|
248
|
+
const response = await fetch(`${this.config.apiUrl}/api/ai/call`, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: {
|
|
251
|
+
"Content-Type": "application/json",
|
|
252
|
+
"Authorization": `Bearer ${session.token}`
|
|
253
|
+
},
|
|
254
|
+
body: JSON.stringify({
|
|
255
|
+
appId: this.config.appId,
|
|
256
|
+
model,
|
|
257
|
+
prompt,
|
|
258
|
+
options
|
|
259
|
+
})
|
|
260
|
+
});
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
const error = await response.json();
|
|
263
|
+
throw new Error(error.error || `API call failed: ${response.status}`);
|
|
264
|
+
}
|
|
265
|
+
const data = await response.json();
|
|
266
|
+
return {
|
|
267
|
+
...data,
|
|
268
|
+
mode: "production"
|
|
269
|
+
};
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error("[OpenCloudSDK Production] AI call failed:", error);
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get current user's balance (production only)
|
|
277
|
+
*/
|
|
278
|
+
async getBalance() {
|
|
279
|
+
if (this._isPreviewMode) {
|
|
280
|
+
console.warn("[OpenCloudSDK] getBalance() not available in preview mode");
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const session = await this.getUserSession();
|
|
285
|
+
const response = await fetch(`${this.config.apiUrl}/api/user/balance`, {
|
|
286
|
+
headers: {
|
|
287
|
+
"Authorization": `Bearer ${session.token}`
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
if (!response.ok) {
|
|
291
|
+
throw new Error("Failed to get balance");
|
|
292
|
+
}
|
|
293
|
+
const data = await response.json();
|
|
294
|
+
return data.balance;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error("[OpenCloudSDK] Get balance failed:", error);
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Request user to purchase more tokens
|
|
302
|
+
*/
|
|
303
|
+
async requestTopUp() {
|
|
304
|
+
if (typeof window === "undefined") return;
|
|
305
|
+
if (this._isPreviewMode) {
|
|
306
|
+
console.warn("[OpenCloudSDK] Top-up not available in preview mode");
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (window.parent !== window) {
|
|
310
|
+
window.parent.postMessage({
|
|
311
|
+
type: "OPENCLOUD_REQUEST_TOPUP",
|
|
312
|
+
appId: this.config.appId
|
|
313
|
+
}, "*");
|
|
314
|
+
} else {
|
|
315
|
+
window.location.href = `${this.config.apiUrl}/topup`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Start sending heartbeats
|
|
320
|
+
*/
|
|
321
|
+
startHeartbeat() {
|
|
322
|
+
if (typeof window === "undefined") return;
|
|
323
|
+
this.sendHeartbeat();
|
|
324
|
+
this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), 3e4);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Send heartbeat to platform
|
|
328
|
+
*/
|
|
329
|
+
sendHeartbeat() {
|
|
330
|
+
if (!this.config.appId || typeof window === "undefined") return;
|
|
331
|
+
const data = JSON.stringify({
|
|
332
|
+
appId: this.config.appId,
|
|
333
|
+
timestamp: Date.now(),
|
|
334
|
+
version: "1.1.0",
|
|
335
|
+
url: window.location.href
|
|
336
|
+
});
|
|
337
|
+
if (navigator.sendBeacon) {
|
|
338
|
+
navigator.sendBeacon(`${this.config.apiUrl}/api/sdk/heartbeat`, data);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get user session token
|
|
343
|
+
*/
|
|
344
|
+
async getUserSession() {
|
|
345
|
+
if (typeof window === "undefined") {
|
|
346
|
+
throw new Error("getUserSession can only be called in browser");
|
|
347
|
+
}
|
|
348
|
+
return new Promise((resolve, reject) => {
|
|
349
|
+
if (window.parent !== window) {
|
|
350
|
+
const messageHandler = (event) => {
|
|
351
|
+
if (event.data.type === "USER_SESSION_RESPONSE") {
|
|
352
|
+
window.removeEventListener("message", messageHandler);
|
|
353
|
+
if (event.data.session) {
|
|
354
|
+
resolve(event.data.session);
|
|
355
|
+
} else {
|
|
356
|
+
reject(new Error("User not authenticated"));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
window.addEventListener("message", messageHandler);
|
|
361
|
+
window.parent.postMessage({
|
|
362
|
+
type: "GET_USER_SESSION",
|
|
363
|
+
appId: this.config.appId
|
|
364
|
+
}, "*");
|
|
365
|
+
setTimeout(() => {
|
|
366
|
+
window.removeEventListener("message", messageHandler);
|
|
367
|
+
reject(new Error("Session request timeout"));
|
|
368
|
+
}, 5e3);
|
|
369
|
+
} else {
|
|
370
|
+
const token = localStorage.getItem("opencloud_session_token");
|
|
371
|
+
if (token) {
|
|
372
|
+
resolve({ token });
|
|
373
|
+
} else {
|
|
374
|
+
reject(new Error("User not authenticated. Please log in."));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Cleanup
|
|
381
|
+
*/
|
|
382
|
+
destroy() {
|
|
383
|
+
if (this.heartbeatInterval) {
|
|
384
|
+
clearInterval(this.heartbeatInterval);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
var opencloud = new OpenCloudSDK();
|
|
389
|
+
if (typeof window !== "undefined") {
|
|
390
|
+
window.OpenCloudSDK = opencloud;
|
|
391
|
+
window.PlatformSDK = opencloud;
|
|
392
|
+
}
|
|
393
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
394
|
+
0 && (module.exports = {
|
|
395
|
+
OpenCloudSDK,
|
|
396
|
+
opencloud
|
|
397
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var STORAGE_KEYS = {
|
|
3
|
+
OPENROUTER_KEY: "openrouter_api_key",
|
|
4
|
+
ANTHROPIC_KEY: "anthropic_api_key",
|
|
5
|
+
OPENAI_KEY: "openai_api_key",
|
|
6
|
+
PREVIEW_PROVIDER: "opencloud_preview_provider"
|
|
7
|
+
};
|
|
8
|
+
var OpenCloudSDK = class {
|
|
9
|
+
constructor() {
|
|
10
|
+
this._isPreviewMode = false;
|
|
11
|
+
this.config = {
|
|
12
|
+
appId: "",
|
|
13
|
+
apiUrl: typeof window !== "undefined" ? window.location.origin : "http://localhost:3000",
|
|
14
|
+
previewApiKey: void 0
|
|
15
|
+
};
|
|
16
|
+
if (typeof window !== "undefined") {
|
|
17
|
+
this._isPreviewMode = this.detectPreviewMode();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Detect if running in preview/development mode
|
|
22
|
+
*/
|
|
23
|
+
detectPreviewMode() {
|
|
24
|
+
if (typeof window === "undefined") return false;
|
|
25
|
+
const hostname = window.location.hostname;
|
|
26
|
+
return hostname.includes("webcontainer") || hostname.includes("localhost") || hostname.includes("127.0.0.1") || hostname.includes(".local") || hostname.includes("stackblitz") || hostname.includes("codesandbox");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if currently in preview mode
|
|
30
|
+
*/
|
|
31
|
+
isPreview() {
|
|
32
|
+
return this._isPreviewMode;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the SDK
|
|
36
|
+
*/
|
|
37
|
+
init(options) {
|
|
38
|
+
if (!options.appId) {
|
|
39
|
+
throw new Error("OpenCloudSDK: appId is required");
|
|
40
|
+
}
|
|
41
|
+
this.config = {
|
|
42
|
+
appId: options.appId,
|
|
43
|
+
apiUrl: options.apiUrl || this.config.apiUrl,
|
|
44
|
+
previewApiKey: options.previewApiKey
|
|
45
|
+
};
|
|
46
|
+
if (!this._isPreviewMode) {
|
|
47
|
+
this.startHeartbeat();
|
|
48
|
+
}
|
|
49
|
+
const mode = this._isPreviewMode ? "preview" : "production";
|
|
50
|
+
console.log(`[OpenCloudSDK] Initialized for app: ${options.appId} (${mode} mode)`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set API key for preview mode
|
|
54
|
+
*/
|
|
55
|
+
setPreviewApiKey(key, provider = "openrouter") {
|
|
56
|
+
if (typeof window === "undefined") return;
|
|
57
|
+
const storageKey = provider === "openrouter" ? STORAGE_KEYS.OPENROUTER_KEY : provider === "anthropic" ? STORAGE_KEYS.ANTHROPIC_KEY : STORAGE_KEYS.OPENAI_KEY;
|
|
58
|
+
localStorage.setItem(storageKey, key);
|
|
59
|
+
localStorage.setItem(STORAGE_KEYS.PREVIEW_PROVIDER, provider);
|
|
60
|
+
this.config.previewApiKey = key;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get API key for preview mode
|
|
64
|
+
*/
|
|
65
|
+
getPreviewApiKey() {
|
|
66
|
+
if (typeof window === "undefined") return null;
|
|
67
|
+
if (this.config.previewApiKey) {
|
|
68
|
+
return { key: this.config.previewApiKey, provider: "openrouter" };
|
|
69
|
+
}
|
|
70
|
+
const provider = localStorage.getItem(STORAGE_KEYS.PREVIEW_PROVIDER) || "openrouter";
|
|
71
|
+
const storageKey = provider === "openrouter" ? STORAGE_KEYS.OPENROUTER_KEY : provider === "anthropic" ? STORAGE_KEYS.ANTHROPIC_KEY : STORAGE_KEYS.OPENAI_KEY;
|
|
72
|
+
const key = localStorage.getItem(storageKey);
|
|
73
|
+
if (key) {
|
|
74
|
+
return { key, provider };
|
|
75
|
+
}
|
|
76
|
+
for (const [name, storageKey2] of Object.entries(STORAGE_KEYS)) {
|
|
77
|
+
if (name === "PREVIEW_PROVIDER") continue;
|
|
78
|
+
const key2 = localStorage.getItem(storageKey2);
|
|
79
|
+
if (key2) {
|
|
80
|
+
return { key: key2, provider: name.replace("_KEY", "").toLowerCase() };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Call an AI model - automatically uses preview or production mode
|
|
87
|
+
*/
|
|
88
|
+
async callAI(params) {
|
|
89
|
+
if (!this.config.appId) {
|
|
90
|
+
throw new Error("OpenCloudSDK: Must call init() before using callAI()");
|
|
91
|
+
}
|
|
92
|
+
const { model, prompt, options = {} } = params;
|
|
93
|
+
if (!model || !prompt) {
|
|
94
|
+
throw new Error("OpenCloudSDK: model and prompt are required");
|
|
95
|
+
}
|
|
96
|
+
if (this._isPreviewMode) {
|
|
97
|
+
return this.callAIPreview(params);
|
|
98
|
+
} else {
|
|
99
|
+
return this.callAIProduction(params);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Call AI in preview mode (direct to OpenRouter/provider)
|
|
104
|
+
*/
|
|
105
|
+
async callAIPreview(params) {
|
|
106
|
+
const { model, prompt, options = {} } = params;
|
|
107
|
+
const apiKeyInfo = this.getPreviewApiKey();
|
|
108
|
+
if (!apiKeyInfo) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'[OpenCloudSDK Preview] No API key found. Please set your API key using opencloud.setPreviewApiKey("your-key") or configure it in the builder settings.'
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const { key, provider } = apiKeyInfo;
|
|
114
|
+
try {
|
|
115
|
+
let response;
|
|
116
|
+
let responseData;
|
|
117
|
+
if (provider === "openrouter" || provider === "openrouter_key") {
|
|
118
|
+
response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
"Authorization": `Bearer ${key}`,
|
|
123
|
+
"HTTP-Referer": window.location.origin,
|
|
124
|
+
"X-Title": this.config.appId
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify({
|
|
127
|
+
model,
|
|
128
|
+
messages: [{ role: "user", content: prompt }],
|
|
129
|
+
...options
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
responseData = await response.json();
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
throw new Error(responseData.error?.message || `OpenRouter API error: ${response.status}`);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
text: responseData.choices?.[0]?.message?.content || "",
|
|
139
|
+
usage: {
|
|
140
|
+
promptTokens: responseData.usage?.prompt_tokens || 0,
|
|
141
|
+
completionTokens: responseData.usage?.completion_tokens || 0,
|
|
142
|
+
totalTokens: responseData.usage?.total_tokens || 0
|
|
143
|
+
},
|
|
144
|
+
cost: 0,
|
|
145
|
+
// Preview mode - no cost tracking
|
|
146
|
+
model,
|
|
147
|
+
mode: "preview"
|
|
148
|
+
};
|
|
149
|
+
} else if (provider === "anthropic" || provider === "anthropic_key") {
|
|
150
|
+
response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
"x-api-key": key,
|
|
155
|
+
"anthropic-version": "2023-06-01",
|
|
156
|
+
"anthropic-dangerous-direct-browser-access": "true"
|
|
157
|
+
},
|
|
158
|
+
body: JSON.stringify({
|
|
159
|
+
model: model.replace("anthropic/", ""),
|
|
160
|
+
max_tokens: options.max_tokens || 1024,
|
|
161
|
+
messages: [{ role: "user", content: prompt }]
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
responseData = await response.json();
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
throw new Error(responseData.error?.message || `Anthropic API error: ${response.status}`);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
text: responseData.content?.[0]?.text || "",
|
|
171
|
+
usage: {
|
|
172
|
+
promptTokens: responseData.usage?.input_tokens || 0,
|
|
173
|
+
completionTokens: responseData.usage?.output_tokens || 0,
|
|
174
|
+
totalTokens: (responseData.usage?.input_tokens || 0) + (responseData.usage?.output_tokens || 0)
|
|
175
|
+
},
|
|
176
|
+
cost: 0,
|
|
177
|
+
model,
|
|
178
|
+
mode: "preview"
|
|
179
|
+
};
|
|
180
|
+
} else if (provider === "openai" || provider === "openai_key") {
|
|
181
|
+
response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: {
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
"Authorization": `Bearer ${key}`
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify({
|
|
188
|
+
model: model.replace("openai/", ""),
|
|
189
|
+
messages: [{ role: "user", content: prompt }],
|
|
190
|
+
...options
|
|
191
|
+
})
|
|
192
|
+
});
|
|
193
|
+
responseData = await response.json();
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
throw new Error(responseData.error?.message || `OpenAI API error: ${response.status}`);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
success: true,
|
|
199
|
+
text: responseData.choices?.[0]?.message?.content || "",
|
|
200
|
+
usage: {
|
|
201
|
+
promptTokens: responseData.usage?.prompt_tokens || 0,
|
|
202
|
+
completionTokens: responseData.usage?.completion_tokens || 0,
|
|
203
|
+
totalTokens: responseData.usage?.total_tokens || 0
|
|
204
|
+
},
|
|
205
|
+
cost: 0,
|
|
206
|
+
model,
|
|
207
|
+
mode: "preview"
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error("[OpenCloudSDK Preview] AI call failed:", error);
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Call AI in production mode (through OpenCloud API)
|
|
218
|
+
*/
|
|
219
|
+
async callAIProduction(params) {
|
|
220
|
+
const { model, prompt, options = {} } = params;
|
|
221
|
+
try {
|
|
222
|
+
const session = await this.getUserSession();
|
|
223
|
+
const response = await fetch(`${this.config.apiUrl}/api/ai/call`, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: {
|
|
226
|
+
"Content-Type": "application/json",
|
|
227
|
+
"Authorization": `Bearer ${session.token}`
|
|
228
|
+
},
|
|
229
|
+
body: JSON.stringify({
|
|
230
|
+
appId: this.config.appId,
|
|
231
|
+
model,
|
|
232
|
+
prompt,
|
|
233
|
+
options
|
|
234
|
+
})
|
|
235
|
+
});
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
const error = await response.json();
|
|
238
|
+
throw new Error(error.error || `API call failed: ${response.status}`);
|
|
239
|
+
}
|
|
240
|
+
const data = await response.json();
|
|
241
|
+
return {
|
|
242
|
+
...data,
|
|
243
|
+
mode: "production"
|
|
244
|
+
};
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error("[OpenCloudSDK Production] AI call failed:", error);
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get current user's balance (production only)
|
|
252
|
+
*/
|
|
253
|
+
async getBalance() {
|
|
254
|
+
if (this._isPreviewMode) {
|
|
255
|
+
console.warn("[OpenCloudSDK] getBalance() not available in preview mode");
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const session = await this.getUserSession();
|
|
260
|
+
const response = await fetch(`${this.config.apiUrl}/api/user/balance`, {
|
|
261
|
+
headers: {
|
|
262
|
+
"Authorization": `Bearer ${session.token}`
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
throw new Error("Failed to get balance");
|
|
267
|
+
}
|
|
268
|
+
const data = await response.json();
|
|
269
|
+
return data.balance;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error("[OpenCloudSDK] Get balance failed:", error);
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Request user to purchase more tokens
|
|
277
|
+
*/
|
|
278
|
+
async requestTopUp() {
|
|
279
|
+
if (typeof window === "undefined") return;
|
|
280
|
+
if (this._isPreviewMode) {
|
|
281
|
+
console.warn("[OpenCloudSDK] Top-up not available in preview mode");
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (window.parent !== window) {
|
|
285
|
+
window.parent.postMessage({
|
|
286
|
+
type: "OPENCLOUD_REQUEST_TOPUP",
|
|
287
|
+
appId: this.config.appId
|
|
288
|
+
}, "*");
|
|
289
|
+
} else {
|
|
290
|
+
window.location.href = `${this.config.apiUrl}/topup`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Start sending heartbeats
|
|
295
|
+
*/
|
|
296
|
+
startHeartbeat() {
|
|
297
|
+
if (typeof window === "undefined") return;
|
|
298
|
+
this.sendHeartbeat();
|
|
299
|
+
this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), 3e4);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Send heartbeat to platform
|
|
303
|
+
*/
|
|
304
|
+
sendHeartbeat() {
|
|
305
|
+
if (!this.config.appId || typeof window === "undefined") return;
|
|
306
|
+
const data = JSON.stringify({
|
|
307
|
+
appId: this.config.appId,
|
|
308
|
+
timestamp: Date.now(),
|
|
309
|
+
version: "1.1.0",
|
|
310
|
+
url: window.location.href
|
|
311
|
+
});
|
|
312
|
+
if (navigator.sendBeacon) {
|
|
313
|
+
navigator.sendBeacon(`${this.config.apiUrl}/api/sdk/heartbeat`, data);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get user session token
|
|
318
|
+
*/
|
|
319
|
+
async getUserSession() {
|
|
320
|
+
if (typeof window === "undefined") {
|
|
321
|
+
throw new Error("getUserSession can only be called in browser");
|
|
322
|
+
}
|
|
323
|
+
return new Promise((resolve, reject) => {
|
|
324
|
+
if (window.parent !== window) {
|
|
325
|
+
const messageHandler = (event) => {
|
|
326
|
+
if (event.data.type === "USER_SESSION_RESPONSE") {
|
|
327
|
+
window.removeEventListener("message", messageHandler);
|
|
328
|
+
if (event.data.session) {
|
|
329
|
+
resolve(event.data.session);
|
|
330
|
+
} else {
|
|
331
|
+
reject(new Error("User not authenticated"));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
window.addEventListener("message", messageHandler);
|
|
336
|
+
window.parent.postMessage({
|
|
337
|
+
type: "GET_USER_SESSION",
|
|
338
|
+
appId: this.config.appId
|
|
339
|
+
}, "*");
|
|
340
|
+
setTimeout(() => {
|
|
341
|
+
window.removeEventListener("message", messageHandler);
|
|
342
|
+
reject(new Error("Session request timeout"));
|
|
343
|
+
}, 5e3);
|
|
344
|
+
} else {
|
|
345
|
+
const token = localStorage.getItem("opencloud_session_token");
|
|
346
|
+
if (token) {
|
|
347
|
+
resolve({ token });
|
|
348
|
+
} else {
|
|
349
|
+
reject(new Error("User not authenticated. Please log in."));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Cleanup
|
|
356
|
+
*/
|
|
357
|
+
destroy() {
|
|
358
|
+
if (this.heartbeatInterval) {
|
|
359
|
+
clearInterval(this.heartbeatInterval);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
var opencloud = new OpenCloudSDK();
|
|
364
|
+
if (typeof window !== "undefined") {
|
|
365
|
+
window.OpenCloudSDK = opencloud;
|
|
366
|
+
window.PlatformSDK = opencloud;
|
|
367
|
+
}
|
|
368
|
+
export {
|
|
369
|
+
OpenCloudSDK,
|
|
370
|
+
opencloud
|
|
371
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencloud-platform-sdk",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Official SDK for OpenCloud - AI App Marketplace",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
13
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"opencloud",
|
|
18
|
+
"ai",
|
|
19
|
+
"sdk",
|
|
20
|
+
"marketplace",
|
|
21
|
+
"openrouter",
|
|
22
|
+
"gpt",
|
|
23
|
+
"claude"
|
|
24
|
+
],
|
|
25
|
+
"author": "OpenCloud",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/opencloud/sdk"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "^8.0.1",
|
|
33
|
+
"typescript": "^5.3.3"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.mjs",
|
|
39
|
+
"require": "./dist/index.js"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|