@finamx/host-bridge

中文文档
3.11.0发布于 2026/11/11TypeScriptESM + CJS近 30 天下载 3828

Primitives for embedding FinamX extensions into host applications — web iframes, React apps, and MCP agents.

原始使用文档
文档目录25 节
  1. Install
  2. Quick Start — New Host Integration
  3. Method remapping (custom methods + platform fallback)
  4. Quick Start — Native-like embed (React)
  5. Quick Start — Web Host
  6. Quick Start — Fill-container layout (D-04)
  7. Size preference (SIZE-02)
  8. Wall overlay (WALL-02)
  9. Quick Start — Design tokens + debug tooling
  10. Unified debug kit (D-05, D-06)
  11. Individual panels (manual composition)
  12. Quick Start — MCP Agent
  13. Exports
  14. Main package ( @finamx/host-bridge )
  15. Widgets sub-package ( @finamx/host-bridge/widgets )
  16. MCP sub-package ( @finamx/host-bridge/mcp )
  17. Extension-owned topics (D-874-23)
  18. Platform catalog client (any host)
  19. Host-signed identity (3.0.0)
  20. Build
  21. Bootstrapping a brand-new host app (agent skill)
  22. Agent-generated compose embeds (Phase 58, AGW-04)
  23. Flow notifications and human decisions (server only)
  24. CMS signing trust (3.9.1)
  25. AG-UI chat transport

@finamx/host-bridge

Primitives for embedding FinamX extensions into host applications — web iframes, React apps, and MCP agents.

Install

npm install @finamx/host-bridge @finamx/bridge-protocol

Quick Start — New Host Integration

The fastest way to embed a FinamX extension with platform capability proxying in ≤20 lines:

npm install @finamx/host-bridge @finamx/bridge-protocol
import {
  fetchEmbedLaunch,
  launchToBridgeSession,
  resolvePlatformBase,
  useExtensionEmbed,
} from '@finamx/host-bridge';

export function MyExtensionEmbed({ extensionId }: { extensionId: string }) {
  const [state, setState] = React.useState<{
    launchUrl: string;
    session: ReturnType<typeof launchToBridgeSession>;
  } | null>(null);

  React.useEffect(() => {
    fetchEmbedLaunch({ extensionId, platformBase: resolvePlatformBase() }).then((launch) => {
      setState({ launchUrl: launch.launchUrl, session: launchToBridgeSession(launch) });
    });
  }, [extensionId]);

  const bridge = useExtensionEmbed({
    launchUrl: state?.launchUrl ?? '',
    allowedOrigin: state?.session?.allowedOrigin ?? '',
    session: state?.session ?? { sessionId: '', extensionId, allowedOrigin: '', grantedScopes: [] },
    // Declarative proxy: both fields → createCapabilityRouterWithPlatformProxy auto-created
    platformBase: resolvePlatformBase(),
    sessionJwt: state?.session?.sessionJwt ?? '',
  });

  if (!state) return <p>Loading…</p>;

  return (
    <div ref={bridge.containerRef} style={{ height: 400 }}>
      <iframe ref={bridge.iframeRef} {...bridge.frameProps} style={{ height: '100%' }} />
    </div>
  );
}

Extension calling portfolio.summary receives real platform data because platformBase/sessionJwt trigger createCapabilityRouterWithPlatformProxy automatically — no manual CapabilityRouter setup required.

Method remapping (custom methods + platform fallback)

When your host handles some methods itself and delegates the rest to the platform:

import { createCapabilityRouterWithPlatformProxy, resolvePlatformBase, CapabilityRouter } from '@finamx/host-bridge';

const platformRouter = createCapabilityRouterWithPlatformProxy({
  platformBase: resolvePlatformBase(),
  sessionJwt: session.sessionJwt ?? '',
});

const myRouter = new CapabilityRouter({
  invokeHandler: async (req) => {
    if (req.method === 'my.custom.method') {
      return { ok: true, data: await myBackend.get() };
    }
    return platformRouter.invoke(req); // fallback to platform
  },
});

// Then pass myRouter explicitly:
useExtensionEmbed({ ..., capabilityRouter: myRouter });

Quick Start — Native-like embed (React)

import {
  ExtensionEmbedFrame,
  fetchEmbedLaunch,
  launchToBridgeSession,
} from '@finamx/host-bridge';

