dsh-mobile-plugin 0.5.17 → 0.5.19
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/config/agent-presets/standard/agent.cordis.yml +9 -0
- package/host.js +26 -4
- package/lib/host-compat.cjs +51 -4
- package/lib/impl.js +6 -0
- package/lib/mobile/cli/acp.js +216 -0
- package/lib/mobile/cli/agents.js +217 -0
- package/lib/mobile/cli/blocks.js +228 -0
- package/lib/mobile/cli/config-prefs.js +155 -0
- package/lib/mobile/cli/icons.js +138 -0
- package/lib/mobile/cli/images.js +132 -0
- package/lib/mobile/cli/mobile.js +1552 -0
- package/lib/mobile/cli/model-prefs.js +77 -0
- package/lib/mobile/cli/model-visibility.js +92 -0
- package/lib/mobile/cli/registry.js +207 -0
- package/lib/mobile/cli/service.js +901 -0
- package/lib/mobile/cli/usage.js +133 -0
- package/lib/mobile/monitor.js +17 -6
- package/lib/mobile/narration.js +205 -0
- package/lib/mobile/phone/binding.js +247 -0
- package/lib/mobile/phone/index.js +63 -0
- package/lib/mobile/phone/routes.js +168 -0
- package/lib/mobile/phone/service.js +327 -0
- package/lib/mobile/phone/tools.js +565 -0
- package/lib/mobile/questions.js +9 -3
- package/lib/mobile/services.js +16 -0
- package/lib/mobile/session-send.js +1 -0
- package/lib/mobile/stats-ledger.cjs +8 -23
- package/lib/mobile/terminal.js +574 -0
- package/lib/routes/mobile-api.js +139 -3
- package/package.json +2 -1
package/lib/routes/mobile-api.js
CHANGED
|
@@ -48,13 +48,17 @@ const { createRelayControlHandlers } = require('../mobile/relay-control.js');
|
|
|
48
48
|
const uploads = require('../mobile/uploads.js');
|
|
49
49
|
const canvasStore = require('../mobile/canvas-store.js');
|
|
50
50
|
const inboxClean = require('../mobile/inbox-clean.js');
|
|
51
|
+
// 过程播报开关(读 / 写同一条路径,由 req.method 分派)。默认关,见那个文件头。
|
|
52
|
+
const narration = require('../mobile/narration.js');
|
|
51
53
|
const git = require('../mobile/git.js');
|
|
54
|
+
const terminal = require('../mobile/terminal.js');
|
|
52
55
|
const pairing = require('../mobile/pairing.js');
|
|
53
56
|
const { credentialsEntry } = require('../shared/credentials-entry.js');
|
|
54
57
|
const { handleCredentialUpgrade } = require('../mobile/credential-upgrade.js');
|
|
55
58
|
const extensions = require('../mobile/extensions.js');
|
|
56
59
|
const extensionDev = require('../mobile/extension-dev.js');
|
|
57
60
|
const sessionDelivery = require('../mobile/session-delivery.js');
|
|
61
|
+
const { createPhoneRoutes } = require('../mobile/phone/routes.js');
|
|
58
62
|
// 体检接口要自报身份:版本号只有一个来源,就是包自己的 package.json(与 relay/status.js 同)。
|
|
59
63
|
const pkg = require('../../package.json');
|
|
60
64
|
const desktopView = require('../desktop-view.cjs');
|
|
@@ -82,8 +86,79 @@ const API_VERSION = '1';
|
|
|
82
86
|
* 所以它走 route() 即包 guarded。
|
|
83
87
|
*/
|
|
84
88
|
function registerMobileRoutes(deps) {
|
|
85
|
-
const { registerRoute, registerUpgradeRoute, guarded, state, hostMetadata, getSidecarSnapshot, safeLanCandidates, safeIpv6Candidates, lanPortInUse, preview, core, pairingApproval, rotatePairing } = deps;
|
|
86
|
-
|
|
89
|
+
const { registerRoute, registerUpgradeRoute, guarded, state, hostMetadata, getSidecarSnapshot, safeLanCandidates, safeIpv6Candidates, lanPortInUse, preview, core, pairingApproval, rotatePairing, terminalService, cliService, phoneService } = deps;
|
|
90
|
+
// CLI 会话(`cli-*`)在这张表上分派一次(见 mobile/cli/mobile.js):它们是 ACP 子进程、
|
|
91
|
+
// 不是宿主的会话,但手机上必须像普通会话一样可用 —— 列表 / 历史 / 发送 / 停止 / 流 /
|
|
92
|
+
// 审批 / 模型全走宿主那套端点。分派发生在 `route()` 里面,下面每条注册都还是原样一行,
|
|
93
|
+
// handler 一个都没抄;`sessionId` 不在 `cli-` 前缀上的请求原样交给原 handler。
|
|
94
|
+
const cliMobile = cliService && cliService.mobile
|
|
95
|
+
const route = (path, handler) => registerRoute({ kind: 'exact', path, handler: guarded(withCliSession(path, handler)) })
|
|
96
|
+
/**
|
|
97
|
+
* 哪些路径要过 CLI 分派。
|
|
98
|
+
*
|
|
99
|
+
* 只收「请求体是 JSON / 只读」的那批:`route()` 里的分派要先读一次 POST 的请求体才能看见
|
|
100
|
+
* sessionId,二进制那条(`/session/file`、上传分片)一读就坏,所以它们不在名单里 ——
|
|
101
|
+
* CLI 会话也没有那些东西。
|
|
102
|
+
*/
|
|
103
|
+
const CLI_SESSION_PATHS = new Set([
|
|
104
|
+
'/api/mobile/v1/session',
|
|
105
|
+
'/api/mobile/v1/session/messages',
|
|
106
|
+
'/api/mobile/v1/session/send',
|
|
107
|
+
'/api/mobile/v1/session/attachment',
|
|
108
|
+
'/api/mobile/v1/session/head',
|
|
109
|
+
'/api/mobile/v1/session/stop',
|
|
110
|
+
'/api/mobile/v1/session/rename',
|
|
111
|
+
'/api/mobile/v1/session/fork',
|
|
112
|
+
'/api/mobile/v1/session/models',
|
|
113
|
+
'/api/mobile/v1/session/select-model',
|
|
114
|
+
'/api/mobile/v1/session/model-option',
|
|
115
|
+
'/api/mobile/v1/session/model-visibility',
|
|
116
|
+
'/api/mobile/v1/session/stats',
|
|
117
|
+
'/api/mobile/v1/session/read',
|
|
118
|
+
'/api/mobile/v1/session/delivery',
|
|
119
|
+
'/api/mobile/v1/session/search',
|
|
120
|
+
'/api/mobile/v1/session/goal',
|
|
121
|
+
'/api/mobile/v1/session/queue/update',
|
|
122
|
+
'/api/mobile/v1/commands',
|
|
123
|
+
'/api/mobile/v1/skills',
|
|
124
|
+
'/api/mobile/v1/subagents',
|
|
125
|
+
'/api/mobile/v1/file-references',
|
|
126
|
+
'/api/mobile/v1/session-references',
|
|
127
|
+
'/api/mobile/v1/extension/cards',
|
|
128
|
+
// 导出:CLI 会话没有宿主日志,这条回一份空 ZIP(见 cli/mobile.js 的 handleExport)。
|
|
129
|
+
'/api/mobile/v1/session-log',
|
|
130
|
+
])
|
|
131
|
+
/**
|
|
132
|
+
* 会话级开关里那四条「属于会话自己、不属于工作区」的写:归档 / 取消归档 / 置顶 /
|
|
133
|
+
* 取消置顶。CLI 会话在宿主那本账里没有条目,原样透过去只会换一张错误卡,所以 `cli-*`
|
|
134
|
+
* 由适配层自己记(内存账,跟 CLI 会话一起没)。
|
|
135
|
+
*/
|
|
136
|
+
const CLI_SESSION_FLAGS = {
|
|
137
|
+
'/api/mobile/v1/workspace/archive-session': 'archive',
|
|
138
|
+
'/api/mobile/v1/workspace/unarchive-session': 'unarchive',
|
|
139
|
+
'/api/mobile/v1/workspace/pin-session': 'pin',
|
|
140
|
+
'/api/mobile/v1/workspace/unpin-session': 'unpin',
|
|
141
|
+
}
|
|
142
|
+
function withCliSession(path, handler) {
|
|
143
|
+
if (!cliMobile) return handler
|
|
144
|
+
if (path === '/api/mobile/v1/sessions') return cliMobile.wrapSessions(handler)
|
|
145
|
+
if (CLI_SESSION_FLAGS[path]) return cliMobile.routeFlag(CLI_SESSION_FLAGS[path], handler)
|
|
146
|
+
if (path === '/api/mobile/v1/workspace/clear-archived') return cliMobile.wrapClearArchived(handler)
|
|
147
|
+
// 后台提醒视图:CLI 会话的「在跑 / 刚完成 / 待回话」也从这里出去(注入成取值器,
|
|
148
|
+
// monitor.js 不认识 CLI 的任何细节,出口形状不变)。
|
|
149
|
+
if (path === '/api/mobile/v1/monitor') {
|
|
150
|
+
return (ctx, req, res) => handler(ctx, req, res, () => cliMobile.monitorItems())
|
|
151
|
+
}
|
|
152
|
+
if (path === '/api/mobile/v1/approval/respond') return cliMobile.wrapApprovalRespond(handler)
|
|
153
|
+
if (path === '/api/mobile/v1/session/stream') {
|
|
154
|
+
return (ctx, req, res) => {
|
|
155
|
+
const sessionId = new URL(req.url || '/', 'http://x').searchParams.get('sessionId')
|
|
156
|
+
return cliMobile.isCliSessionId(sessionId) ? cliMobile.handleStream(ctx, req, res) : handler(ctx, req, res)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (CLI_SESSION_PATHS.has(path)) return cliMobile.route(path, handler)
|
|
160
|
+
return handler
|
|
161
|
+
}
|
|
87
162
|
// 中转配置那一组要**装配期已有的实例**(config store、sidecar 生命周期),不能靠模块级
|
|
88
163
|
// 单例去拿:同一进程里两份 relay.json 的真值只会打架。所以这里按装配层给的依赖建一次。
|
|
89
164
|
const relayControl = createRelayControlHandlers(deps.relayControl || {})
|
|
@@ -260,6 +335,12 @@ function registerMobileRoutes(deps) {
|
|
|
260
335
|
route('/api/mobile/v1/session/select-model', sessions.handleSelectModel)
|
|
261
336
|
// 模型之外的附加选项(速度、输出详略……):写一条已声明的行,落地是宿主自己的设置。
|
|
262
337
|
route('/api/mobile/v1/session/model-option', sessionModelOptions.handleModelOption)
|
|
338
|
+
// CLI 模型管理页的开关(只影响显示,真值存在那台电脑上,见 cli/model-visibility.js)。
|
|
339
|
+
// 这条只对 CLI 会话有意义:宿主那套模型目录没有「跨手机一致」的要求,所以非 CLI 的
|
|
340
|
+
// sessionId 一律回 400,不做静默成功。
|
|
341
|
+
route('/api/mobile/v1/session/model-visibility', async (_ctx, _req, res) => {
|
|
342
|
+
sendJson(res, 400, { ok: false, error: { code: 'cli-only', message: '这条接口只对 CLI 会话有效' } });
|
|
343
|
+
})
|
|
263
344
|
route('/api/mobile/v1/session/stats', sessions.handleStats)
|
|
264
345
|
// 统计总览(统计页)。账本落在宿主家里(`$DSH_HOME/mobile-stats.json`),与会话日志
|
|
265
346
|
// 无关:归档只是挪位置,清理删的是日志,历史数字都还在 —— 见 mobile/stats-ledger.cjs。
|
|
@@ -500,6 +581,9 @@ function registerMobileRoutes(deps) {
|
|
|
500
581
|
route('/api/mobile/v1/inbox/clear', inboxClean.handleInboxClear)
|
|
501
582
|
// 自动清理开关:默认关,打开后宿主每次启动顺手清一次(面板那颗按钮不受它管)。
|
|
502
583
|
route('/api/mobile/v1/inbox/auto', inboxClean.handleInboxAuto)
|
|
584
|
+
// 过程播报开关(GET 读、POST 写 `{enabled}`)。缺省关;关着时宿主一个钩子都不动手。
|
|
585
|
+
// 与「自动清理」同一个位置:都是这台电脑自己的偏好,跟会话无关。
|
|
586
|
+
route('/api/mobile/v1/narration', narration.handleNarration)
|
|
503
587
|
|
|
504
588
|
// 电脑上的 DSH 网页版(见 mobile/host-web.js):App 的内置浏览器要直接用它,而那个页面
|
|
505
589
|
// 只绑在宿主自己的环回端口上、且要一枚一次性登录令牌。令牌是凭据,所以这条**必须**
|
|
@@ -521,7 +605,59 @@ function registerMobileRoutes(deps) {
|
|
|
521
605
|
//
|
|
522
606
|
// 路径字面量必须与 App 侧 apiPaths.ts 的 preview 一致:test/mobile-routes.test.cjs
|
|
523
607
|
// 就是拿这张表反向核对 App 常量表的。
|
|
608
|
+
// 远程终端(见 mobile/terminal.js):白名单 CLI + 受管伪终端。
|
|
609
|
+
//
|
|
610
|
+
// 四条普通路由里只有 `agents` / `sessions` 是读,另两条是起与收;真正的字节流走下面的
|
|
611
|
+
// 升级路由。`terminalService` 从装配层来(会话表要在插件重载时被一起收掉,不能是模块单例)。
|
|
612
|
+
if (terminalService) {
|
|
613
|
+
route('/api/mobile/v1/terminal/agents', terminalService.handleAgents)
|
|
614
|
+
route('/api/mobile/v1/terminal/session', terminalService.handleCreate)
|
|
615
|
+
route('/api/mobile/v1/terminal/close', terminalService.handleClose)
|
|
616
|
+
route('/api/mobile/v1/terminal/sessions', terminalService.handleSessions)
|
|
617
|
+
}
|
|
524
618
|
registerUpgradeRoute({ path: '/api/mobile/v1/preview', handler: preview.handleUpgrade })
|
|
619
|
+
// 终端字节流:与预览同一套升级机制(中转与局域网代理都不看路径)。
|
|
620
|
+
if (terminalService) registerUpgradeRoute({ path: '/api/mobile/v1/terminal/stream', handler: terminalService.handleUpgrade })
|
|
621
|
+
|
|
622
|
+
// CLI 对话(见 mobile/cli/service.js):同一批白名单 CLI,但走 ACP —— 手机拿到的是结构化
|
|
623
|
+
// 的消息 / 工具调用 / 审批请求,所以能像正常对话一样渲染,而不是一块终端屏幕。
|
|
624
|
+
//
|
|
625
|
+
// 与终端那组并列存在,不是替换:终端留着给「我要看原始 TUI」的那一档。`cliService` 同样
|
|
626
|
+
// 从装配层来(会话表要在插件重载时被一起收掉,不能是模块单例)。
|
|
627
|
+
if (cliService) {
|
|
628
|
+
route('/api/mobile/v1/cli/agents', cliService.handleAgents)
|
|
629
|
+
route('/api/mobile/v1/cli/session', cliService.handleCreate)
|
|
630
|
+
route('/api/mobile/v1/cli/close', cliService.handleClose)
|
|
631
|
+
route('/api/mobile/v1/cli/sessions', cliService.handleSessions)
|
|
632
|
+
// 读一份全貌 + 三个写动作。WS 是快的那条路;隧道上偶尔「握手成了、帧进不来」,这一组
|
|
633
|
+
// 是它的兜底(页面轮询 detail、发送走 HTTP),两边共用服务层同一份实现。
|
|
634
|
+
route('/api/mobile/v1/cli/detail', cliService.handleDetail)
|
|
635
|
+
route('/api/mobile/v1/cli/prompt', cliService.handlePrompt)
|
|
636
|
+
route('/api/mobile/v1/cli/permission', cliService.handlePermission)
|
|
637
|
+
route('/api/mobile/v1/cli/cancel', cliService.handleCancel)
|
|
638
|
+
route('/api/mobile/v1/cli/config', cliService.handleConfig)
|
|
639
|
+
// 「这个 CLI 以后按哪一套跑」:配置页读它、也写它。与上一条的区别是**不动正在跑的会话**,
|
|
640
|
+
// 只决定下一次开局(ACP 的配置是会话级的,所以要提前存一份,见 cli/config-prefs.js)。
|
|
641
|
+
route('/api/mobile/v1/cli/prefs', cliService.handlePrefs)
|
|
642
|
+
registerUpgradeRoute({ path: '/api/mobile/v1/cli/stream', handler: cliService.handleUpgrade })
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// 「让 AI 操作手机」(见 mobile/phone/):三条状态路由与一条动作字节流的升级口。
|
|
646
|
+
//
|
|
647
|
+
// 状态那三条都只读账本或做收尾,**没有一条能往手机上发动作**。动作只有模型在会话里通过
|
|
648
|
+
// `phone_*` 工具发得出去,而且只能是那一小张固定动作表里的 —— 这里刻意不开口子,
|
|
649
|
+
// `test/mobile-routes.test.cjs` 也钉着这张表里不许出现 shell / exec 一类路径。
|
|
650
|
+
if (phoneService) {
|
|
651
|
+
const phoneRoutes = createPhoneRoutes({
|
|
652
|
+
getService: () => phoneService,
|
|
653
|
+
credentials: phoneService.credentials,
|
|
654
|
+
instanceId: phoneService.instanceId,
|
|
655
|
+
})
|
|
656
|
+
route('/api/mobile/v1/phone/status', phoneRoutes.status)
|
|
657
|
+
route('/api/mobile/v1/phone/devices', phoneRoutes.devices)
|
|
658
|
+
route('/api/mobile/v1/phone/unbind', phoneRoutes.unbind)
|
|
659
|
+
registerUpgradeRoute({ path: '/api/mobile/v1/phone/control', handler: phoneService.handleUpgrade })
|
|
660
|
+
}
|
|
525
661
|
}
|
|
526
662
|
|
|
527
|
-
module.exports = { registerMobileRoutes };
|
|
663
|
+
module.exports = { registerMobileRoutes };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mobile-plugin",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.19",
|
|
4
4
|
"description": "Mobile API (/api/mobile/v1) and relay client for the DSH mobile app — desktop profile build (dsh 0.2.x, peers resolved by the host)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"icon": "./icon.png",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"release:check": "node scripts/preflight-release.mjs"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
+
"@agentclientprotocol/sdk": "2.4.0",
|
|
57
58
|
"qrcode": "1.5.4",
|
|
58
59
|
"ws": "8.18.0"
|
|
59
60
|
},
|