@waiaas/daemon 2.4.0-rc.2 → 2.4.0-rc.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 (87) hide show
  1. package/README.md +1 -0
  2. package/dist/api/routes/admin.d.ts.map +1 -1
  3. package/dist/api/routes/admin.js +23 -32
  4. package/dist/api/routes/admin.js.map +1 -1
  5. package/dist/api/routes/openapi-schemas.d.ts +57 -0
  6. package/dist/api/routes/openapi-schemas.d.ts.map +1 -1
  7. package/dist/api/routes/openapi-schemas.js +14 -1
  8. package/dist/api/routes/openapi-schemas.js.map +1 -1
  9. package/dist/api/routes/transactions.d.ts +2 -0
  10. package/dist/api/routes/transactions.d.ts.map +1 -1
  11. package/dist/api/routes/transactions.js +1 -0
  12. package/dist/api/routes/transactions.js.map +1 -1
  13. package/dist/api/routes/wallets.d.ts.map +1 -1
  14. package/dist/api/routes/wallets.js +9 -0
  15. package/dist/api/routes/wallets.js.map +1 -1
  16. package/dist/api/server.d.ts +2 -0
  17. package/dist/api/server.d.ts.map +1 -1
  18. package/dist/api/server.js +1 -0
  19. package/dist/api/server.js.map +1 -1
  20. package/dist/infrastructure/database/compatibility.js +2 -2
  21. package/dist/infrastructure/database/compatibility.js.map +1 -1
  22. package/dist/infrastructure/database/migrate.d.ts +1 -1
  23. package/dist/infrastructure/database/migrate.d.ts.map +1 -1
  24. package/dist/infrastructure/database/migrate.js +17 -3
  25. package/dist/infrastructure/database/migrate.js.map +1 -1
  26. package/dist/infrastructure/database/schema.d.ts +20 -0
  27. package/dist/infrastructure/database/schema.d.ts.map +1 -1
  28. package/dist/infrastructure/database/schema.js +2 -0
  29. package/dist/infrastructure/database/schema.js.map +1 -1
  30. package/dist/infrastructure/settings/setting-keys.d.ts +2 -2
  31. package/dist/infrastructure/settings/setting-keys.d.ts.map +1 -1
  32. package/dist/infrastructure/settings/setting-keys.js +10 -1
  33. package/dist/infrastructure/settings/setting-keys.js.map +1 -1
  34. package/dist/infrastructure/telegram/telegram-auth.d.ts +1 -1
  35. package/dist/infrastructure/telegram/telegram-auth.d.ts.map +1 -1
  36. package/dist/infrastructure/telegram/telegram-auth.js +1 -1
  37. package/dist/infrastructure/telegram/telegram-auth.js.map +1 -1
  38. package/dist/infrastructure/telegram/telegram-bot-service.d.ts +9 -0
  39. package/dist/infrastructure/telegram/telegram-bot-service.d.ts.map +1 -1
  40. package/dist/infrastructure/telegram/telegram-bot-service.js +46 -1
  41. package/dist/infrastructure/telegram/telegram-bot-service.js.map +1 -1
  42. package/dist/infrastructure/telegram/telegram-types.d.ts +1 -0
  43. package/dist/infrastructure/telegram/telegram-types.d.ts.map +1 -1
  44. package/dist/lifecycle/daemon.d.ts +2 -0
  45. package/dist/lifecycle/daemon.d.ts.map +1 -1
  46. package/dist/lifecycle/daemon.js +63 -0
  47. package/dist/lifecycle/daemon.js.map +1 -1
  48. package/dist/pipeline/stages.d.ts +2 -0
  49. package/dist/pipeline/stages.d.ts.map +1 -1
  50. package/dist/pipeline/stages.js +14 -0
  51. package/dist/pipeline/stages.js.map +1 -1
  52. package/dist/services/signing-sdk/approval-channel-router.d.ts +65 -0
  53. package/dist/services/signing-sdk/approval-channel-router.d.ts.map +1 -0
  54. package/dist/services/signing-sdk/approval-channel-router.js +147 -0
  55. package/dist/services/signing-sdk/approval-channel-router.js.map +1 -0
  56. package/dist/services/signing-sdk/channels/index.d.ts +10 -0
  57. package/dist/services/signing-sdk/channels/index.d.ts.map +1 -0
  58. package/dist/services/signing-sdk/channels/index.js +8 -0
  59. package/dist/services/signing-sdk/channels/index.js.map +1 -0
  60. package/dist/services/signing-sdk/channels/ntfy-signing-channel.d.ts +66 -0
  61. package/dist/services/signing-sdk/channels/ntfy-signing-channel.d.ts.map +1 -0
  62. package/dist/services/signing-sdk/channels/ntfy-signing-channel.js +257 -0
  63. package/dist/services/signing-sdk/channels/ntfy-signing-channel.js.map +1 -0
  64. package/dist/services/signing-sdk/channels/telegram-signing-channel.d.ts +56 -0
  65. package/dist/services/signing-sdk/channels/telegram-signing-channel.d.ts.map +1 -0
  66. package/dist/services/signing-sdk/channels/telegram-signing-channel.js +87 -0
  67. package/dist/services/signing-sdk/channels/telegram-signing-channel.js.map +1 -0
  68. package/dist/services/signing-sdk/index.d.ts +34 -0
  69. package/dist/services/signing-sdk/index.d.ts.map +1 -0
  70. package/dist/services/signing-sdk/index.js +25 -0
  71. package/dist/services/signing-sdk/index.js.map +1 -0
  72. package/dist/services/signing-sdk/sign-request-builder.d.ts +61 -0
  73. package/dist/services/signing-sdk/sign-request-builder.d.ts.map +1 -0
  74. package/dist/services/signing-sdk/sign-request-builder.js +157 -0
  75. package/dist/services/signing-sdk/sign-request-builder.js.map +1 -0
  76. package/dist/services/signing-sdk/sign-response-handler.d.ts +92 -0
  77. package/dist/services/signing-sdk/sign-response-handler.d.ts.map +1 -0
  78. package/dist/services/signing-sdk/sign-response-handler.js +303 -0
  79. package/dist/services/signing-sdk/sign-response-handler.js.map +1 -0
  80. package/dist/services/signing-sdk/wallet-link-registry.d.ts +44 -0
  81. package/dist/services/signing-sdk/wallet-link-registry.d.ts.map +1 -0
  82. package/dist/services/signing-sdk/wallet-link-registry.js +116 -0
  83. package/dist/services/signing-sdk/wallet-link-registry.js.map +1 -0
  84. package/package.json +4 -4
  85. package/public/admin/assets/index-D7vqMezf.js +1 -0
  86. package/public/admin/index.html +1 -1
  87. package/public/admin/assets/index-BEqsuxTi.js +0 -1
