@huaqiu/dsh-eda-host 0.3.20 → 0.3.21

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.d.mts CHANGED
@@ -94,15 +94,29 @@ declare const inject: readonly ["tools"];
94
94
  declare module '@deepseek-ai/cordis' {
95
95
  interface Context {
96
96
  edaHost: EdaHostClient;
97
+ /**
98
+ * Provided by the hq-edge `edge-bridge` plugin (the HOST). `baseUrl` is the
99
+ * loopback HQ Edge endpoint, e.g. "http://localhost:18080". We read it
100
+ * lazily so this plugin does not hard-depend on the bridge and still works
101
+ * in standalone DSH installs (where the service is absent).
102
+ */
103
+ hqEdge?: {
104
+ baseUrl?: string;
105
+ };
97
106
  }
98
107
  }
99
108
  /**
100
109
  * Host plugin body — provide `edaHost` and register the three netlist tools.
101
110
  *
102
- * When no host base URL is configured, tools return ok:false with
103
- * error.kind "FAILED_PRECONDITION" instead of throwing at load time: the
104
- * plugin can be installed in standalone DSH where hq-edge is absent, and the
105
- * tools degrade to a clear semantic message.
111
+ * The HQ Edge endpoint is resolved **lazily at request time** (see
112
+ * `getHqEdgeBaseUrl`): the edge-bridge plugin provides `ctx.hqEdge.baseUrl`,
113
+ * which wins over the static `config.hqEdgeBaseUrl` / `HQ_EDGE_BASE_URL` env
114
+ * fallback. This matches how `@huaqiu/dsh-artifacts` and `@huaqiu/dsh-tool-
115
+ * symbol-footprint` reach hq-edge, and means the URL is correct even when this
116
+ * plugin is applied before the bridge. When no host URL is available at call
117
+ * time, the tools degrade to a clear FAILED_PRECONDITION instead of throwing at
118
+ * load time — so the plugin still installs in standalone DSH where hq-edge is
119
+ * absent.
106
120
  *
107
121
  * @param ctx - real cordis context (node side).
108
122
  * @returns disposer — unregisters the tools on plugin dispose.
package/lib/index.mjs CHANGED
@@ -51,7 +51,12 @@ function statusToKind(status) {
51
51
  function createEdaHostClient(config, deps = {}) {
52
52
  const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
53
53
  async function fetchScope(scope) {
54
- const url = netlistUrlOf(config, scope);
54
+ const baseUrl = deps.baseUrlResolver?.()?.trim() ?? config.hqEdgeBaseUrl?.trim() ?? "";
55
+ if (baseUrl.length === 0) throw new NetlistError("FAILED_PRECONDITION", "eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — netlist tools require the hq-edge EDA host bridge.");
56
+ const url = netlistUrlOf({
57
+ ...config,
58
+ hqEdgeBaseUrl: baseUrl
59
+ }, scope);
55
60
  let response;
56
61
  try {
57
62
  response = await fetchImpl(url, {
@@ -165,10 +170,15 @@ const log = getLogger("dsh-eda-host");
165
170
  /**
166
171
  * Host plugin body — provide `edaHost` and register the three netlist tools.
167
172
  *
168
- * When no host base URL is configured, tools return ok:false with
169
- * error.kind "FAILED_PRECONDITION" instead of throwing at load time: the
170
- * plugin can be installed in standalone DSH where hq-edge is absent, and the
171
- * tools degrade to a clear semantic message.
173
+ * The HQ Edge endpoint is resolved **lazily at request time** (see
174
+ * `getHqEdgeBaseUrl`): the edge-bridge plugin provides `ctx.hqEdge.baseUrl`,
175
+ * which wins over the static `config.hqEdgeBaseUrl` / `HQ_EDGE_BASE_URL` env
176
+ * fallback. This matches how `@huaqiu/dsh-artifacts` and `@huaqiu/dsh-tool-
177
+ * symbol-footprint` reach hq-edge, and means the URL is correct even when this
178
+ * plugin is applied before the bridge. When no host URL is available at call
179
+ * time, the tools degrade to a clear FAILED_PRECONDITION instead of throwing at
180
+ * load time — so the plugin still installs in standalone DSH where hq-edge is
181
+ * absent.
172
182
  *
173
183
  * @param ctx - real cordis context (node side).
174
184
  * @returns disposer — unregisters the tools on plugin dispose.
@@ -176,31 +186,23 @@ const log = getLogger("dsh-eda-host");
176
186
  function apply(ctx, config = {}) {
177
187
  if (!ctx.tools || typeof ctx.tools.register !== "function") throw new Error("@huaqiu/dsh-eda-host requires the DSH `tools` service (ctx.tools.register).");
178
188
  const resolved = resolveEdaHostConfig(config);
189
+ const getHqEdgeBaseUrl = () => {
190
+ const hq = ctx.hqEdge;
191
+ return hq?.baseUrl && hq.baseUrl.trim().length > 0 ? hq.baseUrl : void 0;
192
+ };
179
193
  log.info("applying dsh-eda-host node half", {
180
- hasHost: hasHost(resolved),
181
- hqEdgeBaseUrl: resolved.hqEdgeBaseUrl ?? null,
194
+ hasConfigHost: hasHost(resolved),
195
+ hqEdgeBaseUrlFromConfig: resolved.hqEdgeBaseUrl ?? null,
182
196
  netlistPathPrefix: resolved.netlistPathPrefix
183
197
  });
184
- let client;
185
- if (hasHost(resolved)) client = createEdaHostClient(resolved);
186
- else client = {
187
- getProjectNetlist: async () => {
188
- throw new NetlistError("FAILED_PRECONDITION", "eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — netlist tools require the hq-edge EDA host bridge.");
189
- },
190
- getSelectionNetlist: async () => {
191
- throw new NetlistError("FAILED_PRECONDITION", "eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — netlist tools require the hq-edge EDA host bridge.");
192
- },
193
- getActivePageNetlist: async () => {
194
- throw new NetlistError("FAILED_PRECONDITION", "eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — netlist tools require the hq-edge EDA host bridge.");
195
- }
196
- };
198
+ const client = createEdaHostClient(resolved, { baseUrlResolver: getHqEdgeBaseUrl });
197
199
  ctx.effect(() => ctx.provide("edaHost", client));
198
200
  const tools = createNetListTools({ client });
199
201
  const disposers = [];
200
202
  for (const tool of tools) disposers.push(ctx.tools.register(tool));
201
203
  log.info("dsh-eda-host node half ready", {
202
204
  tools: 3,
203
- hostMode: hasHost(resolved)
205
+ configHostMode: hasHost(resolved)
204
206
  });
205
207
  return () => {
206
208
  for (const dispose of disposers) try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huaqiu/dsh-eda-host",
3
- "version": "0.3.20",
3
+ "version": "0.3.21",
4
4
  "type": "module",
5
5
  "main": "./lib/index.mjs",
6
6
  "types": "./lib/index.d.mts",
@@ -22,7 +22,7 @@
22
22
  "@deepseek-ai/dsh-tools": "^0.1.0-rc.0"
23
23
  },
24
24
  "dependencies": {
25
- "@huaqiu/dsh-plugin-log": "0.3.20"
25
+ "@huaqiu/dsh-plugin-log": "0.3.21"
26
26
  },
27
27
  "files": [
28
28
  "lib",
package/src/client.ts CHANGED
@@ -17,6 +17,16 @@ import {
17
17
 
18
18
  export interface EdaHostClientDeps {
19
19
  fetchImpl?: typeof fetch
20
+ /**
21
+ * Optional late-bound resolver for the HQ Edge base URL. When supplied it is
22
+ * consulted on EVERY request and takes priority over the static `config`
23
+ * value. This lets the node half read the endpoint from the `ctx.hqEdge`
24
+ * service the edge-bridge plugin provides — the same pattern `@huaqiu/
25
+ * dsh-artifacts` and `@huaqiu/dsh-tool-symbol-footprint` use — so the URL is
26
+ * picked up even if this plugin is applied before the bridge, and stays
27
+ * correct in standalone installs where no host is present.
28
+ */
29
+ baseUrlResolver?: () => string | undefined
20
30
  }