const launch = await fetchEmbedLaunch({
  extensionId: 'com.example.my-extension',
  platformBase: process.env.NEXT_PUBLIC_CMS_URL!,
  userId: 'poc-user-1',
});

<ExtensionEmbedFrame
  session={launchToBridgeSession(launch)}
  launchUrl={launch.launchUrl}
/>;

ExtensionEmbedFrame applies server-driven embedLayout (height bounds, scroll policy, gesture mode), handles ui:resize, ui:scroll-lock, and ui:gesture-claim. See docs/architecture/native-embed-sdk.md.


Quick Start — Web Host

import { createWebHostBridge, CapabilityRouter, ExtensionSessionConfig } from '@finamx/host-bridge';

const config: ExtensionSessionConfig = {
  sessionId: crypto.randomUUID(),
  extensionId: 'com.example.my-extension',
  allowedOrigin: 'https://my-extension.example.com',
  grantedScopes: ['market.read', 'ui.toast'],
};

const bridge = createWebHostBridge({
  iframe: document.getElementById('extension-frame') as HTMLIFrameElement,
  allowedOrigin: config.allowedOrigin,
  session: config,
});

// Tear down (removes window message listener):
bridge.destroy();

Quick Start — Fill-container layout (D-04)

For host UI where the embed should fill a card or modal rather than using a fixed height:

import { ExtensionEmbedFrame } from '@finamx/host-bridge';

// The container div controls the available height; ExtensionEmbedFrame
// fills it and sends host:layout with measured px dimensions via ResizeObserver
// so the extension can respond with appropriate layout (not minHeight: 100vh).
<div style={{ height: 320 }}>
  <ExtensionEmbedFrame
    session={session}
    launchUrl={launch.launchUrl}
    fillContainer  // iframe height = 100% of container; ResizeObserver pushes dimensions
  />
</div>

With useExtensionEmbed, mount containerRef on the wrapper div:

const bridge = useExtensionEmbed({ ..., fillContainer: true });

<div ref={bridge.containerRef} style={{ height: '100%' }}>
  <iframe ref={bridge.iframeRef} {...bridge.frameProps} />
</div>

Size preference (SIZE-02)

ui:sizePreference is not ui:resize. When a preference arrives and fillContainer is false, the container height becomes the clamped resolveSizePreference result. When fillContainer is true and no preference is set, height stays 100%. When both are set, the container uses CSS aspect-ratio (W / H from parsed integers) with height: auto so a Conduit card can honor the preference without ui:resize.

Wall overlay (WALL-02)

Hosts can pass renderWall to replace DefaultWallOverlay. If omitted, the default presentational chrome still works. Consent overlay remains a separate host-app component.

reason: payment is copy only (Требуется оплата) — it does not imply a billing provider or checkout.

<ExtensionEmbedFrame
  session={session}
  launchUrl={launch.launchUrl}
  renderWall={({ visible, reason }) =>
    visible ? <MyWall reason={reason} /> : null
  }
/>

Hook users get wall, setWall (delegates to the controller), and wallOverlay:

const bridge = useExtensionEmbed({ launchUrl, allowedOrigin, session });

<div ref={bridge.containerRef} {...bridge.containerProps}>
  <iframe ref={bridge.iframeRef} {...bridge.frameProps} />
  {bridge.wallOverlay}
</div>

Quick Start — Design tokens + debug tooling

Pass real design tokens so embedded extensions inherit your host's theme instead of the generic shadcn defaults, and opt into bridge traffic / token inspection panels.

Unified debug kit (D-05, D-06)

BridgeHostDebugKit composes the log panel, token preview, and scope revoke controls into a single opt-in component. Always gate it behind a debug flag — never render in production embeds (T-48-02):

import {
  useExtensionEmbed,
  BridgeHostDebugKit,
  CapabilityRouter,
} from '@finamx/host-bridge';

const [debug, setDebug] = useState(false);
const [grantedScopes, setGrantedScopes] = useState(session.grantedScopes);

const bridge = useExtensionEmbed({ ..., debug });

// Render conditionally — never default on in production
{debug && (
  <BridgeHostDebugKit
    entries={bridge.bridgeLog}
    onClear={bridge.clearBridgeLog}
    tokens={uiTokens}
    grantedScopes={grantedScopes}
    onScopesChange={setGrantedScopes}  // simulate scope revoke (dev-only)
    uiTokensEnabled={tokensEnabled}
    onUiTokensEnabledChange={setTokensEnabled}
  />
)}