@@ -0,0 +1,147 @@
1
+ /**
2
+ * ApprovalChannelRouter -- routes PENDING_APPROVAL transactions to the correct
3
+ * signing channel based on wallet's owner_approval_method.
4
+ *
5
+ * Routing logic:
6
+ * 1. Read wallet's owner_approval_method from DB
7
+ * 2. If set: route to that specific channel
8
+ * - sdk_ntfy -> NtfySigningChannel.sendRequest()
9
+ * - sdk_telegram -> TelegramSigningChannel.sendRequest()
10
+ * - walletconnect / telegram_bot / rest -> return method (no channel call)
11
+ * 3. If SDK method but signing_sdk.enabled=false: fall through to global fallback
12
+ * 4. Global fallback priority (CHAN-06):
13
+ * SDK ntfy > SDK Telegram > WalletConnect > Telegram Bot > REST
14
+ *
15
+ * CHAN-07: When signing_sdk.enabled !== 'true', SDK channels are skipped entirely.
16
+ *
17
+ * @see internal/design/74-wallet-sdk-daemon-components.md
18
+ * @see .planning/phases/203-telegram-channel-routing-rest-admin/203-03-PLAN.md
19
+ */
20
+ // ---------------------------------------------------------------------------
21
+ // ApprovalChannelRouter
22
+ // ---------------------------------------------------------------------------
23
+ export class ApprovalChannelRouter {
24
+ sqlite;
25
+ settings;
26
+ ntfyChannel;
27
+ telegramChannel;
28
+ constructor(deps) {
29
+ this.sqlite = deps.sqlite;
30
+ this.settings = deps.settingsService;
31
+ this.ntfyChannel = deps.ntfyChannel;
32
+ this.telegramChannel = deps.telegramChannel;
33
+ }
34
+ // -------------------------------------------------------------------------
35
+ // route() -- determine and execute the appropriate signing channel
36
+ // -------------------------------------------------------------------------
37
+ /**
38
+ * Route a PENDING_APPROVAL transaction to the correct signing channel.
39
+ *
40
+ * @param walletId - The wallet ID to look up approval method for
41
+ * @param params - Transaction parameters for SDK channel sendRequest()
42
+ * @returns RouteResult with selected method and optional channel result
43
+ * @throws Error if wallet not found in DB
44
+ * @throws Error if SDK channel sendRequest() fails (propagated, no silent fallback)
45
+ */
46
+ async route(walletId, params) {
47
+ // 1. Read wallet's owner_approval_method from DB
48
+ const row = this.sqlite.prepare('SELECT owner_approval_method FROM wallets WHERE id = ?').get(walletId);
49
+ if (!row) {
50
+ throw new Error(`Wallet not found: ${walletId}`);
51
+ }
52
+ const explicitMethod = row.owner_approval_method;
53
+ // 2. If explicit method is set, try to use it
54
+ if (explicitMethod) {
55
+ const sdkEnabled = this.isSdkEnabled();
56
+ // Non-SDK methods: return immediately without channel call
57
+ if (explicitMethod === 'walletconnect') {
58
+ return { method: 'walletconnect', channelResult: null };
59
+ }
60
+ if (explicitMethod === 'telegram_bot') {
61
+ return { method: 'telegram_bot', channelResult: null };
62
+ }
63
+ if (explicitMethod === 'rest') {
64
+ return { method: 'rest', channelResult: null };
65
+ }
66
+ // SDK methods: only use if SDK is enabled, otherwise fall through to global fallback
67
+ if (explicitMethod === 'sdk_ntfy' && sdkEnabled && this.ntfyChannel) {
68
+ const result = await this.ntfyChannel.sendRequest(params);
69
+ return { method: 'sdk_ntfy', channelResult: { requestId: result.requestId } };
70
+ }
71
+ if (explicitMethod === 'sdk_telegram' && sdkEnabled && this.telegramChannel) {
72
+ const result = await this.telegramChannel.sendRequest(params);
73
+ return { method: 'sdk_telegram', channelResult: { requestId: result.requestId } };
74
+ }
75
+ // SDK method set but SDK disabled or channel not available -> fall through to global fallback
76
+ }
77
+ // 3. Global fallback priority (CHAN-06)
78
+ return this.globalFallback(params);
79
+ }
80
+ // -------------------------------------------------------------------------
81
+ // shutdown() -- clean up channels
82
+ // -------------------------------------------------------------------------
83
+ /**
84
+ * Shutdown all managed signing channels.
85
+ */
86
+ shutdown() {
87
+ this.ntfyChannel?.shutdown();
88
+ this.telegramChannel?.shutdown();
89
+ }
90
+ // -------------------------------------------------------------------------
91
+ // Private: global fallback logic (CHAN-06, CHAN-07)
92
+ // -------------------------------------------------------------------------
93
+ async globalFallback(params) {
94
+ const sdkEnabled = this.isSdkEnabled();
95
+ // Priority 1: SDK ntfy (if SDK enabled and channel available)
96
+ if (sdkEnabled && this.ntfyChannel) {
97
+ const result = await this.ntfyChannel.sendRequest(params);
98
+ return { method: 'sdk_ntfy', channelResult: { requestId: result.requestId } };
99
+ }
100
+ // Priority 2: SDK Telegram (if SDK enabled and channel available)
101
+ if (sdkEnabled && this.telegramChannel) {
102
+ const result = await this.telegramChannel.sendRequest(params);
103
+ return { method: 'sdk_telegram', channelResult: { requestId: result.requestId } };
104
+ }
105
+ // Priority 3: WalletConnect (if project_id configured)
106
+ if (this.isWalletConnectConfigured()) {
107
+ return { method: 'walletconnect', channelResult: null };
108
+ }
109
+ // Priority 4: Telegram Bot (if enabled and bot_token configured)
110
+ if (this.isTelegramBotConfigured()) {
111
+ return { method: 'telegram_bot', channelResult: null };
112
+ }
113
+ // Priority 5: REST (always available as final fallback)
114
+ return { method: 'rest', channelResult: null };
115
+ }
116
+ // -------------------------------------------------------------------------
117
+ // Private: setting checks
118
+ // -------------------------------------------------------------------------
119
+ isSdkEnabled() {
120
+ try {
121
+ return this.settings.get('signing_sdk.enabled') === 'true';
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ isWalletConnectConfigured() {
128
+ try {
129
+ const projectId = this.settings.get('walletconnect.project_id');
130
+ return !!projectId && projectId.trim().length > 0;
131
+ }
132
+ catch {
133
+ return false;
134
+ }
135
+ }
136
+ isTelegramBotConfigured() {
137
+ try {
138
+ const enabled = this.settings.get('telegram.enabled');
139
+ const botToken = this.settings.get('telegram.bot_token');
140
+ return enabled === 'true' && !!botToken && botToken.trim().length > 0;
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ }
147
+ //# sourceMappingURL=approval-channel-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-channel-router.js","sourceRoot":"","sources":["../../../src/services/signing-sdk/approval-channel-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Signing SDK channel exports.
3
+ *
4
+ * @see internal/design/73-signing-protocol-v1.md
5
+ */
6
+ export { NtfySigningChannel } from './ntfy-signing-channel.js';
7
+ export type { NtfySigningChannelOpts, SendRequestParams, SendRequestResult, } from './ntfy-signing-channel.js';
8
+ export { TelegramSigningChannel } from './telegram-signing-channel.js';
9
+ export type { TelegramSigningChannelOpts } from './telegram-signing-channel.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/signing-sdk/channels/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EACV,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,YAAY,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Signing SDK channel exports.
3
+ *
4
+ * @see internal/design/73-signing-protocol-v1.md
5
+ */
6
+ export { NtfySigningChannel } from './ntfy-signing-channel.js';
7
+ export { TelegramSigningChannel } from './telegram-signing-channel.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/signing-sdk/channels/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAO/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * NtfySigningChannel -- publishes SignRequests to ntfy and subscribes to responses via SSE.
3
+ *
4
+ * When a PENDING_APPROVAL transaction triggers a SignRequest:
5
+ * 1. Publishes the request to the wallet's ntfy request topic (JSON format per doc 73 Section 7.2)
6
+ * 2. Subscribes to the response topic via SSE
7
+ * 3. Parses incoming SignResponse messages (base64url in ntfy message field)
8
+ * 4. Delegates to SignResponseHandler for approval/rejection processing
9
+ *
10
+ * @see internal/design/73-signing-protocol-v1.md (Section 7.2, 7.3)
11
+ * @see internal/design/74-wallet-sdk-daemon-components.md
12
+ */
13
+ import type { SignRequestBuilder, BuildRequestParams } from '../sign-request-builder.js';
14
+ import type { SignResponseHandler } from '../sign-response-handler.js';
15
+ import type { SettingsService } from '../../../infrastructure/settings/settings-service.js';
16
+ export interface NtfySigningChannelOpts {
17
+ signRequestBuilder: SignRequestBuilder;
18
+ signResponseHandler: SignResponseHandler;
19
+ settingsService: SettingsService;
20
+ }
21
+ export interface SendRequestParams extends BuildRequestParams {
22
+ walletId: string;
23
+ }
24
+ export interface SendRequestResult {
25
+ requestId: string;
26
+ requestTopic: string;
27
+ responseTopic: string;
28
+ }
29
+ export declare class NtfySigningChannel {
30
+ private readonly signRequestBuilder;
31
+ private readonly signResponseHandler;
32
+ private readonly settings;
33
+ /** Active SSE subscriptions: requestId -> AbortController */
34
+ private readonly activeSubscriptions;
35
+ constructor(opts: NtfySigningChannelOpts);
36
+ /**
37
+ * Publish a SignRequest to ntfy request topic and subscribe to response topic.
38
+ *
39
+ * @param params - Transaction metadata + walletId for topic routing
40
+ * @returns requestId, requestTopic, responseTopic
41
+ */
42
+ sendRequest(params: SendRequestParams): Promise<SendRequestResult>;
43
+ /**
44
+ * Cancel a specific SSE subscription by requestId.
45
+ */
46
+ cancelSubscription(requestId: string): void;
47
+ /**
48
+ * Shutdown all active SSE subscriptions (daemon shutdown).
49
+ */
50
+ shutdown(): void;
51
+ private getNtfyServer;
52
+ private publishToNtfy;
53
+ private subscribeToResponseTopic;
54
+ private connectSse;
55
+ /**
56
+ * Parse an SSE stream into NtfyMessage objects.
57
+ *
58
+ * ntfy SSE format:
59
+ * event: message
60
+ * data: {"id":"...","time":...,"event":"message","topic":"...","message":"..."}
61
+ *
62
+ * We only care about `data:` lines containing JSON with a `message` field.
63
+ */
64
+ private parseSseStream;
65
+ }
66
+ //# sourceMappingURL=ntfy-signing-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntfy-signing-channel.d.ts","sourceRoot":"","sources":["../../../../src/services/signing-sdk/channels/ntfy-signing-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sDAAsD,CAAC;AAc5F,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,eAAe,EAAE,eAAe,CAAC;CAClC;AAED,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAcD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAE3C,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsC;gBAE9D,IAAI,EAAE,sBAAsB;IAUxC;;;;;OAKG;IACG,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAsCxE;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQ3C;;OAEG;IACH,QAAQ,IAAI,IAAI;IAWhB,OAAO,CAAC,aAAa;YAaP,aAAa;IAsC3B,OAAO,CAAC,wBAAwB;YAyBlB,UAAU;IAgFxB;;;;;;;;OAQG;YACY,cAAc;CAuC9B"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * NtfySigningChannel -- publishes SignRequests to ntfy and subscribes to responses via SSE.
3
+ *
4
+ * When a PENDING_APPROVAL transaction triggers a SignRequest:
5
+ * 1. Publishes the request to the wallet's ntfy request topic (JSON format per doc 73 Section 7.2)
6
+ * 2. Subscribes to the response topic via SSE
7
+ * 3. Parses incoming SignResponse messages (base64url in ntfy message field)
8
+ * 4. Delegates to SignResponseHandler for approval/rejection processing
9
+ *
10
+ * @see internal/design/73-signing-protocol-v1.md (Section 7.2, 7.3)
11
+ * @see internal/design/74-wallet-sdk-daemon-components.md
12
+ */
13
+ import { SignResponseSchema, } from '@waiaas/core';
14
+ // ---------------------------------------------------------------------------
15
+ // Constants
16
+ // ---------------------------------------------------------------------------
17
+ const MAX_RECONNECT_ATTEMPTS = 3;
18
+ const RECONNECT_DELAY_MS = 5_000;
19
+ const DEFAULT_NTFY_SERVER = 'https://ntfy.sh';
20
+ // ---------------------------------------------------------------------------
21
+ // NtfySigningChannel
22
+ // ---------------------------------------------------------------------------
23
+ export class NtfySigningChannel {
24
+ signRequestBuilder;
25
+ signResponseHandler;
26
+ settings;
27
+ /** Active SSE subscriptions: requestId -> AbortController */
28
+ activeSubscriptions = new Map();
29
+ constructor(opts) {
30
+ this.signRequestBuilder = opts.signRequestBuilder;
31
+ this.signResponseHandler = opts.signResponseHandler;
32
+ this.settings = opts.settingsService;
33
+ }
34
+ // -------------------------------------------------------------------------
35
+ // sendRequest -- publish to ntfy + subscribe for response
36
+ // -------------------------------------------------------------------------
37
+ /**
38
+ * Publish a SignRequest to ntfy request topic and subscribe to response topic.
39
+ *
40
+ * @param params - Transaction metadata + walletId for topic routing
41
+ * @returns requestId, requestTopic, responseTopic
42
+ */
43
+ async sendRequest(params) {
44
+ // 1. Build SignRequest via SignRequestBuilder
45
+ const { request, universalLinkUrl, requestTopic } = this.signRequestBuilder.buildRequest(params);
46
+ // 2. Register request with SignResponseHandler for later matching
47
+ this.signResponseHandler.registerRequest(request);
48
+ // 3. Resolve ntfy server URL
49
+ const ntfyServer = this.getNtfyServer();
50
+ // 4. Publish to ntfy request topic (doc 73 Section 7.2)
51
+ await this.publishToNtfy(ntfyServer, requestTopic, request.displayMessage, universalLinkUrl);
52
+ // 5. Determine response topic
53
+ let responseTopic;
54
+ if (request.responseChannel.type === 'ntfy') {
55
+ responseTopic = request.responseChannel.responseTopic;
56
+ }
57
+ else {
58
+ // Fallback: construct response topic from request
59
+ const prefix = this.settings.get('signing_sdk.ntfy_response_topic_prefix');
60
+ responseTopic = `${prefix}-${request.requestId}`;
61
+ }
62
+ // 6. Subscribe to response topic via SSE (background, non-blocking)
63
+ this.subscribeToResponseTopic(ntfyServer, responseTopic, request.requestId, request.expiresAt);
64
+ return {
65
+ requestId: request.requestId,
66
+ requestTopic,
67
+ responseTopic,
68
+ };
69
+ }
70
+ // -------------------------------------------------------------------------
71
+ // Subscription management
72
+ // -------------------------------------------------------------------------
73
+ /**
74
+ * Cancel a specific SSE subscription by requestId.
75
+ */
76
+ cancelSubscription(requestId) {
77
+ const controller = this.activeSubscriptions.get(requestId);
78
+ if (controller) {
79
+ controller.abort();
80
+ this.activeSubscriptions.delete(requestId);
81
+ }
82
+ }
83
+ /**
84
+ * Shutdown all active SSE subscriptions (daemon shutdown).
85
+ */
86
+ shutdown() {
87
+ for (const [requestId, controller] of this.activeSubscriptions) {
88
+ controller.abort();
89
+ this.activeSubscriptions.delete(requestId);
90
+ }
91
+ }
92
+ // -------------------------------------------------------------------------
93
+ // Private: ntfy server URL
94
+ // -------------------------------------------------------------------------
95
+ getNtfyServer() {
96
+ try {
97
+ const server = this.settings.get('notifications.ntfy_server');
98
+ return server || DEFAULT_NTFY_SERVER;
99
+ }
100
+ catch {
101
+ return DEFAULT_NTFY_SERVER;
102
+ }
103
+ }
104
+ // -------------------------------------------------------------------------
105
+ // Private: publish to ntfy (doc 73 Section 7.2)
106
+ // -------------------------------------------------------------------------
107
+ async publishToNtfy(ntfyServer, requestTopic, displayMessage, universalLinkUrl) {
108
+ const url = `${ntfyServer}/${requestTopic}`;
109
+ const body = JSON.stringify({
110
+ topic: requestTopic,
111
+ message: displayMessage,
112
+ title: 'WAIaaS Sign Request',
113
+ priority: 5,
114
+ tags: ['waiaas', 'sign'],
115
+ actions: [
116
+ {
117
+ action: 'view',
118
+ label: 'Sign in wallet',
119
+ url: universalLinkUrl,
120
+ },
121
+ ],
122
+ click: universalLinkUrl,
123
+ });
124
+ const res = await fetch(url, {
125
+ method: 'POST',
126
+ headers: { 'Content-Type': 'application/json' },
127
+ body,
128
+ });
129
+ if (!res.ok) {
130
+ throw new Error(`Failed to publish sign request to ntfy: HTTP ${res.status}`);
131
+ }
132
+ }
133
+ // -------------------------------------------------------------------------
134
+ // Private: subscribe to response topic via SSE
135
+ // -------------------------------------------------------------------------
136
+ subscribeToResponseTopic(ntfyServer, responseTopic, requestId, expiresAt) {
137
+ const abortController = new AbortController();
138
+ this.activeSubscriptions.set(requestId, abortController);
139
+ // Set timeout for request expiration
140
+ const expiresAtMs = new Date(expiresAt).getTime();
141
+ const timeoutMs = Math.max(0, expiresAtMs - Date.now());
142
+ const timer = setTimeout(() => {
143
+ this.cancelSubscription(requestId);
144
+ }, timeoutMs);
145
+ // Unref timer so it doesn't prevent process exit
146
+ if (typeof timer === 'object' && 'unref' in timer) {
147
+ timer.unref();
148
+ }
149
+ // Start SSE connection (non-blocking)
150
+ void this.connectSse(ntfyServer, responseTopic, requestId, abortController, timer);
151
+ }
152
+ async connectSse(ntfyServer, responseTopic, requestId, abortController, expirationTimer, reconnectAttempt = 0) {
153
+ if (abortController.signal.aborted) {
154
+ clearTimeout(expirationTimer);
155
+ return;
156
+ }
157
+ try {
158
+ const url = `${ntfyServer}/${responseTopic}/sse`;
159
+ const res = await fetch(url, {
160
+ signal: abortController.signal,
161
+ });
162
+ if (!res.ok || !res.body) {
163
+ throw new Error(`SSE connection failed: HTTP ${res.status}`);
164
+ }
165
+ const reader = res.body.getReader();
166
+ for await (const ntfyMsg of this.parseSseStream(reader, abortController.signal)) {
167
+ if (abortController.signal.aborted)
168
+ break;
169
+ if (!ntfyMsg.message)
170
+ continue;
171
+ // ntfy message field contains base64url-encoded SignResponse
172
+ try {
173
+ const json = Buffer.from(ntfyMsg.message, 'base64url').toString('utf-8');
174
+ const parsed = JSON.parse(json);
175
+ const signResponse = SignResponseSchema.parse(parsed);
176
+ // Only process responses for this requestId
177
+ if (signResponse.requestId !== requestId)
178
+ continue;
179
+ // Delegate to SignResponseHandler
180
+ await this.signResponseHandler.handle(signResponse);
181
+ // Response processed -- close subscription
182
+ this.cancelSubscription(requestId);
183
+ clearTimeout(expirationTimer);
184
+ return;
185
+ }
186
+ catch {
187
+ // Ignore malformed messages or handler errors
188
+ }
189
+ }
190
+ }
191
+ catch (_err) {
192
+ // Don't reconnect if explicitly aborted
193
+ if (abortController.signal.aborted) {
194
+ clearTimeout(expirationTimer);
195
+ return;
196
+ }
197
+ // Reconnect logic: max 3 attempts, 5s delay
198
+ if (reconnectAttempt < MAX_RECONNECT_ATTEMPTS) {
199
+ await new Promise((resolve) => setTimeout(resolve, RECONNECT_DELAY_MS));
200
+ return this.connectSse(ntfyServer, responseTopic, requestId, abortController, expirationTimer, reconnectAttempt + 1);
201
+ }
202
+ // Max reconnects exceeded -- clean up
203
+ this.activeSubscriptions.delete(requestId);
204
+ clearTimeout(expirationTimer);
205
+ }
206
+ }
207
+ // -------------------------------------------------------------------------
208
+ // Private: SSE stream parser
209
+ // -------------------------------------------------------------------------
210
+ /**
211
+ * Parse an SSE stream into NtfyMessage objects.
212
+ *
213
+ * ntfy SSE format:
214
+ * event: message
215
+ * data: {"id":"...","time":...,"event":"message","topic":"...","message":"..."}
216
+ *
217
+ * We only care about `data:` lines containing JSON with a `message` field.
218
+ */
219
+ async *parseSseStream(reader, signal) {
220
+ const decoder = new TextDecoder();
221
+ let buffer = '';
222
+ try {
223
+ while (!signal.aborted) {
224
+ const { done, value } = await reader.read();
225
+ if (done)
226
+ break;
227
+ buffer += decoder.decode(value, { stream: true });
228
+ const lines = buffer.split('\n');
229
+ // Keep the last incomplete line in buffer
230
+ buffer = lines.pop() ?? '';
231
+ for (const line of lines) {
232
+ if (!line.startsWith('data: '))
233
+ continue;
234
+ const dataStr = line.slice(6).trim();
235
+ if (!dataStr)
236
+ continue;
237
+ try {
238
+ const msg = JSON.parse(dataStr);
239
+ yield msg;
240
+ }
241
+ catch {
242
+ // Ignore malformed JSON
243
+ }
244
+ }
245
+ }
246
+ }
247
+ finally {
248
+ try {
249
+ reader.releaseLock();
250
+ }
251
+ catch {
252
+ // Reader may already be released
253
+ }
254
+ }
255
+ }
256
+ }
257
+ //# sourceMappingURL=ntfy-signing-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntfy-signing-channel.js","sourceRoot":"","sources":["../../../../src/services/signing-sdk/channels/ntfy-signing-channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * TelegramSigningChannel -- sends SignRequests via Telegram inline button message.
3
+ *
4
+ * When a PENDING_APPROVAL transaction triggers a SignRequest:
5
+ * 1. Builds the SignRequest via SignRequestBuilder
6
+ * 2. Registers the request with SignResponseHandler for later matching
7
+ * 3. Sends a Telegram message to the admin chat with an inline "Open in Wallet"
8
+ * button containing the universal link URL
9
+ *
10
+ * Unlike NtfySigningChannel, TelegramSigningChannel does NOT subscribe to a response
11
+ * topic. The response arrives via the /sign_response Telegram bot command, which
12
+ * delegates to SignResponseHandler. This is a one-way push channel.
13
+ *
14
+ * Implements ISigningChannel interface for consistent channel abstraction.
15
+ *
16
+ * @see internal/design/73-signing-protocol-v1.md (Section 7.2, 7.3)
17
+ * @see internal/design/74-wallet-sdk-daemon-components.md
18
+ */
19
+ import type { SignRequestBuilder, BuildRequestParams } from '../sign-request-builder.js';
20
+ import type { SignResponseHandler } from '../sign-response-handler.js';
21
+ import type { SettingsService } from '../../../infrastructure/settings/settings-service.js';
22
+ import type { TelegramApi } from '../../../infrastructure/telegram/telegram-api.js';
23
+ export interface TelegramSigningChannelOpts {
24
+ signRequestBuilder: SignRequestBuilder;
25
+ signResponseHandler: SignResponseHandler;
26
+ settingsService: SettingsService;
27
+ telegramApi: TelegramApi;
28
+ }
29
+ export interface SendRequestParams extends BuildRequestParams {
30
+ walletId: string;
31
+ }
32
+ export interface SendRequestResult {
33
+ requestId: string;
34
+ requestTopic: string;
35
+ responseTopic: string;
36
+ }
37
+ export declare class TelegramSigningChannel {
38
+ private readonly signRequestBuilder;
39
+ private readonly signResponseHandler;
40
+ private readonly settings;
41
+ private readonly telegramApi;
42
+ constructor(opts: TelegramSigningChannelOpts);
43
+ /**
44
+ * Send a SignRequest via Telegram message with an inline "Open in Wallet" button.
45
+ *
46
+ * @param params - Transaction metadata + walletId
47
+ * @returns requestId, requestTopic (empty for Telegram), responseTopic (empty)
48
+ * @throws Error if telegram chat_id is not configured
49
+ */
50
+ sendRequest(params: SendRequestParams): Promise<SendRequestResult>;
51
+ /**
52
+ * Shutdown the channel. No-op for Telegram (no SSE subscriptions to clean up).
53
+ */
54
+ shutdown(): void;
55
+ }
56
+ //# sourceMappingURL=telegram-signing-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram-signing-channel.d.ts","sourceRoot":"","sources":["../../../../src/services/signing-sdk/channels/telegram-signing-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAC;AAOpF,MAAM,WAAW,0BAA0B;IACzC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAMD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,IAAI,EAAE,0BAA0B;IAW5C;;;;;;OAMG;IACG,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4CxE;;OAEG;IACH,QAAQ,IAAI,IAAI;CAGjB"}