diff --git a/server/main.go b/server/main.go
index 3d678f5..7fd31dd 100644
--- a/server/main.go
+++ b/server/main.go
@@ -737,6 +737,8 @@ func main() {
admin.GET("/packages/:id/dependencies", pkgAdm.ListDependencies) // v0.38.2
admin.GET("/packages/:id/consumers", pkgAdm.ListConsumers) // v0.38.2
admin.POST("/packages/:id/test-tool", pkgAdm.TestTool) // v0.38.2
+ admin.POST("/packages/:id/update", pkgAdm.UpdatePackage) // v0.5.4
+ admin.GET("/packages/:id/export", pkgAdm.ExportPackage) // v0.5.4
admin.GET("/dependencies", pkgAdm.ListAllDependencies) // v0.38.2
// Workflow package export (v0.30.2)
diff --git a/src/js/sw/sdk/api-domains.js b/src/js/sw/sdk/api-domains.js
index f13849c..7da25c7 100644
--- a/src/js/sw/sdk/api-domains.js
+++ b/src/js/sw/sdk/api-domains.js
@@ -253,6 +253,8 @@ export function createDomains(restClient) {
updateSettings: (id, data) => rc.put(`/api/v1/admin/packages/${id}/settings`, data),
dependencies: (id) => rc.get(`/api/v1/admin/packages/${id}/dependencies`), // v0.38.2
consumers: (id) => rc.get(`/api/v1/admin/packages/${id}/consumers`), // v0.38.2
+ update: (id, file) => rc.upload(`/api/v1/admin/packages/${id}/update`, file), // v0.5.4
+ exportPkg: (id) => `/api/v1/admin/packages/${id}/export`, // v0.5.4 (URL for window.open)
registry: () => rc.get('/api/v1/admin/packages/registry'),
registryInstall: (url) => rc.post('/api/v1/admin/packages/registry/install', { url }),
// v0.5.0: Extension permissions
diff --git a/src/js/sw/surfaces/admin/packages.js b/src/js/sw/surfaces/admin/packages.js
index 2af4ef0..7a7b906 100644
--- a/src/js/sw/surfaces/admin/packages.js
+++ b/src/js/sw/surfaces/admin/packages.js
@@ -116,6 +116,27 @@ export default function PackagesSection() {
window.open(`${BASE}/api/v1/admin/packages/${id}/export`, '_blank');
}
+ function updatePkg(pkg) {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.pkg,.surface,.zip';
+ input.onchange = async () => {
+ const file = input.files[0];
+ if (!file) return;
+ const ok = await sw.confirm(`Update "${pkg.title}" from v${pkg.version}?\n\nExport a backup first if needed.`, { destructive: true });
+ if (!ok) return;
+ setInstalling(true);
+ try {
+ const res = await sw.api.admin.packages.update(pkg.id, file);
+ const data = res?.data || res || {};
+ sw.toast(`Updated to v${data.version || '?'}`, 'success');
+ load();
+ } catch (e) { sw.toast(e.message, 'error'); }
+ finally { setInstalling(false); }
+ };
+ input.click();
+ }
+
// ── Settings drawer ─────────────────────────
async function toggleSettings(pkgId) {
if (expandedId === pkgId) {
@@ -271,6 +292,9 @@ export default function PackagesSection() {
${permsId === pkg.id ? 'Close' : 'Permissions'}
`}
+ ${pkg.source !== 'core' && html`
+
+ `}
${pkg.source !== 'core' && !pkg.is_system && html`