openkbs 0.0.55 → 0.0.59
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 +1 -1
- package/src/actions.js +947 -10
- package/src/index.js +62 -2
- package/src/utils.js +5 -2
- package/version.json +3 -3
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -17,6 +17,79 @@ const TEMPLATE_DIR = path.join(os.homedir(), '.openkbs', 'templates');
|
|
|
17
17
|
const jwtPath = path.join(os.homedir(), '.openkbs', 'clientJWT');
|
|
18
18
|
const generateTransactionId = () => `${+new Date()}-${Math.floor(100000 + Math.random() * 900000)}`;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Find settings from settings.json - checks current dir, then functions/ or site/ subdirs
|
|
22
|
+
* Returns full settings object with kbId, region, etc.
|
|
23
|
+
*/
|
|
24
|
+
function findSettings() {
|
|
25
|
+
const paths = [
|
|
26
|
+
path.join(process.cwd(), 'settings.json'),
|
|
27
|
+
path.join(process.cwd(), 'app', 'settings.json'),
|
|
28
|
+
path.join(process.cwd(), 'functions', 'settings.json'),
|
|
29
|
+
path.join(process.cwd(), 'site', 'settings.json')
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
for (const settingsPath of paths) {
|
|
33
|
+
if (fs.existsSync(settingsPath)) {
|
|
34
|
+
try {
|
|
35
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
36
|
+
if (settings.kbId) return settings;
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find kbId from settings.json - checks current dir, then functions/ or site/ subdirs
|
|
45
|
+
*/
|
|
46
|
+
function findKbId() {
|
|
47
|
+
const settings = findSettings();
|
|
48
|
+
return settings?.kbId || null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Find region from settings.json - checks current dir, then functions/ or site/ subdirs
|
|
53
|
+
* Default: 'us-east-1'
|
|
54
|
+
*/
|
|
55
|
+
function findRegion() {
|
|
56
|
+
const settings = findSettings();
|
|
57
|
+
return settings?.region || 'us-east-1';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// MIME types for common file extensions
|
|
61
|
+
const MIME_TYPES = {
|
|
62
|
+
'.html': 'text/html',
|
|
63
|
+
'.htm': 'text/html',
|
|
64
|
+
'.css': 'text/css',
|
|
65
|
+
'.js': 'application/javascript',
|
|
66
|
+
'.mjs': 'application/javascript',
|
|
67
|
+
'.json': 'application/json',
|
|
68
|
+
'.png': 'image/png',
|
|
69
|
+
'.jpg': 'image/jpeg',
|
|
70
|
+
'.jpeg': 'image/jpeg',
|
|
71
|
+
'.gif': 'image/gif',
|
|
72
|
+
'.svg': 'image/svg+xml',
|
|
73
|
+
'.ico': 'image/x-icon',
|
|
74
|
+
'.webp': 'image/webp',
|
|
75
|
+
'.woff': 'font/woff',
|
|
76
|
+
'.woff2': 'font/woff2',
|
|
77
|
+
'.ttf': 'font/ttf',
|
|
78
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
79
|
+
'.pdf': 'application/pdf',
|
|
80
|
+
'.txt': 'text/plain',
|
|
81
|
+
'.xml': 'application/xml',
|
|
82
|
+
'.zip': 'application/zip',
|
|
83
|
+
'.mp3': 'audio/mpeg',
|
|
84
|
+
'.mp4': 'video/mp4',
|
|
85
|
+
'.webm': 'video/webm'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
function getMimeType(filePath) {
|
|
89
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
90
|
+
return MIME_TYPES[ext] || 'application/octet-stream';
|
|
91
|
+
}
|
|
92
|
+
|
|
20
93
|
async function signAction(options) {
|
|
21
94
|
try {
|
|
22
95
|
const userProfile = await getUserProfile();
|
|
@@ -840,11 +913,17 @@ async function downloadClaudeMdFromS3(claudeMdPath) {
|
|
|
840
913
|
// ===== Elastic Functions Commands =====
|
|
841
914
|
|
|
842
915
|
async function fnAction(subCommand, args = []) {
|
|
843
|
-
|
|
844
|
-
|
|
916
|
+
// Find kbId from settings.json (current dir, app/, functions/, site/)
|
|
917
|
+
let kbId = findKbId();
|
|
918
|
+
|
|
919
|
+
if (!kbId) {
|
|
920
|
+
// Fallback to standard KB lookup
|
|
921
|
+
const localKBData = await fetchLocalKBData();
|
|
922
|
+
kbId = localKBData?.kbId;
|
|
923
|
+
}
|
|
845
924
|
|
|
846
925
|
if (!kbId) {
|
|
847
|
-
return console.red('No KB found.
|
|
926
|
+
return console.red('No KB found. Create settings.json with {"kbId": "..."} or run from a KB project directory.');
|
|
848
927
|
}
|
|
849
928
|
|
|
850
929
|
const { kbToken } = await fetchKBJWT(kbId);
|
|
@@ -874,7 +953,7 @@ async function fnAction(subCommand, args = []) {
|
|
|
874
953
|
console.log(' invoke <name> [payload] Invoke a function');
|
|
875
954
|
console.log('');
|
|
876
955
|
console.log('Options for deploy:');
|
|
877
|
-
console.log(' --region <region> Region (us-east-
|
|
956
|
+
console.log(' --region <region> Region (us-east-1, eu-central-1, ap-southeast-1)');
|
|
878
957
|
console.log(' --memory <mb> Memory size (128-3008 MB)');
|
|
879
958
|
console.log(' --timeout <seconds> Timeout (1-900 seconds)');
|
|
880
959
|
}
|
|
@@ -899,7 +978,7 @@ async function fnListAction(kbToken) {
|
|
|
899
978
|
console.log('Create a function:');
|
|
900
979
|
console.log(' 1. Create directory: mkdir -p functions/hello');
|
|
901
980
|
console.log(' 2. Create handler: echo "export const handler = async (event) => ({ body: \'Hello!\' });" > functions/hello/index.mjs');
|
|
902
|
-
console.log(' 3. Deploy: openkbs fn deploy hello --region us-east-
|
|
981
|
+
console.log(' 3. Deploy: openkbs fn deploy hello --region us-east-1');
|
|
903
982
|
return;
|
|
904
983
|
}
|
|
905
984
|
|
|
@@ -922,8 +1001,8 @@ async function fnDeployAction(kbToken, functionName, args) {
|
|
|
922
1001
|
return console.red('Function name required. Usage: openkbs fn deploy <name>');
|
|
923
1002
|
}
|
|
924
1003
|
|
|
925
|
-
// Parse arguments
|
|
926
|
-
let region =
|
|
1004
|
+
// Parse arguments - region defaults to settings.json or us-east-1
|
|
1005
|
+
let region = findRegion();
|
|
927
1006
|
let memorySize = 256;
|
|
928
1007
|
let timeout = 30;
|
|
929
1008
|
|
|
@@ -937,15 +1016,28 @@ async function fnDeployAction(kbToken, functionName, args) {
|
|
|
937
1016
|
}
|
|
938
1017
|
}
|
|
939
1018
|
|
|
940
|
-
|
|
1019
|
+
// Try to find the function directory in order:
|
|
1020
|
+
// 1. ./functionName (if running from functions/ directory)
|
|
1021
|
+
// 2. ./functions/functionName (if running from project root)
|
|
1022
|
+
let functionDir = path.join(process.cwd(), functionName);
|
|
1023
|
+
if (!await fs.pathExists(functionDir)) {
|
|
1024
|
+
functionDir = path.join(process.cwd(), 'functions', functionName);
|
|
1025
|
+
}
|
|
941
1026
|
|
|
942
1027
|
if (!await fs.pathExists(functionDir)) {
|
|
943
|
-
return console.red(`Function directory not found
|
|
1028
|
+
return console.red(`Function directory not found. Tried:\n - ./${functionName}\n - ./functions/${functionName}`);
|
|
944
1029
|
}
|
|
945
1030
|
|
|
946
1031
|
console.log(`Deploying function '${functionName}' to ${region}...`);
|
|
947
1032
|
|
|
948
1033
|
try {
|
|
1034
|
+
// Check if package.json exists and run npm install
|
|
1035
|
+
const packageJsonPath = path.join(functionDir, 'package.json');
|
|
1036
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
1037
|
+
console.log('Installing dependencies...');
|
|
1038
|
+
execSync('npm install --production', { cwd: functionDir, stdio: 'inherit' });
|
|
1039
|
+
}
|
|
1040
|
+
|
|
949
1041
|
// Create a zip of the function directory
|
|
950
1042
|
const archiver = require('archiver');
|
|
951
1043
|
const { PassThrough } = require('stream');
|
|
@@ -1180,6 +1272,847 @@ async function fnInvokeAction(kbToken, functionName, args) {
|
|
|
1180
1272
|
}
|
|
1181
1273
|
}
|
|
1182
1274
|
|
|
1275
|
+
// ===== Elastic Storage Commands =====
|
|
1276
|
+
|
|
1277
|
+
async function storageAction(subCommand, args = []) {
|
|
1278
|
+
// Find kbId from settings.json (current dir, app/, functions/, site/)
|
|
1279
|
+
let kbId = findKbId();
|
|
1280
|
+
|
|
1281
|
+
if (!kbId) {
|
|
1282
|
+
const localKBData = await fetchLocalKBData();
|
|
1283
|
+
kbId = localKBData?.kbId;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
if (!kbId) {
|
|
1287
|
+
return console.red('No KB found. Create settings.json with {"kbId": "..."} or run from a KB project directory.');
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
const { kbToken } = await fetchKBJWT(kbId);
|
|
1291
|
+
|
|
1292
|
+
switch (subCommand) {
|
|
1293
|
+
case 'enable':
|
|
1294
|
+
return await storageEnableAction(kbToken);
|
|
1295
|
+
case 'status':
|
|
1296
|
+
return await storageStatusAction(kbToken);
|
|
1297
|
+
case 'ls':
|
|
1298
|
+
case 'list':
|
|
1299
|
+
return await storageListAction(kbToken, args[0]);
|
|
1300
|
+
case 'put':
|
|
1301
|
+
case 'upload':
|
|
1302
|
+
return await storageUploadAction(kbToken, args[0], args[1]);
|
|
1303
|
+
case 'get':
|
|
1304
|
+
case 'download':
|
|
1305
|
+
return await storageDownloadAction(kbToken, args[0], args[1]);
|
|
1306
|
+
case 'rm':
|
|
1307
|
+
case 'delete':
|
|
1308
|
+
return await storageDeleteAction(kbToken, args[0]);
|
|
1309
|
+
case 'disable':
|
|
1310
|
+
return await storageDisableAction(kbToken, args);
|
|
1311
|
+
case 'cloudfront':
|
|
1312
|
+
case 'cf':
|
|
1313
|
+
return await storageCloudFrontAction(kbToken, args[0], args[1]);
|
|
1314
|
+
case 'public':
|
|
1315
|
+
return await storagePublicAction(kbToken, args[0]);
|
|
1316
|
+
default:
|
|
1317
|
+
console.log('Usage: openkbs storage <command> [options]');
|
|
1318
|
+
console.log('');
|
|
1319
|
+
console.log('Commands:');
|
|
1320
|
+
console.log(' enable Enable elastic storage for this KB');
|
|
1321
|
+
console.log(' status Show storage status and info');
|
|
1322
|
+
console.log(' public <true|false> Make storage publicly readable');
|
|
1323
|
+
console.log(' ls [prefix] List objects in storage');
|
|
1324
|
+
console.log(' put <local> <remote> Upload a file to storage');
|
|
1325
|
+
console.log(' get <remote> <local> Download a file from storage');
|
|
1326
|
+
console.log(' rm <key> Delete an object from storage');
|
|
1327
|
+
console.log(' disable [--force] Disable storage (deletes bucket)');
|
|
1328
|
+
console.log(' cloudfront <path> Add storage to CloudFront at path (e.g., /media)');
|
|
1329
|
+
console.log(' cloudfront remove <path> Remove storage from CloudFront');
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
async function storageEnableAction(kbToken) {
|
|
1334
|
+
try {
|
|
1335
|
+
console.log('Enabling elastic storage...');
|
|
1336
|
+
|
|
1337
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1338
|
+
token: kbToken,
|
|
1339
|
+
action: 'enableElasticStorage'
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
if (response.error) {
|
|
1343
|
+
return console.red('Error:', response.error);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
if (response.alreadyEnabled) {
|
|
1347
|
+
console.yellow('Storage is already enabled.');
|
|
1348
|
+
} else {
|
|
1349
|
+
console.green('Storage enabled successfully!');
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
console.log(` Bucket: ${response.bucket}`);
|
|
1353
|
+
console.log(` Region: ${response.region}`);
|
|
1354
|
+
|
|
1355
|
+
if (response.functionsUpdated > 0) {
|
|
1356
|
+
console.log(` Updated ${response.functionsUpdated} function(s) with STORAGE_BUCKET env var`);
|
|
1357
|
+
}
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
console.red('Error enabling storage:', error.message);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
async function storageStatusAction(kbToken) {
|
|
1364
|
+
try {
|
|
1365
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1366
|
+
token: kbToken,
|
|
1367
|
+
action: 'getElasticStorage'
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
if (response.error) {
|
|
1371
|
+
return console.red('Error:', response.error);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
if (!response.enabled) {
|
|
1375
|
+
console.log('Elastic Storage: disabled');
|
|
1376
|
+
console.log('');
|
|
1377
|
+
console.log('Enable with: openkbs storage enable');
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
console.log('Elastic Storage: enabled');
|
|
1382
|
+
console.log(` Bucket: ${response.bucket}`);
|
|
1383
|
+
console.log(` Region: ${response.region}`);
|
|
1384
|
+
console.log(` Public: ${response.public ? 'yes' : 'no'}`);
|
|
1385
|
+
if (response.publicUrl) {
|
|
1386
|
+
console.log(` Public URL: ${response.publicUrl}`);
|
|
1387
|
+
}
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
console.red('Error getting storage status:', error.message);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
async function storageListAction(kbToken, prefix = '') {
|
|
1394
|
+
try {
|
|
1395
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1396
|
+
token: kbToken,
|
|
1397
|
+
action: 'listStorageObjects',
|
|
1398
|
+
prefix: prefix || ''
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
if (response.error) {
|
|
1402
|
+
return console.red('Error:', response.error);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
const objects = response.objects || [];
|
|
1406
|
+
|
|
1407
|
+
if (objects.length === 0) {
|
|
1408
|
+
console.log('No objects found.');
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
console.log(`Objects${prefix ? ` (prefix: ${prefix})` : ''}:\n`);
|
|
1413
|
+
|
|
1414
|
+
objects.forEach(obj => {
|
|
1415
|
+
const size = formatBytes(obj.size);
|
|
1416
|
+
const date = new Date(obj.lastModified).toISOString().split('T')[0];
|
|
1417
|
+
console.log(` ${date} ${size.padStart(10)} ${obj.key}`);
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
if (response.isTruncated) {
|
|
1421
|
+
console.log('\n (more objects exist, use prefix to filter)');
|
|
1422
|
+
}
|
|
1423
|
+
} catch (error) {
|
|
1424
|
+
console.red('Error listing objects:', error.message);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
async function storageUploadAction(kbToken, localPath, remoteKey) {
|
|
1429
|
+
if (!localPath) {
|
|
1430
|
+
return console.red('Usage: openkbs storage put <local-file> [remote-key]');
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
const fullLocalPath = path.resolve(localPath);
|
|
1434
|
+
|
|
1435
|
+
if (!fs.existsSync(fullLocalPath)) {
|
|
1436
|
+
return console.red(`File not found: ${fullLocalPath}`);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// Use filename if remote key not specified
|
|
1440
|
+
if (!remoteKey) {
|
|
1441
|
+
remoteKey = path.basename(localPath);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
try {
|
|
1445
|
+
console.log(`Uploading ${localPath} to ${remoteKey}...`);
|
|
1446
|
+
|
|
1447
|
+
// Get presigned upload URL
|
|
1448
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1449
|
+
token: kbToken,
|
|
1450
|
+
action: 'getStorageUploadUrl',
|
|
1451
|
+
storageKey: remoteKey
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
if (response.error) {
|
|
1455
|
+
return console.red('Error:', response.error);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// Upload file using presigned URL
|
|
1459
|
+
const fileContent = fs.readFileSync(fullLocalPath);
|
|
1460
|
+
await fetch(response.uploadUrl, {
|
|
1461
|
+
method: 'PUT',
|
|
1462
|
+
body: fileContent
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
console.green(`Uploaded: ${remoteKey}`);
|
|
1466
|
+
|
|
1467
|
+
if (response.publicUrl) {
|
|
1468
|
+
console.log(`Public URL: ${response.publicUrl}`);
|
|
1469
|
+
}
|
|
1470
|
+
} catch (error) {
|
|
1471
|
+
console.red('Upload failed:', error.message);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
async function storageDownloadAction(kbToken, remoteKey, localPath) {
|
|
1476
|
+
if (!remoteKey) {
|
|
1477
|
+
return console.red('Usage: openkbs storage get <remote-key> [local-file]');
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// Use remote filename if local path not specified
|
|
1481
|
+
if (!localPath) {
|
|
1482
|
+
localPath = path.basename(remoteKey);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
try {
|
|
1486
|
+
console.log(`Downloading ${remoteKey} to ${localPath}...`);
|
|
1487
|
+
|
|
1488
|
+
// Get presigned download URL
|
|
1489
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1490
|
+
token: kbToken,
|
|
1491
|
+
action: 'getStorageDownloadUrl',
|
|
1492
|
+
storageKey: remoteKey
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
if (response.error) {
|
|
1496
|
+
return console.red('Error:', response.error);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// Download file
|
|
1500
|
+
const fetchResponse = await fetch(response.downloadUrl);
|
|
1501
|
+
if (!fetchResponse.ok) {
|
|
1502
|
+
return console.red(`Download failed: ${fetchResponse.statusText}`);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
const buffer = await fetchResponse.arrayBuffer();
|
|
1506
|
+
fs.writeFileSync(localPath, Buffer.from(buffer));
|
|
1507
|
+
|
|
1508
|
+
console.green(`Downloaded: ${localPath}`);
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
console.red('Download failed:', error.message);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
async function storageDeleteAction(kbToken, key) {
|
|
1515
|
+
if (!key) {
|
|
1516
|
+
return console.red('Usage: openkbs storage rm <key>');
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
try {
|
|
1520
|
+
console.log(`Deleting ${key}...`);
|
|
1521
|
+
|
|
1522
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1523
|
+
token: kbToken,
|
|
1524
|
+
action: 'deleteStorageObject',
|
|
1525
|
+
storageKey: key
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1528
|
+
if (response.error) {
|
|
1529
|
+
return console.red('Error:', response.error);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
console.green(`Deleted: ${key}`);
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
console.red('Delete failed:', error.message);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
async function storageDisableAction(kbToken, args) {
|
|
1539
|
+
const force = args.includes('--force');
|
|
1540
|
+
|
|
1541
|
+
try {
|
|
1542
|
+
console.log('Disabling elastic storage...');
|
|
1543
|
+
|
|
1544
|
+
if (!force) {
|
|
1545
|
+
console.yellow('Warning: This will delete the storage bucket.');
|
|
1546
|
+
console.yellow('Use --force to delete all objects and the bucket.');
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1551
|
+
token: kbToken,
|
|
1552
|
+
action: 'deleteElasticStorage',
|
|
1553
|
+
force: true
|
|
1554
|
+
});
|
|
1555
|
+
|
|
1556
|
+
if (response.error) {
|
|
1557
|
+
return console.red('Error:', response.error);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
console.green('Storage disabled successfully.');
|
|
1561
|
+
|
|
1562
|
+
if (response.functionsUpdated > 0) {
|
|
1563
|
+
console.log(`Removed STORAGE_BUCKET from ${response.functionsUpdated} function(s)`);
|
|
1564
|
+
}
|
|
1565
|
+
} catch (error) {
|
|
1566
|
+
console.red('Error disabling storage:', error.message);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
async function storagePublicAction(kbToken, value) {
|
|
1571
|
+
if (!value || !['true', 'false'].includes(value.toLowerCase())) {
|
|
1572
|
+
return console.red('Usage: openkbs storage public <true|false>');
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
const makePublic = value.toLowerCase() === 'true';
|
|
1576
|
+
|
|
1577
|
+
try {
|
|
1578
|
+
console.log(makePublic ? 'Making storage public...' : 'Making storage private...');
|
|
1579
|
+
|
|
1580
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1581
|
+
token: kbToken,
|
|
1582
|
+
action: 'setElasticStoragePublic',
|
|
1583
|
+
isPublic: makePublic
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
if (response.error) {
|
|
1587
|
+
return console.red('Error:', response.error);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (makePublic) {
|
|
1591
|
+
console.green('Storage is now public!');
|
|
1592
|
+
console.log(`Public URL: ${response.publicUrl}`);
|
|
1593
|
+
} else {
|
|
1594
|
+
console.green('Storage is now private.');
|
|
1595
|
+
}
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
console.red('Error:', error.message);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
async function storageCloudFrontAction(kbToken, pathOrRemove, pathArg) {
|
|
1602
|
+
// Handle "storage cloudfront remove <path>" vs "storage cloudfront <path>"
|
|
1603
|
+
let pathPrefix;
|
|
1604
|
+
let enable = true;
|
|
1605
|
+
|
|
1606
|
+
if (pathOrRemove === 'remove' || pathOrRemove === 'rm') {
|
|
1607
|
+
if (!pathArg) {
|
|
1608
|
+
return console.red('Usage: openkbs storage cloudfront remove <path>');
|
|
1609
|
+
}
|
|
1610
|
+
pathPrefix = pathArg;
|
|
1611
|
+
enable = false;
|
|
1612
|
+
} else {
|
|
1613
|
+
if (!pathOrRemove) {
|
|
1614
|
+
console.log('Usage: openkbs storage cloudfront <path>');
|
|
1615
|
+
console.log(' openkbs storage cloudfront remove <path>');
|
|
1616
|
+
console.log('');
|
|
1617
|
+
console.log('Examples:');
|
|
1618
|
+
console.log(' openkbs storage cloudfront media # Makes storage available at /media/*');
|
|
1619
|
+
console.log(' openkbs storage cloudfront files # Makes storage available at /files/*');
|
|
1620
|
+
console.log(' openkbs storage cloudfront remove media # Remove from CloudFront');
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
pathPrefix = pathOrRemove;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
try {
|
|
1627
|
+
if (enable) {
|
|
1628
|
+
console.log(`Adding storage to CloudFront at /${pathPrefix}/*...`);
|
|
1629
|
+
} else {
|
|
1630
|
+
console.log(`Removing storage from CloudFront at /${pathPrefix}/*...`);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1634
|
+
token: kbToken,
|
|
1635
|
+
action: 'setStorageCloudFront',
|
|
1636
|
+
pathPrefix,
|
|
1637
|
+
enable
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
if (response.error) {
|
|
1641
|
+
return console.red('Error:', response.error);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
if (enable) {
|
|
1645
|
+
console.green('Storage added to CloudFront!');
|
|
1646
|
+
console.log(` Custom URL: ${response.customUrl}`);
|
|
1647
|
+
console.log(` Path: /${response.path}/*`);
|
|
1648
|
+
console.yellow('\n Note: CloudFront changes take 2-5 minutes to propagate.');
|
|
1649
|
+
} else {
|
|
1650
|
+
console.green('Storage removed from CloudFront.');
|
|
1651
|
+
console.yellow(' Note: CloudFront changes take 2-5 minutes to propagate.');
|
|
1652
|
+
}
|
|
1653
|
+
} catch (error) {
|
|
1654
|
+
console.red('Error:', error.message);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
function formatBytes(bytes) {
|
|
1659
|
+
if (bytes === 0) return '0 B';
|
|
1660
|
+
const k = 1024;
|
|
1661
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
1662
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1663
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// ===== Elastic Postgres Commands =====
|
|
1667
|
+
|
|
1668
|
+
async function postgresAction(subCommand, args = []) {
|
|
1669
|
+
// Find kbId from settings.json (current dir, app/, functions/, site/)
|
|
1670
|
+
let kbId = findKbId();
|
|
1671
|
+
|
|
1672
|
+
if (!kbId) {
|
|
1673
|
+
const localKBData = await fetchLocalKBData();
|
|
1674
|
+
kbId = localKBData?.kbId;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
if (!kbId) {
|
|
1678
|
+
return console.red('No KB found. Create settings.json with {"kbId": "..."} or run from a KB project directory.');
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
const { kbToken } = await fetchKBJWT(kbId);
|
|
1682
|
+
|
|
1683
|
+
switch (subCommand) {
|
|
1684
|
+
case 'enable':
|
|
1685
|
+
return await postgresEnableAction(kbToken);
|
|
1686
|
+
case 'status':
|
|
1687
|
+
return await postgresStatusAction(kbToken);
|
|
1688
|
+
case 'connection':
|
|
1689
|
+
case 'conn':
|
|
1690
|
+
return await postgresConnectionAction(kbToken);
|
|
1691
|
+
case 'disable':
|
|
1692
|
+
return await postgresDisableAction(kbToken);
|
|
1693
|
+
default:
|
|
1694
|
+
console.log('Usage: openkbs postgres <command>');
|
|
1695
|
+
console.log('');
|
|
1696
|
+
console.log('Commands:');
|
|
1697
|
+
console.log(' enable Enable Postgres database for this KB');
|
|
1698
|
+
console.log(' status Show Postgres status and info');
|
|
1699
|
+
console.log(' connection Show connection string');
|
|
1700
|
+
console.log(' disable Disable Postgres (deletes database)');
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
async function postgresEnableAction(kbToken) {
|
|
1705
|
+
try {
|
|
1706
|
+
const region = findRegion();
|
|
1707
|
+
console.log(`Enabling Elastic Postgres in ${region}...`);
|
|
1708
|
+
|
|
1709
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1710
|
+
token: kbToken,
|
|
1711
|
+
action: 'enableElasticPostgres',
|
|
1712
|
+
region
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
if (response.error) {
|
|
1716
|
+
return console.red('Error:', response.error);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
if (response.alreadyEnabled) {
|
|
1720
|
+
console.yellow('Postgres already enabled.');
|
|
1721
|
+
} else {
|
|
1722
|
+
console.green('Postgres enabled successfully!');
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
console.log(` Host: ${response.host}`);
|
|
1726
|
+
console.log(` Port: ${response.port}`);
|
|
1727
|
+
console.log(` Database: ${response.dbName}`);
|
|
1728
|
+
console.log(` Region: ${response.region}`);
|
|
1729
|
+
|
|
1730
|
+
if (response.functionsUpdated > 0) {
|
|
1731
|
+
console.log(` Updated ${response.functionsUpdated} function(s) with DATABASE_URL`);
|
|
1732
|
+
}
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
console.red('Error enabling Postgres:', error.message);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
async function postgresStatusAction(kbToken) {
|
|
1739
|
+
try {
|
|
1740
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1741
|
+
token: kbToken,
|
|
1742
|
+
action: 'getElasticPostgres'
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1745
|
+
if (response.error) {
|
|
1746
|
+
return console.red('Error:', response.error);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
if (!response.enabled) {
|
|
1750
|
+
console.yellow('Postgres is not enabled.');
|
|
1751
|
+
console.log('Run: openkbs postgres enable');
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
console.green('Postgres Status: Enabled');
|
|
1756
|
+
console.log(` Host: ${response.host}`);
|
|
1757
|
+
console.log(` Port: ${response.port}`);
|
|
1758
|
+
console.log(` Database: ${response.dbName}`);
|
|
1759
|
+
console.log(` Region: ${response.region}`);
|
|
1760
|
+
console.log(` Project: ${response.projectId}`);
|
|
1761
|
+
} catch (error) {
|
|
1762
|
+
console.red('Error getting Postgres status:', error.message);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
async function postgresConnectionAction(kbToken) {
|
|
1767
|
+
try {
|
|
1768
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1769
|
+
token: kbToken,
|
|
1770
|
+
action: 'getElasticPostgresConnection'
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
if (response.error) {
|
|
1774
|
+
return console.red('Error:', response.error);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
console.log('Connection String:');
|
|
1778
|
+
console.log(response.connectionString);
|
|
1779
|
+
} catch (error) {
|
|
1780
|
+
console.red('Error getting connection:', error.message);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
async function postgresDisableAction(kbToken) {
|
|
1785
|
+
try {
|
|
1786
|
+
console.log('Disabling Elastic Postgres...');
|
|
1787
|
+
console.yellow('Warning: This will permanently delete the database and all data!');
|
|
1788
|
+
|
|
1789
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1790
|
+
token: kbToken,
|
|
1791
|
+
action: 'deleteElasticPostgres'
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
if (response.error) {
|
|
1795
|
+
return console.red('Error:', response.error);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
console.green('Postgres disabled successfully.');
|
|
1799
|
+
|
|
1800
|
+
if (response.functionsUpdated > 0) {
|
|
1801
|
+
console.log(`Removed DATABASE_URL from ${response.functionsUpdated} function(s)`);
|
|
1802
|
+
}
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
console.red('Error disabling Postgres:', error.message);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// ===== Site Commands =====
|
|
1809
|
+
|
|
1810
|
+
async function siteAction(subCommand, args = []) {
|
|
1811
|
+
// Find kbId and site directory
|
|
1812
|
+
let kbId = findKbId();
|
|
1813
|
+
let siteDir = process.cwd();
|
|
1814
|
+
|
|
1815
|
+
// If no settings.json in current dir, check site/ subdirectory
|
|
1816
|
+
if (!fs.existsSync(path.join(process.cwd(), 'settings.json'))) {
|
|
1817
|
+
const siteDirPath = path.join(process.cwd(), 'site');
|
|
1818
|
+
const siteSettingsPath = path.join(siteDirPath, 'settings.json');
|
|
1819
|
+
if (fs.existsSync(siteSettingsPath)) {
|
|
1820
|
+
siteDir = siteDirPath;
|
|
1821
|
+
try {
|
|
1822
|
+
const settings = JSON.parse(fs.readFileSync(siteSettingsPath, 'utf8'));
|
|
1823
|
+
kbId = settings.kbId;
|
|
1824
|
+
} catch (e) {}
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
if (!kbId) {
|
|
1829
|
+
const localKBData = await fetchLocalKBData();
|
|
1830
|
+
kbId = localKBData?.kbId;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
if (!kbId) {
|
|
1834
|
+
return console.red('No KB found. Create settings.json with {"kbId": "..."} in current dir or site/ subdirectory.');
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
const { kbToken } = await fetchKBJWT(kbId);
|
|
1838
|
+
|
|
1839
|
+
switch (subCommand) {
|
|
1840
|
+
case 'deploy':
|
|
1841
|
+
return await siteDeployAction(kbToken, kbId, siteDir, args);
|
|
1842
|
+
default:
|
|
1843
|
+
console.log('Site management commands:\n');
|
|
1844
|
+
console.log(' openkbs site deploy Upload all files to S3');
|
|
1845
|
+
console.log('\nRun from a folder containing settings.json with kbId, or from parent with site/ subdirectory');
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
async function siteDeployAction(kbToken, kbId, siteDir, args) {
|
|
1850
|
+
|
|
1851
|
+
// Walk directory and get all files (excluding settings.json and hidden files)
|
|
1852
|
+
const walkDir = async (dir, baseDir = dir) => {
|
|
1853
|
+
const files = [];
|
|
1854
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1855
|
+
|
|
1856
|
+
for (const entry of entries) {
|
|
1857
|
+
const fullPath = path.join(dir, entry.name);
|
|
1858
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
1859
|
+
|
|
1860
|
+
// Skip hidden files, settings.json, and node_modules
|
|
1861
|
+
if (entry.name.startsWith('.') ||
|
|
1862
|
+
entry.name === 'settings.json' ||
|
|
1863
|
+
entry.name === 'node_modules') {
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
if (entry.isDirectory()) {
|
|
1868
|
+
files.push(...await walkDir(fullPath, baseDir));
|
|
1869
|
+
} else {
|
|
1870
|
+
files.push(relativePath);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
return files;
|
|
1874
|
+
};
|
|
1875
|
+
|
|
1876
|
+
try {
|
|
1877
|
+
console.log(`Uploading site files for KB ${kbId}...`);
|
|
1878
|
+
|
|
1879
|
+
const files = await walkDir(siteDir);
|
|
1880
|
+
|
|
1881
|
+
if (files.length === 0) {
|
|
1882
|
+
return console.yellow('No files found to upload.');
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
console.log(`Found ${files.length} files to upload.`);
|
|
1886
|
+
|
|
1887
|
+
let uploaded = 0;
|
|
1888
|
+
for (const file of files) {
|
|
1889
|
+
const filePath = path.join(siteDir, file);
|
|
1890
|
+
const fileContent = fs.readFileSync(filePath);
|
|
1891
|
+
|
|
1892
|
+
// Get presigned URL for 'files' namespace with correct Content-Type
|
|
1893
|
+
const contentType = getMimeType(file);
|
|
1894
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1895
|
+
token: kbToken,
|
|
1896
|
+
namespace: 'files',
|
|
1897
|
+
kbId,
|
|
1898
|
+
fileName: file,
|
|
1899
|
+
fileType: contentType,
|
|
1900
|
+
presignedOperation: 'putObject',
|
|
1901
|
+
action: 'createPresignedURL'
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
if (response.error) {
|
|
1905
|
+
console.red(`Failed to get presigned URL for ${file}:`, response.error);
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
// Upload file with correct Content-Type
|
|
1910
|
+
await fetch(response, {
|
|
1911
|
+
method: 'PUT',
|
|
1912
|
+
body: fileContent,
|
|
1913
|
+
headers: { 'Content-Type': contentType }
|
|
1914
|
+
});
|
|
1915
|
+
uploaded++;
|
|
1916
|
+
console.log(`Uploaded: ${file} (${contentType})`);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
console.green(`\nUpload complete! ${uploaded}/${files.length} files uploaded.`);
|
|
1920
|
+
console.log(`Files accessible at: https://files.openkbs.com/${kbId}/`);
|
|
1921
|
+
|
|
1922
|
+
} catch (error) {
|
|
1923
|
+
console.red('Upload failed:', error.message);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// ===== Elastic Pulse Commands =====
|
|
1928
|
+
|
|
1929
|
+
async function pulseAction(subCommand, args = []) {
|
|
1930
|
+
// Find kbId from settings.json (current dir, app/, functions/, site/)
|
|
1931
|
+
let kbId = findKbId();
|
|
1932
|
+
|
|
1933
|
+
if (!kbId) {
|
|
1934
|
+
const localKBData = await fetchLocalKBData();
|
|
1935
|
+
kbId = localKBData?.kbId;
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
if (!kbId) {
|
|
1939
|
+
return console.red('No KB found. Create settings.json with {"kbId": "..."} or run from a KB project directory.');
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
const { kbToken } = await fetchKBJWT(kbId);
|
|
1943
|
+
|
|
1944
|
+
switch (subCommand) {
|
|
1945
|
+
case 'enable':
|
|
1946
|
+
return await pulseEnableAction(kbToken);
|
|
1947
|
+
case 'status':
|
|
1948
|
+
return await pulseStatusAction(kbToken);
|
|
1949
|
+
case 'disable':
|
|
1950
|
+
return await pulseDisableAction(kbToken);
|
|
1951
|
+
case 'channels':
|
|
1952
|
+
return await pulseChannelsAction(kbToken);
|
|
1953
|
+
case 'presence':
|
|
1954
|
+
return await pulsePresenceAction(kbToken, args[0]);
|
|
1955
|
+
case 'publish':
|
|
1956
|
+
case 'send':
|
|
1957
|
+
return await pulsePublishAction(kbToken, args[0], args.slice(1).join(' '));
|
|
1958
|
+
default:
|
|
1959
|
+
console.log('Usage: openkbs pulse <command>');
|
|
1960
|
+
console.log('');
|
|
1961
|
+
console.log('Commands:');
|
|
1962
|
+
console.log(' enable Enable Pulse (WebSocket) for this KB');
|
|
1963
|
+
console.log(' status Show Pulse status and endpoint');
|
|
1964
|
+
console.log(' disable Disable Pulse');
|
|
1965
|
+
console.log(' channels List active channels');
|
|
1966
|
+
console.log(' presence <channel> Show connected clients in channel');
|
|
1967
|
+
console.log(' publish <channel> <message> Send message to channel');
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
async function pulseEnableAction(kbToken) {
|
|
1972
|
+
try {
|
|
1973
|
+
console.log('Enabling Elastic Pulse...');
|
|
1974
|
+
|
|
1975
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
1976
|
+
token: kbToken,
|
|
1977
|
+
action: 'enableElasticPulse'
|
|
1978
|
+
});
|
|
1979
|
+
|
|
1980
|
+
if (response.error) {
|
|
1981
|
+
return console.red('Error:', response.error);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
if (response.alreadyEnabled) {
|
|
1985
|
+
console.yellow('Pulse already enabled.');
|
|
1986
|
+
} else {
|
|
1987
|
+
console.green('Pulse enabled successfully!');
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
console.log(` Endpoint: ${response.endpoint}`);
|
|
1991
|
+
console.log(` Region: ${response.region}`);
|
|
1992
|
+
} catch (error) {
|
|
1993
|
+
console.red('Error enabling Pulse:', error.message);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
async function pulseStatusAction(kbToken) {
|
|
1998
|
+
try {
|
|
1999
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
2000
|
+
token: kbToken,
|
|
2001
|
+
action: 'getElasticPulse'
|
|
2002
|
+
});
|
|
2003
|
+
|
|
2004
|
+
if (response.error) {
|
|
2005
|
+
return console.red('Error:', response.error);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
if (!response.enabled) {
|
|
2009
|
+
console.log('Elastic Pulse: disabled');
|
|
2010
|
+
console.log(' Use "openkbs pulse enable" to enable WebSocket messaging.');
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
console.log('Elastic Pulse: enabled');
|
|
2015
|
+
console.log(` Endpoint: ${response.endpoint}`);
|
|
2016
|
+
console.log(` Region: ${response.region}`);
|
|
2017
|
+
} catch (error) {
|
|
2018
|
+
console.red('Error getting Pulse status:', error.message);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
async function pulseDisableAction(kbToken) {
|
|
2023
|
+
try {
|
|
2024
|
+
console.log('Disabling Elastic Pulse...');
|
|
2025
|
+
|
|
2026
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
2027
|
+
token: kbToken,
|
|
2028
|
+
action: 'disableElasticPulse'
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
if (response.error) {
|
|
2032
|
+
return console.red('Error:', response.error);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
console.green('Pulse disabled successfully.');
|
|
2036
|
+
} catch (error) {
|
|
2037
|
+
console.red('Error disabling Pulse:', error.message);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
async function pulseChannelsAction(kbToken) {
|
|
2042
|
+
try {
|
|
2043
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
2044
|
+
token: kbToken,
|
|
2045
|
+
action: 'pulseChannels'
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
if (response.error) {
|
|
2049
|
+
return console.red('Error:', response.error);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
if (!response.channels || response.channels.length === 0) {
|
|
2053
|
+
console.log('No active channels');
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
console.log(`Active channels (${response.totalConnections} total connections):\n`);
|
|
2058
|
+
for (const ch of response.channels) {
|
|
2059
|
+
console.log(` ${ch.channel}: ${ch.count} connection(s)`);
|
|
2060
|
+
}
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
console.red('Error getting channels:', error.message);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
async function pulsePresenceAction(kbToken, channel) {
|
|
2067
|
+
try {
|
|
2068
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
2069
|
+
token: kbToken,
|
|
2070
|
+
action: 'pulsePresence',
|
|
2071
|
+
channel: channel || 'default'
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2074
|
+
if (response.error) {
|
|
2075
|
+
return console.red('Error:', response.error);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
console.log(`Channel: ${response.channel}`);
|
|
2079
|
+
console.log(`Connected: ${response.count}`);
|
|
2080
|
+
|
|
2081
|
+
if (response.members && response.members.length > 0) {
|
|
2082
|
+
console.log('\nMembers:');
|
|
2083
|
+
for (const m of response.members) {
|
|
2084
|
+
const time = new Date(m.connectedAt).toISOString();
|
|
2085
|
+
console.log(` ${m.userId || 'anonymous'} (since ${time})`);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
} catch (error) {
|
|
2089
|
+
console.red('Error getting presence:', error.message);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
async function pulsePublishAction(kbToken, channel, message) {
|
|
2094
|
+
if (!channel || !message) {
|
|
2095
|
+
return console.red('Usage: openkbs pulse publish <channel> <message>');
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
try {
|
|
2099
|
+
const response = await makePostRequest(KB_API_URL, {
|
|
2100
|
+
token: kbToken,
|
|
2101
|
+
action: 'pulsePublish',
|
|
2102
|
+
channel,
|
|
2103
|
+
message
|
|
2104
|
+
});
|
|
2105
|
+
|
|
2106
|
+
if (response.error) {
|
|
2107
|
+
return console.red('Error:', response.error);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
console.green(`Message sent to ${response.sent} client(s) on channel "${response.channel}"`);
|
|
2111
|
+
} catch (error) {
|
|
2112
|
+
console.red('Error publishing message:', error.message);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
|
|
1183
2116
|
module.exports = {
|
|
1184
2117
|
signAction,
|
|
1185
2118
|
loginAction,
|
|
@@ -1201,5 +2134,9 @@ module.exports = {
|
|
|
1201
2134
|
updateCliAction,
|
|
1202
2135
|
publishAction,
|
|
1203
2136
|
unpublishAction,
|
|
1204
|
-
fnAction
|
|
2137
|
+
fnAction,
|
|
2138
|
+
siteAction,
|
|
2139
|
+
storageAction,
|
|
2140
|
+
postgresAction,
|
|
2141
|
+
pulseAction
|
|
1205
2142
|
};
|
package/src/index.js
CHANGED
|
@@ -14,7 +14,11 @@ const {
|
|
|
14
14
|
describeAction, deployAction, createByTemplateAction, initByTemplateAction,
|
|
15
15
|
logoutAction, installFrontendPackageAction, modifyAction, downloadModifyAction,
|
|
16
16
|
updateKnowledgeAction, updateCliAction, publishAction, unpublishAction,
|
|
17
|
-
fnAction
|
|
17
|
+
fnAction,
|
|
18
|
+
siteAction,
|
|
19
|
+
storageAction,
|
|
20
|
+
postgresAction,
|
|
21
|
+
pulseAction
|
|
18
22
|
} = require('./actions');
|
|
19
23
|
|
|
20
24
|
|
|
@@ -201,11 +205,12 @@ Examples:
|
|
|
201
205
|
program
|
|
202
206
|
.command('fn [subCommand] [args...]')
|
|
203
207
|
.description('Manage Elastic Functions (serverless Lambda functions)')
|
|
208
|
+
.allowUnknownOption()
|
|
204
209
|
.action((subCommand, args) => fnAction(subCommand, args))
|
|
205
210
|
.addHelpText('after', `
|
|
206
211
|
Examples:
|
|
207
212
|
$ openkbs fn list List all functions
|
|
208
|
-
$ openkbs fn deploy hello --region us-east-
|
|
213
|
+
$ openkbs fn deploy hello --region us-east-1 Deploy function from ./functions/hello/
|
|
209
214
|
$ openkbs fn delete hello Delete a function
|
|
210
215
|
$ openkbs fn logs hello View function logs
|
|
211
216
|
$ openkbs fn env hello View environment variables
|
|
@@ -213,4 +218,59 @@ Examples:
|
|
|
213
218
|
$ openkbs fn invoke hello '{"test": true}' Invoke a function
|
|
214
219
|
`);
|
|
215
220
|
|
|
221
|
+
program
|
|
222
|
+
.command('site [subCommand] [args...]')
|
|
223
|
+
.description('Manage static site files for whitelabel domains')
|
|
224
|
+
.action((subCommand, args) => siteAction(subCommand, args))
|
|
225
|
+
.addHelpText('after', `
|
|
226
|
+
Examples:
|
|
227
|
+
$ openkbs site deploy Deploy all files to S3
|
|
228
|
+
|
|
229
|
+
Run from a directory containing settings.json with kbId.
|
|
230
|
+
Files are uploaded to the whitelabel domain's files bucket.
|
|
231
|
+
`);
|
|
232
|
+
|
|
233
|
+
program
|
|
234
|
+
.command('storage [subCommand] [args...]')
|
|
235
|
+
.description('Manage Elastic Storage (S3 buckets for persistent file storage)')
|
|
236
|
+
.action((subCommand, args) => storageAction(subCommand, args))
|
|
237
|
+
.addHelpText('after', `
|
|
238
|
+
Examples:
|
|
239
|
+
$ openkbs storage enable Enable storage for current KB
|
|
240
|
+
$ openkbs storage status Show storage status
|
|
241
|
+
$ openkbs storage ls [prefix] List objects in bucket
|
|
242
|
+
$ openkbs storage put <file> <key> Upload a file
|
|
243
|
+
$ openkbs storage get <key> <file> Download a file
|
|
244
|
+
$ openkbs storage rm <key> Delete an object
|
|
245
|
+
$ openkbs storage disable Disable storage (delete bucket)
|
|
246
|
+
$ openkbs storage cloudfront media Add storage to CloudFront at /media/*
|
|
247
|
+
$ openkbs storage cloudfront remove media Remove storage from CloudFront
|
|
248
|
+
`);
|
|
249
|
+
|
|
250
|
+
program
|
|
251
|
+
.command('postgres [subCommand]')
|
|
252
|
+
.description('Manage Elastic Postgres (Neon PostgreSQL database)')
|
|
253
|
+
.action((subCommand) => postgresAction(subCommand))
|
|
254
|
+
.addHelpText('after', `
|
|
255
|
+
Examples:
|
|
256
|
+
$ openkbs postgres enable Enable Postgres for current KB
|
|
257
|
+
$ openkbs postgres status Show Postgres status
|
|
258
|
+
$ openkbs postgres connection Show connection string
|
|
259
|
+
$ openkbs postgres disable Disable Postgres (delete database)
|
|
260
|
+
`);
|
|
261
|
+
|
|
262
|
+
program
|
|
263
|
+
.command('pulse [subCommand] [args...]')
|
|
264
|
+
.description('Manage Elastic Pulse (real-time WebSocket pub/sub)')
|
|
265
|
+
.action((subCommand, args) => pulseAction(subCommand, args))
|
|
266
|
+
.addHelpText('after', `
|
|
267
|
+
Examples:
|
|
268
|
+
$ openkbs pulse enable Enable Pulse for current KB
|
|
269
|
+
$ openkbs pulse status Show Pulse status and endpoint
|
|
270
|
+
$ openkbs pulse channels List active channels
|
|
271
|
+
$ openkbs pulse presence chat Show clients connected to 'chat' channel
|
|
272
|
+
$ openkbs pulse publish chat "Hello!" Send message to 'chat' channel
|
|
273
|
+
$ openkbs pulse disable Disable Pulse
|
|
274
|
+
`);
|
|
275
|
+
|
|
216
276
|
program.parse(process.argv);
|
package/src/utils.js
CHANGED
|
@@ -164,8 +164,11 @@ function makePostRequest(url, data) {
|
|
|
164
164
|
resolve(data);
|
|
165
165
|
} else {
|
|
166
166
|
try {
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
const parsed = JSON.parse(body);
|
|
168
|
+
if (parsed.error) {
|
|
169
|
+
console.red(parsed.error);
|
|
170
|
+
} else if (parsed.message) {
|
|
171
|
+
console.red(parsed.message);
|
|
169
172
|
} else {
|
|
170
173
|
console.red(`Invalid Request`);
|
|
171
174
|
}
|
package/version.json
CHANGED