@astrale-os/shell 0.1.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 (145) hide show
  1. package/dist/adapters/iframe.shell-adapter.d.ts +10 -0
  2. package/dist/adapters/iframe.shell-adapter.d.ts.map +1 -0
  3. package/dist/adapters/iframe.shell-adapter.js +55 -0
  4. package/dist/adapters/kernel-client.adapter.d.ts +53 -0
  5. package/dist/adapters/kernel-client.adapter.d.ts.map +1 -0
  6. package/dist/adapters/kernel-client.adapter.js +69 -0
  7. package/dist/adapters/message-port.transport.d.ts +7 -0
  8. package/dist/adapters/message-port.transport.d.ts.map +1 -0
  9. package/dist/adapters/message-port.transport.js +42 -0
  10. package/dist/application/channels/children-channel.d.ts +21 -0
  11. package/dist/application/channels/children-channel.d.ts.map +1 -0
  12. package/dist/application/channels/children-channel.js +82 -0
  13. package/dist/application/channels/observer.d.ts +9 -0
  14. package/dist/application/channels/observer.d.ts.map +1 -0
  15. package/dist/application/channels/observer.js +23 -0
  16. package/dist/application/channels/parent-channel.d.ts +17 -0
  17. package/dist/application/channels/parent-channel.d.ts.map +1 -0
  18. package/dist/application/channels/parent-channel.js +21 -0
  19. package/dist/application/channels/types.d.ts +12 -0
  20. package/dist/application/channels/types.d.ts.map +1 -0
  21. package/dist/application/channels/types.js +29 -0
  22. package/dist/application/intent/pipeline-runner.d.ts +12 -0
  23. package/dist/application/intent/pipeline-runner.d.ts.map +1 -0
  24. package/dist/application/intent/pipeline-runner.js +27 -0
  25. package/dist/application/intent/request.d.ts +34 -0
  26. package/dist/application/intent/request.d.ts.map +1 -0
  27. package/dist/application/intent/request.js +70 -0
  28. package/dist/application/intent/router.d.ts +56 -0
  29. package/dist/application/intent/router.d.ts.map +1 -0
  30. package/dist/application/intent/router.js +94 -0
  31. package/dist/application/kernel-facade.d.ts +33 -0
  32. package/dist/application/kernel-facade.d.ts.map +1 -0
  33. package/dist/application/kernel-facade.js +77 -0
  34. package/dist/application/shell.d.ts +100 -0
  35. package/dist/application/shell.d.ts.map +1 -0
  36. package/dist/application/shell.js +256 -0
  37. package/dist/application/windowing/child-init.d.ts +23 -0
  38. package/dist/application/windowing/child-init.d.ts.map +1 -0
  39. package/dist/application/windowing/child-init.js +101 -0
  40. package/dist/application/windowing/handshake.d.ts +30 -0
  41. package/dist/application/windowing/handshake.d.ts.map +1 -0
  42. package/dist/application/windowing/handshake.js +63 -0
  43. package/dist/application/windowing/lifecycle-controller.d.ts +10 -0
  44. package/dist/application/windowing/lifecycle-controller.d.ts.map +1 -0
  45. package/dist/application/windowing/lifecycle-controller.js +30 -0
  46. package/dist/application/windowing/mount-controller.d.ts +25 -0
  47. package/dist/application/windowing/mount-controller.d.ts.map +1 -0
  48. package/dist/application/windowing/mount-controller.js +277 -0
  49. package/dist/application/windowing/protocol.d.ts +21 -0
  50. package/dist/application/windowing/protocol.d.ts.map +1 -0
  51. package/dist/application/windowing/protocol.js +19 -0
  52. package/dist/application/windowing/token-refresh.d.ts +19 -0
  53. package/dist/application/windowing/token-refresh.d.ts.map +1 -0
  54. package/dist/application/windowing/token-refresh.js +60 -0
  55. package/dist/domain/identity/delegation.d.ts +15 -0
  56. package/dist/domain/identity/delegation.d.ts.map +1 -0
  57. package/dist/domain/identity/delegation.js +12 -0
  58. package/dist/domain/identity/path.d.ts +11 -0
  59. package/dist/domain/identity/path.d.ts.map +1 -0
  60. package/dist/domain/identity/path.js +13 -0
  61. package/dist/domain/identity/resolve-self.d.ts +21 -0
  62. package/dist/domain/identity/resolve-self.d.ts.map +1 -0
  63. package/dist/domain/identity/resolve-self.js +36 -0
  64. package/dist/domain/intent/middleware/capabilities.d.ts +28 -0
  65. package/dist/domain/intent/middleware/capabilities.d.ts.map +1 -0
  66. package/dist/domain/intent/middleware/capabilities.js +18 -0
  67. package/dist/domain/intent/middleware/logging.d.ts +14 -0
  68. package/dist/domain/intent/middleware/logging.d.ts.map +1 -0
  69. package/dist/domain/intent/middleware/logging.js +21 -0
  70. package/dist/domain/intent/middleware/rate-limit.d.ts +20 -0
  71. package/dist/domain/intent/middleware/rate-limit.d.ts.map +1 -0
  72. package/dist/domain/intent/middleware/rate-limit.js +33 -0
  73. package/dist/domain/intent/pipeline.d.ts +3 -0
  74. package/dist/domain/intent/pipeline.d.ts.map +1 -0
  75. package/dist/domain/intent/pipeline.js +1 -0
  76. package/dist/domain/intent/router.d.ts +6 -0
  77. package/dist/domain/intent/router.d.ts.map +1 -0
  78. package/dist/domain/intent/router.js +1 -0
  79. package/dist/domain/message/errors.d.ts +2 -0
  80. package/dist/domain/message/errors.d.ts.map +1 -0
  81. package/dist/domain/message/errors.js +1 -0
  82. package/dist/domain/message/registries.d.ts +145 -0
  83. package/dist/domain/message/registries.d.ts.map +1 -0
  84. package/dist/domain/message/registries.js +1 -0
  85. package/dist/domain/message/types.d.ts +30 -0
  86. package/dist/domain/message/types.d.ts.map +1 -0
  87. package/dist/domain/message/types.js +1 -0
  88. package/dist/domain/window/config.d.ts +26 -0
  89. package/dist/domain/window/config.d.ts.map +1 -0
  90. package/dist/domain/window/config.js +1 -0
  91. package/dist/domain/window/lifecycle.d.ts +27 -0
  92. package/dist/domain/window/lifecycle.d.ts.map +1 -0
  93. package/dist/domain/window/lifecycle.js +36 -0
  94. package/dist/domain/window/window.d.ts +12 -0
  95. package/dist/domain/window/window.d.ts.map +1 -0
  96. package/dist/domain/window/window.js +1 -0
  97. package/dist/index.d.ts +42 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +20 -0
  100. package/dist/ports/kernel-client.d.ts +33 -0
  101. package/dist/ports/kernel-client.d.ts.map +1 -0
  102. package/dist/ports/kernel-client.js +1 -0
  103. package/dist/ports/shell-adapter.d.ts +13 -0
  104. package/dist/ports/shell-adapter.d.ts.map +1 -0
  105. package/dist/ports/shell-adapter.js +1 -0
  106. package/dist/ports/transport.d.ts +7 -0
  107. package/dist/ports/transport.d.ts.map +1 -0
  108. package/dist/ports/transport.js +1 -0
  109. package/package.json +41 -0
  110. package/src/adapters/iframe.shell-adapter.ts +68 -0
  111. package/src/adapters/kernel-client.adapter.ts +138 -0
  112. package/src/adapters/message-port.transport.ts +50 -0
  113. package/src/application/channels/children-channel.ts +112 -0
  114. package/src/application/channels/observer.ts +33 -0
  115. package/src/application/channels/parent-channel.ts +43 -0
  116. package/src/application/channels/types.ts +41 -0
  117. package/src/application/intent/pipeline-runner.ts +36 -0
  118. package/src/application/intent/request.ts +87 -0
  119. package/src/application/intent/router.ts +161 -0
  120. package/src/application/kernel-facade.ts +143 -0
  121. package/src/application/shell.ts +433 -0
  122. package/src/application/windowing/child-init.ts +137 -0
  123. package/src/application/windowing/handshake.ts +98 -0
  124. package/src/application/windowing/lifecycle-controller.ts +40 -0
  125. package/src/application/windowing/mount-controller.ts +335 -0
  126. package/src/application/windowing/protocol.ts +37 -0
  127. package/src/application/windowing/token-refresh.ts +78 -0
  128. package/src/domain/identity/delegation.ts +25 -0
  129. package/src/domain/identity/path.ts +14 -0
  130. package/src/domain/identity/resolve-self.ts +38 -0
  131. package/src/domain/intent/middleware/capabilities.ts +53 -0
  132. package/src/domain/intent/middleware/logging.ts +45 -0
  133. package/src/domain/intent/middleware/rate-limit.ts +54 -0
  134. package/src/domain/intent/pipeline.ts +3 -0
  135. package/src/domain/intent/router.ts +6 -0
  136. package/src/domain/message/errors.ts +9 -0
  137. package/src/domain/message/registries.ts +76 -0
  138. package/src/domain/message/types.ts +32 -0
  139. package/src/domain/window/config.ts +28 -0
  140. package/src/domain/window/lifecycle.ts +39 -0
  141. package/src/domain/window/window.ts +13 -0
  142. package/src/index.ts +103 -0
  143. package/src/ports/kernel-client.ts +32 -0
  144. package/src/ports/shell-adapter.ts +11 -0
  145. package/src/ports/transport.ts +7 -0