Individual panels (manual composition)

import {
  useExtensionEmbed,
  BridgeDebugLogPanel,
  BridgeTokenPreviewPanel,
  CapabilityRouter,
} from '@finamx/host-bridge';
import { mergeUITokens, type UITokens } from '@finamx/bridge-protocol';

function readHostUITokens(): UITokens {
  const style = getComputedStyle(document.documentElement);
  const colors = { ...mergeUITokens({}).colors };
  for (const key of Object.keys(colors) as (keyof UITokens['colors'])[]) {
    const v = style.getPropertyValue(`--${key}`).trim();
    if (v) colors[key] = v;
  }
  return mergeUITokens({ colors });
}

const capabilityRouter = useMemo(() => new CapabilityRouter(), []);
const uiTokens = useMemo(() => readHostUITokens(), []);
const [debugEnabled, setDebugEnabled] = useState(false);

const bridge = useExtensionEmbed({
  launchUrl: launch.launchUrl,
  allowedOrigin: session.allowedOrigin,
  session,
  capabilityRouter,
  uiTokens,
  debug: debugEnabled,
});

// ...render the iframe via bridge.iframeRef / bridge.frameProps as usual, then:
{debugEnabled && (
  <BridgeDebugLogPanel entries={bridge.bridgeLog} onClear={bridge.clearBridgeLog} />
)}
<BridgeTokenPreviewPanel tokens={uiTokens} />

bridgeLog/clearBridgeLog only populate while debug: true — leave it off by default and gate the toggle behind an internal/dev flag, not exposed to end users.


Quick Start — MCP Agent

import { createMcpWidgetTools } from '@finamx/host-bridge/mcp';

const tools = createMcpWidgetTools({
  widgetCatalog: [
    {
      extensionId: 'com.example.widgets',
      name: 'Portfolio',
      description: 'Portfolio summary widget',
      routes: ['/widgets/portfolio-summary'],
    },
  ],
  launchWidget: async (extensionId, route, params) => {
    // Return { launchUrl, sessionId } from your launch service
    return {
      launchUrl: `https://app.example.com/embed?ext=${extensionId}&route=${route}`,
      sessionId: crypto.randomUUID(),
    };
  },
});

// Register with your MCP server:
// tools.forEach(t => server.tool(t.name, t.description, t.inputSchema, t.handler));

Exports

Main package (@finamx/host-bridge)

ExportDescription
createWebHostBridge(options)Browser iframe bridge with automatic window message listener management
createHostBridge(options)Low-level factory — caller manages window events
createCapabilityRouter(options?)Factory helper for CapabilityRouter
CapabilityRouterValidates scopes, dispatches platform:invoke to handler
ExtensionEmbedFrameDrop-in React iframe with embed layout + scroll-lock side effects
DefaultWallOverlayPresentational wall chrome (auth / payment copy). Override via renderWall
resolveEmbedLayout()Merge session embedLayout with defaults and overrides
getEmbedFrameStyles()CSS for iframe from layout + scroll/gesture state
runScrollLockSideEffects()Apply parent scroll lock when extension sends ui:scroll-lock
sendHostLayoutEvent() / sendHostScrollStateEvent()Dispatch platform:event to embed
useExtensionEmbed(options)React hook — iframe ref, frameProps, layout, lock/claim state, bridgeLog, bridgeReady
fetchEmbedLaunch(options)Calls POST /api/v1/embed/launch, returns LaunchResponse
launchToBridgeSession(launch, userId?)Converts LaunchResponse → BridgeSessionConfig
ExtensionSessionConfigSession config with optional uiTokens and embedLayout
UITokensDesign token shape (re-exported from @finamx/bridge-protocol)
BridgeLogEntryShape of one bridge traffic log entry (useExtensionEmbed({ debug: true }))
BridgeDebugLogPanelOpt-in component — live view of bridge send/recv traffic, latency, ok/error
BridgeTokenPreviewPanelOpt-in component — swatch preview of the UITokens actually sent to the extension
BridgeHostDebugKitUnified dev-only debug UI: log + token preview + scope revoke + token toggle (D-05/D-06)

Widgets sub-package (@finamx/host-bridge/widgets)

