KUMOS/Client/kumos.js
2026-05-02 15:49:06 +02:00

125 lines
5.0 KiB
JavaScript

/**
* KUMO.S App SDK — include in your app with:
* <script src="/kumos.js"></script>
*
* All methods return Promises. Call KUMOS.ready() first.
*
* Permissions required per namespace:
* storage.* — always available (private app namespace)
* fs.* — requires "fs.read" or "fs.write" grant
* notify.* — requires "notify" grant
* window.* — always available
*/
(function (global) {
'use strict';
var _pending = {}; // kmid → { resolve, reject, timer }
// ── Internal helpers ──────────────────────────────────────────────────────
function _uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
function _send(action, args) {
return new Promise(function (resolve, reject) {
var kmid = _uuid();
var timer = setTimeout(function () {
if (_pending[kmid]) {
delete _pending[kmid];
reject(new Error('KUMOS timeout: ' + action));
}
}, 15000);
_pending[kmid] = { resolve: resolve, reject: reject, timer: timer };
window.parent.postMessage(
JSON.stringify({ kmid: kmid, action: action, args: args || {} }),
'*'
);
});
}
// Incoming responses from the shell
window.addEventListener('message', function (e) {
if (e.source !== window.parent) return;
var msg;
try { msg = JSON.parse(e.data); } catch (err) { return; }
if (!msg || !msg.kmid) return;
var p = _pending[msg.kmid];
if (!p) return;
clearTimeout(p.timer);
delete _pending[msg.kmid];
if (msg.success) {
p.resolve(msg.data !== undefined ? msg.data : null);
} else {
p.reject(new Error(msg.error || 'Unknown error'));
}
});
// ── Public API ────────────────────────────────────────────────────────────
var KUMOS = {
/**
* Handshake with the shell. Resolves with { app_id }.
* Always call this first and await it before using any other APIs.
*/
ready: function () {
return _send('window.ready', {});
},
// ── Private app storage (no extra permission required) ───────────────
storage: {
/** Read a file. Resolves with the content string. */
read: function (path) { return _send('storage.read', { path: path }); },
/** Write content to a file. Creates it if it does not exist. */
write: function (path, content) { return _send('storage.write', { path: path, content: content }); },
/** List a directory. Resolves with an array of entry objects. */
list: function (path) { return _send('storage.list', { path: path || '/' }); },
/** Stat a path. */
stat: function (path) { return _send('storage.stat', { path: path }); },
/** Create a directory. */
mkdir: function (path) { return _send('storage.mkdir', { path: path }); },
/** Delete a file. */
delete: function (path) { return _send('storage.delete', { path: path }); }
},
// ── User filesystem (requires fs.read / fs.write permission) ─────────
fs: {
/** Read a file from the user's filesystem. */
read: function (path) { return _send('fs.read', { path: path }); },
/** Write content to a file in the user's filesystem. */
write: function (path, content) { return _send('fs.write', { path: path, content: content }); },
/** List a directory. */
list: function (path) { return _send('fs.list', { path: path || '/' }); },
/** Stat a path. */
stat: function (path) { return _send('fs.stat', { path: path }); }
},
// ── Notifications (requires notify permission) ────────────────────────
notify: {
/** Show a toast. type: 'info' | 'success' | 'warning' | 'error' */
toast: function (message, type) { return _send('notify.toast', { message: message, type: type || 'info' }); },
/** Show a confirm dialog. Resolves with true (OK) or false (Cancel). */
confirm: function (title, message) { return _send('notify.confirm', { title: title, message: message }); }
},
// ── Window control ────────────────────────────────────────────────────
window: {
/** Update the host window title. */
setTitle: function (title) { return _send('window.setTitle', { title: title }); },
/** Close this app instance. */
close: function () { return _send('window.close', {}); }
}
};
global.KUMOS = KUMOS;
}(window));