@xmanrui/dsh-im 4.17.0 → 4.17.1
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.
- package/README.en.md +3 -1
- package/README.md +3 -1
- package/lib/client.js +29 -17
- package/lib/index.js +256 -256
- package/package.json +4 -3
- package/plugin-src/client/index.js +15 -14
- package/plugin-src/host/channels/dingtalk/rpc.mjs +2 -4
- package/plugin-src/host/channels/feishu/rpc.mjs +2 -4
- package/plugin-src/host/channels/office/rpc.mjs +2 -1
- package/plugin-src/host/channels/qq/rpc.mjs +2 -4
- package/plugin-src/host/channels/shared/rpc.mjs +2 -4
- package/plugin-src/host/channels/shared/startup.mjs +2 -4
- package/plugin-src/host/channels/slack/rpc.mjs +2 -4
- package/plugin-src/host/channels/telegram/rpc.mjs +2 -4
- package/plugin-src/host/channels/wecom/rpc.mjs +2 -4
- package/plugin-src/host/channels/wecom-app/rpc.mjs +2 -4
- package/plugin-src/host/channels/weixin/rpc.mjs +2 -4
- package/plugin-src/host/channels/whatsapp/rpc.mjs +2 -4
- package/plugin-src/host/delivery-rpc.mjs +2 -4
- package/plugin-src/host/inbound-ttl-rpc.mjs +2 -4
- package/plugin-src/host/index.mjs +1 -1
- package/plugin-src/host/update-rpc.mjs +2 -1
- package/plugin-src/management-rpc.mjs +77 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmanrui/dsh-im",
|
|
3
|
-
"version": "4.17.
|
|
3
|
+
"version": "4.17.1",
|
|
4
4
|
"description": "把十种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect ten IM channels and a public AI Office to a local DeepSeek Harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -97,12 +97,13 @@
|
|
|
97
97
|
"platform": "web"
|
|
98
98
|
},
|
|
99
99
|
"compatibility": {
|
|
100
|
-
"dsh": "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.1",
|
|
100
|
+
"dsh": "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1",
|
|
101
101
|
"dshReleases": {
|
|
102
102
|
"0.1.2-alpha.4": "compatible",
|
|
103
103
|
"0.1.2-alpha.5": "compatible",
|
|
104
104
|
"0.1.2-rc.1": "compatible",
|
|
105
|
-
"0.1.3-alpha.1": "compatible"
|
|
105
|
+
"0.1.3-alpha.1": "compatible",
|
|
106
|
+
"0.1.5-alpha.1": "compatible"
|
|
106
107
|
},
|
|
107
108
|
"profiles": [
|
|
108
109
|
"web"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { callManagementRpc } from '../management-rpc.mjs';
|
|
1
2
|
import * as React from 'react';
|
|
2
3
|
import manifest from '../../package.json' with { type: 'json' };
|
|
3
4
|
|
|
@@ -403,33 +404,33 @@ export function apply(ctx) {
|
|
|
403
404
|
}, 'im-settings: install combined channel styles');
|
|
404
405
|
|
|
405
406
|
const feishuRpcCall = (endpoint, payload, signal) =>
|
|
406
|
-
ctx.connection
|
|
407
|
+
callManagementRpc(ctx.connection, FEISHU_RPC_CHANNEL, endpoint, payload, signal);
|
|
407
408
|
const weixinRpcCall = (endpoint, payload, signal) =>
|
|
408
|
-
ctx.connection
|
|
409
|
+
callManagementRpc(ctx.connection, WEIXIN_RPC_CHANNEL, endpoint, payload, signal);
|
|
409
410
|
const dingtalkRpcCall = (endpoint, payload, signal) =>
|
|
410
|
-
ctx.connection
|
|
411
|
+
callManagementRpc(ctx.connection, DINGTALK_RPC_CHANNEL, endpoint, payload, signal);
|
|
411
412
|
const qqRpcCall = (endpoint, payload, signal) =>
|
|
412
|
-
ctx.connection
|
|
413
|
+
callManagementRpc(ctx.connection, QQ_RPC_CHANNEL, endpoint, payload, signal);
|
|
413
414
|
const wecomRpcCall = (endpoint, payload, signal) =>
|
|
414
|
-
ctx.connection
|
|
415
|
+
callManagementRpc(ctx.connection, WECOM_RPC_CHANNEL, endpoint, payload, signal);
|
|
415
416
|
const wecomAppRpcCall = (endpoint, payload, signal) =>
|
|
416
|
-
ctx.connection
|
|
417
|
+
callManagementRpc(ctx.connection, WECOM_APP_RPC_CHANNEL, endpoint, payload, signal);
|
|
417
418
|
const telegramRpcCall = (endpoint, payload, signal) =>
|
|
418
|
-
ctx.connection
|
|
419
|
+
callManagementRpc(ctx.connection, TELEGRAM_RPC_CHANNEL, endpoint, payload, signal);
|
|
419
420
|
const discordRpcCall = (endpoint, payload, signal) =>
|
|
420
|
-
ctx.connection
|
|
421
|
+
callManagementRpc(ctx.connection, DISCORD_RPC_CHANNEL, endpoint, payload, signal);
|
|
421
422
|
const whatsappRpcCall = (endpoint, payload, signal) =>
|
|
422
|
-
ctx.connection
|
|
423
|
+
callManagementRpc(ctx.connection, WHATSAPP_RPC_CHANNEL, endpoint, payload, signal);
|
|
423
424
|
const slackRpcCall = (endpoint, payload, signal) =>
|
|
424
|
-
ctx.connection
|
|
425
|
+
callManagementRpc(ctx.connection, SLACK_RPC_CHANNEL, endpoint, payload, signal);
|
|
425
426
|
const officeRpcCall = (endpoint, payload, signal) =>
|
|
426
|
-
ctx.connection
|
|
427
|
+
callManagementRpc(ctx.connection, OFFICE_RPC_CHANNEL, endpoint, payload, signal);
|
|
427
428
|
const updateRpcCall = (endpoint, payload, signal) =>
|
|
428
|
-
ctx.connection
|
|
429
|
+
callManagementRpc(ctx.connection, UPDATE_RPC_CHANNEL, endpoint, payload, signal);
|
|
429
430
|
const deliveryRpcCall = (endpoint, payload, signal) =>
|
|
430
|
-
ctx.connection
|
|
431
|
+
callManagementRpc(ctx.connection, DELIVERY_RPC_CHANNEL, endpoint, payload, signal);
|
|
431
432
|
const globalSettingsRpcCall = (endpoint, payload, signal) =>
|
|
432
|
-
ctx.connection
|
|
433
|
+
callManagementRpc(ctx.connection, GLOBAL_SETTINGS_RPC_CHANNEL, endpoint, payload, signal);
|
|
433
434
|
const workspaceDirectoryPicker = Object.freeze({
|
|
434
435
|
listDirectory: (path, signal) =>
|
|
435
436
|
callWorkspaceDirectoryApi(ctx, 'listDirectory', path, signal),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
@@ -336,10 +337,7 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
336
337
|
}
|
|
337
338
|
|
|
338
339
|
export function installDingtalkRpc(ctx, controller, options, authority) {
|
|
339
|
-
|
|
340
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
341
|
-
}
|
|
342
|
-
return ctx.connection.rpc.handle(
|
|
340
|
+
return registerManagementRpc(ctx,
|
|
343
341
|
DINGTALK_RPC_CHANNEL,
|
|
344
342
|
createDingtalkRpcHandler(controller, options),
|
|
345
343
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import {
|
|
3
4
|
normalizeAgentPresetCatalog,
|
|
@@ -795,10 +796,7 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
795
796
|
|
|
796
797
|
/** Register the `/feishu` logical channel with its configured browser authority. */
|
|
797
798
|
export function installFeishuRpc(ctx, controller, options, authority) {
|
|
798
|
-
|
|
799
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
800
|
-
}
|
|
801
|
-
return ctx.connection.rpc.handle(
|
|
799
|
+
return registerManagementRpc(ctx,
|
|
802
800
|
FEISHU_RPC_CHANNEL,
|
|
803
801
|
createFeishuRpcHandler(controller, options),
|
|
804
802
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
2
3
|
import { OFFICE_RPC_CHANNEL, OFFICE_RPC_ENDPOINTS } from '../../../../src/channels/office/protocol.mjs';
|
|
3
4
|
|
|
@@ -40,7 +41,7 @@ export function createOfficeRpcHandler(controller) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export function installOfficeRpc(ctx, controller, authority) {
|
|
43
|
-
return ctx
|
|
44
|
+
return registerManagementRpc(ctx,
|
|
44
45
|
OFFICE_RPC_CHANNEL,
|
|
45
46
|
createOfficeRpcHandler(controller),
|
|
46
47
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import {
|
|
3
4
|
connectionTestTargetUnavailable,
|
|
@@ -227,10 +228,7 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
export function installQqRpc(ctx, controller, options, authority) {
|
|
230
|
-
|
|
231
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
232
|
-
}
|
|
233
|
-
return ctx.connection.rpc.handle(
|
|
231
|
+
return registerManagementRpc(ctx,
|
|
234
232
|
QQ_RPC_CHANNEL,
|
|
235
233
|
createQqRpcHandler(controller, options),
|
|
236
234
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from './context-enhancement-rpc.mjs';
|
|
2
3
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from './access-policy-rpc.mjs';
|
|
3
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
@@ -197,10 +198,7 @@ export function createTokenBotRpcHandler(controller, { channel }) {
|
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
export function installTokenBotRpc(ctx, controller, { channel, rpcChannel, authority }) {
|
|
200
|
-
|
|
201
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
202
|
-
}
|
|
203
|
-
return ctx.connection.rpc.handle(
|
|
201
|
+
return registerManagementRpc(ctx,
|
|
204
202
|
rpcChannel,
|
|
205
203
|
createTokenBotRpcHandler(controller, { channel }),
|
|
206
204
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
2
3
|
import { publicChannelInitializing, publicChannelStartupError } from './startup-error.mjs';
|
|
3
4
|
|
|
@@ -5,12 +6,9 @@ import { publicChannelInitializing, publicChannelStartupError } from './startup-
|
|
|
5
6
|
export async function installProductionChannel(ctx, config, {
|
|
6
7
|
channel, rpcChannel, createProduction, createHandler,
|
|
7
8
|
}) {
|
|
8
|
-
if (!ctx?.connection?.rpc || typeof ctx.connection.rpc.handle !== 'function') {
|
|
9
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
10
|
-
}
|
|
11
9
|
let startupError = publicChannelInitializing(channel);
|
|
12
10
|
let handler = async () => ({ ok: false, error: startupError });
|
|
13
|
-
const disposeRpc = ctx
|
|
11
|
+
const disposeRpc = registerManagementRpc(ctx, rpcChannel, (endpoint, payload, signal) => {
|
|
14
12
|
if (signal?.aborted) {
|
|
15
13
|
return { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.', details: {} } };
|
|
16
14
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
2
3
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
3
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
@@ -200,10 +201,7 @@ export function createSlackRpcHandler(controller) {
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
export function installSlackRpc(ctx, controller, authority) {
|
|
203
|
-
|
|
204
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
205
|
-
}
|
|
206
|
-
return ctx.connection.rpc.handle(
|
|
204
|
+
return registerManagementRpc(ctx,
|
|
207
205
|
SLACK_RPC_CHANNEL,
|
|
208
206
|
createSlackRpcHandler(controller),
|
|
209
207
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import {
|
|
2
3
|
TOKEN_BOT_ENDPOINTS,
|
|
3
4
|
createTokenBotRpcHandler,
|
|
@@ -13,10 +14,7 @@ export function createTelegramRpcHandler(controller) {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function installTelegramRpc(ctx, controller, authority) {
|
|
16
|
-
|
|
17
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
18
|
-
}
|
|
19
|
-
return ctx.connection.rpc.handle(
|
|
17
|
+
return registerManagementRpc(ctx,
|
|
20
18
|
TELEGRAM_RPC_CHANNEL,
|
|
21
19
|
createTelegramRpcHandler(controller),
|
|
22
20
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
@@ -233,10 +234,7 @@ export function installWecomRpc(ctx, controller, options, authority) {
|
|
|
233
234
|
|
|
234
235
|
/** Mount the existing Connection transport before the production controller is ready. */
|
|
235
236
|
export function installWecomRpcHandler(ctx, handler, authority) {
|
|
236
|
-
|
|
237
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
238
|
-
}
|
|
239
|
-
return ctx.connection.rpc.handle(
|
|
237
|
+
return registerManagementRpc(ctx,
|
|
240
238
|
WECOM_RPC_CHANNEL,
|
|
241
239
|
handler,
|
|
242
240
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
2
3
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
3
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
@@ -214,10 +215,7 @@ export function createWecomAppRpcHandler(controller) {
|
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
export function installWecomAppRpc(ctx, controller, options, authority) {
|
|
217
|
-
|
|
218
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
219
|
-
}
|
|
220
|
-
return ctx.connection.rpc.handle(
|
|
218
|
+
return registerManagementRpc(ctx,
|
|
221
219
|
WECOM_APP_RPC_CHANNEL,
|
|
222
220
|
createWecomAppRpcHandler(controller, options),
|
|
223
221
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
@@ -260,10 +261,7 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
export function installWeixinRpc(ctx, controller, options, authority) {
|
|
263
|
-
|
|
264
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
265
|
-
}
|
|
266
|
-
return ctx.connection.rpc.handle(
|
|
264
|
+
return registerManagementRpc(ctx,
|
|
267
265
|
WEIXIN_RPC_CHANNEL,
|
|
268
266
|
createWeixinRpcHandler(controller, options),
|
|
269
267
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
|
|
3
4
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
@@ -218,10 +219,7 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
export function installWhatsappRpc(ctx, controller, options, authority) {
|
|
221
|
-
|
|
222
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
223
|
-
}
|
|
224
|
-
return ctx.connection.rpc.handle(
|
|
222
|
+
return registerManagementRpc(ctx,
|
|
225
223
|
WHATSAPP_RPC_CHANNEL,
|
|
226
224
|
createWhatsappRpcHandler(controller, options),
|
|
227
225
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../management-rpc.mjs';
|
|
1
2
|
import { resolveRpcAuthority } from './rpc-authority.mjs';
|
|
2
3
|
|
|
3
4
|
export const DELIVERY_RPC_CHANNEL = '/dsh-im-delivery';
|
|
@@ -159,10 +160,7 @@ export function createDeliveryRpcHandler(service) {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
export function installDeliveryRpc(ctx, service, { authority } = {}) {
|
|
162
|
-
|
|
163
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
164
|
-
}
|
|
165
|
-
return ctx.connection.rpc.handle(
|
|
163
|
+
return registerManagementRpc(ctx,
|
|
166
164
|
DELIVERY_RPC_CHANNEL,
|
|
167
165
|
createDeliveryRpcHandler(service),
|
|
168
166
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../management-rpc.mjs';
|
|
1
2
|
import { normalizeInboundTtlHours } from '../../src/channels/shared/inbound-ttl.mjs';
|
|
2
3
|
import { getInboundTtlRuntime } from './inbound-ttl-runtime.mjs';
|
|
3
4
|
|
|
@@ -69,13 +70,10 @@ export function createInboundTtlRpcHandler({ store, service, logger = null } = {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
export function installInboundTtlRpc(ctx, options = {}) {
|
|
72
|
-
if (!ctx?.connection?.rpc || typeof ctx.connection.rpc.handle !== 'function') {
|
|
73
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
74
|
-
}
|
|
75
73
|
const runtime = options.runtime ?? getInboundTtlRuntime(ctx, options.config);
|
|
76
74
|
const logger = typeof ctx?.logger === 'function'
|
|
77
75
|
? ctx.logger('dsh-im:inbound-ttl') : (ctx?.logger ?? null);
|
|
78
|
-
return ctx
|
|
76
|
+
return registerManagementRpc(ctx,
|
|
79
77
|
INBOUND_TTL_RPC_CHANNEL,
|
|
80
78
|
createInboundTtlRpcHandler({ ...runtime, logger }),
|
|
81
79
|
{ authority: 'loopback' },
|
|
@@ -130,7 +130,7 @@ export function createImHostPlugin(internals = {}) {
|
|
|
130
130
|
const logger = typeof ctx?.logger === 'function'
|
|
131
131
|
? ctx.logger(name)
|
|
132
132
|
: (ctx?.logger ?? console);
|
|
133
|
-
if (ctx?.connection?.
|
|
133
|
+
if (ctx?.connection?.fetch) {
|
|
134
134
|
try {
|
|
135
135
|
startUpdate(ctx);
|
|
136
136
|
} catch (error) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../management-rpc.mjs';
|
|
1
2
|
import { createUpdateRuntime } from './update-runtime.mjs';
|
|
2
3
|
import { createUpdateService } from './update-service.mjs';
|
|
3
4
|
|
|
@@ -42,7 +43,7 @@ export function createUpdateRpcHandler(service) {
|
|
|
42
43
|
export function installUpdateRpc(ctx, options = {}) {
|
|
43
44
|
const runtime = options.runtime ?? createUpdateRuntime({ ctx, moduleUrl: import.meta.url });
|
|
44
45
|
const service = options.service ?? createUpdateService({ runtime });
|
|
45
|
-
const dispose = ctx
|
|
46
|
+
const dispose = registerManagementRpc(ctx, UPDATE_RPC_CHANNEL, createUpdateRpcHandler(service), {
|
|
46
47
|
authority: 'loopback',
|
|
47
48
|
});
|
|
48
49
|
ctx.effect(() => () => service.close(), 'dsh-im: close update installer');
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { resolveRpcAuthority } from './host/rpc-authority.mjs';
|
|
2
|
+
|
|
3
|
+
function rpcEndpoint(channel) {
|
|
4
|
+
if (!/^\/[A-Za-z0-9._~-]+$/.test(channel)) throw new TypeError('Invalid IM RPC channel');
|
|
5
|
+
return `dsh-im${channel}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isLoopbackAuthority(authority) {
|
|
13
|
+
if (!authority) return false;
|
|
14
|
+
try {
|
|
15
|
+
const url = new URL(`http://${authority}`);
|
|
16
|
+
if (url.username || url.password || url.pathname !== '/' || url.search || url.hash) return false;
|
|
17
|
+
const hostname = url.hostname.replace(/\.$/, '');
|
|
18
|
+
return hostname === 'localhost' || hostname === '[::1]'
|
|
19
|
+
|| /^127\.\d+\.\d+\.\d+$/.test(hostname);
|
|
20
|
+
} catch { return false; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isLoopbackRequest(request) {
|
|
24
|
+
if (!isLoopbackAuthority(request.headers.get('host'))) return false;
|
|
25
|
+
const origin = request.headers.get('origin');
|
|
26
|
+
if (origin === null) return true;
|
|
27
|
+
try {
|
|
28
|
+
const url = new URL(origin);
|
|
29
|
+
return ['http:', 'https:'].includes(url.protocol) && isLoopbackAuthority(url.host);
|
|
30
|
+
} catch { return false; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function reply(rpcId, result) {
|
|
34
|
+
// Older endpoint handlers omit details; Connection's client requires the field.
|
|
35
|
+
const value = result.ok === false
|
|
36
|
+
? { ...result, error: { ...result.error, details: result.error.details ?? {} } } : result;
|
|
37
|
+
return Response.json({ type: 'server-response', rpcId, result: value });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Register an IM channel through the public /api carrier shared by supported DSH releases. */
|
|
41
|
+
export function registerManagementRpc(ctx, channel, handler, { authority } = {}) {
|
|
42
|
+
const policy = resolveRpcAuthority(authority);
|
|
43
|
+
const endpoint = rpcEndpoint(channel);
|
|
44
|
+
if (typeof ctx?.connection?.fetch?.register !== 'function') {
|
|
45
|
+
throw new TypeError('DSH Host Connection Fetch registry is required');
|
|
46
|
+
}
|
|
47
|
+
return ctx.connection.fetch.register({
|
|
48
|
+
path: `/api/${endpoint}`,
|
|
49
|
+
methods: ['POST'],
|
|
50
|
+
requestBody: 'buffered',
|
|
51
|
+
async fetch(request) {
|
|
52
|
+
// DSH applies browser authentication and Host/Origin trust before this handler.
|
|
53
|
+
if (policy === 'loopback' && !isLoopbackRequest(request)) return new Response('forbidden', { status: 403 });
|
|
54
|
+
if (request.method !== 'POST') return new Response('method not allowed', { status: 405 });
|
|
55
|
+
if (request.headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase() !== 'application/json') {
|
|
56
|
+
return new Response('content type must be application/json', { status: 415 });
|
|
57
|
+
}
|
|
58
|
+
let message;
|
|
59
|
+
try { message = await request.json(); }
|
|
60
|
+
catch { return new Response('body is not JSON', { status: 400 }); }
|
|
61
|
+
const rpcId = typeof message?.rpcId === 'string' ? message.rpcId : 'invalid-request';
|
|
62
|
+
const call = message?.payload;
|
|
63
|
+
if (!isRecord(message) || message.type !== 'client-request' || typeof message.rpcId !== 'string'
|
|
64
|
+
|| message.method !== endpoint || !isRecord(call) || typeof call.method !== 'string'
|
|
65
|
+
|| !Object.hasOwn(call, 'payload')) {
|
|
66
|
+
return reply(rpcId, { ok: false, error: { code: 'gateway/bad-request', message: 'Invalid IM management request.' } });
|
|
67
|
+
}
|
|
68
|
+
try { return reply(rpcId, await handler(call.method, call.payload, request.signal)); }
|
|
69
|
+
catch { return new Response('IM management handler failed', { status: 500 }); }
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Keep channel UI callers unchanged while using DSH's native correlation and response validation. */
|
|
75
|
+
export function callManagementRpc(connection, channel, method, payload, signal) {
|
|
76
|
+
return connection.rpc.call('/api', rpcEndpoint(channel), { method, payload }, signal);
|
|
77
|
+
}
|