opencloud-platform-sdk 1.3.0 → 3.0.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 CHANGED
@@ -1,210 +1,266 @@
1
- # @opencloud/sdk
1
+ # opencloud-platform-sdk
2
2
 
3
- Official SDK for **OpenCloud** - The AI App Marketplace with pay-per-use model.
3
+ Official SDK for **OpenCloud** - Monetize your AI apps with ease.
4
4
 
5
- Build AI-powered apps that automatically handle payments and AI API calls through OpenCloud's platform.
5
+ **You control the AI, we handle the payments.**
6
6
 
7
- ## Features
7
+ ## What's New in v3.0
8
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
9
+ - **Standalone Mode** - Works on any domain, no iframe required
10
+ - **Popup Authentication** - Secure OAuth-style login via popup
11
+ - **Automatic Login** - SDK prompts for login when charging
12
+ - **Cross-Domain Support** - Full CORS support for external apps
15
13
 
16
14
  ## Installation
17
15
 
18
16
  ```bash
19
- npm install @opencloud/sdk
20
- # or
21
- pnpm add @opencloud/sdk
22
- # or
23
- yarn add @opencloud/sdk
17
+ npm install opencloud-platform-sdk
24
18
  ```
25
19
 
26
20
  ## Quick Start
27
21
 
28
- ### For React/Next.js Apps
29
-
30
22
  ```typescript
31
- import { opencloud } from '@opencloud/sdk';
23
+ import { opencloud } from 'opencloud-platform-sdk';
32
24
 
33
- // Initialize with your app ID
25
+ // 1. Initialize with your app ID (slug)
34
26
  opencloud.init({ appId: 'your-app-slug' });
35
27
 
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
- ```
28
+ // 2. Charge the user (opens login popup if needed)
29
+ async function handleAIFeature() {
30
+ const result = await opencloud.charge('generate_image');
45
31
 
46
- ### For HTML/Vanilla JS
32
+ if (!result.success) {
33
+ if (result.error === 'CANCELLED') {
34
+ // User closed the login popup
35
+ return;
36
+ }
37
+ if (result.error === 'INSUFFICIENT_BALANCE') {
38
+ // SDK already opened top-up popup
39
+ return;
40
+ }
41
+ }
47
42
 
48
- ```html
49
- <script src="https://opencloud.com/sdk/platform-sdk.js"></script>
43
+ // Charge successful! Now run your AI logic
44
+ const image = await myAIService.generate(prompt);
45
+ return image;
46
+ }
47
+ ```
50
48
 
51
- <script>
52
- PlatformSDK.init({ appId: 'your-app-slug' });
49
+ ## How It Works
53
50
 
54
- async function generate() {
55
- const result = await PlatformSDK.callAI({
56
- model: 'anthropic/claude-3-5-sonnet',
57
- prompt: 'Generate a logo description'
58
- });
51
+ 1. User visits your app (e.g., `https://your-app.vercel.app`)
52
+ 2. User clicks "Generate Image" (or any paid feature)
53
+ 3. SDK checks if user has OpenCloud session
54
+ 4. If not logged in → Opens OpenCloud login popup
55
+ 5. User logs in → Popup closes → SDK gets session token
56
+ 6. SDK charges user's OpenCloud wallet
57
+ 7. Your app continues with the AI feature
59
58
 
60
- console.log(result.text);
61
- }
62
- </script>
59
+ ```
60
+ Your App OpenCloud
61
+ │ │
62
+ │ User clicks "Generate" │
63
+ │ ─────────────────────────────> │
64
+ │ │
65
+ │ SDK: opencloud.charge() │
66
+ │ ─────────────────────────────> │
67
+ │ │
68
+ │ No session? Open popup │
69
+ │ <───────────────────────────── │
70
+ │ │
71
+ │ User logs in via popup │
72
+ │ ─────────────────────────────> │
73
+ │ │
74
+ │ Session token returned │
75
+ │ <───────────────────────────── │
76
+ │ │
77
+ │ Charge wallet │
78
+ │ ─────────────────────────────> │
79
+ │ │
80
+ │ Success! Continue... │
81
+ │ <───────────────────────────── │
82
+ │ │
63
83
  ```
64
84
 
65
85
  ## API Reference
66
86
 
67
87
  ### `opencloud.init(config)`
68
88
 
69
- Initialize the SDK with your app configuration.
89
+ Initialize the SDK.
70
90
 
71
91
  ```typescript
72
92
  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)
93
+ appId: 'your-app-slug', // Required - your app's slug
94
+ apiUrl: 'https://opencloud.app' // Optional - defaults to production
75
95
  });
76
96
  ```
77
97
 
78
- ### `opencloud.callAI(params)`
98
+ ### `opencloud.charge(action?, metadata?)`
79
99
 
80
- Call an AI model through OpenCloud's platform.
100
+ Charge the user. Opens login popup if not authenticated.
81
101
 
82
102
  ```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
- });
103
+ const result = await opencloud.charge('chat_message', { model: 'gpt-4' });
91
104
 
92
- // Response structure:
105
+ // Result:
93
106
  {
94
107
  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"
108
+ charged: 0.10,
109
+ balance: 4.90,
110
+ transactionId: 'tx_abc123'
111
+ }
112
+
113
+ // Or on error:
114
+ {
115
+ success: false,
116
+ error: 'INSUFFICIENT_BALANCE' | 'CANCELLED' | 'UNKNOWN'
103
117
  }
104
118
  ```
105
119
 
106
- ### `opencloud.getBalance()`
120
+ ### `opencloud.withCharge(fn, options)`
107
121
 
108
- Get the current user's token balance.
122
+ Execute a function and charge the user. Handles auth and balance checks automatically.
109
123
 
110
124
  ```typescript
111
- const balance = await opencloud.getBalance();
112
- console.log(`Balance: $${balance}`);
125
+ const image = await opencloud.withCharge(async () => {
126
+ return await myAIService.generateImage(prompt);
127
+ }, { action: 'generate_image' });
113
128
  ```
114
129
 
115
- ### `opencloud.requestTopUp()`
130
+ ### `opencloud.isAuthenticated()`
116
131
 
117
- Request the user to purchase more tokens (opens payment dialog).
132
+ Check if user is logged in.
118
133
 
119
134
  ```typescript
120
- try {
121
- await opencloud.callAI({...});
122
- } catch (error) {
123
- if (error.message.includes('Insufficient balance')) {
124
- await opencloud.requestTopUp();
125
- }
135
+ if (opencloud.isAuthenticated()) {
136
+ console.log('User is logged in');
126
137
  }
127
138
  ```
128
139
 
129
- ## Supported AI Models
140
+ ### `opencloud.login()`
130
141
 
131
- OpenCloud uses OpenRouter, giving you access to 100+ models:
142
+ Open login popup manually.
132
143
 
133
- **OpenAI:**
134
- - `openai/gpt-4`
135
- - `openai/gpt-4-turbo`
136
- - `openai/gpt-3.5-turbo`
144
+ ```typescript
145
+ const session = await opencloud.login();
146
+ if (session) {
147
+ console.log('Logged in as:', session.user.email);
148
+ }
149
+ ```
137
150
 
138
- **Anthropic:**
139
- - `anthropic/claude-3-5-sonnet`
140
- - `anthropic/claude-3-haiku`
141
- - `anthropic/claude-3-opus`
151
+ ### `opencloud.logout()`
142
152
 
143
- **Google:**
144
- - `google/gemini-pro`
145
- - `google/gemini-pro-vision`
153
+ Log out the current user.
146
154
 
147
- **Meta:**
148
- - `meta-llama/llama-3-70b`
149
- - `meta-llama/llama-3-8b`
155
+ ```typescript
156
+ opencloud.logout();
157
+ ```
150
158
 
151
- **And many more!** See [OpenRouter docs](https://openrouter.ai/docs) for full list.
159
+ ### `opencloud.getUser()`
152
160
 
153
- ## Example Apps
161
+ Get current user info (synchronous).
154
162
 
155
- ### Text Summarizer
163
+ ```typescript
164
+ const user = opencloud.getUser();
165
+ // { id, email, username, balance }
166
+ ```
167
+
168
+ ### `opencloud.getBalance()`
169
+
170
+ Get user's current balance (async, fetches from server).
156
171
 
157
172
  ```typescript
158
- import { opencloud } from '@opencloud/sdk';
173
+ const balance = await opencloud.getBalance();
174
+ console.log(`Balance: $${balance}`);
175
+ ```
159
176
 
160
- opencloud.init({ appId: 'text-summarizer' });
177
+ ### `opencloud.canAfford()`
161
178
 
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
- });
179
+ Check if user can afford to use the app.
167
180
 