DivKit + custom-id widget host helpers for routing CMS-authored widgets (D-07).

Requires optional peer @divkitframework/react for DivKitWidgetHost rendering.

DivKit custom components — shadow DOM workaround

DivKit renders type: "custom" elements by inserting a <template shadowrootmode="open"> into the DOM via JavaScript (insertBefore). Browsers only process declarative shadow DOM during HTML parsing — dynamic JS insertion is silently ignored. The SVG/HTML inside the template ends up in an inert #document-fragment and never renders.

DivKitWidgetHost and WidgetBySlugEmbed handle this automatically: after DivKit mounts, they scan for <template shadowrootmode> elements, call host.attachShadow() imperatively, and move the template content into the real shadow root. A MutationObserver repeats this for any subsequent DivKit DOM updates.

Known limitation — reactive custom_props

The workaround removes the <template> from the DOM after processing (tmpl.remove()). DivKit still holds a reference to that detached element. If DivKit reactively updates custom_props (e.g. the CMS pushes new chartPoints via a live binding), it writes new SVG content to r.content.innerHTML on the detached template — the shadow root is not updated and shows stale content.

Current mitigations:

  • All existing custom components (finamSparkLine, finamNewsCard, webEmbed) receive static custom_props from the CMS — no live bindings, so the limitation is not triggered.

Required fix when reactive custom components are added:

Instead of removing the template, keep it in the DOM and observe it with a second MutationObserver targeting the template's content. On each change, replace the shadow root's content:

// pseudo-code — implement in activateShadowRoots() in each DivKit host component
const shadow = host.attachShadow({ mode });
const sync = () => {
  shadow.replaceChildren(document.importNode(tmpl.content, true));
};
sync();
// Do NOT remove the template — keep it for DivKit to update
const contentObserver = new MutationObserver(sync);
contentObserver.observe(tmpl.content, { subtree: true, childList: true, characterData: true });

This change is required before shipping any DivKit widget whose custom_props change at runtime.

import {
  resolveWidgetRenderMode,
  fetchDivKitWidget,
  DivKitWidgetHost,
  CustomIdEmbedHost,
} from '@finamx/host-bridge/widgets';

// Route a board card to the correct renderer
const mode = resolveWidgetRenderMode(card.meta);
// → 'divkit' | 'extension-embed' | 'external-embed' | 'custom-id'

// Fetch DivKit JSON from CMS (host passes its own auth headers — T-48-05)
const widget = await fetchDivKitWidget('my-slug', {
  platformBase: 'http://localhost:3001',
  headers: { 'X-User-Id': 'poc-user-1' },
});

// All-in-one routing component (render props per path)
<CustomIdEmbedHost
  meta={card.meta}
  renderDivKit={(slug) => (
    <DivKitWidgetHost
      slug={slug}
      fetchOptions={{ platformBase: 'http://localhost:3001', headers: { 'X-User-Id': userId } }}
    />
  )}
  renderExtension={(extensionId) => <ExtensionEmbed extensionId={extensionId} />}
  renderExternalEmbed={(slug) => <ExternalEmbedContent slug={slug} />}
