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 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
@@ -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 };
@@ -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
+ }