168
- return response.text;
181
+ ```typescript
182
+ const canUse = await opencloud.canAfford();
183
+ if (!canUse) {
184
+ opencloud.openTopUp();
169
185
  }
170
186
  ```
171
187
 
172
- ### Image Description Generator
188
+ ### `opencloud.openTopUp()`
189
+
190
+ Open top-up popup for user to add credits.
173
191
 
174
192
  ```typescript
175
- import { opencloud } from '@opencloud/sdk';
193
+ opencloud.openTopUp();
194
+ ```
176
195
 
177
- opencloud.init({ appId: 'image-describer' });
196
+ ### `opencloud.getAppPrice()`
178
197
 
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
- });
198
+ Get your app's price per use.
187
199
 
188
- return response.text;
200
+ ```typescript
201
+ const price = await opencloud.getAppPrice();
202
+ console.log(`Price: $${price}`);
203
+ ```
204
+
205
+ ### `opencloud.isPreview()`
206
+
207
+ Check if running in development mode.
208
+
209
+ ```typescript
210
+ if (opencloud.isPreview()) {
211
+ console.log('Preview mode - no real charges');
189
212
  }
190
213
  ```
191
214
 
192
215
  ## Revenue Model
193
216
 
194
- OpenCloud uses a **70/30 revenue split**:
217
+ **85/15 split** - You keep most of what you earn!
195
218
 