@@ -0,0 +1,60 @@
1
+ import { REFRESH_WINDOW_MS } from '../../domain/identity/delegation';
2
+ const BACKOFF_DELAYS_MS = [1_000, 2_000, 4_000];
3
+ export function createTokenRefresher(config) {
4
+ const now = config.now ?? Date.now;
5
+ let timer = null;
6
+ let stopped = false;
7
+ function schedule() {
8
+ if (stopped)
9
+ return;
10
+ if (timer)
11
+ clearTimeout(timer);
12
+ const token = config.token();
13
+ const leadTimeMs = Math.max(token.expiresAt - now() - REFRESH_WINDOW_MS, 0);
14
+ timer = setTimeout(() => {
15
+ void runRefresh();
16
+ }, leadTimeMs);
17
+ }
18
+ async function runRefresh() {
19
+ for (let attempt = 0; attempt <= BACKOFF_DELAYS_MS.length; attempt += 1) {
20
+ if (stopped)
21
+ return;
22
+ try {
23
+ const next = await config.mint();
24
+ config.setToken({
25
+ credential: next.credential,
26
+ expiresAt: next.expiresAt,
27
+ issuedAt: now(),
28
+ scope: { functionId: config.functionId },
29
+ });
30
+ schedule();
31
+ return;
32
+ }
33
+ catch (err) {
34
+ if (attempt === BACKOFF_DELAYS_MS.length) {
35
+ config.onFailure?.(err);
36
+ return;
37
+ }
38
+ await delay(BACKOFF_DELAYS_MS[attempt]);
39
+ }
40
+ }
41
+ }
42
+ return {
43
+ start() {
44
+ stopped = false;
45
+ schedule();
46
+ },
47
+ stop() {
48
+ stopped = true;
49
+ if (timer)
50
+ clearTimeout(timer);
51
+ timer = null;
52
+ },
53
+ async forceRefreshNow() {
54
+ await runRefresh();
55
+ },
56
+ };
57
+ }
58
+ function delay(ms) {
59
+ return new Promise((resolve) => setTimeout(resolve, ms));
60
+ }
@@ -0,0 +1,15 @@
1
+ export type DelegationScope = {
2
+ readonly functionId: string;
3
+ };
4
+ export type DelegationToken = {
5
+ readonly credential: string;
6
+ readonly scope: DelegationScope;
7
+ readonly issuedAt: number;
8
+ readonly expiresAt: number;
9
+ };
10
+ export type TokenState = 'fresh' | 'expiring' | 'expired' | 'refreshing' | 'failed';
11
+ /** How long before expiration we start proactively refreshing. */
12
+ export declare const REFRESH_WINDOW_MS = 120000;
13
+ export declare function stateOf(token: DelegationToken, now?: number): TokenState;
14
+ export declare function isExpired(token: DelegationToken, now?: number): boolean;
15
+ //# sourceMappingURL=delegation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegation.d.ts","sourceRoot":"","sources":["../../../src/domain/identity/delegation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAA;AAEnF,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,SAAU,CAAA;AAExC,wBAAgB,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,SAAa,GAAG,UAAU,CAI5E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,SAAa,GAAG,OAAO,CAE3E"}
@@ -0,0 +1,12 @@
1
+ /** How long before expiration we start proactively refreshing. */
2
+ export const REFRESH_WINDOW_MS = 120_000;
3
+ export function stateOf(token, now = Date.now()) {
4
+ if (token.expiresAt <= now)
5
+ return 'expired';
6
+ if (token.expiresAt - now <= REFRESH_WINDOW_MS)
7
+ return 'expiring';
8
+ return 'fresh';
9
+ }
10
+ export function isExpired(token, now = Date.now()) {
11
+ return token.expiresAt <= now;
12
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Built-in kernel root identity, used as the default mint subject when no
3
+ * explicit `mintIdentity` is configured.
4
+ */
5
+ export declare const SYSTEM_IDENTITY = "__system__";
6
+ /**
7
+ * Address a method on an identity or path: `@<id>::method` for a bare node id,
8
+ * `<path>::method` for an absolute path (one starting with `/`).
9
+ */
10
+ export declare function identityMethod(identity: string, method: string): string;
11
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/domain/identity/path.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,eAAe,eAAe,CAAA;AAE3C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGvE"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Built-in kernel root identity, used as the default mint subject when no
3
+ * explicit `mintIdentity` is configured.
4
+ */
5
+ export const SYSTEM_IDENTITY = '__system__';
6
+ /**
7
+ * Address a method on an identity or path: `@<id>::method` for a bare node id,
8
+ * `<path>::method` for an absolute path (one starting with `/`).
9
+ */
10
+ export function identityMethod(identity, method) {
11
+ const target = identity.startsWith('/') ? identity : `@${identity}`;
12
+ return `${target}::${method}`;
13
+ }
@@ -0,0 +1,21 @@
1
+ /** Kernel path that returns the AUTHENTICATED caller's own identity node. */
2
+ export declare const WHOAMI_PATH = "/:kernel.astrale.ai:interface.Identity:whoami";
3
+ /**
4
+ * Resolve the mint SUBJECT — the identity node a delegation/credential should be
5
+ * minted for. It MUST be the principal the kernel actually authenticates this
6
+ * caller as, otherwise the minted token impersonates someone else.
7
+ *
8
+ * Why a kernel round-trip and not the local credential: a cookie-fronted SPA
9
+ * holds an EMPTY credential string (the session token lives in an httpOnly
10
+ * cookie it can't read), so the subject cannot be derived locally. We ask the
11
+ * kernel via `whoami`; that request carries the cookie, so the router unseals it
12
+ * and the kernel answers with the logged-in user's node. Without this the shell
13
+ * defaulted every mint to `__system__`, so a View iframe mounted by a logged-in
14
+ * user acted as the kernel ROOT instead of as that user.
15
+ *
16
+ * `fallback` (the configured `mintIdentity`, default `__system__`) is used when
17
+ * whoami can't be resolved — a genuinely system/dev caller, or an unauthenticated
18
+ * boot. The result is cached: the principal is stable for a session.
19
+ */
20
+ export declare function createSelfResolver(fallback?: string): (whoami: () => Promise<unknown>) => Promise<string>;
21
+ //# sourceMappingURL=resolve-self.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-self.d.ts","sourceRoot":"","sources":["../../../src/domain/identity/resolve-self.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,eAAO,MAAM,WAAW,kDAAkD,CAAA;AAE1E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,GAAE,MAAwB,GACjC,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAarD"}
@@ -0,0 +1,36 @@
1
+ import { SYSTEM_IDENTITY } from './path';
2
+ /** Kernel path that returns the AUTHENTICATED caller's own identity node. */
3
+ export const WHOAMI_PATH = '/:kernel.astrale.ai:interface.Identity:whoami';
4
+ /**
5
+ * Resolve the mint SUBJECT — the identity node a delegation/credential should be
6
+ * minted for. It MUST be the principal the kernel actually authenticates this
7
+ * caller as, otherwise the minted token impersonates someone else.
8
+ *
9
+ * Why a kernel round-trip and not the local credential: a cookie-fronted SPA
10
+ * holds an EMPTY credential string (the session token lives in an httpOnly
11
+ * cookie it can't read), so the subject cannot be derived locally. We ask the
12
+ * kernel via `whoami`; that request carries the cookie, so the router unseals it
13
+ * and the kernel answers with the logged-in user's node. Without this the shell
14
+ * defaulted every mint to `__system__`, so a View iframe mounted by a logged-in
15
+ * user acted as the kernel ROOT instead of as that user.
16
+ *
17
+ * `fallback` (the configured `mintIdentity`, default `__system__`) is used when
18
+ * whoami can't be resolved — a genuinely system/dev caller, or an unauthenticated
19
+ * boot. The result is cached: the principal is stable for a session.
20
+ */
21
+ export function createSelfResolver(fallback = SYSTEM_IDENTITY) {
22
+ let cached;
23
+ return async (whoami) => {
24
+ if (cached !== undefined)
25
+ return cached;
26
+ try {
27
+ const me = (await whoami());
28
+ const id = me && typeof me.id === 'string' && me.id.length > 0 ? me.id : undefined;
29
+ cached = id ?? fallback;
30
+ }
31
+ catch {
32
+ cached = fallback;
33
+ }
34
+ return cached;
35
+ };
36
+ }
@@ -0,0 +1,28 @@
1
+ import type { IntentRegistry } from '../../message/registries';
2
+ import type { WindowCapabilities } from '../../window/config';
3
+ import type { IntentMiddleware } from '../pipeline';
4
+ export type CapabilityLookup = (sender: string) => WindowCapabilities | undefined;
5
+ export type CapabilityViolation = {
6
+ readonly sender: string;
7
+ readonly intent: keyof IntentRegistry;
8
+ };
9
+ export type CapabilitiesConfig = {
10
+ /**
11
+ * Map each sender windowId to its declared capabilities. Intents whose
12
+ * name is absent from the sender's capability list are blocked.
13
+ */
14
+ readonly lookup: CapabilityLookup;
15
+ /**
16
+ * Called when an intent is blocked. The violation can be used to emit
17
+ * an ErrorMessage back to the sender (shell wiring does that separately).
18
+ */
19
+ readonly onViolation?: (violation: CapabilityViolation) => void;
20
+ /**
21
+ * If true, intents originated by the shell itself (sender === selfWindowId)
22
+ * are never filtered. Default: true.
23
+ */
24
+ readonly allowSelf?: boolean;
25
+ readonly selfWindowId?: string;
26
+ };
27
+ export declare function capabilitiesMiddleware(config: CapabilitiesConfig): IntentMiddleware;
28
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../../../src/domain/intent/middleware/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAA;AAEjF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,cAAc,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;IAEjC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAE/D;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAE5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B,CAAA;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB,GAAG,gBAAgB,CAkBnF"}
@@ -0,0 +1,18 @@
1
+ export function capabilitiesMiddleware(config) {
2
+ const { lookup, onViolation, selfWindowId } = config;
3
+ const allowSelf = config.allowSelf ?? true;
4
+ return async (message, next) => {
5
+ const sender = message.envelope.sender.windowId;
6
+ if (allowSelf && selfWindowId !== undefined && sender === selfWindowId) {
7
+ await next();
8
+ return;
9
+ }
10
+ const caps = lookup(sender);
11
+ const intentName = message.envelope.name;
12
+ if (!caps || !caps.intents.includes(intentName)) {
13
+ onViolation?.({ sender, intent: intentName });
14
+ return;
15
+ }
16
+ await next();
17
+ };
18
+ }
@@ -0,0 +1,14 @@
1
+ import type { IntentMiddleware } from '../pipeline';
2
+ export type LoggingSink = (entry: LoggingEntry) => void;
3
+ export type LoggingEntry = {
4
+ readonly name: string;
5
+ readonly sender: string;
6
+ readonly correlationId?: string;
7
+ readonly phase: 'in' | 'out';
8
+ readonly timestamp: number;
9
+ };
10
+ export type LoggingConfig = {
11
+ readonly sink?: LoggingSink;
12
+ };
13
+ export declare function loggingMiddleware(config?: LoggingConfig): IntentMiddleware;
14
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../../../src/domain/intent/middleware/logging.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;AAEvD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,KAAK,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAC5B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,aAAkB,GAAG,gBAAgB,CAQ9E"}
@@ -0,0 +1,21 @@
1
+ export function loggingMiddleware(config = {}) {
2
+ const sink = config.sink ?? defaultSink;
3
+ return async (message, next) => {
4
+ sink(toEntry(message, 'in'));
5
+ await next();
6
+ sink(toEntry(message, 'out'));
7
+ };
8
+ }
9
+ function toEntry(message, phase) {
10
+ return {
11
+ name: message.envelope.name,
12
+ sender: message.envelope.sender.windowId,
13
+ correlationId: message.envelope.correlationId,
14
+ phase,
15
+ timestamp: Date.now(),
16
+ };
17
+ }
18
+ function defaultSink(entry) {
19
+ // eslint-disable-next-line no-console -- built-in sink; opt-in via wiring.
20
+ console.debug(`[shell.intent] ${entry.phase} ${entry.name} from=${entry.sender}${entry.correlationId ? ` corr=${entry.correlationId}` : ''}`);
21
+ }
@@ -0,0 +1,20 @@
1
+ import type { IntentMessage } from '../../message/types';
2
+ import type { IntentMiddleware } from '../pipeline';
3
+ export type RateLimitKey = (message: IntentMessage) => string;
4
+ export type RateLimitViolation = {
5
+ readonly key: string;
6
+ readonly intent: string;
7
+ };
8
+ export type RateLimitConfig = {
9
+ readonly windowMs: number;
10
+ readonly max: number;
11
+ readonly key?: RateLimitKey;
12
+ readonly onViolation?: (violation: RateLimitViolation) => void;
13
+ };
14
+ /**
15
+ * Fixed-window rate limiter. An amortized sweep evicts expired buckets on
16
+ * every `windowMs` interval of activity so the map stays bounded by the
17
+ * number of active keys, not the total ever seen.
18
+ */
19
+ export declare function rateLimitMiddleware(config: RateLimitConfig): IntentMiddleware;
20
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../../src/domain/intent/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,aAAa,KAAK,MAAM,CAAA;AAE7D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,kBAAkB,KAAK,IAAI,CAAA;CAC/D,CAAA;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CA+B7E"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Fixed-window rate limiter. An amortized sweep evicts expired buckets on
3
+ * every `windowMs` interval of activity so the map stays bounded by the
4
+ * number of active keys, not the total ever seen.
5
+ */
6
+ export function rateLimitMiddleware(config) {
7
+ const { windowMs, max, onViolation } = config;
8
+ const key = config.key ?? ((m) => `${m.envelope.sender.windowId}|${m.envelope.name}`);
9
+ const buckets = new Map();
10
+ let nextSweepAt = 0;
11
+ return async (message, next) => {
12
+ const k = key(message);
13
+ const now = Date.now();
14
+ if (now >= nextSweepAt) {
15
+ for (const [bucketKey, bucket] of buckets) {
16
+ if (bucket.resetAt <= now)
17
+ buckets.delete(bucketKey);
18
+ }
19
+ nextSweepAt = now + windowMs;
20
+ }
21
+ let bucket = buckets.get(k);
22
+ if (!bucket || bucket.resetAt <= now) {
23
+ bucket = { count: 0, resetAt: now + windowMs };
24
+ buckets.set(k, bucket);
25
+ }
26
+ bucket.count += 1;
27
+ if (bucket.count > max) {
28
+ onViolation?.({ key: k, intent: message.envelope.name });
29
+ return;
30
+ }
31
+ await next();
32
+ };
33
+ }
@@ -0,0 +1,3 @@
1
+ import type { IntentMessage } from '../message/types';
2
+ export type IntentMiddleware = (message: IntentMessage, next: () => Promise<void>) => Promise<void>;
3
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../src/domain/intent/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { IntentRegistry } from '../message/registries';
2
+ import type { IntentMessage } from '../message/types';
3
+ export interface IntentRouter {
4
+ dispatch<K extends keyof IntentRegistry>(message: IntentMessage<K>): Promise<void>;
5
+ }
6
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/domain/intent/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnF"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export type ErrorCode = 'HANDSHAKE_TIMEOUT' | 'ORIGIN_MISMATCH' | 'WINDOW_UNAVAILABLE' | 'UNSUPPORTED_VERSION' | 'INTENT_NOT_ALLOWED' | 'DELIVERY_FAILED' | 'TOKEN_REFRESH_FAILED' | 'NO_VIEW_AVAILABLE';
2
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/domain/message/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,mBAAmB,GACnB,iBAAiB,GACjB,oBAAoB,GACpB,qBAAqB,GACrB,oBAAoB,GACpB,iBAAiB,GACjB,sBAAsB,GACtB,mBAAmB,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,145 @@
1
+ export interface ViewRef {
2
+ readonly viewId: string;
3
+ readonly functionId: string;
4
+ readonly label?: string;
5
+ }
6
+ /**
7
+ * Intent registry — extensible via declaration merging.
8
+ *
9
+ * Consumers extend via:
10
+ * declare module '@astrale-os/shell' {
11
+ * interface IntentRegistry { ... }
12
+ * }
13
+ */
14
+ export interface IntentRegistry {
15
+ open: {
16
+ payload: {
17
+ nodeId: string;
18
+ viewId?: string;
19
+ };
20
+ response: {
21
+ windowId: string;
22
+ };
23
+ };
24
+ close: {
25
+ payload: {
26
+ windowId: string;
27
+ };
28
+ response: void;
29
+ };
30
+ focus: {
31
+ payload: {
32
+ nodeId: string;
33
+ };
34
+ response: void;
35
+ };
36
+ willClose: {
37
+ payload: Record<string, never>;
38
+ response: void;
39
+ };
40
+ closeAck: {
41
+ payload: Record<string, never>;
42
+ response: void;
43
+ };
44
+ closeRefuse: {
45
+ payload: {
46
+ reason: string;
47
+ };
48
+ response: void;
49
+ };
50
+ pickView: {
51
+ payload: {
52
+ views: ViewRef[];
53
+ };
54
+ response: {
55
+ viewId: string;
56
+ };
57
+ };
58
+ receive: {
59
+ payload: {
60
+ data: unknown;
61
+ sourceIntent: string;
62
+ };
63
+ response: void;
64
+ };
65
+ /**
66
+ * Hot-swap — parent pushes a new target node to an already-mounted view
67
+ * without remounting. The child reads `targetNodeId` at handshake time
68
+ * for the initial value; `setTarget` replaces it for the lifetime of the
69
+ * window.
70
+ */
71
+ setTarget: {
72
+ payload: {
73
+ nodeId: string;
74
+ };
75
+ response: void;
76
+ };
77
+ /**
78
+ * Child → parent: a view asks the user to grant `perms` (a permission
79
+ * bitmask) on `node` to `identity` (the beneficiary subject). Both `node`
80
+ * and `identity` are bare node ids (e.g. the view's `targetNodeId`, or
81
+ * `whoami().id` for "grant to myself") — the root shell addresses them as
82
+ * `@<id>`. The root shell (GUI) surfaces a modal, computes the delta vs.
83
+ * what the subject already holds, and — on approval — grants only the
84
+ * missing bits under the GUI user's own credential. `reason` is shown to
85
+ * the user. `response.perms` is the bitmask actually granted (0 if
86
+ * declined / nothing was missing).
87
+ *
88
+ * Reaches a handler only from a view mounted as a DIRECT child of the root
89
+ * shell — the default intent policy does not bubble past one hop.
90
+ */
91
+ requestAccess: {
92
+ payload: {
93
+ node: string;
94
+ identity: string;
95
+ perms: number;
96
+ reason?: string;
97
+ };
98
+ response: {
99
+ granted: boolean;
100
+ perms: number;
101
+ };
102
+ };
103
+ /**
104
+ * Generic correlated reply channel. The default intent transport is
105
+ * fire-and-forget; `requestIntent()` sends an intent carrying a
106
+ * `correlationId` and awaits an `intentReply` whose payload echoes it back
107
+ * with the handler's `result`. Routed to the resolver purely by
108
+ * `correlationId` — it intentionally matches no local handler.
109
+ */
110
+ intentReply: {
111
+ payload: {
112
+ correlationId: string;
113
+ result: unknown;
114
+ };
115
+ response: void;
116
+ };
117
+ }
118
+ /**
119
+ * Control message registry — extensible via declaration merging.
120
+ */
121
+ export interface CtrlRegistry {
122
+ handshake: {
123
+ data: {
124
+ delegationToken: string;
125
+ tokenExpiresAt: number;
126
+ windowId: string;
127
+ kernelUrl: string;
128
+ functionId: string;
129
+ targetNodeId?: string;
130
+ mintIdentity?: string;
131
+ };
132
+ };
133
+ handshakeAck: {
134
+ data: {
135
+ windowId: string;
136
+ };
137
+ };
138
+ tokenRefresh: {
139
+ data: {
140
+ delegationToken: string;
141
+ tokenExpiresAt: number;
142
+ };
143
+ };
144
+ }
145
+ //# sourceMappingURL=registries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registries.d.ts","sourceRoot":"","sources":["../../../src/domain/message/registries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { ErrorCode } from './errors';
2
+ import type { CtrlRegistry, IntentRegistry } from './registries';
3
+ export type IntentEnvelope<K extends keyof IntentRegistry = keyof IntentRegistry> = {
4
+ readonly name: K;
5
+ readonly payload: IntentRegistry[K]['payload'];
6
+ readonly sender: {
7
+ readonly windowId: string;
8
+ };
9
+ readonly correlationId?: string;
10
+ };
11
+ export type IntentMessage<K extends keyof IntentRegistry = keyof IntentRegistry> = {
12
+ readonly type: 'intent';
13
+ readonly version: 1;
14
+ readonly envelope: IntentEnvelope<K>;
15
+ };
16
+ export type CtrlMessage<K extends keyof CtrlRegistry = keyof CtrlRegistry> = {
17
+ readonly type: 'ctrl';
18
+ readonly version: 1;
19
+ readonly action: K;
20
+ readonly data: CtrlRegistry[K]['data'];
21
+ };
22
+ export type ErrorMessage = {
23
+ readonly type: 'error';
24
+ readonly version: 1;
25
+ readonly code: ErrorCode;
26
+ readonly message: string;
27
+ readonly context?: unknown;
28
+ };
29
+ export type ShellMessage = IntentMessage | CtrlMessage | ErrorMessage;
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/domain/message/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAEhE,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,cAAc,GAAG,MAAM,cAAc,IAAI;IAClF,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAChB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC9C,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,cAAc,GAAG,MAAM,cAAc,IAAI;IACjF,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,YAAY,GAAG,MAAM,YAAY,IAAI;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IAClB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import type { IntentRegistry } from '../message/registries';
2
+ export interface SandboxProfile {
3
+ readonly allowScripts: boolean;
4
+ readonly allowForms: boolean;
5
+ readonly allowPopups: boolean;
6
+ readonly allowSameOrigin: boolean;
7
+ readonly allowModals: boolean;
8
+ }
9
+ export interface WindowCapabilities {
10
+ readonly intents: ReadonlyArray<keyof IntentRegistry>;
11
+ }
12
+ export interface WindowConfig {
13
+ readonly functionId: string;
14
+ readonly isolation: 'sandboxed' | 'shared';
15
+ readonly capabilities: WindowCapabilities;
16
+ /**
17
+ * Sandbox profile for the iframe.
18
+ * - `undefined` → adapter default (`DEFAULT_SANDBOX` in iframe.shell-adapter).
19
+ * - `SandboxProfile` → explicit flag set, becomes the `sandbox=…` attribute.
20
+ * - `null` → opt out: no `sandbox` attribute on the iframe at all (the
21
+ * browser's cross-origin policy is the only remaining isolation). Used
22
+ * for first-party trusted views per the GUI allowlist.
23
+ */
24
+ readonly sandbox?: SandboxProfile | null;
25
+ }
26
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/domain/window/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,cAAc,CAAC,CAAA;CACtD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,QAAQ,CAAA;IAC1C,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;IACzC;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;CACzC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import type { WindowState } from './window';
2
+ export type LifecycleEvent = {
3
+ kind: 'hide';
4
+ } | {
5
+ kind: 'restore';
6
+ } | {
7
+ kind: 'requestClose';
8
+ } | {
9
+ kind: 'closeAck';
10
+ } | {
11
+ kind: 'closeRefuse';
12
+ } | {
13
+ kind: 'forceClose';
14
+ };
15
+ export type TransitionResult = {
16
+ ok: true;
17
+ next: WindowState;
18
+ } | {
19
+ ok: false;
20
+ reason: string;
21
+ };
22
+ /**
23
+ * Pure state-machine transition. Used by the lifecycle controller —
24
+ * no timers, no side effects here.
25
+ */
26
+ export declare function transition(state: WindowState, event: LifecycleEvent): TransitionResult;
27
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/domain/window/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAA;AAE1B,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9F;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAsBtF"}