@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 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 loopback route
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 loopback route the settings
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 loopback-only bridge that answers
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
- * Loopback literal check plus browser same-origin markers (mirrors the imagegen
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
- * @returns whether the request may be served.
41
+ * @param response - the response, written to when the request is refused.
42
+ * @returns whether the request was refused.
31
43
  */
32
- function isLoopbackRequest(request) {
33
- const address = request.socket.remoteAddress;
34
- if (address !== '127.0.0.1' && address !== '::1' && address !== '::ffff:127.0.0.1') return false;
35
- const host = request.headers.host;
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's `ctx.llm` service.
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 (!isLoopbackRequest(request)) {
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.5",
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",