196
- - **70%** goes to you (the app creator)
197
- - **30%** goes to OpenCloud (platform fee)
198
-
199
- Example:
200
219
  ```
201
220
  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%)
221
+ ├─ You get: $0.085 (85%)
222
+ └─ Platform: $0.015 (15%)
223
+ ```
224
+
225
+ ## Preview Mode
226
+
227
+ When running locally (localhost), the SDK automatically enters preview mode:
228
+ - `charge()` doesn't charge real money
229
+ - `getBalance()` returns 999.99
230
+ - `canAfford()` always returns true
231
+
232
+ This lets you develop without worrying about charges.
233
+
234
+ ## Error Handling
235
+
236
+ ```typescript
237
+ const result = await opencloud.charge('chat');
238
+
239
+ if (!result.success) {
240
+ switch (result.error) {
241
+ case 'CANCELLED':
242
+ // User closed the login popup
243
+ showMessage('Please log in to use this feature');
244
+ break;
245
+ case 'INSUFFICIENT_BALANCE':
246
+ // SDK already opened top-up popup
247
+ showMessage('Please add credits to continue');
248
+ break;
249
+ default:
250
+ showMessage('Something went wrong');
251
+ }
252
+ }
206
253
  ```
207
254
 
255
+ ## Publishing Your App
256
+
257
+ 1. Deploy your app to Vercel
258
+ 2. Go to [opencloud.app/publish](/publish)
259
+ 3. Create a new app with your deployed URL
260
+ 4. Set name, description, and **price per use**
261
+ 5. Get your app slug
262
+ 6. Use the slug in `opencloud.init({ appId: 'your-slug' })`
263
+
208
264
  ## TypeScript Support
209
265
 
210
266
  Full TypeScript definitions included:
@@ -213,35 +269,17 @@ Full TypeScript definitions included:
213
269
  import {
214
270
  opencloud,
215
271
  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);
