dsh-plugin-weapp-gateway 0.8.10 → 0.8.11
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.md +6 -0
- package/lib/dsh-host-adapter.mjs +6 -0
- package/lib/index.mjs +91 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
# dsh-plugin-weapp-gateway
|
|
6
6
|
|
|
7
|
+
### v0.8.11 · 小程序 ClawBot 私聊接线控制
|
|
8
|
+
|
|
9
|
+
- 在已配对的小程序控制通道中增加受限的 ClawBot 控制 RPC,复用 `dsh-wechat-remote` 自身的访问授权、状态冲突检查和本地凭据存储。
|
|
10
|
+
- 支持读取状态、绑定流程、选择普通 DSH 会话、启停与移除本地绑定;微信凭据不传回小程序。
|
|
11
|
+
- 新增 `wechat-clawbot-control-v1` 能力标记,客户端可对旧版网关明确降级。
|
|
12
|
+
|
|
7
13
|
### v0.8.9 · 插件在线更新
|
|
8
14
|
|
|
9
15
|
- 打开「设置 → 移动设备」时自动检查 npm 最新版本,并可在面板中一键安装更新。
|
package/lib/dsh-host-adapter.mjs
CHANGED
|
@@ -325,6 +325,12 @@ export function createDshHostAdapter(typertGateway) {
|
|
|
325
325
|
permissionPresets: {
|
|
326
326
|
catalog: (_payload = {}, signal) => invoke('permissionPresets', 'catalog', {}, signal),
|
|
327
327
|
},
|
|
328
|
+
// This endpoint intentionally forwards only the ClawBot control DTO. It
|
|
329
|
+
// stays behind the paired gateway and the ClawBot service's own access
|
|
330
|
+
// checks; it never exposes the channel credentials to the client.
|
|
331
|
+
clawbot: {
|
|
332
|
+
control: (request, signal) => invoke('wechatClawbot', 'control', request, signal),
|
|
333
|
+
},
|
|
328
334
|
schedule: {
|
|
329
335
|
async catalog(_payload = {}, signal) {
|
|
330
336
|
return { items: requireArray(await invoke('schedule', 'catalog', {}, signal), 'schedule/catalog') }
|
package/lib/index.mjs
CHANGED
|
@@ -1369,8 +1369,93 @@ function parseAgentPreset(msg, requestType, required = false) {
|
|
|
1369
1369
|
return { value: msg.agentPreset.trim() }
|
|
1370
1370
|
}
|
|
1371
1371
|
|
|
1372
|
+
const CLAWBOT_ACTIONS = new Set([
|
|
1373
|
+
'status', 'sessions', 'route', 'enabled', 'login.start', 'login.poll', 'login.cancel', 'disconnect',
|
|
1374
|
+
])
|
|
1375
|
+
|
|
1376
|
+
function validClawbotId(value) {
|
|
1377
|
+
return typeof value === 'string' && value.trim().length > 0 && value.length <= 256
|
|
1378
|
+
&& !/[\u0000-\u001f]/.test(value)
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
async function handleClawbotControl(api, msg) {
|
|
1382
|
+
const type = 'clawbot-control'
|
|
1383
|
+
const requestId = typeof msg.requestId === 'string' && msg.requestId.trim().length > 0
|
|
1384
|
+
&& msg.requestId.length <= 128 ? msg.requestId.trim() : null
|
|
1385
|
+
const error = (code, message) => ({ kind: 'error', code, message, requestType: type, ...(requestId ? { requestId } : {}) })
|
|
1386
|
+
if (!requestId) return error('bad-request', 'ClawBot control requires a requestId')
|
|
1387
|
+
if (!CLAWBOT_ACTIONS.has(msg.action)) return error('bad-request', 'Unsupported ClawBot action')
|
|
1388
|
+
if (typeof api.clawbot?.control !== 'function') {
|
|
1389
|
+
return error('clawbot-unavailable', 'The gateway host adapter does not provide ClawBot control')
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
const request = { action: msg.action }
|
|
1393
|
+
if (msg.expectedRevision !== undefined) {
|
|
1394
|
+
if (!Number.isSafeInteger(msg.expectedRevision) || msg.expectedRevision < 0) {
|
|
1395
|
+
return error('bad-request', 'expectedRevision must be a non-negative integer')
|
|
1396
|
+
}
|
|
1397
|
+
request.expectedRevision = msg.expectedRevision
|
|
1398
|
+
}
|
|
1399
|
+
if (msg.sessionId !== undefined) {
|
|
1400
|
+
if (!validClawbotId(msg.sessionId)) return error('bad-request', 'sessionId is invalid')
|
|
1401
|
+
request.sessionId = msg.sessionId.trim()
|
|
1402
|
+
}
|
|
1403
|
+
if (msg.loginId !== undefined) {
|
|
1404
|
+
if (!validClawbotId(msg.loginId)) return error('bad-request', 'loginId is invalid')
|
|
1405
|
+
request.loginId = msg.loginId.trim()
|
|
1406
|
+
}
|
|
1407
|
+
if (msg.enabled !== undefined) {
|
|
1408
|
+
if (typeof msg.enabled !== 'boolean') return error('bad-request', 'enabled must be a boolean')
|
|
1409
|
+
request.enabled = msg.enabled
|
|
1410
|
+
}
|
|
1411
|
+
if (msg.verifyCode !== undefined) {
|
|
1412
|
+
if (typeof msg.verifyCode !== 'string' || msg.verifyCode.length > 128 || /[\u0000-\u001f]/.test(msg.verifyCode)) {
|
|
1413
|
+
return error('bad-request', 'verifyCode is invalid')
|
|
1414
|
+
}
|
|
1415
|
+
request.verifyCode = msg.verifyCode
|
|
1416
|
+
}
|
|
1417
|
+
if (msg.abandonPending !== undefined) {
|
|
1418
|
+
if (typeof msg.abandonPending !== 'boolean') return error('bad-request', 'abandonPending must be a boolean')
|
|
1419
|
+
request.abandonPending = msg.abandonPending
|
|
1420
|
+
}
|
|
1421
|
+
if (['route', 'enabled'].includes(request.action) && !Number.isSafeInteger(request.expectedRevision)) {
|
|
1422
|
+
return error('bad-request', 'This ClawBot action requires expectedRevision')
|
|
1423
|
+
}
|
|
1424
|
+
if (request.action === 'route' && !request.sessionId) return error('bad-request', 'route requires sessionId')
|
|
1425
|
+
if (request.action === 'enabled' && typeof request.enabled !== 'boolean') return error('bad-request', 'enabled requires a boolean')
|
|
1426
|
+
if (request.action === 'login.poll' && !request.loginId) return error('bad-request', 'login.poll requires loginId')
|
|
1427
|
+
if (request.action === 'login.cancel' && !request.loginId) return error('bad-request', 'login.cancel requires loginId')
|
|
1428
|
+
if (request.action === 'disconnect' && !Number.isSafeInteger(request.expectedRevision)) {
|
|
1429
|
+
return error('bad-request', 'disconnect requires expectedRevision')
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
try {
|
|
1433
|
+
const result = await api.clawbot.control(request)
|
|
1434
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
|
1435
|
+
return error('clawbot-unavailable', 'ClawBot returned an invalid response')
|
|
1436
|
+
}
|
|
1437
|
+
if (result.ok !== true) {
|
|
1438
|
+
const failure = result.error
|
|
1439
|
+
const code = typeof failure?.code === 'string' && /^[a-z0-9./_-]{1,80}$/i.test(failure.code)
|
|
1440
|
+
? failure.code : 'clawbot-operation-failed'
|
|
1441
|
+
const message = typeof failure?.message === 'string' && failure.message.length <= 500
|
|
1442
|
+
? failure.message : 'ClawBot operation failed; refresh status before retrying.'
|
|
1443
|
+
return error(code, message)
|
|
1444
|
+
}
|
|
1445
|
+
if (!result.value || typeof result.value !== 'object' || Array.isArray(result.value)) {
|
|
1446
|
+
return error('clawbot-unavailable', 'ClawBot returned an invalid response')
|
|
1447
|
+
}
|
|
1448
|
+
return { kind: type, requestId, value: result.value }
|
|
1449
|
+
} catch (cause) {
|
|
1450
|
+
// Do not expose arbitrary host/plugin exception details over the mobile API.
|
|
1451
|
+
log(`ClawBot control unavailable: ${cause?.code || 'unknown error'}`)
|
|
1452
|
+
return error('clawbot-unavailable', 'ClawBot is unavailable. Update dsh-wechat-remote and check its component status.')
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1372
1456
|
// Dispatch one mobile query frame; returns the wire frame to send back.
|
|
1373
1457
|
async function handleQuery(api, host, agentDefaultModel, msg, pluginContext) {
|
|
1458
|
+
if (msg.type === 'clawbot-control') return handleClawbotControl(api, msg)
|
|
1374
1459
|
if (WORKSPACE_CHANGES_TYPES.has(msg.type)) return handleWorkspaceChanges(api, msg)
|
|
1375
1460
|
if (PLUGIN_REQUEST_TYPES.has(msg.type)) return handlePluginQuery(api, msg, pluginContext)
|
|
1376
1461
|
if (msg.type === 'session-agent-preset' || msg.type === 'select-agent-preset') {
|
|
@@ -3127,6 +3212,7 @@ const plugin = {
|
|
|
3127
3212
|
msg.type === 'command-options' || msg.type === 'command-select' || msg.type === 'select-model' ||
|
|
3128
3213
|
msg.type === 'permission-options' || msg.type === 'permission' || msg.type === 'context-usage' ||
|
|
3129
3214
|
msg.type === 'agent-presets' || msg.type === 'session-agent-preset' || msg.type === 'select-agent-preset' ||
|
|
3215
|
+
msg.type === 'clawbot-control' ||
|
|
3130
3216
|
msg.type === 'defaults' || msg.type === 'set-default' ||
|
|
3131
3217
|
msg.type === 'session-stats' || msg.type === 'default-model' ||
|
|
3132
3218
|
msg.type === 'save-default-model' || msg.type === 'fork' || msg.type === 'session-cancel' || msg.type === 'queue-update' ||
|
|
@@ -3136,10 +3222,12 @@ const plugin = {
|
|
|
3136
3222
|
msg.type === 'schedule-catalog' || msg.type === 'schedule-list' || msg.type === 'schedule-history' ||
|
|
3137
3223
|
msg.type === 'schedule-update' || msg.type === 'schedule-delete' ||
|
|
3138
3224
|
WORKSPACE_CHANGES_TYPES.has(msg.type) || PLUGIN_REQUEST_TYPES.has(msg.type)) {
|
|
3139
|
-
if ((WORKSPACE_CHANGES_TYPES.has(msg.type) || PLUGIN_REQUEST_TYPES.has(msg.type)) && !ws.deviceId) {
|
|
3225
|
+
if ((WORKSPACE_CHANGES_TYPES.has(msg.type) || PLUGIN_REQUEST_TYPES.has(msg.type) || msg.type === 'clawbot-control') && !ws.deviceId) {
|
|
3140
3226
|
sendFrame({ kind: 'error', code: 'authentication-required', requestType: msg.type,
|
|
3141
3227
|
...(typeof msg.requestId === 'string' ? { requestId: msg.requestId } : {}),
|
|
3142
|
-
message:
|
|
3228
|
+
message: msg.type === 'clawbot-control'
|
|
3229
|
+
? 'ClawBot control requires a paired device'
|
|
3230
|
+
: 'Plugin management requires a paired device' })
|
|
3143
3231
|
return
|
|
3144
3232
|
}
|
|
3145
3233
|
if (msg.type === 'plugin-install' && pluginInstallOwners.has(msg.requestId)) {
|
|
@@ -3193,6 +3281,7 @@ const plugin = {
|
|
|
3193
3281
|
'images',
|
|
3194
3282
|
'session-create',
|
|
3195
3283
|
'session-agent-preset',
|
|
3284
|
+
'wechat-clawbot-control-v1',
|
|
3196
3285
|
'commands',
|
|
3197
3286
|
'tasks',
|
|
3198
3287
|
'goals',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-weapp-gateway",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.11",
|
|
4
4
|
"description": "Authenticated mobile gateway for dsh 0.1.7-rc.2 with Session V4 history, live permissions, inbox-based queue sync, plugin management (DSH 0.2.0 hosts), LAN and Cloudflare Tunnel access with public endpoint health checks, device pairing, per-session Agent preset selection, relay access for the WeChat mini-program, and the official speech-input bridge (speech-input-v1)",
|
|
5
5
|
"main": "lib/index.mjs",
|
|
6
6
|
"files": [
|