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,43 @@
1
+ /*
2
+ * Scrollback store for a single shell.terminal view instance.
3
+ *
4
+ * One instance per view mount. Holds ScrollbackEntry[], supports:
5
+ * - push(entry): append or coalesce with the previous entry if both
6
+ * are text entries with the same stream.
7
+ * - clear(): wipe all entries (local only — does not affect other
8
+ * attached views).
9
+ * - cap at a per-view limit (default 2000) — oldest trimmed.
10
+ *
11
+ * Uses Svelte 5 runes. The backing array is $state() so consumers just
12
+ * read .entries and reactivity Just Works™.
13
+ */
14
+ const DEFAULT_CAP = 2000;
15
+ let nextId = 0;
16
+ function mkId() {
17
+ return `e${++nextId}`;
18
+ }
19
+ export class Scrollback {
20
+ constructor(cap = DEFAULT_CAP) {
21
+ this.entries = $state([]);
22
+ this.cap = cap;
23
+ }
24
+ push(entry) {
25
+ // Coalesce consecutive text entries of the same stream
26
+ const last = this.entries[this.entries.length - 1];
27
+ if (last &&
28
+ last.kind === 'text' &&
29
+ entry.kind === 'text' &&
30
+ last.stream === entry.stream) {
31
+ last.chunks.push(...entry.chunks);
32
+ return;
33
+ }
34
+ const full = Object.assign(Object.assign({}, entry), { id: mkId() });
35
+ this.entries.push(full);
36
+ if (this.entries.length > this.cap) {
37
+ this.entries.splice(0, this.entries.length - this.cap);
38
+ }
39
+ }
40
+ clear() {
41
+ this.entries.length = 0;
42
+ }
43
+ }
@@ -0,0 +1,23 @@
1
+ import type { ClientMessage, ServerMessage } from './protocol';
2
+ type MessageHandler = (msg: ServerMessage) => void;
3
+ export declare class SessionClient {
4
+ private readonly url;
5
+ private ws;
6
+ private handlers;
7
+ private pendingHistoryLogs;
8
+ private lastSeq;
9
+ private backoffIndex;
10
+ private closed;
11
+ connected: boolean;
12
+ cwd: string;
13
+ env: Record<string, string>;
14
+ history: string[];
15
+ constructor(url: string);
16
+ connect(): void;
17
+ private scheduleReconnect;
18
+ send(msg: ClientMessage): void;
19
+ private sendRaw;
20
+ onMessage(handler: MessageHandler): () => void;
21
+ close(): void;
22
+ }
23
+ export {};
@@ -0,0 +1,120 @@
1
+ /*
2
+ * Session client for shell-shard.
3
+ *
4
+ * One instance per view mount. Wraps a WebSocket to /api/shell/session.
5
+ *
6
+ * Responsibilities:
7
+ * - Connect with exponential backoff on initial failure
8
+ * - Type-safe send() / onMessage()
9
+ * - Reconnect with replayFrom cursor on drop
10
+ * - Queue history-log messages while disconnected; flush on reconnect
11
+ * - Expose a reactive `connected` signal + latest known cwd/env
12
+ *
13
+ * The client does NOT parse message meaning beyond the `t` discriminator —
14
+ * it's a transport, not a dispatcher. Higher layers (Terminal.svelte) read
15
+ * messages via onMessage.
16
+ */
17
+ const BACKOFFS = [1000, 2000, 5000, 10000];
18
+ export class SessionClient {
19
+ constructor(url) {
20
+ this.url = url;
21
+ this.ws = null;
22
+ this.handlers = new Set();
23
+ this.pendingHistoryLogs = [];
24
+ this.lastSeq = 0;
25
+ this.backoffIndex = 0;
26
+ this.closed = false;
27
+ this.connected = $state(false);
28
+ this.cwd = $state('');
29
+ this.env = $state({});
30
+ this.history = $state([]);
31
+ }
32
+ connect() {
33
+ if (this.closed)
34
+ return;
35
+ try {
36
+ this.ws = new WebSocket(this.url);
37
+ }
38
+ catch (_a) {
39
+ this.scheduleReconnect();
40
+ return;
41
+ }
42
+ this.ws.addEventListener('open', () => {
43
+ this.backoffIndex = 0;
44
+ this.connected = true;
45
+ const hello = this.lastSeq > 0
46
+ ? { t: 'hello', replayFrom: this.lastSeq }
47
+ : { t: 'hello' };
48
+ this.sendRaw(hello);
49
+ // Flush any queued history-log messages accumulated while offline
50
+ for (const queued of this.pendingHistoryLogs) {
51
+ this.sendRaw(queued);
52
+ }
53
+ this.pendingHistoryLogs = [];
54
+ });
55
+ this.ws.addEventListener('message', (evt) => {
56
+ let msg;
57
+ try {
58
+ msg = JSON.parse(evt.data);
59
+ }
60
+ catch (_a) {
61
+ return; // ignore malformed
62
+ }
63
+ // Track latest seq for reconnect replay
64
+ if (msg.t === 'event' && typeof msg.event.seq === 'number') {
65
+ this.lastSeq = msg.event.seq;
66
+ }
67
+ if (msg.t === 'welcome') {
68
+ this.cwd = msg.cwd;
69
+ this.env = msg.env;
70
+ this.lastSeq = msg.seq;
71
+ }
72
+ if (msg.t === 'cwd') {
73
+ this.cwd = msg.cwd;
74
+ }
75
+ if (msg.t === 'history') {
76
+ this.history = msg.lines;
77
+ }
78
+ for (const h of this.handlers)
79
+ h(msg);
80
+ });
81
+ this.ws.addEventListener('close', () => {
82
+ this.connected = false;
83
+ this.ws = null;
84
+ this.scheduleReconnect();
85
+ });
86
+ this.ws.addEventListener('error', () => {
87
+ // close event will fire next
88
+ });
89
+ }
90
+ scheduleReconnect() {
91
+ if (this.closed)
92
+ return;
93
+ const delay = BACKOFFS[Math.min(this.backoffIndex, BACKOFFS.length - 1)];
94
+ this.backoffIndex++;
95
+ setTimeout(() => this.connect(), delay);
96
+ }
97
+ send(msg) {
98
+ if (msg.t === 'history-log' && !this.connected) {
99
+ // Queue for flush on reconnect
100
+ this.pendingHistoryLogs.push(msg);
101
+ return;
102
+ }
103
+ this.sendRaw(msg);
104
+ }
105
+ sendRaw(msg) {
106
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
107
+ return;
108
+ this.ws.send(JSON.stringify(msg));
109
+ }
110
+ onMessage(handler) {
111
+ this.handlers.add(handler);
112
+ return () => this.handlers.delete(handler);
113
+ }
114
+ close() {
115
+ var _a;
116
+ this.closed = true;
117
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
118
+ this.ws = null;
119
+ }
120
+ }
@@ -0,0 +1,2 @@
1
+ import type { Shard } from '../api';
2
+ export declare const shellShard: Shard;
@@ -0,0 +1,139 @@
1
+ /*
2
+ * shell-shard — framework built-in providing the `shell.terminal` view.
3
+ *
4
+ * Client half of the terminal/REPL shard. See spec at
5
+ * docs/superpowers/specs/2026-04-10-shell-shard-design.md.
6
+ *
7
+ * activate() is admin-gated: when ctx.isAdmin is false the shard activates
8
+ * but registers no views, so non-admin users literally cannot mount the
9
+ * terminal. The server WebSocket endpoint (/api/shell/session) also refuses
10
+ * non-admin upgrades as a second line of defense.
11
+ *
12
+ * autostart() is defined so the shard activates at boot without requiring
13
+ * a dedicated app to launch it first, matching the __sh3core__ pattern.
14
+ */
15
+ import { mount, unmount } from 'svelte';
16
+ import { manifest } from './manifest';
17
+ import Terminal from './Terminal.svelte';
18
+ import { listRegisteredApps, getActiveApp } from '../apps/registry.svelte';
19
+ import { launchApp } from '../apps/lifecycle';
20
+ import { registeredShards } from '../shards/activate.svelte';
21
+ import { inspectActiveLayout, focusView, closeTab } from '../layout/inspection';
22
+ import { getUser, isAdmin } from '../auth/index';
23
+ /** Walk a layout tree and collect all tab entries (slotId + viewId + label). */
24
+ function collectTabEntries(node) {
25
+ if (node.type === 'tabs') {
26
+ return node.tabs.filter((t) => t.viewId !== null);
27
+ }
28
+ if (node.type === 'split') {
29
+ return node.children.flatMap(collectTabEntries);
30
+ }
31
+ // slot node: wrap as a synthetic tab entry if it has a viewId
32
+ if (node.viewId !== null) {
33
+ return [{ slotId: node.slotId, viewId: node.viewId, label: node.viewId }];
34
+ }
35
+ return [];
36
+ }
37
+ function makeShellApi(_ctx) {
38
+ return {
39
+ // → apps/registry.svelte: listRegisteredApps() returns AppManifest[]
40
+ listApps() {
41
+ return listRegisteredApps().map((m) => ({ id: m.id, label: m.label }));
42
+ },
43
+ // → apps/registry.svelte: getActiveApp() returns AppManifest | null
44
+ getActiveApp() {
45
+ const m = getActiveApp();
46
+ return m ? { id: m.id, label: m.label } : null;
47
+ },
48
+ // → apps/lifecycle: launchApp() is async; fire-and-forget to keep ShellApi sync.
49
+ // Verb handlers display feedback independently via scrollback.
50
+ launchApp(id) {
51
+ void launchApp(id);
52
+ },
53
+ // → shards/activate.svelte: registeredShards reactive map
54
+ listShards() {
55
+ return Array.from(registeredShards.values()).map((s) => ({
56
+ id: s.manifest.id,
57
+ label: s.manifest.label,
58
+ version: s.manifest.version,
59
+ }));
60
+ },
61
+ // → layout/inspection: inspectActiveLayout() + tree walk
62
+ listViewsInCurrentLayout() {
63
+ try {
64
+ const { root } = inspectActiveLayout();
65
+ return collectTabEntries(root).map((t) => {
66
+ var _a;
67
+ return ({
68
+ slotId: t.slotId,
69
+ viewId: (_a = t.viewId) !== null && _a !== void 0 ? _a : '',
70
+ label: t.label,
71
+ });
72
+ });
73
+ }
74
+ catch (_a) {
75
+ return [];
76
+ }
77
+ },
78
+ // → layout/inspection: focusView(viewId) returns boolean
79
+ openViewInCurrentLayout(viewId) {
80
+ try {
81
+ const found = focusView(viewId);
82
+ return found ? { ok: true } : { ok: false, error: `view "${viewId}" not found in current layout` };
83
+ }
84
+ catch (err) {
85
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
86
+ }
87
+ },
88
+ // → layout/inspection: closeTab(slotId) is async (guarded close).
89
+ // Fire-and-forget; the tab disappears asynchronously. ShellApi stays sync.
90
+ closeSlot(slotId) {
91
+ void closeTab(slotId);
92
+ return { ok: true };
93
+ },
94
+ // TODO Phase 10: wire to zone manager when state:manage permission is available.
95
+ // The shell manifest declares permissions: [] so ctx.zones is undefined.
96
+ // A future permission grant + ctx.zones.list() would power these.
97
+ listZones(_shardId) { return []; },
98
+ readZone(_shardId, _zoneName) { return null; },
99
+ // → auth/index: getUser() + isAdmin()
100
+ whoAmI() {
101
+ var _a;
102
+ const user = getUser();
103
+ return {
104
+ userId: (_a = user === null || user === void 0 ? void 0 : user.id) !== null && _a !== void 0 ? _a : 'guest',
105
+ admin: isAdmin(),
106
+ };
107
+ },
108
+ };
109
+ }
110
+ export const shellShard = {
111
+ manifest,
112
+ activate(ctx) {
113
+ if (!ctx.isAdmin) {
114
+ // Non-admin: don't expose the view. Nothing to register.
115
+ return;
116
+ }
117
+ const shell = makeShellApi(ctx);
118
+ const factory = {
119
+ mount(container, _context) {
120
+ const proto = typeof location !== 'undefined' && location.protocol === 'https:' ? 'wss' : 'ws';
121
+ const host = typeof location !== 'undefined' ? location.host : 'localhost';
122
+ const wsUrl = `${proto}://${host}/api/shell/session`;
123
+ const instance = mount(Terminal, {
124
+ target: container,
125
+ props: { shell, wsUrl },
126
+ });
127
+ return {
128
+ unmount() {
129
+ unmount(instance);
130
+ },
131
+ };
132
+ },
133
+ };
134
+ ctx.registerView('shell:terminal', factory);
135
+ },
136
+ autostart() {
137
+ // Intentionally empty — same pattern as __sh3core__.
138
+ },
139
+ };
@@ -0,0 +1,3 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const appsVerb: Verb;
3
+ export declare const appVerb: Verb;
@@ -0,0 +1,50 @@
1
+ import AppsTable from '../rich/AppsTable.svelte';
2
+ import AppCard from '../rich/AppCard.svelte';
3
+ export const appsVerb = {
4
+ name: 'apps',
5
+ summary: 'List installed apps. Click a row to launch.',
6
+ async run(ctx) {
7
+ const apps = ctx.shell.listApps();
8
+ ctx.scrollback.push({
9
+ kind: 'rich',
10
+ component: AppsTable,
11
+ props: {
12
+ data: {
13
+ apps,
14
+ onClickApp: (id) => {
15
+ ctx.shell.launchApp(id);
16
+ ctx.scrollback.push({
17
+ kind: 'status',
18
+ text: `shell: launched app ${id}`,
19
+ level: 'info',
20
+ ts: Date.now(),
21
+ });
22
+ },
23
+ },
24
+ },
25
+ ts: Date.now(),
26
+ });
27
+ },
28
+ };
29
+ export const appVerb = {
30
+ name: 'app',
31
+ summary: 'Show the currently active app.',
32
+ async run(ctx) {
33
+ const active = ctx.shell.getActiveApp();
34
+ if (!active) {
35
+ ctx.scrollback.push({
36
+ kind: 'status',
37
+ text: 'shell: no active app',
38
+ level: 'info',
39
+ ts: Date.now(),
40
+ });
41
+ return;
42
+ }
43
+ ctx.scrollback.push({
44
+ kind: 'rich',
45
+ component: AppCard,
46
+ props: { data: { id: active.id, label: active.label, shards: [] } },
47
+ ts: Date.now(),
48
+ });
49
+ },
50
+ };
@@ -0,0 +1,2 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const clearVerb: Verb;
@@ -0,0 +1,7 @@
1
+ export const clearVerb = {
2
+ name: 'clear',
3
+ summary: 'Clear the scrollback (local only — other views are unaffected).',
4
+ async run(ctx) {
5
+ ctx.scrollback.clear();
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import type { Verb, VerbRegistry } from '../registry';
2
+ export declare function makeHelpVerb(registry: VerbRegistry): Verb;
@@ -0,0 +1,21 @@
1
+ import HelpTable from '../rich/HelpTable.svelte';
2
+ export function makeHelpVerb(registry) {
3
+ return {
4
+ name: 'help',
5
+ summary: 'List verbs or show detail for one.',
6
+ async run(ctx) {
7
+ const rows = registry.list().map((v) => ({ name: v.name, summary: v.summary }));
8
+ ctx.scrollback.push({
9
+ kind: 'rich',
10
+ component: HelpTable,
11
+ props: {
12
+ data: {
13
+ rows,
14
+ onClickName: (name) => ctx.dispatch(name),
15
+ },
16
+ },
17
+ ts: Date.now(),
18
+ });
19
+ },
20
+ };
21
+ }
@@ -0,0 +1,2 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const historyVerb: Verb;
@@ -0,0 +1,20 @@
1
+ import HistoryList from '../rich/HistoryList.svelte';
2
+ export const historyVerb = {
3
+ name: 'history',
4
+ summary: 'Show the last N history lines. Default 50.',
5
+ async run(ctx, args) {
6
+ const n = args[0] ? Math.max(1, parseInt(args[0], 10) || 50) : 50;
7
+ const lines = ctx.session.history.slice(-n);
8
+ ctx.scrollback.push({
9
+ kind: 'rich',
10
+ component: HistoryList,
11
+ props: {
12
+ data: {
13
+ lines,
14
+ onClickLine: (line) => ctx.dispatch(line),
15
+ },
16
+ },
17
+ ts: Date.now(),
18
+ });
19
+ },
20
+ };
@@ -0,0 +1,2 @@
1
+ import type { VerbRegistry } from '../registry';
2
+ export declare function registerV1Verbs(registry: VerbRegistry): void;
@@ -0,0 +1,29 @@
1
+ /*
2
+ * Bundle of v1 verbs for shell-shard. Call registerV1Verbs(registry) once
3
+ * at activate time to populate the registry.
4
+ */
5
+ import { makeHelpVerb } from './help';
6
+ import { clearVerb } from './clear';
7
+ import { historyVerb } from './history';
8
+ import { appsVerb, appVerb } from './apps';
9
+ import { shardsVerb } from './shards';
10
+ import { viewsVerb, openVerb, closeVerb } from './views';
11
+ import { zonesVerb, zoneVerb } from './zones';
12
+ import { pwdVerb, cdVerb, envVerb, whoamiVerb } from './session';
13
+ export function registerV1Verbs(registry) {
14
+ registry.register(makeHelpVerb(registry));
15
+ registry.register(clearVerb);
16
+ registry.register(historyVerb);
17
+ registry.register(appsVerb);
18
+ registry.register(appVerb);
19
+ registry.register(shardsVerb);
20
+ registry.register(viewsVerb);
21
+ registry.register(openVerb);
22
+ registry.register(closeVerb);
23
+ registry.register(zonesVerb);
24
+ registry.register(zoneVerb);
25
+ registry.register(pwdVerb);
26
+ registry.register(cdVerb);
27
+ registry.register(envVerb);
28
+ registry.register(whoamiVerb);
29
+ }
@@ -0,0 +1,5 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const pwdVerb: Verb;
3
+ export declare const cdVerb: Verb;
4
+ export declare const envVerb: Verb;
5
+ export declare const whoamiVerb: Verb;
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Session verbs: pwd, cd, env, whoami.
3
+ *
4
+ * cd is server-authoritative — the client forwards it as a 'submit' message
5
+ * so ws.ts can intercept and update session.cwd without spawning a process.
6
+ */
7
+ import EnvTable from '../rich/EnvTable.svelte';
8
+ export const pwdVerb = {
9
+ name: 'pwd',
10
+ summary: 'Show the server session cwd.',
11
+ async run(ctx) {
12
+ ctx.scrollback.push({
13
+ kind: 'text',
14
+ stream: 'stdout',
15
+ chunks: [ctx.cwd + '\n'],
16
+ ts: Date.now(),
17
+ });
18
+ },
19
+ };
20
+ export const cdVerb = {
21
+ name: 'cd',
22
+ summary: 'Change the session working directory.',
23
+ async run(ctx, args) {
24
+ var _a;
25
+ const path = (_a = args[0]) !== null && _a !== void 0 ? _a : '';
26
+ if (!path) {
27
+ ctx.scrollback.push({
28
+ kind: 'status',
29
+ text: 'usage: cd <path>',
30
+ level: 'warn',
31
+ ts: Date.now(),
32
+ });
33
+ return;
34
+ }
35
+ // cd is server-authoritative. Forward the submit so the server's
36
+ // dispatcher can intercept it and update session.cwd (see ws.ts).
37
+ ctx.session.send({ t: 'submit', line: `cd ${path}` });
38
+ },
39
+ };
40
+ export const envVerb = {
41
+ name: 'env',
42
+ summary: 'Show the session environment.',
43
+ async run(ctx) {
44
+ const env = ctx.session.env;
45
+ ctx.scrollback.push({
46
+ kind: 'rich',
47
+ component: EnvTable,
48
+ props: { data: { env } },
49
+ ts: Date.now(),
50
+ });
51
+ },
52
+ };
53
+ export const whoamiVerb = {
54
+ name: 'whoami',
55
+ summary: 'Show the current admin user and session info.',
56
+ async run(ctx) {
57
+ const me = ctx.shell.whoAmI();
58
+ ctx.scrollback.push({
59
+ kind: 'text',
60
+ stream: 'stdout',
61
+ chunks: [`${me.userId}${me.admin ? ' (admin)' : ''}\n`],
62
+ ts: Date.now(),
63
+ });
64
+ },
65
+ };
@@ -0,0 +1,2 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const shardsVerb: Verb;
@@ -0,0 +1,14 @@
1
+ import ShardsTable from '../rich/ShardsTable.svelte';
2
+ export const shardsVerb = {
3
+ name: 'shards',
4
+ summary: 'List active shards.',
5
+ async run(ctx) {
6
+ const shards = ctx.shell.listShards();
7
+ ctx.scrollback.push({
8
+ kind: 'rich',
9
+ component: ShardsTable,
10
+ props: { data: { shards } },
11
+ ts: Date.now(),
12
+ });
13
+ },
14
+ };
@@ -0,0 +1,4 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const viewsVerb: Verb;
3
+ export declare const openVerb: Verb;
4
+ export declare const closeVerb: Verb;
@@ -0,0 +1,90 @@
1
+ import ViewsTable from '../rich/ViewsTable.svelte';
2
+ export const viewsVerb = {
3
+ name: 'views',
4
+ summary: 'List views currently mounted in the active layout.',
5
+ async run(ctx) {
6
+ const views = ctx.shell.listViewsInCurrentLayout();
7
+ ctx.scrollback.push({
8
+ kind: 'rich',
9
+ component: ViewsTable,
10
+ props: {
11
+ data: {
12
+ views,
13
+ onClose: (slotId) => {
14
+ var _a;
15
+ const result = ctx.shell.closeSlot(slotId);
16
+ if (!result.ok) {
17
+ ctx.scrollback.push({
18
+ kind: 'status',
19
+ text: `shell: close failed — ${(_a = result.error) !== null && _a !== void 0 ? _a : 'unknown'}`,
20
+ level: 'error',
21
+ ts: Date.now(),
22
+ });
23
+ }
24
+ },
25
+ },
26
+ },
27
+ ts: Date.now(),
28
+ });
29
+ },
30
+ };
31
+ export const openVerb = {
32
+ name: 'open',
33
+ summary: 'Open a view from any active shard into the current layout.',
34
+ async run(ctx, args) {
35
+ var _a;
36
+ const viewId = args[0];
37
+ if (!viewId) {
38
+ ctx.scrollback.push({
39
+ kind: 'status',
40
+ text: 'usage: open <viewId>',
41
+ level: 'warn',
42
+ ts: Date.now(),
43
+ });
44
+ return;
45
+ }
46
+ const result = ctx.shell.openViewInCurrentLayout(viewId);
47
+ if (!result.ok) {
48
+ ctx.scrollback.push({
49
+ kind: 'status',
50
+ text: `shell: open failed — ${(_a = result.error) !== null && _a !== void 0 ? _a : 'unknown'}`,
51
+ level: 'error',
52
+ ts: Date.now(),
53
+ });
54
+ }
55
+ else {
56
+ ctx.scrollback.push({
57
+ kind: 'status',
58
+ text: `shell: opened ${viewId}`,
59
+ level: 'info',
60
+ ts: Date.now(),
61
+ });
62
+ }
63
+ },
64
+ };
65
+ export const closeVerb = {
66
+ name: 'close',
67
+ summary: 'Close a view by slot id.',
68
+ async run(ctx, args) {
69
+ var _a;
70
+ const slotId = args[0];
71
+ if (!slotId) {
72
+ ctx.scrollback.push({
73
+ kind: 'status',
74
+ text: 'usage: close <slotId>',
75
+ level: 'warn',
76
+ ts: Date.now(),
77
+ });
78
+ return;
79
+ }
80
+ const result = ctx.shell.closeSlot(slotId);
81
+ if (!result.ok) {
82
+ ctx.scrollback.push({
83
+ kind: 'status',
84
+ text: `shell: close failed — ${(_a = result.error) !== null && _a !== void 0 ? _a : 'unknown'}`,
85
+ level: 'error',
86
+ ts: Date.now(),
87
+ });
88
+ }
89
+ },
90
+ };
@@ -0,0 +1,3 @@
1
+ import type { Verb } from '../registry';
2
+ export declare const zonesVerb: Verb;
3
+ export declare const zoneVerb: Verb;