sh3-core 0.6.0

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.
Files changed (242) hide show
  1. package/README.md +9 -0
  2. package/dist/Shell.svelte +283 -0
  3. package/dist/Shell.svelte.d.ts +5 -0
  4. package/dist/api.d.ts +28 -0
  5. package/dist/api.js +50 -0
  6. package/dist/app/admin/ApiKeysView.svelte +169 -0
  7. package/dist/app/admin/ApiKeysView.svelte.d.ts +3 -0
  8. package/dist/app/admin/AuthSettingsView.svelte +105 -0
  9. package/dist/app/admin/AuthSettingsView.svelte.d.ts +3 -0
  10. package/dist/app/admin/SystemView.svelte +73 -0
  11. package/dist/app/admin/SystemView.svelte.d.ts +3 -0
  12. package/dist/app/admin/UsersView.svelte +188 -0
  13. package/dist/app/admin/UsersView.svelte.d.ts +3 -0
  14. package/dist/app/admin/adminApp.d.ts +7 -0
  15. package/dist/app/admin/adminApp.js +25 -0
  16. package/dist/app/admin/adminShard.svelte.d.ts +4 -0
  17. package/dist/app/admin/adminShard.svelte.js +62 -0
  18. package/dist/app/store/InstalledView.svelte +246 -0
  19. package/dist/app/store/InstalledView.svelte.d.ts +3 -0
  20. package/dist/app/store/StoreView.svelte +522 -0
  21. package/dist/app/store/StoreView.svelte.d.ts +3 -0
  22. package/dist/app/store/storeApp.d.ts +10 -0
  23. package/dist/app/store/storeApp.js +26 -0
  24. package/dist/app/store/storeShard.svelte.d.ts +38 -0
  25. package/dist/app/store/storeShard.svelte.js +218 -0
  26. package/dist/apps/lifecycle.d.ts +42 -0
  27. package/dist/apps/lifecycle.js +184 -0
  28. package/dist/apps/registry.svelte.d.ts +40 -0
  29. package/dist/apps/registry.svelte.js +59 -0
  30. package/dist/apps/terminal/manifest.d.ts +8 -0
  31. package/dist/apps/terminal/manifest.js +13 -0
  32. package/dist/apps/terminal/terminal-app.d.ts +7 -0
  33. package/dist/apps/terminal/terminal-app.js +14 -0
  34. package/dist/apps/types.d.ts +93 -0
  35. package/dist/apps/types.js +10 -0
  36. package/dist/artifact.d.ts +32 -0
  37. package/dist/artifact.js +1 -0
  38. package/dist/assets/SH3.png +0 -0
  39. package/dist/assets/icons.svg +1126 -0
  40. package/dist/assets.d.ts +13 -0
  41. package/dist/auth/GuestBanner.svelte +134 -0
  42. package/dist/auth/GuestBanner.svelte.d.ts +3 -0
  43. package/dist/auth/SignInWall.svelte +203 -0
  44. package/dist/auth/SignInWall.svelte.d.ts +7 -0
  45. package/dist/auth/auth.svelte.d.ts +69 -0
  46. package/dist/auth/auth.svelte.js +165 -0
  47. package/dist/auth/index.d.ts +2 -0
  48. package/dist/auth/index.js +1 -0
  49. package/dist/auth/types.d.ts +41 -0
  50. package/dist/auth/types.js +6 -0
  51. package/dist/build.d.ts +49 -0
  52. package/dist/build.js +236 -0
  53. package/dist/contract.d.ts +20 -0
  54. package/dist/contract.js +28 -0
  55. package/dist/createShell.d.ts +24 -0
  56. package/dist/createShell.js +131 -0
  57. package/dist/documents/backends.d.ts +17 -0
  58. package/dist/documents/backends.js +156 -0
  59. package/dist/documents/config.d.ts +7 -0
  60. package/dist/documents/config.js +27 -0
  61. package/dist/documents/handle.d.ts +6 -0
  62. package/dist/documents/handle.js +154 -0
  63. package/dist/documents/http-backend.d.ts +22 -0
  64. package/dist/documents/http-backend.js +78 -0
  65. package/dist/documents/index.d.ts +6 -0
  66. package/dist/documents/index.js +8 -0
  67. package/dist/documents/notifications.d.ts +9 -0
  68. package/dist/documents/notifications.js +39 -0
  69. package/dist/documents/types.d.ts +97 -0
  70. package/dist/documents/types.js +12 -0
  71. package/dist/env/client.d.ts +44 -0
  72. package/dist/env/client.js +106 -0
  73. package/dist/env/index.d.ts +2 -0
  74. package/dist/env/index.js +1 -0
  75. package/dist/env/types.d.ts +12 -0
  76. package/dist/env/types.js +8 -0
  77. package/dist/host-entry.d.ts +13 -0
  78. package/dist/host-entry.js +17 -0
  79. package/dist/host.d.ts +15 -0
  80. package/dist/host.js +86 -0
  81. package/dist/index.d.ts +4 -0
  82. package/dist/index.js +14 -0
  83. package/dist/layout/DragPreview.svelte +63 -0
  84. package/dist/layout/DragPreview.svelte.d.ts +3 -0
  85. package/dist/layout/LayoutRenderer.svelte +262 -0
  86. package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
  87. package/dist/layout/SlotContainer.svelte +140 -0
  88. package/dist/layout/SlotContainer.svelte.d.ts +8 -0
  89. package/dist/layout/SlotDropZone.svelte +122 -0
  90. package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
  91. package/dist/layout/drag.svelte.d.ts +45 -0
  92. package/dist/layout/drag.svelte.js +200 -0
  93. package/dist/layout/inspection.d.ts +72 -0
  94. package/dist/layout/inspection.js +209 -0
  95. package/dist/layout/ops.d.ts +100 -0
  96. package/dist/layout/ops.js +310 -0
  97. package/dist/layout/slotHostPool.svelte.d.ts +36 -0
  98. package/dist/layout/slotHostPool.svelte.js +229 -0
  99. package/dist/layout/store.svelte.d.ts +39 -0
  100. package/dist/layout/store.svelte.js +153 -0
  101. package/dist/layout/tree-walk.d.ts +15 -0
  102. package/dist/layout/tree-walk.js +33 -0
  103. package/dist/layout/types.d.ts +108 -0
  104. package/dist/layout/types.js +25 -0
  105. package/dist/migrations/shell-rename.d.ts +16 -0
  106. package/dist/migrations/shell-rename.js +48 -0
  107. package/dist/overlays/ModalFrame.svelte +87 -0
  108. package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
  109. package/dist/overlays/PopupFrame.svelte +85 -0
  110. package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
  111. package/dist/overlays/ToastItem.svelte +77 -0
  112. package/dist/overlays/ToastItem.svelte.d.ts +9 -0
  113. package/dist/overlays/focusTrap.d.ts +1 -0
  114. package/dist/overlays/focusTrap.js +64 -0
  115. package/dist/overlays/modal.d.ts +9 -0
  116. package/dist/overlays/modal.js +141 -0
  117. package/dist/overlays/popup.d.ts +9 -0
  118. package/dist/overlays/popup.js +108 -0
  119. package/dist/overlays/roots.d.ts +4 -0
  120. package/dist/overlays/roots.js +31 -0
  121. package/dist/overlays/toast.d.ts +6 -0
  122. package/dist/overlays/toast.js +93 -0
  123. package/dist/overlays/types.d.ts +31 -0
  124. package/dist/overlays/types.js +15 -0
  125. package/dist/platform/index.d.ts +10 -0
  126. package/dist/platform/index.js +33 -0
  127. package/dist/platform/tauri-backend.d.ts +15 -0
  128. package/dist/platform/tauri-backend.js +58 -0
  129. package/dist/primitives/.gitkeep +0 -0
  130. package/dist/primitives/ResizableSplitter.svelte +333 -0
  131. package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
  132. package/dist/primitives/TabbedPanel.svelte +305 -0
  133. package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
  134. package/dist/primitives/base.css +42 -0
  135. package/dist/registry/client.d.ts +74 -0
  136. package/dist/registry/client.js +117 -0
  137. package/dist/registry/index.d.ts +13 -0
  138. package/dist/registry/index.js +14 -0
  139. package/dist/registry/installer.d.ts +53 -0
  140. package/dist/registry/installer.js +168 -0
  141. package/dist/registry/integrity.d.ts +32 -0
  142. package/dist/registry/integrity.js +92 -0
  143. package/dist/registry/loader.d.ts +50 -0
  144. package/dist/registry/loader.js +145 -0
  145. package/dist/registry/schema.d.ts +47 -0
  146. package/dist/registry/schema.js +185 -0
  147. package/dist/registry/storage.d.ts +37 -0
  148. package/dist/registry/storage.js +101 -0
  149. package/dist/registry/types.d.ts +262 -0
  150. package/dist/registry/types.js +14 -0
  151. package/dist/server-shard/types.d.ts +67 -0
  152. package/dist/server-shard/types.js +13 -0
  153. package/dist/sh3core-shard/ShellHome.svelte +192 -0
  154. package/dist/sh3core-shard/ShellHome.svelte.d.ts +3 -0
  155. package/dist/sh3core-shard/ShellTitle.svelte +171 -0
  156. package/dist/sh3core-shard/ShellTitle.svelte.d.ts +3 -0
  157. package/dist/sh3core-shard/sh3coreShard.svelte.d.ts +2 -0
  158. package/dist/sh3core-shard/sh3coreShard.svelte.js +53 -0
  159. package/dist/shards/activate.svelte.d.ts +52 -0
  160. package/dist/shards/activate.svelte.js +186 -0
  161. package/dist/shards/registry.d.ts +4 -0
  162. package/dist/shards/registry.js +28 -0
  163. package/dist/shards/types.d.ts +207 -0
  164. package/dist/shards/types.js +20 -0
  165. package/dist/shell-shard/InputLine.svelte +133 -0
  166. package/dist/shell-shard/InputLine.svelte.d.ts +11 -0
  167. package/dist/shell-shard/ScrollbackView.svelte +47 -0
  168. package/dist/shell-shard/ScrollbackView.svelte.d.ts +7 -0
  169. package/dist/shell-shard/Terminal.svelte +122 -0
  170. package/dist/shell-shard/Terminal.svelte.d.ts +8 -0
  171. package/dist/shell-shard/entries/PromptEntry.svelte +25 -0
  172. package/dist/shell-shard/entries/PromptEntry.svelte.d.ts +7 -0
  173. package/dist/shell-shard/entries/RichEntry.svelte +19 -0
  174. package/dist/shell-shard/entries/RichEntry.svelte.d.ts +8 -0
  175. package/dist/shell-shard/entries/StatusEntry.svelte +22 -0
  176. package/dist/shell-shard/entries/StatusEntry.svelte.d.ts +7 -0
  177. package/dist/shell-shard/entries/TextEntry.svelte +25 -0
  178. package/dist/shell-shard/entries/TextEntry.svelte.d.ts +7 -0
  179. package/dist/shell-shard/manifest.d.ts +2 -0
  180. package/dist/shell-shard/manifest.js +11 -0
  181. package/dist/shell-shard/protocol.d.ts +90 -0
  182. package/dist/shell-shard/protocol.js +11 -0
  183. package/dist/shell-shard/registry.d.ts +69 -0
  184. package/dist/shell-shard/registry.js +47 -0
  185. package/dist/shell-shard/rich/AppCard.svelte +25 -0
  186. package/dist/shell-shard/rich/AppCard.svelte.d.ts +10 -0
  187. package/dist/shell-shard/rich/AppsTable.svelte +29 -0
  188. package/dist/shell-shard/rich/AppsTable.svelte.d.ts +12 -0
  189. package/dist/shell-shard/rich/EnvTable.svelte +27 -0
  190. package/dist/shell-shard/rich/EnvTable.svelte.d.ts +8 -0
  191. package/dist/shell-shard/rich/HelpTable.svelte +29 -0
  192. package/dist/shell-shard/rich/HelpTable.svelte.d.ts +12 -0
  193. package/dist/shell-shard/rich/HistoryList.svelte +37 -0
  194. package/dist/shell-shard/rich/HistoryList.svelte.d.ts +9 -0
  195. package/dist/shell-shard/rich/ShardsTable.svelte +28 -0
  196. package/dist/shell-shard/rich/ShardsTable.svelte.d.ts +12 -0
  197. package/dist/shell-shard/rich/ViewsTable.svelte +31 -0
  198. package/dist/shell-shard/rich/ViewsTable.svelte.d.ts +13 -0
  199. package/dist/shell-shard/rich/ZoneTree.svelte +19 -0
  200. package/dist/shell-shard/rich/ZoneTree.svelte.d.ts +8 -0
  201. package/dist/shell-shard/rich/ZonesTable.svelte +27 -0
  202. package/dist/shell-shard/rich/ZonesTable.svelte.d.ts +11 -0
  203. package/dist/shell-shard/scrollback.svelte.d.ts +36 -0
  204. package/dist/shell-shard/scrollback.svelte.js +43 -0
  205. package/dist/shell-shard/session-client.svelte.d.ts +23 -0
  206. package/dist/shell-shard/session-client.svelte.js +120 -0
  207. package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
  208. package/dist/shell-shard/shellShard.svelte.js +139 -0
  209. package/dist/shell-shard/verbs/apps.d.ts +3 -0
  210. package/dist/shell-shard/verbs/apps.js +50 -0
  211. package/dist/shell-shard/verbs/clear.d.ts +2 -0
  212. package/dist/shell-shard/verbs/clear.js +7 -0
  213. package/dist/shell-shard/verbs/help.d.ts +2 -0
  214. package/dist/shell-shard/verbs/help.js +21 -0
  215. package/dist/shell-shard/verbs/history.d.ts +2 -0
  216. package/dist/shell-shard/verbs/history.js +20 -0
  217. package/dist/shell-shard/verbs/index.d.ts +2 -0
  218. package/dist/shell-shard/verbs/index.js +29 -0
  219. package/dist/shell-shard/verbs/session.d.ts +5 -0
  220. package/dist/shell-shard/verbs/session.js +65 -0
  221. package/dist/shell-shard/verbs/shards.d.ts +2 -0
  222. package/dist/shell-shard/verbs/shards.js +14 -0
  223. package/dist/shell-shard/verbs/views.d.ts +4 -0
  224. package/dist/shell-shard/verbs/views.js +90 -0
  225. package/dist/shell-shard/verbs/zones.d.ts +3 -0
  226. package/dist/shell-shard/verbs/zones.js +38 -0
  227. package/dist/shellRuntime.svelte.d.ts +27 -0
  228. package/dist/shellRuntime.svelte.js +27 -0
  229. package/dist/state/backends.d.ts +26 -0
  230. package/dist/state/backends.js +99 -0
  231. package/dist/state/manage.d.ts +14 -0
  232. package/dist/state/manage.js +40 -0
  233. package/dist/state/types.d.ts +55 -0
  234. package/dist/state/types.js +17 -0
  235. package/dist/state/zones.svelte.d.ts +53 -0
  236. package/dist/state/zones.svelte.js +141 -0
  237. package/dist/theme.d.ts +28 -0
  238. package/dist/theme.js +92 -0
  239. package/dist/tokens.css +102 -0
  240. package/dist/version.d.ts +2 -0
  241. package/dist/version.js +2 -0
  242. package/package.json +60 -0