/>
ExportDescription
resolveWidgetRenderMode(meta)Returns `'divkit'
fetchDivKitWidget(slug, options)GET /api/v1/divkit/widgets/:slug — host passes platformBase + auth headers
DivKitWidgetResponseResponse type for fetchDivKitWidget
DivKitWidgetHostReact component — fetches + renders DivKit JSON; dynamic import of @divkitframework/react
CustomIdEmbedHostRoutes card metadata to render props for divkit/extension/external paths

MCP sub-package (@finamx/host-bridge/mcp)

ExportDescription
createMcpWidgetTools(options)Creates plain-object MCP tool definitions for widget rendering
renderWidgetForAgUi(options)Builds an ag-ui widget_render event payload
createWidgetAdapter(options)Generic launch→render adapter for non-MCP systems
McpToolDefinitionPlain-object MCP tool type (no @modelcontextprotocol/sdk hard dep)
AgUiWidgetEventag-ui widget render event type

Extension-owned topics (D-874-23)

LaunchResponse.extensionTopics ({ publishes: [{ topicType, schema?, legacy? }], subscribes: [] }) travels through launchToBridgeSession into BridgeSessionConfig and PubSubBroker.register, so hosts using useExtensionEmbed need no code change. A topic an extension declares in publishes passes between extensions without a host grant and is validated against its schema; external subscribers must declare it in subscribes. A name that is in the host domain always follows host rules.

Platform catalog client (any host)

import {
  createPlatformClient,
  createHostCapabilityRouter,
  fetchEmbedLaunch,
  launchToBridgeSession,
  useExtensionEmbed,
} from '@finamx/host-bridge';

const client = createPlatformClient({
  platformBase: 'http://127.0.0.1:3001',
  userId: 'user-1',
});

const catalog = await client.fetchExtensionCatalog();
const installed = await client.fetchInstalledExtensions();

// Launch + embed + local invoke overrides:
const launch = await fetchEmbedLaunch({ extensionId: 'com.example.widget', userId: 'user-1' });
const session = launchToBridgeSession(launch);
const router = createHostCapabilityRouter({
  platformBase: 'http://127.0.0.1:3001',
  sessionJwt: session.sessionJwt ?? '',
  localHandler: async (req) => {
    if (req.method === 'my.host.method') return { ok: true, data: {} };
    return null; // fall through to platform proxy
  },
});

DivKit widgets: fetchDivKitWidget from @finamx/host-bridge/widgets.


Host-signed identity (3.0.0)

Signing is server-only: signHostIdentity is exported from @finamx/host-bridge/server and signs platform.identity.v1 (ES256, iss = host id, sub = host-local user id, aud = extension id, kid, TTL at most 300 s) with a private JWK from the host secret store. The browser bundle does not sign and never holds the key. Call it only when the extension declares requiresIdentityContext, then hand the result to the iframe as identityToken in widget.identity.grant. The CMS stores only the public JWK; there is no HMAC fallback. sessionJwt and Core/Gateway tokens must not be placed in the iframe or in the identity grant.

import { signHostIdentity } from '@finamx/host-bridge/server';

const identityToken = await signHostIdentity({ hostId, userId, extensionId, kid, privateKey });

Build

npm run build   # tsc → dist/ (includes dist/mcp/)
npm test        # build + run capability-router tests

Output is ESM-only ("type": "module"). Part of the FinamX Mobile monorepo.


Bootstrapping a brand-new host app (agent skill)

Everything in this README is also packaged as the plugin skill openplatform-host-init (plugin skill /openplatform:openplatform-host-init, https://github.com/NikitaEfimenko/openplatform-plugin) — so a new project doesn't need a human to manually replay these steps. It covers: installing @finamx/bridge-protocol + @finamx/host-bridge from npm, fetching a launch, rendering via the hook or ExtensionEmbedFrame, wiring real design tokens from the host's own theme, opt-in debug tooling, capability proxying, host domain, and grants. Templates live in the skills/openplatform-host-init/resources/ folder of the openplatform plugin.

To run it, install the plugin in Claude Code, Codex or Cursor and run:

/openplatform:openplatform-host-init

or describe the goal in plain language (e.g. "wire up extension hosting in this app") — the skill matches automatically. The skill's first step always re-checks npm view @finamx/host-bridge version before doing anything, since this package's publish status can change between sessions.

If you're not using an agent, follow the "Quick Start" sections above in order: Install → launch → render (hook or component) → design tokens → debug tooling → capability proxying — that's the same sequence the skill automates.

For a full walkthrough that also covers starting the platform locally, picking a seeded test extension, and a concrete verification checklist, see docs/guides/application-layer/connect-a-new-host.md.


Agent-generated compose embeds (Phase 58, AGW-04)

MCP compose and Canvas workspace.launchWidget({ divkitJson }) return a server-built embedUrl pointing at CMS /widget/inline?json=…&trust=agent-generated. Hosts must not construct this URL client-side from raw DivKit JSON — the CMS inline route re-validates via validateAgentGeneratedDivKit.

When mounting the compose iframe, pass a bridge session with trustClass: 'agent-generated':

import {
  buildAgentGeneratedEmbedSession,
  useExtensionEmbed,
} from '@finamx/host-bridge';

const session = buildAgentGeneratedEmbedSession({
  sessionId: bridgeSessionId,
  embedUrl, // from MCP compose structuredContent or LAUNCH_WIDGET SSE payload
  extensionId: 'agent://compose/<widgetId>', // optional; defaults to agent://compose/{sessionId}
});

const bridge = useExtensionEmbed({
  launchUrl: embedUrl,
  allowedOrigin: session.allowedOrigin,
  session, // trustClass: 'agent-generated' gates pubsub / MCP-UI sensitivity (Phase 54)
});

Audit: compose (MCP), inline render (CMS), and graph instance launch (platform MCP graph.addInstance) each POST to /api/v1/embed/audit with method: agent-generated.v1, extensionId, and sessionId. Host implementers should correlate their own session/user IDs with these audit fields.

web-chat: wiring deferred until the submodule is restored; use buildAgentGeneratedEmbedSession in any React host that mounts compose iframes.

Flow notifications and human decisions (server only)

createHostS2sEndpoint checks the caller's registered scope and validates method body schemas loaded through createCachedDomainLoader. Declare requestBody / responseBody as { "$ref": "#/topics/<topicType>" } and provide those topic schemas in the same host domain. Parameters are checked before invoking the handler, and results after unwrapping hostResult. Missing or malformed references refuse the call before invoking the handler. Legacy inline paramsSchema / resultSchema remain descriptive, as on the browser path.

METHOD_PARAMS_INVALID and METHOD_SCHEMA_UNRESOLVED mean the handler was not called. METHOD_RESULT_INVALID means its result was withheld; the handler may already have performed an effect. Effect methods still need durable idempotency and reconciliation in the host.

The server subpath exports createSelfNotificationSender, flowNotificationKey, createFlowDecisionStore and createFlowDecisionService. The notification sender completes at a validated Outreach HTTP 202 acceptance, with a deterministic key per host/extension/rpcId. The caller must resolve the recipient and message from an authenticated host context; the graph cannot choose a recipient.

Decision storage requires a host-owned atomic persistence adapter (a Mongo collection matches DecisionCollection). Pending requests have no expiry. The answer and completion outbox are written atomically; callbacks are retried by the host worker. Host session/CSRF checks, current consent/entitlement, artifact access, outbox draining and host UI remain integration responsibilities. These helpers do not install host topics/methods, grant background access, or authenticate a raw request body. They must never be called from browser or Temporal workflow code.

CMS signing trust (3.9.1)

Deploy /op/jwks and /host/jwks in CMS before upgrading. Standard CMS token verifiers select this dedicated source when configured with the legacy aggregate JWKS URL, without fallback. Custom JWKS sources and injected getKey/cmsGetKey resolvers must contain only CMS signing keys. Host identity keeps its separate host key source (/host/jwks) and CMS-signed install binding. Current/overlap host keys remain available at the legacy /.well-known/jwks.json endpoint for compatibility; aggregate membership never grants CMS authority.

AG-UI chat transport

Use @finamx/host-bridge/ag-ui in the browser and @finamx/host-bridge/server for the Node stream, scoped receipts and durable ACK/cancel lifecycle. Both ESM and CommonJS server consumers are supported. Host auth, endpoint routing and chat/board persistence are supplied as ports. See the host integration guide for the lifecycle and the Trading Diary entry-point seam. Preview patches are inert; a stream commit never grants an action capability.

版本与发布记录

@finamx/host-bridge 共发布 22 个版本,首次发布于 2026/06/26;过去 12 个月发布了 22 个版本。

版本发布日期距今
3.11.0latest2026/11/114 天前
3.10.02026/11/105 天前
3.9.12026/11/095 天前
3.9.02026/11/086 天前
3.8.02026/11/086 天前
3.7.02026/11/077 天前

关于 @finamx/host-bridge 的常见问题

@finamx/host-bridge 是什么?

Primitives for embedding FinamX extensions into host applications — web iframes, React apps, and MCP agents.

如何安装 @finamx/host-bridge?

在项目目录运行 npm install @finamx/host-bridge,或使用 pnpm add / yarn add / bun add。

@finamx/host-bridge 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@finamx/host-bridge 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@finamx/host-bridge 可以商用吗?

@finamx/host-bridge 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@finamx/host-bridge 的下载量有多少?

根据 npm 官方下载统计,@finamx/host-bridge 在 2026/10/15 – 2026/11/13 这 30 天内被下载 3,828 次,日均约 128 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@finamx/host-bridge 还在维护吗?

最新版本 3.11.0 发布于 2026/11/11(4 天前);至今共发布 22 个版本,其中过去 12 个月发布了 22 个;首次发布于 2026/06/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。