@hirey/hi-mcp-server 0.1.23 → 0.1.25
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/README.md +20 -0
- package/dist/defaultReplyRoute.d.ts +4 -2
- package/dist/defaultReplyRoute.d.ts.map +1 -1
- package/dist/defaultReplyRoute.js +3 -0
- package/dist/installDefaults.d.ts +2 -1
- package/dist/installDefaults.d.ts.map +1 -1
- package/dist/installDefaults.js +14 -3
- package/dist/oauthRequestAuth.d.ts +67 -0
- package/dist/oauthRequestAuth.d.ts.map +1 -0
- package/dist/oauthRequestAuth.js +117 -0
- package/dist/receiver-command.d.ts +2 -1
- package/dist/receiver-command.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +889 -72
- package/dist/state.d.ts +2 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +25 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,20 @@
|
|
|
22
22
|
|
|
23
23
|
## 对外暴露的两类工具
|
|
24
24
|
|
|
25
|
+
默认 `HI_MCP_TOOL_SURFACE=full` 会暴露下面两类工具,适合 OpenClaw、Claude Code、Claude Desktop、本地 Codex MCP 等需要安装/诊断/事件消费能力的 MCP-first 宿主。
|
|
26
|
+
|
|
27
|
+
ChatGPT App / OpenAI Apps SDK 的 hosted OAuth 入口应使用 `/mcp/chatgpt`,或在专用部署里设置 `HI_MCP_TOOL_SURFACE=chatgpt_app`。这个 surface 不暴露 `hi_agent_*` 安装和事件控制面,只暴露一个小而明确的业务工具集,方便 ChatGPT 选择工具、降低审核解释成本,也避免把本地安装语义带进公共 App:
|
|
28
|
+
|
|
29
|
+
- `listing_taxonomy`
|
|
30
|
+
- `agent_listings`
|
|
31
|
+
- `matching_sessions`
|
|
32
|
+
- `pairings`
|
|
33
|
+
- `thread_meetings`
|
|
34
|
+
- `faq_search`
|
|
35
|
+
- `faq_get`
|
|
36
|
+
- `content_render`
|
|
37
|
+
- `content_get`
|
|
38
|
+
|
|
25
39
|
### 1. 控制面工具
|
|
26
40
|
|
|
27
41
|
这些工具都以 `hi_agent_*` 命名,保持和 gateway 控制面同一语义边界:
|
|
@@ -117,6 +131,11 @@ npm install -g @hirey/hi-mcp-server
|
|
|
117
131
|
- `HI_MCP_TRANSPORT`
|
|
118
132
|
- 可选。`http` 或 `stdio`。
|
|
119
133
|
- 默认 `http`。
|
|
134
|
+
- `HI_MCP_TOOL_SURFACE`
|
|
135
|
+
- 可选。`full` 或 `chatgpt_app`。
|
|
136
|
+
- 默认 `full`。
|
|
137
|
+
- `chatgpt_app` 只暴露 ChatGPT App 审核/用户体验需要的业务工具,并在 `tools/call` 路径同步拒绝隐藏工具。
|
|
138
|
+
- 在共享 hosted 服务里,推荐保留默认 `full`,并把 ChatGPT App 的 MCP URL 配成 `/mcp/chatgpt`;这样 `/mcp` 仍可服务 Codex/OpenClaw/Claude 的完整工具面。
|
|
120
139
|
- `HI_MCP_HOST`
|
|
121
140
|
- `http` 模式监听地址,默认 `127.0.0.1`。
|
|
122
141
|
- `HI_MCP_PORT`
|
|
@@ -163,6 +182,7 @@ hi-mcp-server
|
|
|
163
182
|
MCP endpoint:
|
|
164
183
|
|
|
165
184
|
- `POST /mcp`
|
|
185
|
+
- `POST /mcp/chatgpt`(ChatGPT App 专用裁剪工具面)
|
|
166
186
|
- `GET /healthz`
|
|
167
187
|
|
|
168
188
|
### Stdio
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type HostKind = 'openclaw' | 'generic' | 'codex' | 'claude_code' | 'claude_chat' | 'claude_cowork' | 'claude_desktop' | 'chatgpt_app';
|
|
2
|
+
export declare function isOpenClawHost(hostKind: HostKind): hostKind is 'openclaw';
|
|
1
3
|
export type DefaultReplyDeliveryContext = {
|
|
2
4
|
channel: string | null;
|
|
3
5
|
to: string | null;
|
|
@@ -5,7 +7,7 @@ export type DefaultReplyDeliveryContext = {
|
|
|
5
7
|
thread_id: string | null;
|
|
6
8
|
};
|
|
7
9
|
export declare function resolveInstallDefaultReplyDeliveryContext(args: {
|
|
8
|
-
hostKind:
|
|
10
|
+
hostKind: HostKind;
|
|
9
11
|
hasSessionKey: boolean;
|
|
10
12
|
defaultReplyChannel?: unknown;
|
|
11
13
|
defaultReplyTo?: unknown;
|
|
@@ -13,7 +15,7 @@ export declare function resolveInstallDefaultReplyDeliveryContext(args: {
|
|
|
13
15
|
defaultReplyThreadId?: unknown;
|
|
14
16
|
}): DefaultReplyDeliveryContext | null;
|
|
15
17
|
export declare function resolveInstallRouteMissingPolicy(args: {
|
|
16
|
-
hostKind:
|
|
18
|
+
hostKind: HostKind;
|
|
17
19
|
explicitRouteMissingPolicy?: unknown;
|
|
18
20
|
defaultReplyRoute?: Record<string, unknown> | null;
|
|
19
21
|
}): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultReplyRoute.d.ts","sourceRoot":"","sources":["../src/defaultReplyRoute.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"defaultReplyRoute.d.ts","sourceRoot":"","sources":["../src/defaultReplyRoute.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,QAAQ,GAChB,UAAU,GACV,SAAS,GACT,OAAO,GACP,aAAa,GACb,aAAa,GACb,eAAe,GACf,gBAAgB,GAChB,aAAa,CAAC;AAElB,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,UAAU,CAEzE;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,wBAAgB,yCAAyC,CAAC,IAAI,EAAE;IAC9D,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GAAG,2BAA2B,GAAG,IAAI,CAqErC;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,QAAQ,CAAC;IACnB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACpD,GAAG;IACF,EAAE,EAAE,IAAI,CAAC;IACT,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG;IACF,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,sCAAsC,CAAC;CAC/C,CAcA"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
function normalizeText(input) {
|
|
2
2
|
return String(input || '').trim();
|
|
3
3
|
}
|
|
4
|
+
export function isOpenClawHost(hostKind) {
|
|
5
|
+
return hostKind === 'openclaw';
|
|
6
|
+
}
|
|
4
7
|
export function resolveInstallDefaultReplyDeliveryContext(args) {
|
|
5
8
|
const explicit = {
|
|
6
9
|
channel: normalizeText(args.defaultReplyChannel) || null,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { HostKind } from './defaultReplyRoute.js';
|
|
1
2
|
export declare const DEFAULT_OPENCLAW_INSTALL_DISPLAY_NAME = "OpenClaw Hi Agent";
|
|
2
3
|
export declare const DEFAULT_GENERIC_INSTALL_DISPLAY_NAME = "Hi Agent";
|
|
3
4
|
export declare function resolveInstallDisplayName(args: {
|
|
4
5
|
explicitDisplayName?: unknown;
|
|
5
|
-
hostKind:
|
|
6
|
+
hostKind: HostKind;
|
|
6
7
|
}): string;
|
|
7
8
|
//# sourceMappingURL=installDefaults.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installDefaults.d.ts","sourceRoot":"","sources":["../src/installDefaults.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installDefaults.d.ts","sourceRoot":"","sources":["../src/installDefaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAMvD,eAAO,MAAM,qCAAqC,sBAAsB,CAAC;AACzE,eAAO,MAAM,oCAAoC,aAAa,CAAC;AAe/D,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;CACpB,UAIA"}
|
package/dist/installDefaults.js
CHANGED
|
@@ -3,11 +3,22 @@ function normalizeText(input) {
|
|
|
3
3
|
}
|
|
4
4
|
export const DEFAULT_OPENCLAW_INSTALL_DISPLAY_NAME = 'OpenClaw Hi Agent';
|
|
5
5
|
export const DEFAULT_GENERIC_INSTALL_DISPLAY_NAME = 'Hi Agent';
|
|
6
|
+
// Per-host defaults so the agent shows up in Hi's admin panel with a stable
|
|
7
|
+
// human-readable label that matches the host the user actually onboarded
|
|
8
|
+
// from. None of these carry openclaw-style install semantics; only the
|
|
9
|
+
// display name changes.
|
|
10
|
+
const HOST_KIND_DISPLAY_NAME_DEFAULTS = {
|
|
11
|
+
openclaw: DEFAULT_OPENCLAW_INSTALL_DISPLAY_NAME,
|
|
12
|
+
codex: 'Codex Hi Agent',
|
|
13
|
+
claude_code: 'Claude Code Hi Agent',
|
|
14
|
+
claude_chat: 'Claude Hi Agent',
|
|
15
|
+
claude_cowork: 'Cowork Hi Agent',
|
|
16
|
+
claude_desktop: 'Claude Desktop Hi Agent',
|
|
17
|
+
chatgpt_app: 'ChatGPT Hi Agent',
|
|
18
|
+
};
|
|
6
19
|
export function resolveInstallDisplayName(args) {
|
|
7
20
|
const explicitDisplayName = normalizeText(args.explicitDisplayName);
|
|
8
21
|
if (explicitDisplayName)
|
|
9
22
|
return explicitDisplayName;
|
|
10
|
-
return args.hostKind
|
|
11
|
-
? DEFAULT_OPENCLAW_INSTALL_DISPLAY_NAME
|
|
12
|
-
: DEFAULT_GENERIC_INSTALL_DISPLAY_NAME;
|
|
23
|
+
return HOST_KIND_DISPLAY_NAME_DEFAULTS[args.hostKind] || DEFAULT_GENERIC_INSTALL_DISPLAY_NAME;
|
|
13
24
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import type express from 'express';
|
|
3
|
+
import { type JWTPayload, type JWTVerifyResult } from 'jose';
|
|
4
|
+
export type RequestAuth = {
|
|
5
|
+
bearer: string;
|
|
6
|
+
subjectId: string;
|
|
7
|
+
claims: JWTPayload;
|
|
8
|
+
};
|
|
9
|
+
export declare const requestAuthStorage: AsyncLocalStorage<RequestAuth | null>;
|
|
10
|
+
export declare function getCurrentRequestAuth(): RequestAuth | null;
|
|
11
|
+
export type OAuthVerifierConfig = {
|
|
12
|
+
issuer: string;
|
|
13
|
+
audience: string | string[];
|
|
14
|
+
jwksUri?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function buildOAuthVerifier(config: OAuthVerifierConfig): (token: string) => Promise<JWTVerifyResult<JWTPayload>>;
|
|
17
|
+
export declare function extractBearer(req: express.Request): string | null;
|
|
18
|
+
export declare function buildWwwAuthenticateChallenge(input: {
|
|
19
|
+
protectedResourceMetadataUrl: string;
|
|
20
|
+
scope?: string[];
|
|
21
|
+
error?: 'invalid_token' | 'insufficient_scope';
|
|
22
|
+
errorDescription?: string;
|
|
23
|
+
}): string;
|
|
24
|
+
export type ProtectedResourceMetadata = {
|
|
25
|
+
resource: string;
|
|
26
|
+
authorization_servers: string[];
|
|
27
|
+
scopes_supported: string[];
|
|
28
|
+
bearer_methods_supported: ['header'];
|
|
29
|
+
resource_documentation?: string;
|
|
30
|
+
};
|
|
31
|
+
export type AuthorizationServerMetadataAlias = {
|
|
32
|
+
issuer: string;
|
|
33
|
+
authorization_endpoint: string;
|
|
34
|
+
token_endpoint: string;
|
|
35
|
+
registration_endpoint: string;
|
|
36
|
+
jwks_uri: string;
|
|
37
|
+
response_types_supported: ['code'];
|
|
38
|
+
response_modes_supported: ['query'];
|
|
39
|
+
grant_types_supported: ['authorization_code', 'refresh_token', 'client_credentials'];
|
|
40
|
+
token_endpoint_auth_methods_supported: ['none', 'client_secret_basic', 'client_secret_post'];
|
|
41
|
+
code_challenge_methods_supported: ['S256'];
|
|
42
|
+
scopes_supported: string[];
|
|
43
|
+
resource_indicators_supported: true;
|
|
44
|
+
service_documentation?: string;
|
|
45
|
+
};
|
|
46
|
+
export declare function buildAuthorizationServerMetadataAlias(input: {
|
|
47
|
+
issuer: string;
|
|
48
|
+
authorizationServer: string;
|
|
49
|
+
scopes: string[];
|
|
50
|
+
serviceDocumentationUrl?: string;
|
|
51
|
+
}): AuthorizationServerMetadataAlias;
|
|
52
|
+
export declare function buildProtectedResourceMetadata(input: {
|
|
53
|
+
resource: string;
|
|
54
|
+
authorizationServer: string;
|
|
55
|
+
scopes: string[];
|
|
56
|
+
resourceDocumentationUrl?: string;
|
|
57
|
+
}): ProtectedResourceMetadata;
|
|
58
|
+
export type BearerVerifyOutcome = {
|
|
59
|
+
ok: true;
|
|
60
|
+
auth: RequestAuth;
|
|
61
|
+
} | {
|
|
62
|
+
ok: false;
|
|
63
|
+
error: 'invalid_token';
|
|
64
|
+
description: string;
|
|
65
|
+
};
|
|
66
|
+
export declare function verifyRequestBearer(req: express.Request, verifier: (token: string) => Promise<JWTVerifyResult<JWTPayload>>): Promise<BearerVerifyOutcome>;
|
|
67
|
+
//# sourceMappingURL=oauthRequestAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthRequestAuth.d.ts","sourceRoot":"","sources":["../src/oauthRequestAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
3
|
+
function normalizeText(input) {
|
|
4
|
+
return String(input || '').trim();
|
|
5
|
+
}
|
|
6
|
+
// Per-request identity for the multi-tenant HTTP /mcp surface. Tool handlers
|
|
7
|
+
// pull the bearer out of this storage instead of loading a disk-resident
|
|
8
|
+
// identity, so the same hi-mcp-server process can serve any number of
|
|
9
|
+
// Codex / Claude Code installs concurrently.
|
|
10
|
+
export const requestAuthStorage = new AsyncLocalStorage();
|
|
11
|
+
export function getCurrentRequestAuth() {
|
|
12
|
+
return requestAuthStorage.getStore() || null;
|
|
13
|
+
}
|
|
14
|
+
function trimTrailingSlash(value) {
|
|
15
|
+
return value.replace(/\/+$/, '');
|
|
16
|
+
}
|
|
17
|
+
export function buildOAuthVerifier(config) {
|
|
18
|
+
const issuer = trimTrailingSlash(config.issuer);
|
|
19
|
+
const audience = config.audience;
|
|
20
|
+
const jwksUri = config.jwksUri || `${issuer}/.well-known/jwks.json`;
|
|
21
|
+
const jwks = createRemoteJWKSet(new URL(jwksUri), {
|
|
22
|
+
cacheMaxAge: 5 * 60_000,
|
|
23
|
+
cooldownDuration: 30_000,
|
|
24
|
+
});
|
|
25
|
+
return async function verify(token) {
|
|
26
|
+
return await jwtVerify(token, jwks, {
|
|
27
|
+
issuer,
|
|
28
|
+
audience,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function extractBearer(req) {
|
|
33
|
+
const header = String(req.headers.authorization || '');
|
|
34
|
+
if (!header)
|
|
35
|
+
return null;
|
|
36
|
+
const lower = header.toLowerCase();
|
|
37
|
+
if (!lower.startsWith('bearer '))
|
|
38
|
+
return null;
|
|
39
|
+
const token = header.slice('bearer '.length).trim();
|
|
40
|
+
return token || null;
|
|
41
|
+
}
|
|
42
|
+
// RFC 6750 / RFC 9728 — the 401 response challenge that points
|
|
43
|
+
// MCP clients at our oauth-protected-resource metadata. Codex / Claude
|
|
44
|
+
// Code parse this exact header to start their auto-discovery flow.
|
|
45
|
+
export function buildWwwAuthenticateChallenge(input) {
|
|
46
|
+
const parts = [];
|
|
47
|
+
if (input.error) {
|
|
48
|
+
parts.push(`error="${input.error}"`);
|
|
49
|
+
if (input.errorDescription) {
|
|
50
|
+
parts.push(`error_description="${input.errorDescription.replace(/"/g, '\\"')}"`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (input.scope && input.scope.length > 0) {
|
|
54
|
+
parts.push(`scope="${input.scope.join(' ')}"`);
|
|
55
|
+
}
|
|
56
|
+
parts.push(`resource_metadata="${input.protectedResourceMetadataUrl}"`);
|
|
57
|
+
return `Bearer ${parts.join(', ')}`;
|
|
58
|
+
}
|
|
59
|
+
// Compatibility alias for clients that still probe RFC 8414 authorization
|
|
60
|
+
// metadata on the MCP resource host before trying RFC 9728 protected-resource
|
|
61
|
+
// discovery. The canonical metadata is served by hi-auth; this mirrors the
|
|
62
|
+
// same endpoints without making the MCP server an authorization server.
|
|
63
|
+
export function buildAuthorizationServerMetadataAlias(input) {
|
|
64
|
+
const base = trimTrailingSlash(input.authorizationServer);
|
|
65
|
+
return {
|
|
66
|
+
issuer: trimTrailingSlash(input.issuer),
|
|
67
|
+
authorization_endpoint: `${base}/oauth/authorize`,
|
|
68
|
+
token_endpoint: `${base}/oauth/token`,
|
|
69
|
+
registration_endpoint: `${base}/oauth/register`,
|
|
70
|
+
jwks_uri: `${base}/.well-known/jwks.json`,
|
|
71
|
+
response_types_supported: ['code'],
|
|
72
|
+
response_modes_supported: ['query'],
|
|
73
|
+
grant_types_supported: ['authorization_code', 'refresh_token', 'client_credentials'],
|
|
74
|
+
token_endpoint_auth_methods_supported: ['none', 'client_secret_basic', 'client_secret_post'],
|
|
75
|
+
code_challenge_methods_supported: ['S256'],
|
|
76
|
+
scopes_supported: input.scopes,
|
|
77
|
+
resource_indicators_supported: true,
|
|
78
|
+
...(input.serviceDocumentationUrl ? { service_documentation: input.serviceDocumentationUrl } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// RFC 9728 §3 — minimum required surface: `resource` (canonical URI, exact
|
|
82
|
+
// string that tokens must `aud`-match), `authorization_servers` (so MCP
|
|
83
|
+
// clients know where to discover AS metadata). Everything else is optional.
|
|
84
|
+
export function buildProtectedResourceMetadata(input) {
|
|
85
|
+
return {
|
|
86
|
+
resource: input.resource,
|
|
87
|
+
authorization_servers: [trimTrailingSlash(input.authorizationServer)],
|
|
88
|
+
scopes_supported: input.scopes,
|
|
89
|
+
bearer_methods_supported: ['header'],
|
|
90
|
+
...(input.resourceDocumentationUrl ? { resource_documentation: input.resourceDocumentationUrl } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export async function verifyRequestBearer(req, verifier) {
|
|
94
|
+
const bearer = extractBearer(req);
|
|
95
|
+
if (!bearer) {
|
|
96
|
+
return { ok: false, error: 'invalid_token', description: 'missing_bearer' };
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const { payload } = await verifier(bearer);
|
|
100
|
+
const subjectId = normalizeText(payload.sub);
|
|
101
|
+
if (!subjectId) {
|
|
102
|
+
return { ok: false, error: 'invalid_token', description: 'missing_sub' };
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
ok: true,
|
|
106
|
+
auth: {
|
|
107
|
+
bearer,
|
|
108
|
+
subjectId,
|
|
109
|
+
claims: payload,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
115
|
+
return { ok: false, error: 'invalid_token', description: message };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { HostKind } from './defaultReplyRoute.js';
|
|
1
2
|
export declare function resolveCanonicalOpenClawReceiverBinaryPath(homeDir?: string): string;
|
|
2
3
|
export declare function buildInstallReceiverCommandArgv(args: {
|
|
3
4
|
explicitArgv: string[];
|
|
4
5
|
receiverCommand: string;
|
|
5
|
-
hostKind:
|
|
6
|
+
hostKind: HostKind;
|
|
6
7
|
enableLocalReceiver: boolean;
|
|
7
8
|
homeDir?: string;
|
|
8
9
|
}): string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receiver-command.d.ts","sourceRoot":"","sources":["../src/receiver-command.ts"],"names":[],"mappings":"AAGA,wBAAgB,0CAA0C,CAAC,OAAO,GAAE,MAAqB,GAAG,MAAM,CAEjG;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"receiver-command.d.ts","sourceRoot":"","sources":["../src/receiver-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,wBAAgB,0CAA0C,CAAC,OAAO,GAAE,MAAqB,GAAG,MAAM,CAEjG;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,EAAE,CAmBX"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAgDA,OAAO,EASL,KAAK,6BAA6B,EAMnC,MAAM,YAAY,CAAC;AAgqBpB,wBAAgB,oBAAoB,yCAEnC"}
|