7月19提交:发布首个上游请求追踪插件
This commit is contained in:
+11
-1
@@ -1,4 +1,14 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"plugins": []
|
"plugins": [
|
||||||
|
{
|
||||||
|
"id": "request-trace",
|
||||||
|
"name": "上游请求追踪",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "为上游请求加入不含敏感信息的部署追踪标记,便于确认插件链和网关路由。",
|
||||||
|
"manifest_url": "https://git.9nn.cn/xiaodong/99code-plugins/raw/branch/main/request-trace/manifest.json",
|
||||||
|
"platforms": ["openai", "codex", "anthropic", "gemini", "antigravity", "grok", "xai", "kimi"],
|
||||||
|
"sha256": "20639523163a8253b8589dbf65ded5c1efe22c5534093e5ce729c9d181b08630"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# 请求追踪插件
|
||||||
|
|
||||||
|
这是 99code 插件商店的首个可安装生产插件。启用后,它会在上游请求中加入不含敏感信息的 `X-99Code-Plugin: request-trace/1.0.0` 标记,便于在反向代理、网关和上游抓包中确认插件链确实生效。
|
||||||
|
|
||||||
|
可在插件中心配置:
|
||||||
|
|
||||||
|
- `header_name`:必须以 `X-` 开头,最长 64 字符。
|
||||||
|
- `header_value`:最长 128 字符,禁止换行。
|
||||||
|
|
||||||
|
该插件不会读取、记录或上传账号凭据。
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "request-trace",
|
||||||
|
"name": "上游请求追踪",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "为上游请求加入不含敏感信息的部署追踪标记,便于确认插件链和网关路由。",
|
||||||
|
"entrypoint": "com.ninecode.plugins.RequestTracePlugin",
|
||||||
|
"platforms": [
|
||||||
|
"openai",
|
||||||
|
"codex",
|
||||||
|
"anthropic",
|
||||||
|
"gemini",
|
||||||
|
"antigravity",
|
||||||
|
"grok",
|
||||||
|
"xai",
|
||||||
|
"kimi"
|
||||||
|
],
|
||||||
|
"artifact": {
|
||||||
|
"url": "https://git.9nn.cn/xiaodong/99code-plugins/raw/branch/main/request-trace/request-trace-1.0.0.jar",
|
||||||
|
"sha256": "20639523163a8253b8589dbf65ded5c1efe22c5534093e5ce729c9d181b08630",
|
||||||
|
"signature": "",
|
||||||
|
"key_id": "",
|
||||||
|
"size": 2457
|
||||||
|
},
|
||||||
|
"credential_sync": null,
|
||||||
|
"metadata": {
|
||||||
|
"category": "observability",
|
||||||
|
"homepage": "https://git.9nn.cn/xiaodong/99code-plugins"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,63 @@
|
|||||||
|
package com.ninecode.plugins;
|
||||||
|
|
||||||
|
import com.ninecode.app.plugin.PluginRelayExchange;
|
||||||
|
import com.ninecode.app.plugin.ProviderPlugin;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Adds a non-sensitive deployment marker to outbound provider requests. */
|
||||||
|
public final class RequestTracePlugin implements ProviderPlugin {
|
||||||
|
private static final Set<String> SUPPORTED_PLATFORMS = Set.of(
|
||||||
|
"openai", "codex", "anthropic", "gemini", "antigravity", "grok", "xai", "kimi"
|
||||||
|
);
|
||||||
|
private String headerName = "X-99Code-Plugin";
|
||||||
|
private String headerValue = "request-trace/1.0.0";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String id() {
|
||||||
|
return "request-trace";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> platforms() {
|
||||||
|
return SUPPORTED_PLATFORMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Map<String, Object> configuration) {
|
||||||
|
String configuredName = text(configuration, "header_name");
|
||||||
|
String configuredValue = text(configuration, "header_value");
|
||||||
|
if (!configuredName.isBlank()) {
|
||||||
|
if (!configuredName.toLowerCase(Locale.ROOT).startsWith("x-") || configuredName.length() > 64) {
|
||||||
|
throw new IllegalArgumentException("header_name 必须是长度不超过 64 的 X- 自定义请求头");
|
||||||
|
}
|
||||||
|
headerName = configuredName;
|
||||||
|
}
|
||||||
|
if (!configuredValue.isBlank()) {
|
||||||
|
if (configuredValue.length() > 128 || configuredValue.contains("\r") || configuredValue.contains("\n")) {
|
||||||
|
throw new IllegalArgumentException("header_value 不能为空、换行或超过 128 个字符");
|
||||||
|
}
|
||||||
|
headerValue = configuredValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluginRelayExchange beforeRequest(PluginRelayExchange exchange) {
|
||||||
|
Map<String, List<String>> headers = new LinkedHashMap<>(exchange.headers());
|
||||||
|
headers.putIfAbsent(headerName, List.of(headerValue));
|
||||||
|
return new PluginRelayExchange(
|
||||||
|
exchange.pluginId(), exchange.platform(), exchange.accountId(), exchange.model(), exchange.method(),
|
||||||
|
exchange.uri(), headers, exchange.body(), exchange.statusCode()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String text(Map<String, Object> configuration, String key) {
|
||||||
|
if (configuration == null || configuration.get(key) == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return String.valueOf(configuration.get(key)).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user