openkbs 0.0.71 → 0.0.73
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/package.json +3 -2
- package/src/actions.js +147 -0
- package/src/index.js +40 -0
- package/templates/.claude/skills/openkbs/SKILL.md +37 -0
- package/version.json +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openkbs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.73",
|
|
4
4
|
"description": "OpenKBS - Command Line Interface",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"cli-spinner": "^0.2.10",
|
|
42
42
|
"commander": "^12.1.0",
|
|
43
43
|
"express": "^4.21.0",
|
|
44
|
-
"fs-extra": "^11.2.0"
|
|
44
|
+
"fs-extra": "^11.2.0",
|
|
45
|
+
"jsonwebtoken": "^9.0.3"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"pkg": "^5.8.1"
|
package/src/actions.js
CHANGED
|
@@ -4,6 +4,8 @@ const path = require('path');
|
|
|
4
4
|
const express = require('express');
|
|
5
5
|
const { exec, execSync } = require('child_process');
|
|
6
6
|
const https = require('https');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
const jwt = require('jsonwebtoken');
|
|
7
9
|
|
|
8
10
|
const {
|
|
9
11
|
fetchLocalKBData, fetchKBJWT, createAccountIdFromPublicKey, signPayload, getUserProfile, getKB,
|
|
@@ -17,6 +19,16 @@ const TEMPLATE_DIR = path.join(os.homedir(), '.openkbs', 'templates');
|
|
|
17
19
|
const jwtPath = path.join(os.homedir(), '.openkbs', 'clientJWT');
|
|
18
20
|
const generateTransactionId = () => `${+new Date()}-${Math.floor(100000 + Math.random() * 900000)}`;
|
|
19
21
|
|
|
22
|
+
// Service registry for OpenKBS AI services (image generation)
|
|
23
|
+
const SERVICES = {
|
|
24
|
+
// Short aliases (recommended)
|
|
25
|
+
"gpt-image": { accountId: "e69424d275873af94993240df041ed78", model: "gpt-image-1" },
|
|
26
|
+
"gemini-image": { accountId: "bc7ab06216fa2bf6db5d8e573d4d2415", model: "gemini-2.5-flash-image" },
|
|
27
|
+
// Full names
|
|
28
|
+
"gpt-image-1": { accountId: "e69424d275873af94993240df041ed78", model: "gpt-image-1" },
|
|
29
|
+
"gemini-2.5-flash-image": { accountId: "bc7ab06216fa2bf6db5d8e573d4d2415", model: "gemini-2.5-flash-image" }
|
|
30
|
+
};
|
|
31
|
+
|
|
20
32
|
/**
|
|
21
33
|
* Find settings from settings.json - checks current dir, then functions/ or site/ subdirs
|
|
22
34
|
* Returns full settings object with kbId, region, etc.
|
|
@@ -90,6 +102,140 @@ function getMimeType(filePath) {
|
|
|
90
102
|
return MIME_TYPES[ext] || 'application/octet-stream';
|
|
91
103
|
}
|
|
92
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Read JSON payload from --data option or stdin
|
|
107
|
+
*/
|
|
108
|
+
async function getPayloadFromInput(dataOption) {
|
|
109
|
+
if (dataOption) {
|
|
110
|
+
return JSON.parse(dataOption);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Read from stdin if not a TTY
|
|
114
|
+
if (!process.stdin.isTTY) {
|
|
115
|
+
const chunks = [];
|
|
116
|
+
for await (const chunk of process.stdin) {
|
|
117
|
+
chunks.push(chunk);
|
|
118
|
+
}
|
|
119
|
+
const input = Buffer.concat(chunks).toString('utf8').trim();
|
|
120
|
+
if (input) {
|
|
121
|
+
return JSON.parse(input);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new Error('No payload provided. Use --data or pipe JSON to stdin.');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Call OpenKBS AI services directly with transactionJWT
|
|
130
|
+
*/
|
|
131
|
+
async function serviceAction(options) {
|
|
132
|
+
try {
|
|
133
|
+
// 1. Get and validate payload
|
|
134
|
+
let payload;
|
|
135
|
+
try {
|
|
136
|
+
payload = await getPayloadFromInput(options.data);
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.red(`Error parsing payload: ${e.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 2. Validate model
|
|
143
|
+
const model = options.model;
|
|
144
|
+
if (!SERVICES[model]) {
|
|
145
|
+
console.red(`Unknown model: ${model}`);
|
|
146
|
+
console.log(`Available models: ${Object.keys(SERVICES).join(', ')}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 3. Add model to payload if not present (use actual model name from registry)
|
|
151
|
+
if (!payload.model) {
|
|
152
|
+
payload.model = SERVICES[model].model;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 4. Get user profile and generate transactionJWT
|
|
156
|
+
const userProfile = await getUserProfile();
|
|
157
|
+
const publicKey = userProfile.walletPublicKey;
|
|
158
|
+
const privateKey = userProfile.walletPrivateKey;
|
|
159
|
+
const accountId = createAccountIdFromPublicKey(publicKey);
|
|
160
|
+
|
|
161
|
+
if (!publicKey || !privateKey) {
|
|
162
|
+
console.red('Wallet keys not found. Please login first with: openkbs login');
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const txPayload = {
|
|
167
|
+
operation: "transfer",
|
|
168
|
+
resourceId: "credits",
|
|
169
|
+
transactionId: generateTransactionId(),
|
|
170
|
+
fromAccountId: accountId,
|
|
171
|
+
fromAccountPublicKey: publicKey,
|
|
172
|
+
toAccountId: SERVICES[model].accountId,
|
|
173
|
+
message: "",
|
|
174
|
+
maxAmount: parseInt(options.maxAmount || '300000'),
|
|
175
|
+
iat: Math.floor(Date.now() / 1000)
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Create private key object for jwt.sign()
|
|
179
|
+
const privateKeyObj = crypto.createPrivateKey({
|
|
180
|
+
key: Buffer.from(privateKey, 'base64'),
|
|
181
|
+
format: 'der',
|
|
182
|
+
type: 'pkcs8'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const transactionJWT = jwt.sign(txPayload, privateKeyObj, {
|
|
186
|
+
algorithm: 'ES256',
|
|
187
|
+
expiresIn: 60
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// 5. Make POST request to openai.openkbs.com
|
|
191
|
+
const response = await fetch('https://openai.openkbs.com', {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
headers: {
|
|
194
|
+
'Content-Type': 'application/json',
|
|
195
|
+
'transaction-jwt': transactionJWT
|
|
196
|
+
},
|
|
197
|
+
body: JSON.stringify(payload)
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const data = await response.json();
|
|
201
|
+
|
|
202
|
+
// 6. Handle errors
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
if (response.status === 498) {
|
|
205
|
+
console.red('Invalid service transaction');
|
|
206
|
+
} else if (response.status === 499) {
|
|
207
|
+
console.red('Insufficient credits. Check your balance at https://openkbs.com');
|
|
208
|
+
} else {
|
|
209
|
+
console.red(`Service error (${response.status}): ${JSON.stringify(data)}`);
|
|
210
|
+
}
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 7. Handle image output - save to file if -o specified
|
|
215
|
+
// Support both {data:[{b64_json}]} and [{b64_json}] formats
|
|
216
|
+
const imageData = data?.data?.[0]?.b64_json || data?.[0]?.b64_json;
|
|
217
|
+
if (options.output && imageData) {
|
|
218
|
+
const buffer = Buffer.from(imageData, 'base64');
|
|
219
|
+
const outputPath = path.resolve(options.output);
|
|
220
|
+
|
|
221
|
+
// Ensure directory exists
|
|
222
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
223
|
+
await fs.writeFile(outputPath, buffer);
|
|
224
|
+
|
|
225
|
+
console.green(`Image saved: ${outputPath}`);
|
|
226
|
+
console.log(`Size: ${(buffer.length / 1024).toFixed(1)} KB`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 8. Output response as JSON (for non-image or no -o flag)
|
|
231
|
+
console.log(JSON.stringify(data, null, 2));
|
|
232
|
+
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.red(`Error: ${error.message}`);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
93
239
|
async function signAction(options) {
|
|
94
240
|
try {
|
|
95
241
|
const userProfile = await getUserProfile();
|
|
@@ -2491,6 +2637,7 @@ async function stackCreateAction(name) {
|
|
|
2491
2637
|
|
|
2492
2638
|
module.exports = {
|
|
2493
2639
|
signAction,
|
|
2640
|
+
serviceAction,
|
|
2494
2641
|
loginAction,
|
|
2495
2642
|
pullAction,
|
|
2496
2643
|
pushAction,
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const packageJson = require('../package.json');
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
signAction,
|
|
7
|
+
serviceAction,
|
|
7
8
|
loginAction,
|
|
8
9
|
pullAction,
|
|
9
10
|
pushAction,
|
|
@@ -308,4 +309,43 @@ Examples:
|
|
|
308
309
|
$ openkbs pulse disable Disable Pulse
|
|
309
310
|
`);
|
|
310
311
|
|
|
312
|
+
program
|
|
313
|
+
.command('service')
|
|
314
|
+
.description('Generate images using OpenKBS AI services')
|
|
315
|
+
.requiredOption('-m, --model <model>', 'Model: gpt-image or gemini-image')
|
|
316
|
+
.option('-d, --data <json>', 'JSON payload (or pipe via stdin)')
|
|
317
|
+
.option('-o, --output <path>', 'Save image output to file (for image generation)')
|
|
318
|
+
.option('--max-amount <amount>', 'Max credits to authorize', '300000')
|
|
319
|
+
.action(serviceAction)
|
|
320
|
+
.addHelpText('after', `
|
|
321
|
+
Examples:
|
|
322
|
+
# Generate image with GPT
|
|
323
|
+
$ openkbs service -m gpt-image -d '{"action":"createImage","prompt":"a cat"}' -o cat.png
|
|
324
|
+
|
|
325
|
+
# Generate with Gemini
|
|
326
|
+
$ openkbs service -m gemini-image -d '{"action":"createImage","prompt":"logo"}' -o logo.png
|
|
327
|
+
|
|
328
|
+
# Edit image (provide reference URL)
|
|
329
|
+
$ openkbs service -m gpt-image -d '{"action":"createImage","prompt":"make it blue","imageUrls":["https://..."]}' -o edited.png
|
|
330
|
+
|
|
331
|
+
Available models:
|
|
332
|
+
gpt-image OpenAI GPT Image (gpt-image-1.5)
|
|
333
|
+
gemini-image Google Gemini Flash (gemini-2.5-flash-image)
|
|
334
|
+
|
|
335
|
+
Options for gpt-image:
|
|
336
|
+
prompt (required) Text description of the image
|
|
337
|
+
size "1024x1024", "1024x1536", "1536x1024", "auto" (default: "auto")
|
|
338
|
+
quality "low", "medium", "high", "auto" (default: "auto")
|
|
339
|
+
n Number of images (default: 1)
|
|
340
|
+
output_format "png", "jpg", "webp" (default: "png")
|
|
341
|
+
background "transparent", "opaque", "auto" (default: "auto")
|
|
342
|
+
output_compression 0-100 for jpg/webp (default: 100)
|
|
343
|
+
imageUrls Array of URLs for editing/reference
|
|
344
|
+
|
|
345
|
+
Options for gemini-image:
|
|
346
|
+
prompt (required) Text description of the image
|
|
347
|
+
aspect_ratio "1:1", "16:9", "9:16", "4:3", "3:4" (default: "1:1")
|
|
348
|
+
imageUrls Array of URLs for reference
|
|
349
|
+
`);
|
|
350
|
+
|
|
311
351
|
program.parse(process.argv);
|
|
@@ -123,6 +123,43 @@ openkbs pulse status # WebSocket status
|
|
|
123
123
|
openkbs site push # Deploy static site
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
### Image Generation Service
|
|
127
|
+
Generate images directly from CLI using OpenKBS AI services:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Generate with GPT
|
|
131
|
+
openkbs service -m gpt-image -d '{"action":"createImage","prompt":"a logo"}' -o logo.png
|
|
132
|
+
|
|
133
|
+
# Generate with Gemini
|
|
134
|
+
openkbs service -m gemini-image -d '{"action":"createImage","prompt":"hero image"}' -o hero.png
|
|
135
|
+
|
|
136
|
+
# Edit existing image
|
|
137
|
+
openkbs service -m gpt-image -d '{"action":"createImage","prompt":"make it blue","imageUrls":["https://..."]}' -o edited.png
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Available models:**
|
|
141
|
+
- `gpt-image` - OpenAI GPT Image (gpt-image-1.5)
|
|
142
|
+
- `gemini-image` - Google Gemini Flash (gemini-2.5-flash-image)
|
|
143
|
+
|
|
144
|
+
**Options for gpt-image:**
|
|
145
|
+
| Option | Values | Default |
|
|
146
|
+
|--------|--------|---------|
|
|
147
|
+
| prompt | (required) | - |
|
|
148
|
+
| size | "1024x1024", "1024x1536", "1536x1024", "auto" | "auto" |
|
|
149
|
+
| quality | "low", "medium", "high", "auto" | "auto" |
|
|
150
|
+
| n | Number of images | 1 |
|
|
151
|
+
| output_format | "png", "jpg", "webp" | "png" |
|
|
152
|
+
| background | "transparent", "opaque", "auto" | "auto" |
|
|
153
|
+
| output_compression | 0-100 | 100 |
|
|
154
|
+
| imageUrls | Array of URLs for editing | - |
|
|
155
|
+
|
|
156
|
+
**Options for gemini-image:**
|
|
157
|
+
| Option | Values | Default |
|
|
158
|
+
|--------|--------|---------|
|
|
159
|
+
| prompt | (required) | - |
|
|
160
|
+
| aspect_ratio | "1:1", "16:9", "9:16", "4:3", "3:4" | "1:1" |
|
|
161
|
+
| imageUrls | Array of URLs for reference | - |
|
|
162
|
+
|
|
126
163
|
## Backend Handler Pattern
|
|
127
164
|
|
|
128
165
|
Commands are XML tags with JSON content that the LLM outputs:
|
package/version.json
CHANGED