Changeset 0.37.14 (#226)

Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
2026-03-23 16:47:48 +00:00
committed by xcaliber
parent fcb998bff9
commit b7746c3004
164 changed files with 6972 additions and 3527 deletions

View File

@@ -73,6 +73,7 @@ export function createDomains(restClient) {
complete: (data, signal) => rc.stream('/api/v1/chat/completions', data, signal),
regenerate: (id, msgId, data, signal) => rc.stream(`/api/v1/channels/${id}/messages/${msgId}/regenerate`, data, signal),
editMessage: (id, msgId, content) => rc.post(`/api/v1/channels/${id}/messages/${msgId}/edit`, { content }),
deleteMessage: (id, msgId) => rc.del(`/api/v1/channels/${id}/messages/${msgId}`),
siblings: (id, msgId) => rc.get(`/api/v1/channels/${id}/messages/${msgId}/siblings`),
path: (id) => rc.get(`/api/v1/channels/${id}/path`),
cursor: (id, leafId) => rc.put(`/api/v1/channels/${id}/cursor`, { active_leaf_id: leafId }),
@@ -184,11 +185,13 @@ export function createDomains(restClient) {
// ── 11. Notifications ──────────────────
notifications: {
list: (opts) => rc.get('/api/v1/notifications' + _qs(opts)),
markRead: (id) => rc.post(`/api/v1/notifications/${id}/read`, {}),
prefs: () => rc.get('/api/v1/notifications/preferences'),
setPref: (type, data) => rc.put(`/api/v1/notifications/preferences/${encodeURIComponent(type)}`, data),
delPref: (type) => rc.del(`/api/v1/notifications/preferences/${encodeURIComponent(type)}`),
list: (opts) => rc.get('/api/v1/notifications' + _qs(opts)),
unreadCount: () => rc.get('/api/v1/notifications/unread-count'),
markRead: (id) => rc.post(`/api/v1/notifications/${id}/read`, {}),
markAllRead: () => rc.post('/api/v1/notifications/mark-all-read', {}),
prefs: () => rc.get('/api/v1/notifications/preferences'),
setPref: (type, data) => rc.put(`/api/v1/notifications/preferences/${encodeURIComponent(type)}`, data),
delPref: (type) => rc.del(`/api/v1/notifications/preferences/${encodeURIComponent(type)}`),
},
// ── 12. Extensions ─────────────────────
@@ -443,6 +446,19 @@ export function createDomains(restClient) {
del: (id) => rc.del(`/api/v1/admin/extensions/${id}`),
},
packages: {
list: (opts) => rc.get('/api/v1/admin/packages' + _qs(opts)),
get: (id) => rc.get(`/api/v1/admin/packages/${id}`),
install: (file) => rc.upload('/api/v1/admin/packages/install', file),
enable: (id) => rc.put(`/api/v1/admin/packages/${id}/enable`, {}),
disable: (id) => rc.put(`/api/v1/admin/packages/${id}/disable`, {}),
del: (id) => rc.del(`/api/v1/admin/packages/${id}`),
settings: (id) => rc.get(`/api/v1/admin/packages/${id}/settings`),
updateSettings: (id, data) => rc.put(`/api/v1/admin/packages/${id}/settings`, data),
registry: () => rc.get('/api/v1/admin/packages/registry'),
registryInstall: (url) => rc.post('/api/v1/admin/packages/registry/install', { url }),
},
tasks: {
list: () => rc.get('/api/v1/admin/tasks'),
run: (id) => rc.post(`/api/v1/admin/tasks/${id}/run`, {}),
@@ -478,11 +494,19 @@ export function createDomains(restClient) {
// ── Misc (not domain-specific) ─────────
folders: {
list: () => rc.get('/api/v1/folders'),
create: (name, sort) => rc.post('/api/v1/folders', { name, sort_order: sort || 0 }),
create: (name, opts) => rc.post('/api/v1/folders', { name, parent_id: opts?.parent_id || null, sort_order: opts?.sort_order || 0 }),
update: (id, data) => rc.put(`/api/v1/folders/${id}`, data),
del: (id) => rc.del(`/api/v1/folders/${id}`),
},
notifications: {
list: (params) => rc.get('/api/v1/notifications', params),
unreadCount: () => rc.get('/api/v1/notifications/unread-count'),
markRead: (id) => rc.patch(`/api/v1/notifications/${id}/read`),
markAllRead: () => rc.post('/api/v1/notifications/mark-all-read'),
del: (id) => rc.del(`/api/v1/notifications/${id}`),
},
files: {
get: (id) => rc.get(`/api/v1/files/${id}`),
del: (id) => rc.del(`/api/v1/files/${id}`),
@@ -492,6 +516,11 @@ export function createDomains(restClient) {
list: () => rc.get('/api/v1/tools'),
},
// ── Users ──────────────────────────────
users: {
search: (q) => rc.get('/api/v1/users/search' + _qs({ q })),
},
presence: {
heartbeat: () => rc.post('/api/v1/presence/heartbeat', {}),
},

View File

@@ -30,6 +30,7 @@ export function createAuth() {
let _policies = {};
let _refreshTimer = null;
let _refreshPromise = null;
let _booting = false;
let _restClient = null;
let _emit = () => {};
@@ -69,6 +70,7 @@ export function createAuth() {
_groups = [];
_policies = {};
localStorage.removeItem(_storageKey);
sessionStorage.removeItem('sw-chat-active');
document.cookie = 'sb_token=; path=/; max-age=0';
if (_refreshTimer) { clearTimeout(_refreshTimer); _refreshTimer = null; }
}
@@ -194,11 +196,19 @@ export function createAuth() {
*/
async boot() {
_loadTokens();
if (!_refreshToken) return;
if (!_refreshToken) {
// No tokens in localStorage — clear any stale sb_token cookie
// so Go SSR middleware doesn't trust a leftover cookie.
document.cookie = 'sb_token=; path=/; max-age=0';
return;
}
// Unknown token age — schedule refresh soon
_scheduleRefresh(60);
// Suppress _on401Failure redirect during boot — boot handles 401 itself.
_booting = true;
// Fetch permissions (validates the access token)
try {
await _fetchPermissions();
@@ -215,6 +225,8 @@ export function createAuth() {
// Network error or other — keep tokens, user may be offline
console.warn('[sw.auth] Boot: permissions fetch failed (keeping tokens):', e.message);
}
} finally {
_booting = false;
}
if (_accessToken) {
@@ -233,8 +245,15 @@ export function createAuth() {
_on401Failure() {
_clearTokens();
// During boot(), auth.boot() handles 401 gracefully — don't redirect.
if (_booting) return;
// Already on login page — don't redirect (prevents blank-page reload loop
// when stale localStorage tokens trigger 401 during SDK boot on /login).
const base = window.__BASE__ || '';
window.location.href = base + '/login';
const loginPath = base + '/login';
const here = window.location.pathname.replace(/\/+$/, '');
if (here === loginPath) return;
window.location.href = loginPath;
},
_setAuth(data) { _setAuth(data); },

View File

@@ -139,7 +139,7 @@ export async function boot() {
};
// Marker for idempotency
sw._sdk = '0.37.9';
sw._sdk = '0.37.14';
// 8. Expose globally
window.sw = sw;
@@ -161,7 +161,7 @@ export async function boot() {
// 10. Signal ready
events.emit('sdk.ready', {}, { localOnly: true });
document.dispatchEvent(new CustomEvent('sw:ready', { detail: { sw } }));
console.log('[sw] SDK v0.37.9 ready');
console.log(`[sw] SDK v${sw._sdk} ready`);
return sw;
}

View File

@@ -85,6 +85,12 @@ export function createPipe() {
entry.stats.calls++;
entry.stats.totalMs += performance.now() - t0;
if (result instanceof Promise) {
console.error(`[sw.pipe] ${stage}: filter '${entry.source}' returned Promise (async not supported)`);
entry.stats.errors++;
continue;
}
if (result === null || result === undefined) {
return null; // halt chain
}

View File

@@ -97,8 +97,10 @@ export function createRestClient(getToken, onRefresh, on401Failure) {
if (!text) return null;
const json = JSON.parse(text);
// List envelope: { data: Array, ... }
if (json && Array.isArray(json.data) && ('page' in json || 'total' in json || 'per_page' in json)) {
// List envelope: { data: Array } with no sibling keys → unwrap.
// If siblings exist (total, page, etc.) return full object so
// callers can access metadata alongside the list.
if (json && Array.isArray(json.data) && Object.keys(json).length === 1) {
return json.data;
}
return json;