21
31
 
22
32
  export interface EdaHostClient {
@@ -43,7 +53,20 @@ export function createEdaHostClient(
43
53
  const fetchImpl = deps.fetchImpl ?? globalThis.fetch
44
54
 
45
55
  async function fetchScope(scope: NetlistScope): Promise<SchematicNetlist> {
46
- const url = netlistUrlOf(config, scope)
56
+ // Resolve the host endpoint per request. A resolver (ctx.hqEdge) wins over
57
+ // the static config/env value; if neither yields a URL we degrade to the
58
+ // same clear FAILED_PRECONDITION the standalone install path uses.
59
+ const baseUrl = deps.baseUrlResolver?.()?.trim()
60
+ ?? config.hqEdgeBaseUrl?.trim()
61
+ ?? ''
62
+ if (baseUrl.length === 0) {
63
+ throw new NetlistError(
64
+ 'FAILED_PRECONDITION',
65
+ 'eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — ' +
66
+ 'netlist tools require the hq-edge EDA host bridge.',
67
+ )
68
+ }
69
+ const url = netlistUrlOf({ ...config, hqEdgeBaseUrl: baseUrl }, scope)
47
70
 
48
71
  let response: Response
49
72
  try {
package/src/index.ts CHANGED
@@ -24,7 +24,6 @@ import { getLogger } from '@huaqiu/dsh-plugin-log'
24
24
  import { createEdaHostClient, type EdaHostClient } from './client.js'
25
25
  import { hasHost, resolveEdaHostConfig, type EdaHostConfig } from './config.js'
26
26
  import { createNetListTools } from './tools.js'
27
- import { NetlistError } from './types.js'
28
27
 
29
28
  /** Plugin id — matches package.json. */
30
29
  export const name = '@huaqiu/dsh-eda-host'
@@ -48,6 +47,13 @@ export { NetlistError } from './types.js'
48
47
  declare module '@deepseek-ai/cordis' {
49
48
  interface Context {
50
49
  edaHost: EdaHostClient
50
+ /**
51
+ * Provided by the hq-edge `edge-bridge` plugin (the HOST). `baseUrl` is the
52
+ * loopback HQ Edge endpoint, e.g. "http://localhost:18080". We read it
53
+ * lazily so this plugin does not hard-depend on the bridge and still works
54
+ * in standalone DSH installs (where the service is absent).
55
+ */
56
+ hqEdge?: { baseUrl?: string }
51
57
  }
52
58
  }
53
59
 
@@ -58,10 +64,15 @@ const log = getLogger(COMPONENT)
58
64
  /**
59
65
  * Host plugin body — provide `edaHost` and register the three netlist tools.
60
66
  *
61
- * When no host base URL is configured, tools return ok:false with
62
- * error.kind "FAILED_PRECONDITION" instead of throwing at load time: the
63
- * plugin can be installed in standalone DSH where hq-edge is absent, and the
64
- * tools degrade to a clear semantic message.
67
+ * The HQ Edge endpoint is resolved **lazily at request time** (see
68
+ * `getHqEdgeBaseUrl`): the edge-bridge plugin provides `ctx.hqEdge.baseUrl`,
69
+ * which wins over the static `config.hqEdgeBaseUrl` / `HQ_EDGE_BASE_URL` env
70
+ * fallback. This matches how `@huaqiu/dsh-artifacts` and `@huaqiu/dsh-tool-
71
+ * symbol-footprint` reach hq-edge, and means the URL is correct even when this
72
+ * plugin is applied before the bridge. When no host URL is available at call
73
+ * time, the tools degrade to a clear FAILED_PRECONDITION instead of throwing at
74
+ * load time — so the plugin still installs in standalone DSH where hq-edge is
75
+ * absent.
65
76
  *
66
77
  * @param ctx - real cordis context (node side).
67
78
  * @returns disposer — unregisters the tools on plugin dispose.
@@ -74,44 +85,21 @@ export function apply(ctx: Context, config: Partial<EdaHostConfig> = {}): () =>
74
85
  }
75
86
 
76
87
  const resolved = resolveEdaHostConfig(config)
88
+
89
+ // Late-bound host endpoint: prefer the edge-bridge service, then the overlay
90
+ // config / env value. Consulted on every request (see client.ts).
91
+ const getHqEdgeBaseUrl = (): string | undefined => {
92
+ const hq = ctx.hqEdge
93
+ return hq?.baseUrl && hq.baseUrl.trim().length > 0 ? hq.baseUrl : undefined
94
+ }
95
+
77
96
  log.info('applying dsh-eda-host node half', {
78
- hasHost: hasHost(resolved),
79
- hqEdgeBaseUrl: resolved.hqEdgeBaseUrl ?? null,
97
+ hasConfigHost: hasHost(resolved),
98
+ hqEdgeBaseUrlFromConfig: resolved.hqEdgeBaseUrl ?? null,
80
99
  netlistPathPrefix: resolved.netlistPathPrefix,
81
100
  })
82
101
 
83
- let client: EdaHostClient
84
-
85
- if (hasHost(resolved)) {
86
- client = createEdaHostClient(resolved)
87
- } else {
88
- // Standalone install (no hq-edge supervisor): every scope degrades to
89
- // FAILED_PRECONDITION with a clear message.
90
- const unavailable: EdaHostClient = {
91
- getProjectNetlist: async () => {
92
- throw new NetlistError(
93
- 'FAILED_PRECONDITION',
94
- 'eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — ' +
95
- 'netlist tools require the hq-edge EDA host bridge.',
96
- )
97
- },
98
- getSelectionNetlist: async () => {
99
- throw new NetlistError(
100
- 'FAILED_PRECONDITION',
101
- 'eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — ' +
102
- 'netlist tools require the hq-edge EDA host bridge.',
103
- )
104
- },
105
- getActivePageNetlist: async () => {
106
- throw new NetlistError(
107
- 'FAILED_PRECONDITION',
108
- 'eda-host: no hq-edge base URL configured (hqEdgeBaseUrl / HQ_EDGE_BASE_URL) — ' +
109
- 'netlist tools require the hq-edge EDA host bridge.',
110
- )
111
- },
112
- }
113
- client = unavailable
114
- }
102
+ const client = createEdaHostClient(resolved, { baseUrlResolver: getHqEdgeBaseUrl })
115
103
 
116
104
  ctx.effect(() => ctx.provide('edaHost', client))
117
105
 
@@ -121,7 +109,7 @@ export function apply(ctx: Context, config: Partial<EdaHostConfig> = {}): () =>
121
109
  disposers.push(ctx.tools.register(tool))
122
110
  }
123
111
 
124
- log.info('dsh-eda-host node half ready', { tools: 3, hostMode: hasHost(resolved) })
112
+ log.info('dsh-eda-host node half ready', { tools: 3, configHostMode: hasHost(resolved) })
125
113
 
126
114
  return () => {
127
115
  for (const dispose of disposers) {