@logictan/dsh-desktop-agent 0.1.5 → 0.1.6
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/lib/client.js +1 -1
- package/lib/index.js +2 -2
- package/lib/routes.js +30 -28
- package/package.json +6 -1
package/lib/client.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Two catalogs feed this card, and they are not interchangeable:
|
|
16
16
|
*
|
|
17
|
-
* - The VISION list comes from this plugin's own
|
|
17
|
+
* - The VISION list comes from this plugin's own same-origin route
|
|
18
18
|
* (`/api/dsh-desktop-agent/vision-models`). It has to: the host's
|
|
19
19
|
* `session.modelCatalog` does not carry `inputModalities` at all — its
|
|
20
20
|
* builder maps a fixed set of fields off the resolved model info and drops
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* dsh-desktop-agent — host half.
|
|
3
3
|
*
|
|
4
4
|
* Registers one model-facing tool, `desktop_agent`, that runs a native-vision
|
|
5
|
-
* decision loop against the host desktop, plus the
|
|
5
|
+
* decision loop against the host desktop, plus the same-origin route the settings
|
|
6
6
|
* card reads its model list from.
|
|
7
7
|
*
|
|
8
8
|
* The plugin owns no execution engine of its own. Every action is dispatched to
|
|
@@ -220,7 +220,7 @@ export async function apply(ctx, config, loadSdk) {
|
|
|
220
220
|
ctx.inject(['webServer'], (scoped) => {
|
|
221
221
|
scoped.effect(
|
|
222
222
|
() => {
|
|
223
|
-
const disposers = makeRoutes({ llm }).map((route) => scoped.webServer.register(route));
|
|
223
|
+
const disposers = makeRoutes({ llm, connection: () => scoped.get('connection') }).map((route) => scoped.webServer.register(route));
|
|
224
224
|
return () => {
|
|
225
225
|
for (const dispose of disposers) dispose();
|
|
226
226
|
};
|
package/lib/routes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The /api/dsh-desktop-agent route family: a
|
|
2
|
+
* The /api/dsh-desktop-agent route family: a bridge that answers
|
|
3
3
|
* one question the browser half cannot answer for itself.
|
|
4
4
|
*
|
|
5
5
|
* The settings card must list only models that can actually see, and the fact it
|
|
@@ -23,32 +23,33 @@ import { visionCatalog } from './route.js';
|
|
|
23
23
|
export const VISION_MODELS_API = '/api/dsh-desktop-agent/vision-models';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
* bridge and dsh-ssh).
|
|
26
|
+
* Ask the composition whether one request may be served.
|
|
28
27
|
*
|
|
28
|
+
* `connection.requestRejection` is the deployment's single trust fence: it
|
|
29
|
+
* applies the Host/Origin checks (loopback plus the LAN authorities the
|
|
30
|
+
* deployment declares) and the browser authentication that rides with them.
|
|
31
|
+
* This plugin answers with the verdict instead of re-deriving it, because the
|
|
32
|
+
* declared LAN authorities live in `connection`'s config and a local check
|
|
33
|
+
* cannot see them.
|
|
34
|
+
*
|
|
35
|
+
* A missing `connection` refuses rather than serves: these routes sit in front
|
|
36
|
+
* of Connection's `/api` prefix route, so serving them without its fence would
|
|
37
|
+
* serve them unfenced.
|
|
38
|
+
*
|
|
39
|
+
* @param connection - the composition's Connection service, when it is up.
|
|
29
40
|
* @param request - the incoming request.
|
|
30
|
-
* @
|
|
41
|
+
* @param response - the response, written to when the request is refused.
|
|
42
|
+
* @returns whether the request was refused.
|
|
31
43
|
*/
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (typeof host !== 'string') return false;
|
|
37
|
-
let hostUrl;
|
|
38
|
-
try {
|
|
39
|
-
hostUrl = new URL(`http://${host}`);
|
|
40
|
-
} catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
if (hostUrl.hostname !== '127.0.0.1' && hostUrl.hostname !== 'localhost' && hostUrl.hostname !== '[::1]') return false;
|
|
44
|
-
if (request.headers['sec-fetch-site'] === 'cross-site') return false;
|
|
45
|
-
const origin = request.headers.origin;
|
|
46
|
-
if (origin === undefined) return true;
|
|
47
|
-
try {
|
|
48
|
-
return new URL(origin).host === hostUrl.host;
|
|
49
|
-
} catch {
|
|
50
|
-
return false;
|
|
44
|
+
function refuseUntrusted(connection, request, response) {
|
|
45
|
+
if (connection === undefined) {
|
|
46
|
+
writeJson(response, 403, { error: 'forbidden' });
|
|
47
|
+
return true;
|
|
51
48
|
}
|
|
49
|
+
const rejection = connection.requestRejection(request);
|
|
50
|
+
if (rejection === undefined) return false;
|
|
51
|
+
writeJson(response, rejection, { error: rejection === 401 ? 'unauthorized' : 'forbidden' });
|
|
52
|
+
return true;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/** Write one JSON response. */
|
|
@@ -66,7 +67,11 @@ function writeJson(response, status, body) {
|
|
|
66
67
|
/**
|
|
67
68
|
* Build the route family.
|
|
68
69
|
*
|
|
69
|
-
* @param deps - the host
|
|
70
|
+
* @param deps - the host dependencies.
|
|
71
|
+
* @param deps.llm - the host's `ctx.llm` service.
|
|
72
|
+
* @param deps.connection - resolves the composition's trust fence per request.
|
|
73
|
+
* Resolved per call rather than captured: a composed row order does not imply
|
|
74
|
+
* an activation order, so the service can still be absent when routes are built.
|
|
70
75
|
* @returns the routes to register.
|
|
71
76
|
*/
|
|
72
77
|
export function makeRoutes(deps) {
|
|
@@ -75,10 +80,7 @@ export function makeRoutes(deps) {
|
|
|
75
80
|
kind: 'exact',
|
|
76
81
|
path: VISION_MODELS_API,
|
|
77
82
|
async handler(request, response) {
|
|
78
|
-
if (
|
|
79
|
-
writeJson(response, 403, { error: 'loopback-only' });
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
83
|
+
if (refuseUntrusted(deps.connection(), request, response)) return;
|
|
82
84
|
if (request.method !== 'GET' && request.method !== 'POST') {
|
|
83
85
|
writeJson(response, 405, { error: 'method-not-allowed' });
|
|
84
86
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logictan/dsh-desktop-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "DSH desktop agent: a native-vision decision loop that drives the host desktop through the Cua Driver tools. DSH 桌面 Agent 子插件。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
57
58
|
"@deepseek-ai/dsh-llm": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
58
59
|
"@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
59
60
|
"@deepseek-ai/dsh-mcp-client": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
@@ -64,6 +65,9 @@
|
|
|
64
65
|
"@deepseek-ai/cordis": {
|
|
65
66
|
"optional": true
|
|
66
67
|
},
|
|
68
|
+
"@deepseek-ai/dsh-client-connection": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
67
71
|
"@deepseek-ai/dsh-llm": {
|
|
68
72
|
"optional": true
|
|
69
73
|
},
|
|
@@ -85,6 +89,7 @@
|
|
|
85
89
|
},
|
|
86
90
|
"devDependencies": {
|
|
87
91
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
92
|
+
"@deepseek-ai/dsh-client-connection": "^0.2.0-rc.2",
|
|
88
93
|
"@deepseek-ai/dsh-llm": "^0.2.0-rc.2",
|
|
89
94
|
"@deepseek-ai/dsh-mcp-client": "^0.2.0-rc.2",
|
|
90
95
|
"@deepseek-ai/dsh-system-prompt": "^0.2.0-rc.2",
|