paseo-plugin-zcode-provider 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/NOTICE.md +29 -0
- package/README.md +112 -2
- package/client/account.tsx +827 -0
- package/client/diagnostics.tsx +379 -0
- package/client/runtime.tsx +296 -0
- package/client/settings.tsx +84 -0
- package/client/value.tsx +20 -0
- package/icon.svg +1 -0
- package/index.client.tsx +22 -0
- package/index.server.ts +39 -0
- package/package.json +77 -4
- package/paseo-plugin.json +6 -0
- package/server/account.ts +781 -0
- package/server/attachments.ts +150 -0
- package/server/build-info.ts +2 -0
- package/server/conversation.ts +319 -0
- package/server/diagnostics.ts +252 -0
- package/server/discovery/discover.ts +225 -0
- package/server/discovery/manifest.ts +36 -0
- package/server/discovery/types.ts +30 -0
- package/server/errors.ts +36 -0
- package/server/host/bridge.ts +490 -0
- package/server/host/schemas.ts +119 -0
- package/server/logger.ts +19 -0
- package/server/mapping.ts +270 -0
- package/server/models.ts +68 -0
- package/server/persistence.ts +146 -0
- package/server/presentation.ts +470 -0
- package/server/provider-status.ts +81 -0
- package/server/provider.ts +838 -0
- package/server/runtime/managed.ts +501 -0
- package/server/runtime/pins.ts +72 -0
- package/server/runtime-setup.ts +77 -0
- package/server/session-types.ts +49 -0
- package/server/session.ts +866 -0
- package/server/status.ts +89 -0
- package/server/timeline.ts +18 -0
- package/server/usage.ts +362 -0
- package/server/vendor/zcode/LICENSE +202 -0
- package/server/vendor/zcode/NOTICE.md +70 -0
- package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
- package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
- package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
- package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
- package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
- package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
- package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
- package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
- package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
- package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
- package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
- package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
- package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
- package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
- package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
- package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
- package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
- package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
- package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
- package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
- package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
- package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
- package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
- package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
- package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
- package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
- package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
- package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
- package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
- package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
- package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
- package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
- package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
- package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
- package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
- package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
- package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
- package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
- package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
- package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
- package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
- package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
- package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
- package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
- package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
- package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
- package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
- package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
- package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
- package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
- package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
- package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
- package/server/vendor/zcode/provenance.json +405 -0
- package/shared/account.ts +201 -0
- package/shared/diagnostics.ts +52 -0
- package/shared/runtime-setup.ts +73 -0
- package/vendor/acp-registry/LICENSE +176 -0
- package/vendor/paseo/LICENSE +211 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 领域类型定义
|
|
3
|
+
*
|
|
4
|
+
* 说明:敏感信息(如 appSecret)以及 provider 默认端点配置
|
|
5
|
+
* 只允许放在 services 的 provider 模块中,不能放 shared 层。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** 内置 BigModel provider id */
|
|
9
|
+
export const BIGMODEL_PROVIDER_ID = "bigmodel" as const;
|
|
10
|
+
|
|
11
|
+
/** 内置 ZAI provider id */
|
|
12
|
+
export const ZAI_PROVIDER_ID = "zai" as const;
|
|
13
|
+
|
|
14
|
+
/** 凭据解密失败错误前缀 */
|
|
15
|
+
export const CREDENTIAL_DECRYPT_ERROR_PREFIX = "凭据解密失败:" as const;
|
|
16
|
+
|
|
17
|
+
/** 凭据解密失败稳定错误码 */
|
|
18
|
+
export const CREDENTIAL_DECRYPT_ERROR_CODE = "ZCODE_CREDENTIAL_DECRYPT_FAILED" as const;
|
|
19
|
+
|
|
20
|
+
/** 判断错误是否来自本地凭据解密失败 */
|
|
21
|
+
export function isCredentialDecryptError(error: unknown): boolean {
|
|
22
|
+
const code = readCredentialErrorCode(error);
|
|
23
|
+
if (code) {
|
|
24
|
+
return code === CREDENTIAL_DECRYPT_ERROR_CODE;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 兼容历史错误和跨边界丢失 code 的旧 payload;新错误应优先携带稳定 code。
|
|
28
|
+
if (readCredentialErrorMessage(error).startsWith(CREDENTIAL_DECRYPT_ERROR_PREFIX)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readCredentialErrorCode(error: unknown): string {
|
|
36
|
+
if (typeof error === "object" && error !== null && "code" in error) {
|
|
37
|
+
return String((error as { code?: unknown }).code ?? "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readCredentialErrorMessage(error: unknown): string {
|
|
44
|
+
if (error instanceof Error) {
|
|
45
|
+
return error.message;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
49
|
+
return String((error as { message?: unknown }).message ?? "");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** OAuth provider 标识 */
|
|
56
|
+
export type OAuthProviderId =
|
|
57
|
+
| typeof BIGMODEL_PROVIDER_ID
|
|
58
|
+
| typeof ZAI_PROVIDER_ID
|
|
59
|
+
| (string & { readonly __oauthProviderBrand?: never });
|
|
60
|
+
|
|
61
|
+
/** Provider 展示元信息 */
|
|
62
|
+
export interface OAuthProviderMeta {
|
|
63
|
+
id: OAuthProviderId;
|
|
64
|
+
displayName: string;
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
order: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 发起 OAuth 请求 */
|
|
70
|
+
export interface OAuthStartRequest {
|
|
71
|
+
provider: OAuthProviderId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** 发起 OAuth 返回 */
|
|
75
|
+
export interface OAuthStartResponse {
|
|
76
|
+
provider: OAuthProviderId;
|
|
77
|
+
authorizeUrl: string;
|
|
78
|
+
state: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 应用登录回调结果 */
|
|
82
|
+
export interface OAuthSessionCallbackResult {
|
|
83
|
+
kind: "session";
|
|
84
|
+
provider: OAuthProviderId;
|
|
85
|
+
userInfo: {
|
|
86
|
+
id: string;
|
|
87
|
+
username: string;
|
|
88
|
+
displayName: string;
|
|
89
|
+
avatarUrl?: string;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 只携带归因参数的 OAuth deep link 回调结果 */
|
|
94
|
+
export interface OAuthAttributionCallbackResult {
|
|
95
|
+
kind: "attribution";
|
|
96
|
+
provider: OAuthProviderId;
|
|
97
|
+
attribution: OAuthLoginAttribution;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** 同一登录已由 polling 完成后迟到的 deep link;调用方只需忽略。 */
|
|
101
|
+
export interface OAuthDuplicateCallbackResult {
|
|
102
|
+
kind: "duplicate";
|
|
103
|
+
provider: OAuthProviderId;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** OAuth 回调归一化结果 */
|
|
107
|
+
export type OAuthCallbackResult =
|
|
108
|
+
| OAuthSessionCallbackResult
|
|
109
|
+
| OAuthAttributionCallbackResult
|
|
110
|
+
| OAuthDuplicateCallbackResult;
|
|
111
|
+
|
|
112
|
+
/** Main 进程路由 deep link 时使用的 state 上报结构 */
|
|
113
|
+
export interface OAuthStateRegistration {
|
|
114
|
+
state: string;
|
|
115
|
+
provider?: OAuthProviderId;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** 归一化后的回调参数 */
|
|
119
|
+
export interface OAuthCallbackParams {
|
|
120
|
+
state: string;
|
|
121
|
+
code: string;
|
|
122
|
+
attribution?: OAuthLoginAttribution;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** OAuth 登录归因参数:来自官网中转页或投放链接 */
|
|
126
|
+
export interface OAuthLoginAttribution {
|
|
127
|
+
channel_id?: string;
|
|
128
|
+
utm_source?: string;
|
|
129
|
+
utm_campaign?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** 归一化 token 结构 */
|
|
133
|
+
export interface OAuthTokenSet {
|
|
134
|
+
accessToken: string;
|
|
135
|
+
refreshToken?: string;
|
|
136
|
+
expiresAt?: number;
|
|
137
|
+
zcodeJwtToken?: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface UserInfo {
|
|
141
|
+
id: string;
|
|
142
|
+
username: string;
|
|
143
|
+
displayName: string;
|
|
144
|
+
avatarUrl?: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export type OAuthCachedSessionRestoreResult =
|
|
148
|
+
| { status: "authenticated"; userInfo: UserInfo }
|
|
149
|
+
| { status: "signed-out" }
|
|
150
|
+
| { status: "reauthentication-required"; reason: "jwt-expired" };
|
|
151
|
+
|
|
152
|
+
/** Host 在检测到 ZCode JWT 失效后通知 Renderer 展示确认并重启。 */
|
|
153
|
+
export const ZCODE_JWT_INVALID_BROADCAST_CHANNEL = "auth:zcode-jwt-invalid";
|
|
154
|
+
|
|
155
|
+
export type JwtExpirationResult =
|
|
156
|
+
| { kind: "valid"; expiresAt: number }
|
|
157
|
+
| { kind: "expired"; expiresAt: number }
|
|
158
|
+
| { kind: "unknown" };
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 只解析 JWT 的 exp 来判断本地生命周期,不承担签名校验。
|
|
162
|
+
* 无法证明已过期的历史或非标准 token 保持兼容,最终有效性仍由服务端决定。
|
|
163
|
+
*/
|
|
164
|
+
export function resolveJwtExpiration(
|
|
165
|
+
token: string,
|
|
166
|
+
now = Date.now(),
|
|
167
|
+
clockSkewMs = 30_000,
|
|
168
|
+
): JwtExpirationResult {
|
|
169
|
+
try {
|
|
170
|
+
const payloadSegment = token.split(".")[1];
|
|
171
|
+
if (!payloadSegment) {
|
|
172
|
+
return { kind: "unknown" };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const normalized = payloadSegment.replace(/-/g, "+").replace(/_/g, "/");
|
|
176
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
|
|
177
|
+
const decoded = globalThis.atob(padded);
|
|
178
|
+
const bytes = Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
179
|
+
const payload = JSON.parse(new TextDecoder().decode(bytes)) as { exp?: unknown };
|
|
180
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp) || payload.exp <= 0) {
|
|
181
|
+
return { kind: "unknown" };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const expiresAt = payload.exp * 1_000;
|
|
185
|
+
return now + Math.max(0, clockSkewMs) >= expiresAt
|
|
186
|
+
? { kind: "expired", expiresAt }
|
|
187
|
+
: { kind: "valid", expiresAt };
|
|
188
|
+
} catch {
|
|
189
|
+
return { kind: "unknown" };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** 归一化用户信息 */
|
|
194
|
+
export interface OAuthUserProfile {
|
|
195
|
+
id: string;
|
|
196
|
+
username: string;
|
|
197
|
+
displayName: string;
|
|
198
|
+
avatarUrl?: string;
|
|
199
|
+
rawProfile?: unknown;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** 登出范围 */
|
|
203
|
+
export type OAuthLogoutScope = "active" | "all";
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/* ZCode 官方 Server MCP 鉴权的共享常量与类型。
|
|
2
|
+
放在 shared 是因为头集合有两个消费者且分属不同包:
|
|
3
|
+
- `packages/services` 侧生产身份头;
|
|
4
|
+
- `apps/zcode-cli/packages/adapters` 侧(Plugin parser + MCP adapter)拦截保留头。
|
|
5
|
+
两侧必须同源,否则新增身份头时会漏掉黑名单,出现静态 header 覆盖凭证的缺口。 */
|
|
6
|
+
|
|
7
|
+
/** `.mcp.json` 中 `auth.type` 的唯一合法值;区分大小写,不接受别名。 */
|
|
8
|
+
export const ZCODE_OFFICIAL_MCP_AUTH_TYPE = "zcode_official" as const;
|
|
9
|
+
|
|
10
|
+
/** 第一阶段唯一合法的 provider。后续新增短期 Token 应新增 provider 值,不改变本值语义。 */
|
|
11
|
+
export const ZCODE_OFFICIAL_MCP_AUTH_PROVIDER_JWT_TOKEN = "jwt_token" as const;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 官方 MCP 使用用户身份和套餐身份两组独立凭据。
|
|
15
|
+
* codingPlanAuthorization 必须携带 MaaS 登录 JWT;不能用 Coding Plan 业务 API key
|
|
16
|
+
* 代替。服务端按 JWT 中的 customer_id 校验它与当前用户的关联。
|
|
17
|
+
*/
|
|
18
|
+
export const OFFICIAL_MCP_AUTH_HEADER_NAMES = {
|
|
19
|
+
authorization: "Authorization",
|
|
20
|
+
codingPlanAuthorization: "X-Bigmodel-Authorization",
|
|
21
|
+
targetType: "Bigmodel-Target-Type",
|
|
22
|
+
organization: "Bigmodel-Organization",
|
|
23
|
+
project: "Bigmodel-Project",
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* stdio 官方 MCP 的身份头在所有出站请求与通知的 `params._meta` 上使用的键。
|
|
28
|
+
*
|
|
29
|
+
* 这是与插件进程之间的**跨语言协议常量**——Plugin 侧(如插件的 Python server)
|
|
30
|
+
* 按同一字符串读取。改名即破坏所有已发布插件,等同于协议 breaking change。
|
|
31
|
+
* 命名空间前缀沿用 `com.zcode/`,与既有的 `com.zcode/request-context` 一致。
|
|
32
|
+
*/
|
|
33
|
+
export const OFFICIAL_MCP_AUTH_META_KEY = "com.zcode/official-mcp-auth" as const;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 静态 Plugin `headers` 中禁止出现的保留头(小写,比较时大小写不敏感)。
|
|
37
|
+
*
|
|
38
|
+
* 身份头部分:避免 Plugin 静态配置伪造或覆盖凭证。
|
|
39
|
+
* 协议头部分(mcp-session-id / mcp-protocol-version):SDK 组装请求头时
|
|
40
|
+
* `requestInit.headers` 优先级高于协议头,静态配置能覆盖 session id,故一并禁止。
|
|
41
|
+
*
|
|
42
|
+
* `x-coding-plan-api-key` 已不再由客户端发送(已切到 MaaS JWT 通道),但**必须继续留在
|
|
43
|
+
* 黑名单里**:服务端那条通道仍然有效(`credential := cmp.Or(Authorization, APIKey)`),
|
|
44
|
+
* 放开就等于允许 Plugin 用静态 header 自带一份 Coding Plan 凭证冒用官方端点。
|
|
45
|
+
* 这里显式列出而不是从头名表推导,正是因为表里已经没有它了。
|
|
46
|
+
*/
|
|
47
|
+
export const OFFICIAL_MCP_RESERVED_HEADER_NAMES: readonly string[] = [
|
|
48
|
+
...Object.values(OFFICIAL_MCP_AUTH_HEADER_NAMES).map((name) => name.toLowerCase()),
|
|
49
|
+
"x-coding-plan-api-key",
|
|
50
|
+
"mcp-session-id",
|
|
51
|
+
"mcp-protocol-version",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const RESERVED_HEADER_SET = new Set(OFFICIAL_MCP_RESERVED_HEADER_NAMES);
|
|
55
|
+
|
|
56
|
+
/** 大小写不敏感地判断是否为保留头。 */
|
|
57
|
+
export function isOfficialMcpReservedHeaderName(name: string): boolean {
|
|
58
|
+
return RESERVED_HEADER_SET.has(name.trim().toLowerCase());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 返回静态 header 记录中命中的保留头(小写,去重且稳定排序),无命中时为空数组。 */
|
|
62
|
+
export function findOfficialMcpReservedHeaders(
|
|
63
|
+
headers: Record<string, string> | undefined,
|
|
64
|
+
): string[] {
|
|
65
|
+
if (!headers) return [];
|
|
66
|
+
const hits = new Set<string>();
|
|
67
|
+
for (const name of Object.keys(headers)) {
|
|
68
|
+
const normalized = name.trim().toLowerCase();
|
|
69
|
+
if (RESERVED_HEADER_SET.has(normalized)) hits.add(normalized);
|
|
70
|
+
}
|
|
71
|
+
return [...hits].sort();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** 服务端 `Bigmodel-Target-Type` 的取值(对齐 zcode-server 的 CodingPlanTargetType)。 */
|
|
75
|
+
export type OfficialMcpTargetType = "PERSONAL" | "TEAM";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 端口/协议层的失败分类("不发任何请求"的两类)。
|
|
79
|
+
* 网络层失败(401/403/3xx)由 MCP adapter 在收到响应后自行分类,不经过该枚举。
|
|
80
|
+
*/
|
|
81
|
+
export const OFFICIAL_MCP_AUTH_FAILURE_REASONS = [
|
|
82
|
+
"official_auth_unavailable",
|
|
83
|
+
"official_auth_plan_required",
|
|
84
|
+
] as const;
|
|
85
|
+
|
|
86
|
+
export type OfficialMcpAuthFailureReason = (typeof OFFICIAL_MCP_AUTH_FAILURE_REASONS)[number];
|
|
87
|
+
|
|
88
|
+
export const OFFICIAL_MCP_AUTH_PORT_FAILURE_REASONS = [
|
|
89
|
+
...OFFICIAL_MCP_AUTH_FAILURE_REASONS,
|
|
90
|
+
"official_mcp_origin_untrusted",
|
|
91
|
+
] as const;
|
|
92
|
+
|
|
93
|
+
export type OfficialMcpAuthPortFailureReason =
|
|
94
|
+
(typeof OFFICIAL_MCP_AUTH_PORT_FAILURE_REASONS)[number];
|
|
95
|
+
|
|
96
|
+
/** MCP adapter 侧的完整失败分类,包含网络层结果。仅用于 record status 与日志。 */
|
|
97
|
+
export type OfficialMcpAuthFailureKind =
|
|
98
|
+
| OfficialMcpAuthFailureReason
|
|
99
|
+
| "official_mcp_origin_untrusted"
|
|
100
|
+
| "official_auth_rejected"
|
|
101
|
+
| "official_auth_forbidden"
|
|
102
|
+
| "official_auth_redirect_blocked";
|
|
103
|
+
|
|
104
|
+
// ── 官方 MCP 信任判定──
|
|
105
|
+
// 放在 shared 而非 CLI bootstrap,是因为有两个消费者且分属互不可见的包:
|
|
106
|
+
// - apps/zcode-cli/packages/adapters:请求发出前的本地校验;
|
|
107
|
+
// - packages/services(host):身份权威边界的二次校验(只依赖 @zcode/shared,
|
|
108
|
+
// 无法 import CLI 侧包)。
|
|
109
|
+
// 单源是硬要求:双处判定分叉会让一侧放行、另一侧拒绝。
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 归一化 origin:必须是 https、无 username/password,且 URL 本身即 origin 形态。
|
|
113
|
+
* 拒绝带凭证的 URL 是因为 `https://user:pass@a.example` 的 origin 是 `https://a.example`,
|
|
114
|
+
* 只比 origin 会让它通过。
|
|
115
|
+
*/
|
|
116
|
+
function normalizeHttpsOrigin(candidate: string): string | undefined {
|
|
117
|
+
try {
|
|
118
|
+
const url = new URL(candidate);
|
|
119
|
+
if (url.username !== "" || url.password !== "") return undefined;
|
|
120
|
+
return url.protocol === "https:" ? url.origin : undefined;
|
|
121
|
+
} catch {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeLoopbackOrigin(candidate: string): string | undefined {
|
|
127
|
+
try {
|
|
128
|
+
const url = new URL(candidate);
|
|
129
|
+
if (url.username !== "" || url.password !== "") return undefined;
|
|
130
|
+
const loopback =
|
|
131
|
+
url.hostname === "127.0.0.1" || url.hostname === "localhost" || url.hostname === "[::1]";
|
|
132
|
+
return url.protocol === "http:" && loopback ? url.origin : undefined;
|
|
133
|
+
} catch {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const OFFICIAL_MCP_DEV_TRUSTED_ORIGINS_ENV = "ZCODE_OFFICIAL_MCP_DEV_TRUSTED_ORIGINS";
|
|
139
|
+
|
|
140
|
+
/** Host 在 spawn 时注入的真实 workspace identity;只用于隔离/审计,不用于文件执行。 */
|
|
141
|
+
export const ZCODE_WORKSPACE_IDENTITY_ENV = "ZCODE_WORKSPACE_IDENTITY";
|
|
142
|
+
|
|
143
|
+
/** 身份头的安全日志摘要:只含 header 名、Team 成对性与 TargetType,不含任何值。 */
|
|
144
|
+
export function summarizeOfficialMcpIdentityHeaders(
|
|
145
|
+
headers: Record<string, string>,
|
|
146
|
+
): Record<string, unknown> {
|
|
147
|
+
const lower = new Map(
|
|
148
|
+
Object.entries(headers).map(([name, value]) => [name.toLowerCase(), value]),
|
|
149
|
+
);
|
|
150
|
+
const organization = lower.has("bigmodel-organization");
|
|
151
|
+
const project = lower.has("bigmodel-project");
|
|
152
|
+
return {
|
|
153
|
+
identityHeaderNames: [...lower.keys()].sort(),
|
|
154
|
+
identityOrganizationPresent: organization,
|
|
155
|
+
identityProjectPresent: project,
|
|
156
|
+
identityTeamPaired: organization === project,
|
|
157
|
+
...(lower.get("bigmodel-target-type")
|
|
158
|
+
? { identityTargetType: lower.get("bigmodel-target-type") }
|
|
159
|
+
: {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface OfficialMcpTrustResult {
|
|
164
|
+
trusted: boolean;
|
|
165
|
+
/** 拒绝时的可读原因,仅用于日志,不用于流程分流。 */
|
|
166
|
+
detail: "ok" | "invalid_input" | "origin_mismatch" | "zcode_origin_unresolved";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface IsOfficialMcpOriginTrustedInput {
|
|
170
|
+
/** 本地自测开关的原始值(通常来自 env),只放开 http loopback。 */
|
|
171
|
+
devTrustedOriginsRaw?: string | undefined;
|
|
172
|
+
origin: string;
|
|
173
|
+
/**
|
|
174
|
+
* 声明该 MCP 的插件 id。**不参与信任判定**,仅用于日志与凭证解析的
|
|
175
|
+
* 归属标识。保留在入参里是为了让日志能回答"是哪个插件在要凭证"。
|
|
176
|
+
*/
|
|
177
|
+
pluginId: string;
|
|
178
|
+
/** 当前 ZCode API origin,由调用方按各自口径解析后传入。 */
|
|
179
|
+
zcodeApiOrigin: string | undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 校验官方 MCP 的凭据目标:要求 HTTPS origin 与运行时 ZCode API origin 相等,
|
|
184
|
+
* 且 URL 不携带 username/password。开发配置只允许显式列出的 HTTP loopback origin。
|
|
185
|
+
*
|
|
186
|
+
* pluginId 用于归属和日志,不是授权过滤条件;任何已加载插件都可以请求官方鉴权。
|
|
187
|
+
* 目的地校验不能替代逐接口的用户权限、套餐和配额校验,也不提供逐插件授权确认。
|
|
188
|
+
* 使用 stdio 鉴权的插件会持有凭据,应按受信任的可执行代码管理。
|
|
189
|
+
*/
|
|
190
|
+
export function isOfficialMcpOriginTrusted(
|
|
191
|
+
input: IsOfficialMcpOriginTrustedInput,
|
|
192
|
+
): OfficialMcpTrustResult {
|
|
193
|
+
const origin = input.origin.trim();
|
|
194
|
+
if (!origin) {
|
|
195
|
+
return { detail: "invalid_input", trusted: false };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 本地自测开关:只接受 http loopback,因此无法把凭证导向远端。
|
|
199
|
+
// 必须先确认**目标 origin 本身是 loopback**:否则两侧 normalize 都得到 undefined,
|
|
200
|
+
// `undefined === undefined` 会让该开关放开任意 origin(含 https 远端站点)。
|
|
201
|
+
const loopbackOrigin = normalizeLoopbackOrigin(origin);
|
|
202
|
+
if (loopbackOrigin) {
|
|
203
|
+
for (const candidate of parseDevTrustedOrigins(input.devTrustedOriginsRaw)) {
|
|
204
|
+
if (normalizeLoopbackOrigin(candidate) === loopbackOrigin) {
|
|
205
|
+
return { detail: "ok", trusted: true };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const expected = input.zcodeApiOrigin ? normalizeHttpsOrigin(input.zcodeApiOrigin) : undefined;
|
|
211
|
+
if (!expected) return { detail: "zcode_origin_unresolved", trusted: false };
|
|
212
|
+
if (normalizeHttpsOrigin(origin) !== expected) {
|
|
213
|
+
return { detail: "origin_mismatch", trusted: false };
|
|
214
|
+
}
|
|
215
|
+
return { detail: "ok", trusted: true };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 本地自测开关,值为逗号分隔的 loopback origin,例如 `http://127.0.0.1:3999`。
|
|
220
|
+
* 只对 http loopback 生效:最坏情况是把自己的 JWT 发给本机进程,而本机任意程序本来就能
|
|
221
|
+
* 读到同一份凭证,不构成新的信任面扩张。不设置时行为与未实现该开关时完全一致。
|
|
222
|
+
*/
|
|
223
|
+
function parseDevTrustedOrigins(raw: string | undefined): string[] {
|
|
224
|
+
const value = raw?.trim();
|
|
225
|
+
if (!value) return [];
|
|
226
|
+
return value
|
|
227
|
+
.split(",")
|
|
228
|
+
.map((item) => item.trim())
|
|
229
|
+
.filter(Boolean);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 信任判定器。保持 `isTrusted` 这一 DI 形状不变,便于 adapter 与 host 共用同一实现。
|
|
234
|
+
* 异步是为了让 host 能按 settings 覆盖解析 origin(与闲时任务同口径),避免出现
|
|
235
|
+
* "闲时任务能连、官方 MCP 连不上"的割裂。
|
|
236
|
+
*/
|
|
237
|
+
export interface OfficialMcpTrustedOriginRegistry {
|
|
238
|
+
isTrusted(input: {
|
|
239
|
+
mcpKey: string;
|
|
240
|
+
origin: string;
|
|
241
|
+
pluginId: string;
|
|
242
|
+
}): Promise<OfficialMcpTrustResult>;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface CreateOfficialMcpTrustedOriginRegistryOptions {
|
|
246
|
+
/** 本地自测开关原始值(逗号分隔的 loopback origin),通常来自 env。 */
|
|
247
|
+
devTrustedOriginsRaw?: string | undefined;
|
|
248
|
+
/** 当前 ZCode API origin 的解析器;两侧必须用等价口径,否则会一侧放行一侧拒绝。 */
|
|
249
|
+
resolveZCodeApiOrigin: () => string | undefined | Promise<string | undefined>;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function createOfficialMcpTrustedOriginRegistry(
|
|
253
|
+
options: CreateOfficialMcpTrustedOriginRegistryOptions,
|
|
254
|
+
): OfficialMcpTrustedOriginRegistry {
|
|
255
|
+
return {
|
|
256
|
+
async isTrusted({ origin, pluginId }) {
|
|
257
|
+
let zcodeApiOrigin: string | undefined;
|
|
258
|
+
try {
|
|
259
|
+
zcodeApiOrigin = await options.resolveZCodeApiOrigin();
|
|
260
|
+
} catch {
|
|
261
|
+
// 解析失败按不可信处理,绝不因为拿不到 origin 就放行。
|
|
262
|
+
return { detail: "zcode_origin_unresolved", trusted: false };
|
|
263
|
+
}
|
|
264
|
+
return isOfficialMcpOriginTrusted({
|
|
265
|
+
devTrustedOriginsRaw: options.devTrustedOriginsRaw,
|
|
266
|
+
origin,
|
|
267
|
+
pluginId,
|
|
268
|
+
zcodeApiOrigin,
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* 官方 Server MCP 在 tool 调用被拦截 / 失败时下发的结构化标识。
|
|
2
|
+
*
|
|
3
|
+
* 服务端把它渲染成 **tool error content 的 JSON 文本**(不是 `_meta`):
|
|
4
|
+
* `{"error_code":"quota_exceeded","message":"...","request_id":"..."}`
|
|
5
|
+
* 见 zcode-server `internal/domain/servermcp/toolerror.go` 的 `ToolError.Error()`。
|
|
6
|
+
*
|
|
7
|
+
* 放在 shared 是因为有三个分属不同包的消费者:
|
|
8
|
+
* - `apps/zcode-cli/packages/core`:解析 MCP 结果,把 code 带进 tool result display;
|
|
9
|
+
* - `packages/shared/src/zcode-protocol-v4/rows.ts`:row schema 校验该 code;
|
|
10
|
+
* - `packages/ui`:按 code 决定输入框上方提示的文案与动作。
|
|
11
|
+
* 三处必须同源,否则新增 code 时会出现一侧识别、另一侧丢弃。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 客户端会据此改变界面行为的 code。
|
|
16
|
+
*
|
|
17
|
+
* 服务端还有 `internal_error`,**故意不在这里**:它是"其它一切失败"的兜底掩码
|
|
18
|
+
* (详情只留在服务端日志),用户无法自助解决,不该弹提示。
|
|
19
|
+
*/
|
|
20
|
+
export const OFFICIAL_MCP_TOOL_ERROR_CODES = ["quota_exceeded", "coding_plan_required"] as const;
|
|
21
|
+
|
|
22
|
+
export type OfficialMcpToolErrorCode = (typeof OFFICIAL_MCP_TOOL_ERROR_CODES)[number];
|
|
23
|
+
|
|
24
|
+
const OFFICIAL_MCP_TOOL_ERROR_CODE_SET = new Set<string>(OFFICIAL_MCP_TOOL_ERROR_CODES);
|
|
25
|
+
|
|
26
|
+
export interface OfficialMcpToolError {
|
|
27
|
+
code: OfficialMcpToolErrorCode;
|
|
28
|
+
/** 服务端给的英文可读文案;仅用于日志与排障,界面文案走 i18n。 */
|
|
29
|
+
message?: string;
|
|
30
|
+
/** 服务端 request id,便于与后端日志对账。 */
|
|
31
|
+
requestId?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readString(value: unknown): string | undefined {
|
|
35
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 从 tool error 文本里解析结构化标识。
|
|
40
|
+
*
|
|
41
|
+
* 严格解析:必须是 JSON 对象且 `error_code` 命中已知 code,否则返回 undefined。
|
|
42
|
+
* 不做文案匹配兜底——那会让服务端改一句话就静默失效。
|
|
43
|
+
*/
|
|
44
|
+
export function parseOfficialMcpToolError(text: string): OfficialMcpToolError | undefined {
|
|
45
|
+
const trimmed = text.trim();
|
|
46
|
+
// 先按首字符快速排除绝大多数普通错误文本,避免每次失败都进 JSON.parse。
|
|
47
|
+
if (!trimmed.startsWith("{")) return undefined;
|
|
48
|
+
|
|
49
|
+
let payload: unknown;
|
|
50
|
+
try {
|
|
51
|
+
payload = JSON.parse(trimmed) as unknown;
|
|
52
|
+
} catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
if (typeof payload !== "object" || payload === null) return undefined;
|
|
56
|
+
|
|
57
|
+
const record = payload as Record<string, unknown>;
|
|
58
|
+
const code = readString(record.error_code);
|
|
59
|
+
if (!code || !OFFICIAL_MCP_TOOL_ERROR_CODE_SET.has(code)) return undefined;
|
|
60
|
+
|
|
61
|
+
const message = readString(record.message);
|
|
62
|
+
const requestId = readString(record.request_id);
|
|
63
|
+
return {
|
|
64
|
+
code: code as OfficialMcpToolErrorCode,
|
|
65
|
+
...(message ? { message } : {}),
|
|
66
|
+
...(requestId ? { requestId } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// 启动早期或协议故障时 stdout 尚不可用,进程诊断使用独立的 stderr 单行契约。
|
|
4
|
+
export const ZCODE_PROCESS_DIAGNOSTIC_PREFIX = "[zcode-process-exception] ";
|
|
5
|
+
export const ZCODE_PROCESS_DIAGNOSTIC_NAME_MAX_CHARS = 128;
|
|
6
|
+
export const ZCODE_PROCESS_DIAGNOSTIC_MESSAGE_MAX_CHARS = 4_000;
|
|
7
|
+
export const ZCODE_PROCESS_DIAGNOSTIC_STACK_MAX_CHARS = 16_000;
|
|
8
|
+
export const ZCODE_PROCESS_DIAGNOSTIC_MAX_LINE_CHARS = 128 * 1024;
|
|
9
|
+
export const ZCODE_AGENT_LIFECYCLE_LOG_MARKER = "[zcode-agent-lifecycle-reported]";
|
|
10
|
+
|
|
11
|
+
const processErrorKindSchema = z.enum(["uncaughtException", "unhandledRejection"]);
|
|
12
|
+
export const zcodeProcessDiagnosticSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
version: z.literal(1),
|
|
15
|
+
errorId: z.uuid(),
|
|
16
|
+
kind: processErrorKindSchema,
|
|
17
|
+
origin: processErrorKindSchema,
|
|
18
|
+
name: z.string().min(1).max(ZCODE_PROCESS_DIAGNOSTIC_NAME_MAX_CHARS),
|
|
19
|
+
message: z.string().max(ZCODE_PROCESS_DIAGNOSTIC_MESSAGE_MAX_CHARS),
|
|
20
|
+
stack: z.string().max(ZCODE_PROCESS_DIAGNOSTIC_STACK_MAX_CHARS).optional(),
|
|
21
|
+
occurredAt: z.number().int().nonnegative(),
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
export type ZCodeProcessDiagnostic = z.infer<typeof zcodeProcessDiagnosticSchema>;
|
|
25
|
+
|
|
26
|
+
export function parseZCodeProcessDiagnostic(line: string): ZCodeProcessDiagnostic | undefined {
|
|
27
|
+
if (
|
|
28
|
+
!line.startsWith(ZCODE_PROCESS_DIAGNOSTIC_PREFIX) ||
|
|
29
|
+
line.length > ZCODE_PROCESS_DIAGNOSTIC_MAX_LINE_CHARS
|
|
30
|
+
) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const result = zcodeProcessDiagnosticSchema.safeParse(
|
|
35
|
+
JSON.parse(line.slice(ZCODE_PROCESS_DIAGNOSTIC_PREFIX.length)),
|
|
36
|
+
);
|
|
37
|
+
return result.success ? result.data : undefined;
|
|
38
|
+
} catch {
|
|
39
|
+
// 诊断旁路不得因损坏帧中断业务协议或退出处理。
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|