ttyd-mux 0.4.2 → 0.4.3

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 (84) hide show
  1. package/dist/commands/share.d.ts +25 -0
  2. package/dist/commands/share.d.ts.map +1 -0
  3. package/dist/commands/share.js +108 -0
  4. package/dist/commands/share.js.map +1 -0
  5. package/dist/config/state-store.d.ts +9 -1
  6. package/dist/config/state-store.d.ts.map +1 -1
  7. package/dist/config/state-store.js +30 -2
  8. package/dist/config/state-store.js.map +1 -1
  9. package/dist/config/state.d.ts +9 -1
  10. package/dist/config/state.d.ts.map +1 -1
  11. package/dist/config/state.js +67 -1
  12. package/dist/config/state.js.map +1 -1
  13. package/dist/config/types.d.ts +52 -0
  14. package/dist/config/types.d.ts.map +1 -1
  15. package/dist/config/types.js +23 -1
  16. package/dist/config/types.js.map +1 -1
  17. package/dist/daemon/api-handler.d.ts.map +1 -1
  18. package/dist/daemon/api-handler.js +180 -2
  19. package/dist/daemon/api-handler.js.map +1 -1
  20. package/dist/daemon/http-proxy.d.ts +3 -2
  21. package/dist/daemon/http-proxy.d.ts.map +1 -1
  22. package/dist/daemon/http-proxy.js +7 -4
  23. package/dist/daemon/http-proxy.js.map +1 -1
  24. package/dist/daemon/index.d.ts.map +1 -1
  25. package/dist/daemon/index.js +22 -2
  26. package/dist/daemon/index.js.map +1 -1
  27. package/dist/daemon/notification/index.d.ts +42 -0
  28. package/dist/daemon/notification/index.d.ts.map +1 -0
  29. package/dist/daemon/notification/index.js +83 -0
  30. package/dist/daemon/notification/index.js.map +1 -0
  31. package/dist/daemon/notification/matcher.d.ts +30 -0
  32. package/dist/daemon/notification/matcher.d.ts.map +1 -0
  33. package/dist/daemon/notification/matcher.js +72 -0
  34. package/dist/daemon/notification/matcher.js.map +1 -0
  35. package/dist/daemon/notification/sender.d.ts +38 -0
  36. package/dist/daemon/notification/sender.d.ts.map +1 -0
  37. package/dist/daemon/notification/sender.js +74 -0
  38. package/dist/daemon/notification/sender.js.map +1 -0
  39. package/dist/daemon/notification/subscription.d.ts +35 -0
  40. package/dist/daemon/notification/subscription.d.ts.map +1 -0
  41. package/dist/daemon/notification/subscription.js +52 -0
  42. package/dist/daemon/notification/subscription.js.map +1 -0
  43. package/dist/daemon/notification/types.d.ts +70 -0
  44. package/dist/daemon/notification/types.d.ts.map +1 -0
  45. package/dist/daemon/notification/types.js +5 -0
  46. package/dist/daemon/notification/types.js.map +1 -0
  47. package/dist/daemon/notification/vapid.d.ts +25 -0
  48. package/dist/daemon/notification/vapid.d.ts.map +1 -0
  49. package/dist/daemon/notification/vapid.js +56 -0
  50. package/dist/daemon/notification/vapid.js.map +1 -0
  51. package/dist/daemon/pwa.d.ts +2 -1
  52. package/dist/daemon/pwa.d.ts.map +1 -1
  53. package/dist/daemon/pwa.js +51 -0
  54. package/dist/daemon/pwa.js.map +1 -1
  55. package/dist/daemon/router.d.ts +10 -0
  56. package/dist/daemon/router.d.ts.map +1 -1
  57. package/dist/daemon/router.js +122 -10
  58. package/dist/daemon/router.js.map +1 -1
  59. package/dist/daemon/share-manager.d.ts +55 -0
  60. package/dist/daemon/share-manager.d.ts.map +1 -0
  61. package/dist/daemon/share-manager.js +115 -0
  62. package/dist/daemon/share-manager.js.map +1 -0
  63. package/dist/daemon/toolbar/index.d.ts +4 -12
  64. package/dist/daemon/toolbar/index.d.ts.map +1 -1
  65. package/dist/daemon/toolbar/index.js +6 -794
  66. package/dist/daemon/toolbar/index.js.map +1 -1
  67. package/dist/daemon/toolbar/styles.d.ts +1 -1
  68. package/dist/daemon/toolbar/styles.d.ts.map +1 -1
  69. package/dist/daemon/toolbar/styles.js +106 -0
  70. package/dist/daemon/toolbar/styles.js.map +1 -1
  71. package/dist/daemon/toolbar/template.d.ts +1 -1
  72. package/dist/daemon/toolbar/template.d.ts.map +1 -1
  73. package/dist/daemon/toolbar/template.js +11 -0
  74. package/dist/daemon/toolbar/template.js.map +1 -1
  75. package/dist/daemon/ws-proxy.d.ts +21 -1
  76. package/dist/daemon/ws-proxy.d.ts.map +1 -1
  77. package/dist/daemon/ws-proxy.js +107 -5
  78. package/dist/daemon/ws-proxy.js.map +1 -1
  79. package/dist/index.js +18 -0
  80. package/dist/index.js.map +1 -1
  81. package/dist/toolbar.js +2 -0
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/package.json +6 -2
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pattern matcher for notification triggers
3
+ */
4
+ /**
5
+ * Create a cooldown key for tracking
6
+ */
7
+ function getCooldownKey(sessionName, regex) {
8
+ return `${sessionName}:${regex}`;
9
+ }
10
+ /**
11
+ * Create a notification matcher
12
+ */
13
+ export function createNotificationMatcher(config) {
14
+ const { patterns, defaultCooldown } = config;
15
+ // Map of cooldown key -> timestamp when cooldown expires
16
+ const cooldowns = new Map();
17
+ // Pre-compile regexes
18
+ const compiledPatterns = [];
19
+ for (const pattern of patterns) {
20
+ try {
21
+ compiledPatterns.push({
22
+ config: pattern,
23
+ regex: new RegExp(pattern.regex)
24
+ });
25
+ }
26
+ catch {
27
+ // Skip invalid regex
28
+ console.error(`[Notification] Invalid regex: ${pattern.regex}`);
29
+ }
30
+ }
31
+ return {
32
+ match(sessionName, text) {
33
+ const now = Date.now();
34
+ for (const { config: pattern, regex } of compiledPatterns) {
35
+ if (regex.test(text)) {
36
+ const key = getCooldownKey(sessionName, pattern.regex);
37
+ const cooldownExpires = cooldowns.get(key);
38
+ // Check if in cooldown period
39
+ if (cooldownExpires && now < cooldownExpires) {
40
+ continue; // Still in cooldown, try next pattern
41
+ }
42
+ // Set cooldown
43
+ const cooldownSeconds = pattern.cooldown ?? defaultCooldown;
44
+ cooldowns.set(key, now + cooldownSeconds * 1000);
45
+ return {
46
+ pattern,
47
+ matchedText: text,
48
+ sessionName,
49
+ timestamp: new Date().toISOString()
50
+ };
51
+ }
52
+ }
53
+ return null;
54
+ },
55
+ matchesPattern(regex, text) {
56
+ try {
57
+ return new RegExp(regex).test(text);
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ },
63
+ clearCooldown(sessionName, regex) {
64
+ const key = getCooldownKey(sessionName, regex);
65
+ cooldowns.delete(key);
66
+ },
67
+ reset() {
68
+ cooldowns.clear();
69
+ }
70
+ };
71
+ }
72
+ //# sourceMappingURL=matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../../src/daemon/notification/matcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6BH;;GAEG;AACH,SAAS,cAAc,CAAC,WAAmB,EAAE,KAAa;IACxD,OAAO,GAAG,WAAW,IAAI,KAAK,EAAE,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAqB;IAC7D,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC;IAE7C,yDAAyD;IACzD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,sBAAsB;IACtB,MAAM,gBAAgB,GAAoD,EAAE,CAAC;IAC7E,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,gBAAgB,CAAC,IAAI,CAAC;gBACpB,MAAM,EAAE,OAAO;gBACf,KAAK,EAAE,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;aACjC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;YACrB,OAAO,CAAC,KAAK,CAAC,iCAAiC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,WAAmB,EAAE,IAAY;YACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAEvB,KAAK,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,gBAAgB,EAAE,CAAC;gBAC1D,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,MAAM,GAAG,GAAG,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;oBACvD,MAAM,eAAe,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBAE3C,8BAA8B;oBAC9B,IAAI,eAAe,IAAI,GAAG,GAAG,eAAe,EAAE,CAAC;wBAC7C,SAAS,CAAC,sCAAsC;oBAClD,CAAC;oBAED,eAAe;oBACf,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC;oBAC5D,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,GAAG,IAAI,CAAC,CAAC;oBAEjD,OAAO;wBACL,OAAO;wBACP,WAAW,EAAE,IAAI;wBACjB,WAAW;wBACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBACpC,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAED,cAAc,CAAC,KAAa,EAAE,IAAY;YACxC,IAAI,CAAC;gBACH,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,aAAa,CAAC,WAAmB,EAAE,KAAa;YAC9C,MAAM,GAAG,GAAG,cAAc,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YAC/C,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QAED,KAAK;YACH,SAAS,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Push notification sender
3
+ */
4
+ import type { MatchResult, PushSubscription, VapidKeys } from './types.js';
5
+ /**
6
+ * Notification payload sent to client
7
+ */
8
+ export interface NotificationPayload {
9
+ title: string;
10
+ body: string;
11
+ sessionName: string;
12
+ matchedText: string;
13
+ timestamp: string;
14
+ icon?: string;
15
+ tag?: string;
16
+ }
17
+ /**
18
+ * Subscription store interface
19
+ */
20
+ export interface SubscriptionStore {
21
+ getSubscriptions(): PushSubscription[];
22
+ getSubscriptionsForSession(sessionName: string): PushSubscription[];
23
+ removeSubscription(id: string): void;
24
+ }
25
+ /**
26
+ * NotificationSender interface
27
+ */
28
+ export interface NotificationSender {
29
+ /** Send notification to all relevant subscribers */
30
+ sendNotification(match: MatchResult): Promise<number>;
31
+ /** Send notification to a specific subscription */
32
+ sendToSubscription(subscription: PushSubscription, payload: NotificationPayload): Promise<boolean>;
33
+ }
34
+ /**
35
+ * Create a notification sender
36
+ */
37
+ export declare function createNotificationSender(vapidKeys: VapidKeys, contactEmail: string, store: SubscriptionStore): NotificationSender;
38
+ //# sourceMappingURL=sender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sender.d.ts","sourceRoot":"","sources":["../../../src/daemon/notification/sender.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI3E;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,IAAI,gBAAgB,EAAE,CAAC;IACvC,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAAC;IACpE,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,mDAAmD;IACnD,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpG;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,iBAAiB,GACvB,kBAAkB,CA0EpB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Push notification sender
3
+ */
4
+ import webpush from 'web-push';
5
+ import { createLogger } from '../../utils/logger.js';
6
+ const log = createLogger('notification');
7
+ /**
8
+ * Create a notification sender
9
+ */
10
+ export function createNotificationSender(vapidKeys, contactEmail, store) {
11
+ // Configure web-push with VAPID details
12
+ webpush.setVapidDetails(`mailto:${contactEmail}`, vapidKeys.publicKey, vapidKeys.privateKey);
13
+ return {
14
+ async sendNotification(match) {
15
+ const payload = {
16
+ title: match.pattern.message,
17
+ body: match.matchedText.slice(0, 200), // Truncate long text
18
+ sessionName: match.sessionName,
19
+ matchedText: match.matchedText,
20
+ timestamp: match.timestamp,
21
+ tag: `ttyd-mux-${match.sessionName}`
22
+ };
23
+ // Get subscriptions for this session (or all if no session filter)
24
+ const subscriptions = store.getSubscriptionsForSession(match.sessionName);
25
+ if (subscriptions.length === 0) {
26
+ log.debug(`No subscriptions for session: ${match.sessionName}`);
27
+ return 0;
28
+ }
29
+ let sent = 0;
30
+ const invalidSubscriptions = [];
31
+ for (const subscription of subscriptions) {
32
+ try {
33
+ await webpush.sendNotification({
34
+ endpoint: subscription.endpoint,
35
+ keys: subscription.keys
36
+ }, JSON.stringify(payload));
37
+ sent++;
38
+ log.debug(`Sent notification to subscription: ${subscription.id}`);
39
+ }
40
+ catch (error) {
41
+ const err = error;
42
+ if (err.statusCode === 410 || err.statusCode === 404) {
43
+ // Subscription no longer valid
44
+ invalidSubscriptions.push(subscription.id);
45
+ log.debug(`Subscription expired: ${subscription.id}`);
46
+ }
47
+ else {
48
+ log.error(`Failed to send notification: ${String(error)}`);
49
+ }
50
+ }
51
+ }
52
+ // Remove invalid subscriptions
53
+ for (const id of invalidSubscriptions) {
54
+ store.removeSubscription(id);
55
+ }
56
+ log.info(`Sent ${sent}/${subscriptions.length} notifications for pattern: ${match.pattern.message}`);
57
+ return sent;
58
+ },
59
+ async sendToSubscription(subscription, payload) {
60
+ try {
61
+ await webpush.sendNotification({
62
+ endpoint: subscription.endpoint,
63
+ keys: subscription.keys
64
+ }, JSON.stringify(payload));
65
+ return true;
66
+ }
67
+ catch (error) {
68
+ log.error(`Failed to send notification: ${String(error)}`);
69
+ return false;
70
+ }
71
+ }
72
+ };
73
+ }
74
+ //# sourceMappingURL=sender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sender.js","sourceRoot":"","sources":["../../../src/daemon/notification/sender.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Push subscription management
3
+ */
4
+ import type { PushSubscription } from './types.js';
5
+ /**
6
+ * Subscription store with persistence
7
+ */
8
+ export interface SubscriptionStoreConfig {
9
+ getSubscriptions(): PushSubscription[];
10
+ addSubscription(subscription: PushSubscription): void;
11
+ removeSubscription(id: string): void;
12
+ }
13
+ /**
14
+ * Subscription manager interface
15
+ */
16
+ export interface SubscriptionManager {
17
+ /** Add a new subscription */
18
+ subscribe(endpoint: string, keys: {
19
+ p256dh: string;
20
+ auth: string;
21
+ }, sessionName?: string): PushSubscription;
22
+ /** Remove a subscription by ID */
23
+ unsubscribe(id: string): boolean;
24
+ /** Get all subscriptions */
25
+ getAll(): PushSubscription[];
26
+ /** Get subscriptions for a specific session (includes global subscriptions) */
27
+ getForSession(sessionName: string): PushSubscription[];
28
+ /** Check if a subscription exists by endpoint */
29
+ hasSubscription(endpoint: string): boolean;
30
+ }
31
+ /**
32
+ * Create a subscription manager
33
+ */
34
+ export declare function createSubscriptionManager(store: SubscriptionStoreConfig): SubscriptionManager;
35
+ //# sourceMappingURL=subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../../src/daemon/notification/subscription.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,IAAI,gBAAgB,EAAE,CAAC;IACvC,eAAe,CAAC,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtD,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC5G,kCAAkC;IAClC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,4BAA4B;IAC5B,MAAM,IAAI,gBAAgB,EAAE,CAAC;IAC7B,+EAA+E;IAC/E,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAAC;IACvD,iDAAiD;IACjD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5C;AASD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,GAAG,mBAAmB,CAiD7F"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Push subscription management
3
+ */
4
+ import { randomBytes } from 'node:crypto';
5
+ /**
6
+ * Generate a subscription ID
7
+ */
8
+ function generateSubscriptionId() {
9
+ return randomBytes(8).toString('hex');
10
+ }
11
+ /**
12
+ * Create a subscription manager
13
+ */
14
+ export function createSubscriptionManager(store) {
15
+ return {
16
+ subscribe(endpoint, keys, sessionName) {
17
+ // Check if already subscribed
18
+ const existing = store.getSubscriptions().find((s) => s.endpoint === endpoint);
19
+ if (existing) {
20
+ return existing;
21
+ }
22
+ const subscription = {
23
+ id: generateSubscriptionId(),
24
+ endpoint,
25
+ keys,
26
+ sessionName,
27
+ createdAt: new Date().toISOString()
28
+ };
29
+ store.addSubscription(subscription);
30
+ return subscription;
31
+ },
32
+ unsubscribe(id) {
33
+ const subscriptions = store.getSubscriptions();
34
+ const exists = subscriptions.some((s) => s.id === id);
35
+ if (exists) {
36
+ store.removeSubscription(id);
37
+ return true;
38
+ }
39
+ return false;
40
+ },
41
+ getAll() {
42
+ return store.getSubscriptions();
43
+ },
44
+ getForSession(sessionName) {
45
+ return store.getSubscriptions().filter((s) => !s.sessionName || s.sessionName === sessionName);
46
+ },
47
+ hasSubscription(endpoint) {
48
+ return store.getSubscriptions().some((s) => s.endpoint === endpoint);
49
+ }
50
+ };
51
+ }
52
+ //# sourceMappingURL=subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../../src/daemon/notification/subscription.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA4B1C;;GAEG;AACH,SAAS,sBAAsB;IAC7B,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAA8B;IACtE,OAAO;QACL,SAAS,CACP,QAAgB,EAChB,IAAsC,EACtC,WAAoB;YAEpB,8BAA8B;YAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;YAC/E,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,MAAM,YAAY,GAAqB;gBACrC,EAAE,EAAE,sBAAsB,EAAE;gBAC5B,QAAQ;gBACR,IAAI;gBACJ,WAAW;gBACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC;YAEF,KAAK,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;YACpC,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,WAAW,CAAC,EAAU;YACpB,MAAM,aAAa,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACtD,IAAI,MAAM,EAAE,CAAC;gBACX,KAAK,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;gBAC7B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM;YACJ,OAAO,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAClC,CAAC;QAED,aAAa,CAAC,WAAmB;YAC/B,OAAO,KAAK,CAAC,gBAAgB,EAAE,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,WAAW,KAAK,WAAW,CACvD,CAAC;QACJ,CAAC;QAED,eAAe,CAAC,QAAgB;YAC9B,OAAO,KAAK,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QACvE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Notification system types
3
+ */
4
+ /**
5
+ * Pattern configuration for notification triggers
6
+ */
7
+ export interface PatternConfig {
8
+ /** Regular expression pattern to match */
9
+ regex: string;
10
+ /** Message to include in notification */
11
+ message: string;
12
+ /** Cooldown in seconds (optional, uses default if not set) */
13
+ cooldown?: number;
14
+ }
15
+ /**
16
+ * Push subscription from browser
17
+ */
18
+ export interface PushSubscription {
19
+ /** Unique subscription ID */
20
+ id: string;
21
+ /** Browser push endpoint URL */
22
+ endpoint: string;
23
+ /** Subscription keys */
24
+ keys: {
25
+ p256dh: string;
26
+ auth: string;
27
+ };
28
+ /** Session name this subscription is for (optional, null = all sessions) */
29
+ sessionName?: string;
30
+ /** Created timestamp */
31
+ createdAt: string;
32
+ }
33
+ /**
34
+ * VAPID keys for Web Push
35
+ */
36
+ export interface VapidKeys {
37
+ publicKey: string;
38
+ privateKey: string;
39
+ }
40
+ /**
41
+ * Notification configuration in config.yaml
42
+ */
43
+ export interface NotificationConfig {
44
+ /** Enable/disable notifications */
45
+ enabled?: boolean;
46
+ /** VAPID contact email */
47
+ contact_email?: string;
48
+ /** Enable bell notification (default: true) */
49
+ bell_notification?: boolean;
50
+ /** Bell notification cooldown in seconds (default: 10) */
51
+ bell_cooldown?: number;
52
+ /** Patterns to match */
53
+ patterns?: PatternConfig[];
54
+ /** Default cooldown in seconds */
55
+ default_cooldown?: number;
56
+ }
57
+ /**
58
+ * Match result when a pattern matches
59
+ */
60
+ export interface MatchResult {
61
+ /** The matched pattern configuration */
62
+ pattern: PatternConfig;
63
+ /** The text that matched */
64
+ matchedText: string;
65
+ /** Session name where match occurred */
66
+ sessionName: string;
67
+ /** Timestamp of match */
68
+ timestamp: string;
69
+ }
70
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/daemon/notification/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,mCAAmC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,wCAAwC;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Notification system types
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/daemon/notification/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * VAPID key management for Web Push
3
+ */
4
+ import type { VapidKeys } from './types.js';
5
+ /**
6
+ * Get VAPID keys file path
7
+ */
8
+ export declare function getVapidKeysPath(stateDir: string): string;
9
+ /**
10
+ * Generate new VAPID keys
11
+ */
12
+ export declare function generateVapidKeys(): VapidKeys;
13
+ /**
14
+ * Load VAPID keys from file, or generate if not exists
15
+ */
16
+ export declare function loadOrGenerateVapidKeys(stateDir: string): VapidKeys;
17
+ /**
18
+ * Save VAPID keys to file
19
+ */
20
+ export declare function saveVapidKeys(stateDir: string, keys: VapidKeys): void;
21
+ /**
22
+ * Get public VAPID key (safe to share with clients)
23
+ */
24
+ export declare function getPublicVapidKey(stateDir: string): string;
25
+ //# sourceMappingURL=vapid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vapid.d.ts","sourceRoot":"","sources":["../../../src/daemon/notification/vapid.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,SAAS,CAM7C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAgBnE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAGrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * VAPID key management for Web Push
3
+ */
4
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import webpush from 'web-push';
7
+ /**
8
+ * Get VAPID keys file path
9
+ */
10
+ export function getVapidKeysPath(stateDir) {
11
+ return join(stateDir, 'vapid-keys.json');
12
+ }
13
+ /**
14
+ * Generate new VAPID keys
15
+ */
16
+ export function generateVapidKeys() {
17
+ const keys = webpush.generateVAPIDKeys();
18
+ return {
19
+ publicKey: keys.publicKey,
20
+ privateKey: keys.privateKey
21
+ };
22
+ }
23
+ /**
24
+ * Load VAPID keys from file, or generate if not exists
25
+ */
26
+ export function loadOrGenerateVapidKeys(stateDir) {
27
+ const keysPath = getVapidKeysPath(stateDir);
28
+ if (existsSync(keysPath)) {
29
+ try {
30
+ const content = readFileSync(keysPath, 'utf-8');
31
+ return JSON.parse(content);
32
+ }
33
+ catch {
34
+ // Fall through to generate new keys
35
+ }
36
+ }
37
+ // Generate new keys
38
+ const keys = generateVapidKeys();
39
+ saveVapidKeys(stateDir, keys);
40
+ return keys;
41
+ }
42
+ /**
43
+ * Save VAPID keys to file
44
+ */
45
+ export function saveVapidKeys(stateDir, keys) {
46
+ const keysPath = getVapidKeysPath(stateDir);
47
+ writeFileSync(keysPath, JSON.stringify(keys, null, 2), { mode: 0o600 });
48
+ }
49
+ /**
50
+ * Get public VAPID key (safe to share with clients)
51
+ */
52
+ export function getPublicVapidKey(stateDir) {
53
+ const keys = loadOrGenerateVapidKeys(stateDir);
54
+ return keys.publicKey;
55
+ }
56
+ //# sourceMappingURL=vapid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vapid.js","sourceRoot":"","sources":["../../../src/daemon/notification/vapid.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,OAAO,MAAM,UAAU,CAAC;AAG/B;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IACzC,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAE5C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAChD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAe;IAC7D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC5C,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,IAAI,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC"}
@@ -15,8 +15,9 @@ export declare function generateManifest(basePath: string): object;
15
15
  *
16
16
  * Minimal implementation for PWA installability.
17
17
  * Uses network-first strategy since terminal requires online connectivity.
18
+ * Also handles push notifications.
18
19
  */
19
- export declare const serviceWorkerScript = "// ttyd-mux Service Worker\nself.addEventListener('install', (event) => {\n // Skip waiting to activate immediately\n self.skipWaiting();\n});\n\nself.addEventListener('activate', (event) => {\n // Claim all clients immediately\n event.waitUntil(clients.claim());\n});\n\nself.addEventListener('fetch', (event) => {\n // Network-first strategy - terminal requires online connectivity\n event.respondWith(fetch(event.request));\n});\n";
20
+ export declare const serviceWorkerScript = "// ttyd-mux Service Worker\nself.addEventListener('install', (event) => {\n // Skip waiting to activate immediately\n self.skipWaiting();\n});\n\nself.addEventListener('activate', (event) => {\n // Claim all clients immediately\n event.waitUntil(clients.claim());\n});\n\nself.addEventListener('fetch', (event) => {\n // Network-first strategy - terminal requires online connectivity\n event.respondWith(fetch(event.request));\n});\n\n// Push notification handler\nself.addEventListener('push', (event) => {\n if (!event.data) return;\n\n try {\n const data = event.data.json();\n const options = {\n body: data.body || 'Terminal notification',\n icon: '/ttyd-mux/icon-192.png',\n badge: '/ttyd-mux/icon-192.png',\n tag: data.tag || 'ttyd-mux-notification',\n requireInteraction: true,\n data: {\n sessionName: data.sessionName,\n timestamp: data.timestamp,\n url: data.sessionName ? '/ttyd-mux/' + data.sessionName : '/ttyd-mux/'\n }\n };\n\n event.waitUntil(\n self.registration.showNotification(data.title || 'ttyd-mux', options)\n );\n } catch (e) {\n console.error('[SW] Push notification error:', e);\n }\n});\n\n// Notification click handler\nself.addEventListener('notificationclick', (event) => {\n event.notification.close();\n\n const url = event.notification.data?.url || '/ttyd-mux/';\n\n event.waitUntil(\n clients.matchAll({ type: 'window', includeUncontrolled: true })\n .then((clientList) => {\n // Try to focus existing window\n for (const client of clientList) {\n if (client.url.includes('/ttyd-mux/') && 'focus' in client) {\n return client.focus();\n }\n }\n // Open new window if none exists\n if (clients.openWindow) {\n return clients.openWindow(url);\n }\n })\n );\n});\n";
20
21
  /**
21
22
  * SVG icon for ttyd-mux
22
23
  *
@@ -1 +1 @@
1
- {"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../../src/daemon/pwa.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6BzD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,4bAe/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,qSAGb,CAAC;AAER;;;;;;;GAOG;AAIH,eAAO,MAAM,aAAa,QAA6B,CAAC;AAGxD,eAAO,MAAM,aAAa,QAA6B,CAAC;AAsJxD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAGlD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD"}
1
+ {"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../../src/daemon/pwa.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6BzD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,g2DAiE/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,qSAGb,CAAC;AAER;;;;;;;GAOG;AAIH,eAAO,MAAM,aAAa,QAA6B,CAAC;AAGxD,eAAO,MAAM,aAAa,QAA6B,CAAC;AAsJxD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAGlD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -45,6 +45,7 @@ export function generateManifest(basePath) {
45
45
  *
46
46
  * Minimal implementation for PWA installability.
47
47
  * Uses network-first strategy since terminal requires online connectivity.
48
+ * Also handles push notifications.
48
49
  */
49
50
  export const serviceWorkerScript = `// ttyd-mux Service Worker
50
51
  self.addEventListener('install', (event) => {
@@ -61,6 +62,56 @@ self.addEventListener('fetch', (event) => {
61
62
  // Network-first strategy - terminal requires online connectivity
62
63
  event.respondWith(fetch(event.request));
63
64
  });
65
+
66
+ // Push notification handler
67
+ self.addEventListener('push', (event) => {
68
+ if (!event.data) return;
69
+
70
+ try {
71
+ const data = event.data.json();
72
+ const options = {
73
+ body: data.body || 'Terminal notification',
74
+ icon: '/ttyd-mux/icon-192.png',
75
+ badge: '/ttyd-mux/icon-192.png',
76
+ tag: data.tag || 'ttyd-mux-notification',
77
+ requireInteraction: true,
78
+ data: {
79
+ sessionName: data.sessionName,
80
+ timestamp: data.timestamp,
81
+ url: data.sessionName ? '/ttyd-mux/' + data.sessionName : '/ttyd-mux/'
82
+ }
83
+ };
84
+
85
+ event.waitUntil(
86
+ self.registration.showNotification(data.title || 'ttyd-mux', options)
87
+ );
88
+ } catch (e) {
89
+ console.error('[SW] Push notification error:', e);
90
+ }
91
+ });
92
+
93
+ // Notification click handler
94
+ self.addEventListener('notificationclick', (event) => {
95
+ event.notification.close();
96
+
97
+ const url = event.notification.data?.url || '/ttyd-mux/';
98
+
99
+ event.waitUntil(
100
+ clients.matchAll({ type: 'window', includeUncontrolled: true })
101
+ .then((clientList) => {
102
+ // Try to focus existing window
103
+ for (const client of clientList) {
104
+ if (client.url.includes('/ttyd-mux/') && 'focus' in client) {
105
+ return client.focus();
106
+ }
107
+ }
108
+ // Open new window if none exists
109
+ if (clients.openWindow) {
110
+ return clients.openWindow(url);
111
+ }
112
+ })
113
+ );
114
+ });
64
115
  `;
65
116
  /**
66
117
  * SVG icon for ttyd-mux
@@ -1 +1 @@
1
- {"version":3,"file":"pwa.js","sourceRoot":"","sources":["../../src/daemon/pwa.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pwa.js","sourceRoot":"","sources":["../../src/daemon/pwa.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,15 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import type { Config, SessionState } from '../config/types.js';
3
+ /**
4
+ * Generate ETag from content using MD5 hash
5
+ * @param content - Content to hash
6
+ * @returns ETag string with quotes (e.g., '"abc123..."')
7
+ */
8
+ export declare function generateEtag(content: string): string;
9
+ /**
10
+ * Reset toolbar.js cache (for testing)
11
+ */
12
+ export declare function resetToolbarCache(): void;
3
13
  /**
4
14
  * Set security headers on response
5
15
  */
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/daemon/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAW9D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAK5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAYpF;AA+ED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAgE7F"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/daemon/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAKjE,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAiB9D;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAeD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAK5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAYpF;AAyHD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CA4G7F"}