272
+ ChargeResult,
273
+ UserSession,
274
+ UserInfo
275
+ } from 'opencloud-platform-sdk';
227
276
  ```
228
277
 
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
278
  ## Support
239
279
 
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
280
+ - Docs: [opencloud.app/docs](/docs)
281
+ - Issues: [github.com/opencloud/sdk](https://github.com/opencloud/sdk/issues)
244
282
 
245
283
  ## License
246
284
 
247
- MIT © OpenCloud
285
+ MIT
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var fs = __toESM(require("fs"));
28
+ var path = __toESM(require("path"));
29
+ var VERCEL_HEADERS = {
30
+ headers: [
31
+ {
32
+ source: "/(.*)",
33
+ headers: [
34
+ { key: "X-Frame-Options", value: "ALLOWALL" },
35
+ { key: "Content-Security-Policy", value: "frame-ancestors *" }
36
+ ]
37
+ }
38
+ ]
39
+ };
40
+ function findProjectRoot() {
41
+ let dir = process.cwd();
42
+ while (dir !== path.dirname(dir)) {
43
+ if (fs.existsSync(path.join(dir, "package.json"))) {
44
+ return dir;
45
+ }
46
+ dir = path.dirname(dir);
47
+ }
48
+ return process.cwd();
49
+ }
50
+ function setupVercelJson(projectRoot) {
51
+ const vercelJsonPath = path.join(projectRoot, "vercel.json");
52
+ try {
53
+ let config = {};
54
+ if (fs.existsSync(vercelJsonPath)) {
55
+ const content = fs.readFileSync(vercelJsonPath, "utf-8");
56
+ config = JSON.parse(content);
57
+ console.log(" Found existing vercel.json");
58
+ } else {
59
+ console.log(" Creating vercel.json");
60
+ }
61
+ if (config.headers) {
62
+ const hasFrameHeaders = config.headers.some(
63
+ (h) => h.headers?.some(
64
+ (header) => header.key === "X-Frame-Options" || header.key === "Content-Security-Policy"
65
+ )
66
+ );
67
+ if (hasFrameHeaders) {
68
+ console.log(" \u2713 Headers already configured in vercel.json");
69
+ return true;
70
+ }
71
+ config.headers.push(...VERCEL_HEADERS.headers);
72
+ } else {
73
+ config.headers = VERCEL_HEADERS.headers;
74
+ }
75
+ fs.writeFileSync(vercelJsonPath, JSON.stringify(config, null, 2));
76
+ console.log(" \u2713 Added iframe headers to vercel.json");
77
+ return true;
78
+ } catch (error) {
79
+ console.error(" \u2717 Failed to update vercel.json:", error);
80
+ return false;
81
+ }
82
+ }
83
+ function printManualInstructions() {
84
+ console.log(`
85
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
86
+ \u2502 Manual Configuration Required \u2502
87
+ \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
88
+ \u2502 \u2502
89
+ \u2502 Add this to your vercel.json: \u2502
90
+ \u2502 \u2502
91
+ \u2502 { \u2502
92
+ \u2502 "headers": [ \u2502
93
+ \u2502 { \u2502
94
+ \u2502 "source": "/(.*)", \u2502
95
+ \u2502 "headers": [ \u2502
96
+ \u2502 { "key": "X-Frame-Options", "value": "ALLOWALL" },
97
+ \u2502 { "key": "Content-Security-Policy", \u2502
98
+ \u2502 "value": "frame-ancestors *" } \u2502
99
+ \u2502 ] \u2502
100
+ \u2502 } \u2502
101
+ \u2502 ] \u2502
102
+ \u2502 } \u2502
103
+ \u2502 \u2502
104
+ \u2502 Or add headers() to next.config.js: \u2502
105
+ \u2502 \u2502
106
+ \u2502 async headers() { \u2502
107
+ \u2502 return [{ \u2502
108
+ \u2502 source: '/(.*)', \u2502
109
+ \u2502 headers: [ \u2502
110
+ \u2502 { key: 'X-Frame-Options', value: 'ALLOWALL' }, \u2502
111
+ \u2502 { key: 'Content-Security-Policy', \u2502
112
+ \u2502 value: 'frame-ancestors *' } \u2502
113
+ \u2502 ] \u2502
114
+ \u2502 }]; \u2502
115
+ \u2502 } \u2502
116
+ \u2502 \u2502
117
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
118
+ `);
119
+ }
120
+ function main() {
121
+ const args = process.argv.slice(2);
122
+ const command = args[0];
123
+ console.log("\n\u{1F680} OpenCloud SDK Setup\n");
124
+ if (command === "setup" || !command) {
125
+ const projectRoot = findProjectRoot();
126
+ console.log(`Project root: ${projectRoot}
127
+ `);
128
+ console.log("Configuring iframe headers for OpenCloud...\n");
129
+ const success = setupVercelJson(projectRoot);
130
+ if (success) {
131
+ console.log(`
132
+ \u2705 Setup complete!
133
+
134
+ Your app is now configured to work with OpenCloud.
135
+ After deploying, your app will be embeddable in the OpenCloud marketplace.
136
+
137
+ Next steps:
138
+ 1. Deploy your app: vercel deploy --prod
139
+ 2. Publish on OpenCloud: https://opencloud.app/publish
140
+ `);
141
+ } else {
142
+ printManualInstructions();
143
+ }
144
+ } else if (command === "help" || command === "--help" || command === "-h") {
145
+ console.log(`
146
+ Usage: opencloud-sdk [command]
147
+
148
+ Commands:
149
+ setup Configure your project for OpenCloud (default)
150
+ help Show this help message
151
+
152
+ Examples:
153
+ npx opencloud-platform-sdk setup
154
+ npx opencloud-platform-sdk
155
+ `);
156
+ } else {
157
+ console.log(`Unknown command: ${command}`);
158
+ console.log('Run "opencloud-sdk help" for usage information.');
159
+ }
160
+ }
161
+ main();