dshmarket 1.65.3 → 1.65.4

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/index.js CHANGED
@@ -104,12 +104,31 @@ function agentsLookupOf(ctx) {
104
104
  * exactly as it was, which is the behaviour every release so far has had.
105
105
  * @returns a restore function for the effect that installed it.
106
106
  */
107
- function useTrustedHosts(ctx) {
108
- const connection = ctx.get('connection');
109
- const declared = Array.isArray(connection?.trustedHosts)
110
- ? connection.trustedHosts.filter((entry) => typeof entry === 'string')
111
- : [];
112
- const previous = setTrustedHostsSource(() => declared);
107
+ export function useTrustedHosts(ctx) {
108
+ // Resolved per request, not at mount -- the same reason `agentsLookupOf` is
109
+ // lazy. The host provides `connection` only after its own async init
110
+ // (`HostConnectionService` is constructed behind `await BrowserAuth.create`),
111
+ // and the market mounts on `webServer` + `loader`, which are ready first: a
112
+ // mount-time read therefore sees no service on a web host, and the [] fallback
113
+ // then narrows the fence to loopback for the life of the process -- every
114
+ // mutating route 403 on a deployment that is reached by a name, while loopback
115
+ // keeps working (#729).
116
+ //
117
+ // Exported for the spec that covers this wiring; the fence it feeds is tested
118
+ // through `sameOrigin` in tests/http.spec.ts.
119
+ let warned = false;
120
+ const previous = setTrustedHostsSource(() => {
121
+ const connection = ctx.get('connection');
122
+ if (connection === undefined && !warned) {
123
+ // The silent version of this is what hid #729: "no service" and "no
124
+ // authorities declared" produced the same fence.
125
+ warned = true;
126
+ console.warn('dsh-market: the host connection service is not available; the origin fence accepts loopback only');
127
+ }
128
+ return Array.isArray(connection?.trustedHosts)
129
+ ? connection.trustedHosts.filter((entry) => typeof entry === 'string')
130
+ : [];
131
+ });
113
132
  // The setter returns the PREVIOUS source, not a restore closure — the same
114
133
  // contract as setBuildEnvSource in dsh-cli.ts.
115
134
  return () => { setTrustedHostsSource(previous); };
@@ -7,4 +7,19 @@ import { type MarketConfig } from './routes.ts';
7
7
  export declare const name = "dsh-market";
8
8
  /** Optional cordis.yml configuration; profile defaults to `web`. */
9
9
  export type Config = Partial<Pick<MarketConfig, 'profile' | 'allowRestart' | 'maxSnapshots' | 'buildEnv'>>;
10
+ /**
11
+ * The deployment's declared authorities, read from the host (#729).
12
+ *
13
+ * DSH's own /api fence accepts loopback OR an authority the operator declared
14
+ * (`dsh web --trusted-host <name>`, plus the LAN literals the CLI derives when
15
+ * bound to 0.0.0.0). The market's routes are `exact` registrations on the bare
16
+ * webServer, and exact matches win over that fence's prefix, so they never
17
+ * pass through it — and a loopback-only rule of its own made every mutating
18
+ * route 403 on any deployment reached by a name.
19
+ *
20
+ * The `connection` service is optional: a host without it leaves the fence
21
+ * exactly as it was, which is the behaviour every release so far has had.
22
+ * @returns a restore function for the effect that installed it.
23
+ */
24
+ export declare function useTrustedHosts(ctx: Context): () => void;
10
25
  export declare function apply(ctx: Context, config?: Config): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dshmarket",
3
3
  "description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
4
- "version": "1.65.3",
4
+ "version": "1.65.4",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
package/src/index.ts CHANGED
@@ -147,12 +147,31 @@ function agentsLookupOf(ctx: Context): () => AgentsServiceLike | undefined {
147
147
  * exactly as it was, which is the behaviour every release so far has had.
148
148
  * @returns a restore function for the effect that installed it.
149
149
  */
150
- function useTrustedHosts(ctx: Context): () => void {
151
- const connection = ctx.get('connection') as { trustedHosts?: unknown } | undefined
152
- const declared = Array.isArray(connection?.trustedHosts)
153
- ? connection.trustedHosts.filter((entry): entry is string => typeof entry === 'string')
154
- : []
155
- const previous = setTrustedHostsSource(() => declared)
150
+ export function useTrustedHosts(ctx: Context): () => void {
151
+ // Resolved per request, not at mount -- the same reason `agentsLookupOf` is
152
+ // lazy. The host provides `connection` only after its own async init
153
+ // (`HostConnectionService` is constructed behind `await BrowserAuth.create`),
154
+ // and the market mounts on `webServer` + `loader`, which are ready first: a
155
+ // mount-time read therefore sees no service on a web host, and the [] fallback
156
+ // then narrows the fence to loopback for the life of the process -- every
157
+ // mutating route 403 on a deployment that is reached by a name, while loopback
158
+ // keeps working (#729).
159
+ //
160
+ // Exported for the spec that covers this wiring; the fence it feeds is tested
161
+ // through `sameOrigin` in tests/http.spec.ts.
162
+ let warned = false
163
+ const previous = setTrustedHostsSource(() => {
164
+ const connection = ctx.get('connection') as { trustedHosts?: unknown } | undefined
165
+ if (connection === undefined && !warned) {
166
+ // The silent version of this is what hid #729: "no service" and "no
167
+ // authorities declared" produced the same fence.
168
+ warned = true
169
+ console.warn('dsh-market: the host connection service is not available; the origin fence accepts loopback only')
170
+ }
171
+ return Array.isArray(connection?.trustedHosts)
172
+ ? connection.trustedHosts.filter((entry): entry is string => typeof entry === 'string')
173
+ : []
174
+ })
156
175
  // The setter returns the PREVIOUS source, not a restore closure — the same
157
176
  // contract as setBuildEnvSource in dsh-cli.ts.
158
177
  return () => { setTrustedHostsSource(previous) }