@@ -0,0 +1,73 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Admin System view — server status and restart.
4
+ */
5
+
6
+ let version = $state('...');
7
+ let restarting = $state(false);
8
+ let restartError = $state<string | null>(null);
9
+
10
+ async function fetchVersion() {
11
+ try {
12
+ const res = await fetch('/api/version');
13
+ if (res.ok) {
14
+ const body = await res.json();
15
+ version = body.version;
16
+ }
17
+ } catch { /* ignore */ }
18
+ }
19
+
20
+ async function restart() {
21
+ restarting = true;
22
+ restartError = null;
23
+ try {
24
+ const res = await fetch('/api/admin/restart', {
25
+ method: 'POST',
26
+ credentials: 'include',
27
+ });
28
+ if (!res.ok) {
29
+ const body = await res.json().catch(() => ({}));
30
+ restartError = body.error || 'Restart failed';
31
+ restarting = false;
32
+ }
33
+ // If 202, server will restart — page will reconnect
34
+ } catch {
35
+ restartError = 'Network error';
36
+ restarting = false;
37
+ }
38
+ }
39
+
40
+ fetchVersion();
41
+ </script>
42
+
43
+ <div class="admin-system">
44
+ <h2>System</h2>
45
+
46
+ <div class="admin-system-info">
47
+ <div class="admin-system-row">
48
+ <span class="admin-system-label">Server version</span>
49
+ <span>{version}</span>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="admin-system-actions">
54
+ <button type="button" class="admin-btn-danger" onclick={restart} disabled={restarting}>
55
+ {restarting ? 'Restarting...' : 'Restart server'}
56
+ </button>
57
+ {#if restartError}
58
+ <div class="admin-error">{restartError}</div>
59
+ {/if}
60
+ </div>
61
+ </div>
62
+
63
+ <style>
64
+ .admin-system { padding: 24px; font-family: system-ui, sans-serif; color: var(--shell-fg); }
65
+ .admin-system h2 { margin: 0 0 16px; font-size: 18px; }
66
+ .admin-system-info { margin-bottom: 24px; }
67
+ .admin-system-row { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--shell-border, #3a3a5c); font-size: 13px; }
68
+ .admin-system-label { color: var(--shell-fg-subtle); min-width: 140px; }
69
+ .admin-system-actions { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
70
+ .admin-btn-danger { padding: 8px 16px; background: transparent; color: var(--shell-error, #d32f2f); border: 1px solid var(--shell-error, #d32f2f); font-weight: 600; }
71
+ .admin-btn-danger:disabled { opacity: 0.6; cursor: not-allowed; }
72
+ .admin-error { color: var(--shell-error, #d32f2f); font-size: 13px; }
73
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const SystemView: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type SystemView = ReturnType<typeof SystemView>;
3
+ export default SystemView;
@@ -0,0 +1,188 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Admin Users view — list, create, edit, delete users.
4
+ */
5
+
6
+ import type { AuthUser } from '../../auth/types';
7
+
8
+ let users = $state<AuthUser[]>([]);
9
+ let loading = $state(true);
10
+ let error = $state<string | null>(null);
11
+
12
+ // Create form
13
+ let showCreate = $state(false);
14
+ let newUsername = $state('');
15
+ let newDisplayName = $state('');
16
+ let newPassword = $state('');
17
+ let newRole = $state<'admin' | 'user'>('user');
18
+ let createError = $state<string | null>(null);
19
+
20
+ // Edit state
21
+ let editingId = $state<string | null>(null);
22
+ let editDisplayName = $state('');
23
+ let editRole = $state<'admin' | 'user'>('user');
24
+ let editPassword = $state('');
25
+
26
+ async function fetchUsers() {
27
+ loading = true;
28
+ error = null;
29
+ try {
30
+ const res = await fetch('/api/admin/users', { credentials: 'include' });
31
+ if (!res.ok) throw new Error('Failed to fetch users');
32
+ users = await res.json();
33
+ } catch (err) {
34
+ error = err instanceof Error ? err.message : 'Failed to load users';
35
+ } finally {
36
+ loading = false;
37
+ }
38
+ }
39
+
40
+ async function createUser() {
41
+ createError = null;
42
+ try {
43
+ const res = await fetch('/api/admin/users', {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ credentials: 'include',
47
+ body: JSON.stringify({
48
+ username: newUsername,
49
+ displayName: newDisplayName || newUsername,
50
+ password: newPassword,
51
+ role: newRole,
52
+ }),
53
+ });
54
+ if (!res.ok) {
55
+ const body = await res.json().catch(() => ({}));
56
+ createError = body.error || 'Failed to create user';
57
+ return;
58
+ }
59
+ newUsername = '';
60
+ newDisplayName = '';
61
+ newPassword = '';
62
+ newRole = 'user';
63
+ showCreate = false;
64
+ await fetchUsers();
65
+ } catch {
66
+ createError = 'Network error';
67
+ }
68
+ }
69
+
70
+ function startEdit(user: AuthUser) {
71
+ editingId = user.id;
72
+ editDisplayName = user.displayName;
73
+ editRole = user.role;
74
+ editPassword = '';
75
+ }
76
+
77
+ async function saveEdit() {
78
+ if (!editingId) return;
79
+ const patch: Record<string, unknown> = {
80
+ displayName: editDisplayName,
81
+ role: editRole,
82
+ };
83
+ if (editPassword.trim()) patch.password = editPassword;
84
+ try {
85
+ const res = await fetch(`/api/admin/users/${editingId}`, {
86
+ method: 'PUT',
87
+ headers: { 'Content-Type': 'application/json' },
88
+ credentials: 'include',
89
+ body: JSON.stringify(patch),
90
+ });
91
+ if (!res.ok) return;
92
+ editingId = null;
93
+ await fetchUsers();
94
+ } catch { /* ignore */ }
95
+ }
96
+
97
+ async function deleteUser(id: string) {
98
+ try {
99
+ await fetch(`/api/admin/users/${id}`, {
100
+ method: 'DELETE',
101
+ credentials: 'include',
102
+ });
103
+ await fetchUsers();
104
+ } catch { /* ignore */ }
105
+ }
106
+
107
+ fetchUsers();
108
+ </script>
109
+
110
+ <div class="admin-users">
111
+ <div class="admin-users-header">
112
+ <h2>Users</h2>
113
+ <button type="button" class="admin-btn" onclick={() => { showCreate = !showCreate; }}>
114
+ {showCreate ? 'Cancel' : 'New user'}
115
+ </button>
116
+ </div>
117
+
118
+ {#if showCreate}
119
+ <form class="admin-create-form" onsubmit={(e) => { e.preventDefault(); createUser(); }}>
120
+ <input class="admin-input" type="text" placeholder="Username" bind:value={newUsername} />
121
+ <input class="admin-input" type="text" placeholder="Display name" bind:value={newDisplayName} />
122
+ <input class="admin-input" type="password" placeholder="Password" bind:value={newPassword} />
123
+ <select class="admin-input" bind:value={newRole}>
124
+ <option value="user">User</option>
125
+ <option value="admin">Admin</option>
126
+ </select>
127
+ <button type="submit" class="admin-btn" disabled={!newUsername.trim() || !newPassword.trim()}>Create</button>
128
+ {#if createError}<div class="admin-error">{createError}</div>{/if}
129
+ </form>
130
+ {/if}
131
+
132
+ {#if loading}
133
+ <p class="admin-muted">Loading...</p>
134
+ {:else if error}
135
+ <p class="admin-error">{error}</p>
136
+ {:else}
137
+ <ul class="admin-user-list">
138
+ {#each users as user (user.id)}
139
+ <li class="admin-user-item">
140
+ {#if editingId === user.id}
141
+ <form class="admin-edit-form" onsubmit={(e) => { e.preventDefault(); saveEdit(); }}>
142
+ <input class="admin-input" type="text" bind:value={editDisplayName} />
143
+ <input class="admin-input" type="password" placeholder="New password (leave empty to keep)" bind:value={editPassword} />
144
+ <select class="admin-input" bind:value={editRole}>
145
+ <option value="user">User</option>
146
+ <option value="admin">Admin</option>
147
+ </select>
148
+ <div class="admin-edit-actions">
149
+ <button type="submit" class="admin-btn">Save</button>
150
+ <button type="button" class="admin-btn-secondary" onclick={() => { editingId = null; }}>Cancel</button>
151
+ </div>
152
+ </form>
153
+ {:else}
154
+ <div class="admin-user-info">
155
+ <span class="admin-user-name">{user.displayName}</span>
156
+ <span class="admin-user-meta">{user.username} · {user.role}</span>
157
+ </div>
158
+ <div class="admin-user-actions">
159
+ <button type="button" class="admin-btn-secondary" onclick={() => startEdit(user)}>Edit</button>
160
+ <button type="button" class="admin-btn-danger" onclick={() => deleteUser(user.id)}>Delete</button>
161
+ </div>
162
+ {/if}
163
+ </li>
164
+ {/each}
165
+ </ul>
166
+ {/if}
167
+ </div>
168
+
169
+ <style>
170
+ .admin-users { padding: 24px; font-family: system-ui, sans-serif; color: var(--shell-fg); }
171
+ .admin-users-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
172
+ .admin-users-header h2 { margin: 0; font-size: 18px; }
173
+ .admin-create-form, .admin-edit-form { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; max-width: 400px; }
174
+ .admin-input { padding: 8px 12px; background: var(--shell-bg, #1a1a2e); color: var(--shell-fg); border: 1px solid var(--shell-border, #3a3a5c); border-radius: var(--shell-radius, 6px); font-size: 13px; }
175
+ .admin-btn { font-weight: 600; font-size: 13px; }
176
+ .admin-btn:disabled { opacity: 0.6; cursor: not-allowed; }
177
+ .admin-btn-secondary { background: transparent; color: var(--shell-fg-subtle); border: 1px solid var(--shell-border); font-size: 12px; }
178
+ .admin-btn-danger { background: transparent; color: var(--shell-error, #d32f2f); border: 1px solid var(--shell-error, #d32f2f); font-size: 12px; }
179
+ .admin-user-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
180
+ .admin-user-item { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: var(--shell-bg-elevated, #252540); border: 1px solid var(--shell-border, #3a3a5c); border-radius: var(--shell-radius, 6px); }
181
+ .admin-user-info { display: flex; flex-direction: column; gap: 2px; }
182
+ .admin-user-name { font-weight: 600; }
183
+ .admin-user-meta { font-size: 11px; color: var(--shell-fg-subtle); }
184
+ .admin-user-actions { display: flex; gap: 6px; }
185
+ .admin-edit-actions { display: flex; gap: 6px; }
186
+ .admin-error { color: var(--shell-error, #d32f2f); font-size: 13px; }
187
+ .admin-muted { color: var(--shell-fg-muted); font-style: italic; }
188
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const UsersView: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type UsersView = ReturnType<typeof UsersView>;
3
+ export default UsersView;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Built-in Admin app — user management, auth settings, and system controls.
3
+ * Framework-shipped: registered in host.ts during bootstrap.
4
+ * Admin-gated via manifest flag (ADR-011).
5
+ */
6
+ import type { App } from '../../apps/types';
7
+ export declare const adminApp: App;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Built-in Admin app — user management, auth settings, and system controls.
3
+ * Framework-shipped: registered in host.ts during bootstrap.
4
+ * Admin-gated via manifest flag (ADR-011).
5
+ */
6
+ export const adminApp = {
7
+ manifest: {
8
+ id: 'sh3-admin-app',
9
+ label: 'Admin',
10
+ version: '0.1.0',
11
+ requiredShards: ['sh3-admin'],
12
+ layoutVersion: 1,
13
+ admin: true,
14
+ },
15
+ initialLayout: {
16
+ type: 'tabs',
17
+ activeTab: 0,
18
+ tabs: [
19
+ { slotId: 'admin.users', viewId: 'sh3-admin:users', label: 'Users' },
20
+ { slotId: 'admin.auth', viewId: 'sh3-admin:auth', label: 'Auth' },
21
+ { slotId: 'admin.system', viewId: 'sh3-admin:system', label: 'System' },
22
+ { slotId: 'admin.keys', viewId: 'sh3-admin:keys', label: 'Keys' },
23
+ ],
24
+ },
25
+ };
@@ -0,0 +1,4 @@
1
+ import type { Shard } from '../../shards/types';
2
+ /** Module-level server URL, set during activate. */
3
+ export declare let adminServerUrl: string;
4
+ export declare const adminShard: Shard;
@@ -0,0 +1,62 @@
1
+ /*
2
+ * Admin shard — framework-shipped shard for user management,
3
+ * auth settings, system controls, and API key management.
4
+ *
5
+ * Contributes four views:
6
+ * - `sh3-admin:users` — user CRUD
7
+ * - `sh3-admin:auth` — auth settings toggles
8
+ * - `sh3-admin:system` — restart + status
9
+ * - `sh3-admin:keys` — API key lifecycle
10
+ *
11
+ * `.svelte.ts` because mounting Svelte components requires rune access.
12
+ */
13
+ import { mount, unmount } from 'svelte';
14
+ import UsersView from './UsersView.svelte';
15
+ import AuthSettingsView from './AuthSettingsView.svelte';
16
+ import SystemView from './SystemView.svelte';
17
+ import ApiKeysView from './ApiKeysView.svelte';
18
+ /** Module-level server URL, set during activate. */
19
+ export let adminServerUrl = '';
20
+ export const adminShard = {
21
+ manifest: {
22
+ id: 'sh3-admin',
23
+ label: 'Admin',
24
+ version: '0.1.0',
25
+ views: [
26
+ { id: 'sh3-admin:users', label: 'Users' },
27
+ { id: 'sh3-admin:auth', label: 'Auth Settings' },
28
+ { id: 'sh3-admin:system', label: 'System' },
29
+ { id: 'sh3-admin:keys', label: 'API Keys' },
30
+ ],
31
+ },
32
+ activate(ctx) {
33
+ const usersFactory = {
34
+ mount(container, _context) {
35
+ const instance = mount(UsersView, { target: container });
36
+ return { unmount() { unmount(instance); } };
37
+ },
38
+ };
39
+ const authFactory = {
40
+ mount(container, _context) {
41
+ const instance = mount(AuthSettingsView, { target: container });
42
+ return { unmount() { unmount(instance); } };
43
+ },
44
+ };
45
+ const systemFactory = {
46
+ mount(container, _context) {
47
+ const instance = mount(SystemView, { target: container });
48
+ return { unmount() { unmount(instance); } };
49
+ },
50
+ };
51
+ const keysFactory = {
52
+ mount(container, _context) {
53
+ const instance = mount(ApiKeysView, { target: container });
54
+ return { unmount() { unmount(instance); } };
55
+ },
56
+ };
57
+ ctx.registerView('sh3-admin:users', usersFactory);
58
+ ctx.registerView('sh3-admin:auth', authFactory);
59
+ ctx.registerView('sh3-admin:system', systemFactory);
60
+ ctx.registerView('sh3-admin:keys', keysFactory);
61
+ },
62
+ };
@@ -0,0 +1,246 @@
1
+ <script lang="ts">
2
+ /*
3
+ * InstalledView — lists installed packages with uninstall capability.
4
+ *
5
+ * Simpler than the browse view: a flat list of installed packages with
6
+ * metadata (type, version, contract version, source registry, install date)
7
+ * and an uninstall button per entry.
8
+ */
9
+
10
+ import { storeContext } from './storeShard.svelte';
11
+ import { uninstallPackage } from '../../registry/installer';
12
+ import { serverUninstallPackage } from '../../env/client';
13
+
14
+ const ctx = storeContext;
15
+
16
+ let uninstallingIds = $state<Set<string>>(new Set());
17
+ let updatingIds = $state<Set<string>>(new Set());
18
+ let updateError = $state<string | null>(null);
19
+
20
+ async function handleUninstall(id: string) {
21
+ if (uninstallingIds.has(id)) return;
22
+
23
+ uninstallingIds = new Set([...uninstallingIds, id]);
24
+ try {
25
+ await serverUninstallPackage(id);
26
+ await uninstallPackage(id);
27
+ await ctx.refreshInstalled();
28
+ } catch (err) {
29
+ console.warn('[sh3-store] Uninstall failed:', err instanceof Error ? err.message : err);
30
+ } finally {
31
+ const next = new Set(uninstallingIds);
32
+ next.delete(id);
33
+ uninstallingIds = next;
34
+ }
35
+ }
36
+
37
+ async function handleUpdate(id: string) {
38
+ if (updatingIds.has(id)) return;
39
+
40
+ updatingIds = new Set([...updatingIds, id]);
41
+ updateError = null;
42
+
43
+ try {
44
+ await ctx.updatePackage(id);
45
+ } catch (err) {
46
+ updateError = err instanceof Error ? err.message : String(err);
47
+ } finally {
48
+ const next = new Set(updatingIds);
49
+ next.delete(id);
50
+ updatingIds = next;
51
+ }
52
+ }
53
+
54
+ function handleRefresh() {
55
+ ctx.refreshInstalled();
56
+ }
57
+
58
+ function formatDate(iso: string): string {
59
+ try {
60
+ return new Date(iso).toLocaleDateString(undefined, {
61
+ year: 'numeric',
62
+ month: 'short',
63
+ day: 'numeric',
64
+ });
65
+ } catch {
66
+ return iso;
67
+ }
68
+ }
69
+ </script>
70
+
71
+ <div class="installed-view">
72
+ <header class="installed-header">
73
+ <h2>Installed Packages</h2>
74
+ <button onclick={handleRefresh}>Refresh</button>
75
+ </header>
76
+
77
+ {#if updateError}
78
+ <div class="installed-error">{updateError}</div>
79
+ {/if}
80
+
81
+ {#if ctx.state.ephemeral.installed.length === 0}
82
+ <div class="installed-empty">No packages installed.</div>
83
+ {:else}
84
+ <ul class="installed-list">
85
+ {#each ctx.state.ephemeral.installed as pkg (pkg.id)}
86
+ {@const uninstalling = uninstallingIds.has(pkg.id)}
87
+ <li class="installed-item">
88
+ <div class="installed-item-main">
89
+ <span class="installed-item-id">{pkg.id}</span>
90
+ <span class="installed-item-badge" class:badge-shard={pkg.type === 'shard'} class:badge-app={pkg.type === 'app'}>
91
+ {pkg.type}
92
+ </span>
93
+ <span class="installed-item-version">{pkg.version}</span>
94
+ </div>
95
+ <div class="installed-item-meta">
96
+ <span>Contract: v{pkg.contractVersion}</span>
97
+ <span>Source: {pkg.sourceRegistry}</span>
98
+ <span>Installed: {formatDate(pkg.installedAt)}</span>
99
+ </div>
100
+ <div class="installed-item-actions">
101
+ {#if pkg.id in ctx.state.ephemeral.updatable}
102
+ {@const target = ctx.state.ephemeral.updatable[pkg.id]}
103
+ {@const updating = updatingIds.has(pkg.id)}
104
+ <button
105
+ class="installed-update-btn"
106
+ onclick={() => handleUpdate(pkg.id)}
107
+ disabled={updating || uninstalling}
108
+ >
109
+ {updating ? 'Updating...' : `Update -> ${target.latest.version}`}
110
+ </button>
111
+ {/if}
112
+ <button
113
+ class="installed-uninstall-btn"
114
+ onclick={() => handleUninstall(pkg.id)}
115
+ disabled={uninstalling}
116
+ >
117
+ {uninstalling ? 'Removing...' : 'Uninstall'}
118
+ </button>
119
+ </div>
120
+ </li>
121
+ {/each}
122
+ </ul>
123
+ {/if}
124
+ </div>
125
+
126
+ <style>
127
+ .installed-view {
128
+ font-family: var(--shell-font-ui);
129
+ color: var(--shell-fg, #e0e0e0);
130
+ background: var(--shell-bg, #1e1e1e);
131
+ padding: 16px;
132
+ height: 100%;
133
+ overflow-y: auto;
134
+ box-sizing: border-box;
135
+ }
136
+ .installed-header {
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: space-between;
140
+ margin-bottom: 16px;
141
+ }
142
+ .installed-header h2 {
143
+ margin: 0;
144
+ font-size: 1.25rem;
145
+ font-weight: 600;
146
+ }
147
+ .installed-empty {
148
+ text-align: center;
149
+ padding: 32px 16px;
150
+ color: var(--shell-fg-muted, #888);
151
+ font-size: 0.875rem;
152
+ }
153
+ .installed-list {
154
+ list-style: none;
155
+ margin: 0;
156
+ padding: 0;
157
+ display: flex;
158
+ flex-direction: column;
159
+ gap: 8px;
160
+ }
161
+ .installed-item {
162
+ background: var(--shell-input-bg, #2a2a2a);
163
+ border: 1px solid var(--shell-border, #444);
164
+ border-radius: var(--shell-radius-md);
165
+ padding: 12px 14px;
166
+ display: flex;
167
+ flex-direction: column;
168
+ gap: 6px;
169
+ }
170
+ .installed-item-main {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 8px;
174
+ }
175
+ .installed-item-id {
176
+ font-weight: 600;
177
+ font-size: 0.9375rem;
178
+ }
179
+ .installed-item-badge {
180
+ font-size: 0.6875rem;
181
+ padding: 1px 6px;
182
+ border-radius: var(--shell-radius-sm);
183
+ text-transform: uppercase;
184
+ font-weight: 600;
185
+ letter-spacing: 0.04em;
186
+ }
187
+ .badge-shard {
188
+ background: color-mix(in srgb, var(--shell-accent, #007acc) 25%, transparent);
189
+ color: var(--shell-accent, #007acc);
190
+ }
191
+ .badge-app {
192
+ background: color-mix(in srgb, var(--shell-success, #4caf50) 25%, transparent);
193
+ color: var(--shell-success, #4caf50);
194
+ }
195
+ .installed-item-version {
196
+ font-size: 0.75rem;
197
+ color: var(--shell-fg-muted, #888);
198
+ }
199
+ .installed-item-meta {
200
+ display: flex;
201
+ gap: 16px;
202
+ flex-wrap: wrap;
203
+ font-size: 0.75rem;
204
+ color: var(--shell-fg-muted, #888);
205
+ }
206
+ .installed-item-actions {
207
+ display: flex;
208
+ justify-content: flex-end;
209
+ gap: 8px;
210
+ }
211
+ .installed-uninstall-btn {
212
+ padding: 4px 12px;
213
+ background: transparent;
214
+ color: var(--shell-error, #d32f2f);
215
+ border: 1px solid var(--shell-error, #d32f2f);
216
+ font-size: 0.8125rem;
217
+ }
218
+ .installed-uninstall-btn:hover:not(:disabled) {
219
+ background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
220
+ }
221
+ .installed-uninstall-btn:disabled {
222
+ opacity: 0.6;
223
+ cursor: not-allowed;
224
+ }
225
+ .installed-update-btn {
226
+ padding: 4px 12px;
227
+ background: var(--shell-warning, #ff9800);
228
+ font-size: 0.8125rem;
229
+ }
230
+ .installed-update-btn:hover:not(:disabled) {
231
+ filter: brightness(1.1);
232
+ }
233
+ .installed-update-btn:disabled {
234
+ opacity: 0.6;
235
+ cursor: not-allowed;
236
+ }
237
+ .installed-error {
238
+ padding: 8px 12px;
239
+ margin-bottom: 12px;
240
+ background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
241
+ color: var(--shell-error, #d32f2f);
242
+ border: 1px solid var(--shell-error, #d32f2f);
243
+ border-radius: var(--shell-radius);
244
+ font-size: 0.8125rem;
245
+ }
246
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const InstalledView: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type InstalledView = ReturnType<typeof InstalledView>;
3
+ export default InstalledView;