@numa-tech/numa 1.26.8 → 1.26.9
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 +13 -2
- package/dist/ai-registry/commands.js +192 -129
- package/dist/ai-registry/commands.js.map +1 -1
- package/dist/ai-registry/install-conflicts.d.ts +9 -0
- package/dist/ai-registry/install-conflicts.js +59 -0
- package/dist/ai-registry/install-conflicts.js.map +1 -0
- package/dist/ai-registry/install-defaults.js +3 -2
- package/dist/ai-registry/install-defaults.js.map +1 -1
- package/dist/cli.js +39 -28
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.js +10 -3
- package/dist/command-catalog.js.map +1 -1
- package/dist/interactive/entry.js +1 -1
- package/dist/interactive/entry.js.map +1 -1
- package/dist/interactive/prompts.js +1 -1
- package/dist/interactive/prompts.js.map +1 -1
- package/dist/interactive/registry.d.ts +15 -0
- package/dist/interactive/registry.js +51 -2
- package/dist/interactive/registry.js.map +1 -1
- package/dist/jev/client.d.ts +17 -0
- package/dist/jev/client.js +127 -0
- package/dist/jev/client.js.map +1 -0
- package/dist/jev/commands.d.ts +8 -0
- package/dist/jev/commands.js +40 -0
- package/dist/jev/commands.js.map +1 -0
- package/dist/jev/errors.d.ts +39 -0
- package/dist/jev/errors.js +48 -0
- package/dist/jev/errors.js.map +1 -0
- package/dist/jev/input.d.ts +11 -0
- package/dist/jev/input.js +126 -0
- package/dist/jev/input.js.map +1 -0
- package/dist/jev/profile.d.ts +3 -0
- package/dist/jev/profile.js +26 -0
- package/dist/jev/profile.js.map +1 -0
- package/dist/jev/response.d.ts +3 -0
- package/dist/jev/response.js +38 -0
- package/dist/jev/response.js.map +1 -0
- package/dist/kb/web-page.js +70 -13
- package/dist/kb/web-page.js.map +1 -1
- package/dist/kb/web-queue.d.ts +3 -0
- package/dist/kb/web-queue.js +21 -1
- package/dist/kb/web-queue.js.map +1 -1
- package/dist/kb/web.js +12 -0
- package/dist/kb/web.js.map +1 -1
- package/dist/mcp-gateway/runtime.d.ts +95 -0
- package/dist/mcp-gateway/runtime.js +25 -3
- package/dist/mcp-gateway/runtime.js.map +1 -1
- package/docs/design/kb-web-workspace.md +19 -1
- package/docs/module-maintenance-and-skills-updates.md +18 -0
- package/examples/jev/README.md +6 -2
- package/examples/oss/__pycache__/roundtrip.cpython-314.pyc +0 -0
- package/examples/oss/__pycache__/test_roundtrip.cpython-314.pyc +0 -0
- package/package.json +4 -4
- package/skills/numa-cli/SKILL.md +10 -6
- package/skills/numa-cli/evals/evals.json +27 -0
- package/skills/numa-cli/numa-skill.json +1 -1
- package/skills/numa-cli/references/authoring.md +29 -0
- package/skills/numa-cli/references/cli-bootstrap.md +3 -3
- package/skills/numa-jev/SKILL.md +11 -9
- package/skills/numa-jev/numa-skill.json +5 -2
- package/skills/numa-jev/references/application-integration.md +2 -2
- package/skills/numa-jev/references/cli-usage.md +62 -0
- package/skills/numa-jev/references/mcp-usage.md +3 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AuthConfig } from "../config.js";
|
|
2
|
+
import { type KeycloakTokenProvider } from "../oauth.js";
|
|
3
|
+
import { type JevInput, type JsonObject } from "./input.js";
|
|
4
|
+
export interface JevDependencies {
|
|
5
|
+
config?: () => AuthConfig;
|
|
6
|
+
initializeProfile?: () => Promise<void>;
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
tokenProvider?: KeycloakTokenProvider;
|
|
9
|
+
}
|
|
10
|
+
/** A decision is one platform HTTP POST. Availability and authorization are reported by that request. */
|
|
11
|
+
export declare class JevClient {
|
|
12
|
+
private readonly dependencies;
|
|
13
|
+
constructor(dependencies?: JevDependencies);
|
|
14
|
+
private request;
|
|
15
|
+
capabilities(): Promise<JsonObject>;
|
|
16
|
+
decide(input: JevInput): Promise<JsonObject>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { resolveBackendUrl } from "../backend.js";
|
|
2
|
+
import { ensureAccessToken, fetchWithKeycloakIdentity } from "../oauth.js";
|
|
3
|
+
import { initializePlatformProfile } from "../platform-profile.js";
|
|
4
|
+
import { platformJevTarget } from "./profile.js";
|
|
5
|
+
import { JevError } from "./errors.js";
|
|
6
|
+
import { object, parseJevInput } from "./input.js";
|
|
7
|
+
import { validateJevResponse } from "./response.js";
|
|
8
|
+
const knownCodes = new Set([
|
|
9
|
+
"JEV_ACCESS_DENIED", "JEV_AUTHENTICATION_REQUIRED", "JEV_PERMISSION_UNAVAILABLE", "JEV_INVALID_REQUEST",
|
|
10
|
+
"JEV_REQUEST_TOO_LARGE", "JEV_INVALID_RESPONSE", "JEV_NOT_CONFIGURED", "JEV_BUSY", "JEV_AUDIT_UNAVAILABLE",
|
|
11
|
+
"JEV_BALANCE_INSUFFICIENT", "JEV_RATE_LIMITED", "JEV_PROXY_AUTH_REQUIRED", "JEV_UPSTREAM_FORBIDDEN",
|
|
12
|
+
"JEV_UPSTREAM_REJECTED", "JEV_UPSTREAM_TIMEOUT", "JEV_UPSTREAM_UNAUTHORIZED", "JEV_UPSTREAM_UNAVAILABLE", "JEV_UNAVAILABLE",
|
|
13
|
+
"OUTBOUND_PROXY_REQUIRED", "OUTBOUND_PROXY_UNAVAILABLE", "OUTBOUND_PROXY_AUTH_REQUIRED", "OUTBOUND_CONNECT_FAILED",
|
|
14
|
+
"OUTBOUND_CREDENTIAL_UNAVAILABLE", "OUTBOUND_INVALID_CONFIGURATION",
|
|
15
|
+
]);
|
|
16
|
+
const uncertainCodes = new Set(["JEV_AUDIT_UNAVAILABLE", "JEV_UNAVAILABLE", "JEV_UPSTREAM_UNAVAILABLE", "JEV_UPSTREAM_TIMEOUT", "JEV_INVALID_RESPONSE"]);
|
|
17
|
+
async function readBounded(response, limit) {
|
|
18
|
+
const reader = response.body?.getReader();
|
|
19
|
+
if (!reader)
|
|
20
|
+
throw new Error("empty response");
|
|
21
|
+
const chunks = [];
|
|
22
|
+
let size = 0;
|
|
23
|
+
try {
|
|
24
|
+
while (true) {
|
|
25
|
+
const { value, done } = await reader.read();
|
|
26
|
+
if (done)
|
|
27
|
+
break;
|
|
28
|
+
size += value.length;
|
|
29
|
+
if (size > limit)
|
|
30
|
+
throw new Error("response limit");
|
|
31
|
+
chunks.push(value);
|
|
32
|
+
}
|
|
33
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)));
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await reader.cancel().catch(() => undefined);
|
|
37
|
+
reader.releaseLock();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** A decision is one platform HTTP POST. Availability and authorization are reported by that request. */
|
|
41
|
+
export class JevClient {
|
|
42
|
+
dependencies;
|
|
43
|
+
constructor(dependencies = {}) {
|
|
44
|
+
this.dependencies = dependencies;
|
|
45
|
+
}
|
|
46
|
+
async request(path, body) {
|
|
47
|
+
let auth;
|
|
48
|
+
try {
|
|
49
|
+
if (this.dependencies.initializeProfile)
|
|
50
|
+
await this.dependencies.initializeProfile();
|
|
51
|
+
else if (!this.dependencies.config)
|
|
52
|
+
await initializePlatformProfile({ warn: message => process.stderr.write(`警告: ${message}\n`) });
|
|
53
|
+
auth = (this.dependencies.config ?? platformJevTarget)();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof JevError)
|
|
57
|
+
throw error;
|
|
58
|
+
throw new JevError("无法加载当前平台配置;请求尚未发送。", "JEV_CONFIGURATION_REQUIRED");
|
|
59
|
+
}
|
|
60
|
+
const method = body ? "POST" : "GET";
|
|
61
|
+
let url;
|
|
62
|
+
try {
|
|
63
|
+
url = resolveBackendUrl(auth, { method, path });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new JevError("平台 API 地址配置无效;请求尚未发送。", "JEV_CONFIGURATION_REQUIRED");
|
|
67
|
+
}
|
|
68
|
+
let sent = false, token = "", response;
|
|
69
|
+
try {
|
|
70
|
+
response = await fetchWithKeycloakIdentity(auth, url, () => ({
|
|
71
|
+
method, redirect: "error", signal: AbortSignal.timeout(60_000),
|
|
72
|
+
headers: { Accept: "application/json", ...(body ? { "Content-Type": "application/json" } : {}) },
|
|
73
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
74
|
+
}), {
|
|
75
|
+
tokenProvider: async (config, refresh) => { token = await (this.dependencies.tokenProvider ?? ensureAccessToken)(config, refresh); return token; },
|
|
76
|
+
fetch: async (target, init) => { sent = true; return (this.dependencies.fetch ?? fetch)(target, init); },
|
|
77
|
+
retryUnauthorized: false,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (!sent) {
|
|
82
|
+
if (auth.authenticationMode !== "client_credentials" && error instanceof Error && "code" in error && error.code === "login_required")
|
|
83
|
+
throw new JevError("当前平台身份需要登录;请求尚未发送。", "login_required", 401);
|
|
84
|
+
throw new JevError("当前平台身份无法获取访问凭据;请求尚未发送。", "JEV_IDENTITY_UNAVAILABLE");
|
|
85
|
+
}
|
|
86
|
+
throw new JevError(body ? "Jev 请求中断,结果可能未知且可能已计费;未自动重试。" : "无法读取 Jev 可用性;未自动重试。", body ? "JEV_RESULT_UNKNOWN" : "JEV_NETWORK_ERROR", undefined, body ? "unknown" : "rejected");
|
|
87
|
+
}
|
|
88
|
+
let value;
|
|
89
|
+
try {
|
|
90
|
+
value = await readBounded(response, response.ok ? 1024 * 1024 : 16 * 1024);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
if (response.ok)
|
|
94
|
+
throw new JevError("Jev 响应格式无效或超过大小限制;未自动重试。", "JEV_INVALID_RESPONSE", 502, body ? "unknown" : "rejected");
|
|
95
|
+
}
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
const code = response.status === 401 && auth.authenticationMode === "client_credentials" ? "JEV_MACHINE_AUTHENTICATION_REQUIRED"
|
|
98
|
+
: object(value) && typeof value.code === "string" && knownCodes.has(value.code) ? value.code
|
|
99
|
+
: response.status === 401 ? "login_required" : response.status === 403 ? "JEV_ACCESS_DENIED" : "JEV_HTTP_ERROR";
|
|
100
|
+
const unknown = Boolean(body && (uncertainCodes.has(code) || code === "JEV_HTTP_ERROR" && (response.status >= 500 || response.status === 408)));
|
|
101
|
+
throw new JevError(`Jev API 调用失败(HTTP ${response.status},${code});未自动重试。`, code, response.status, unknown ? "unknown" : "rejected");
|
|
102
|
+
}
|
|
103
|
+
if (!object(value))
|
|
104
|
+
throw new JevError("Jev 响应不是有效对象;未自动重试。", "JEV_INVALID_RESPONSE", 502, body ? "unknown" : "rejected");
|
|
105
|
+
try {
|
|
106
|
+
// Redact JSON string tokens, including keys, without rewriting numeric answers/usage.
|
|
107
|
+
const secrets = [token, auth.clientSecret].filter((secret) => Boolean(secret));
|
|
108
|
+
const serialized = JSON.stringify(value).replace(/"(?:\\[\s\S]|[^"\\])*"/gu, literal => JSON.stringify(secrets.reduce((text, secret) => text.split(secret).join("[REDACTED]"), JSON.parse(literal))));
|
|
109
|
+
return JSON.parse(serialized);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new JevError("Jev 响应无法安全解析;未自动重试。", "JEV_INVALID_RESPONSE", 502, body ? "unknown" : "rejected");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async capabilities() {
|
|
116
|
+
const value = await this.request("/api/v1/jev/capabilities");
|
|
117
|
+
if (typeof value.enabled !== "boolean" || typeof value.configured !== "boolean")
|
|
118
|
+
throw new JevError("Jev 可用性响应格式无效。", "JEV_INVALID_RESPONSE", 502, "rejected");
|
|
119
|
+
return Object.fromEntries(["enabled", "configured", "models", "defaultModel", "questionTypes", "maxRequestBytes", "maxQuestions", "maxChoiceOptions", "maxScoreLevels"]
|
|
120
|
+
.filter(k => Object.hasOwn(value, k)).map(k => [k, value[k]]));
|
|
121
|
+
}
|
|
122
|
+
async decide(input) {
|
|
123
|
+
const body = parseJevInput(JSON.stringify(input));
|
|
124
|
+
return validateJevResponse(await this.request("/api/v1/jev/decisions", body), body);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/jev/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { JevClient } from "./client.js";
|
|
3
|
+
export interface JevCommandDependencies {
|
|
4
|
+
clientFactory?: () => Pick<JevClient, "capabilities" | "decide">;
|
|
5
|
+
write?: (text: string) => void;
|
|
6
|
+
stdin?: AsyncIterable<Uint8Array | string>;
|
|
7
|
+
}
|
|
8
|
+
export declare function registerJevCommands(program: Command, dependencies?: JevCommandDependencies): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { JevClient } from "./client.js";
|
|
2
|
+
import { JevError, jevExitCode, jevFailure } from "./errors.js";
|
|
3
|
+
import { readJevInput } from "./input.js";
|
|
4
|
+
export function registerJevCommands(program, dependencies = {}) {
|
|
5
|
+
const client = () => dependencies.clientFactory?.() ?? new JevClient();
|
|
6
|
+
const output = (data) => (dependencies.write ?? (text => process.stdout.write(text)))(`${JSON.stringify(data, null, 2)}\n`);
|
|
7
|
+
async function run(action) {
|
|
8
|
+
try {
|
|
9
|
+
output(await action());
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (!(error instanceof JevError))
|
|
13
|
+
throw error;
|
|
14
|
+
output(jevFailure(error));
|
|
15
|
+
process.exitCode = jevExitCode(error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const jev = program.command("jev").description("通过平台 HTTP API 直接调用 Jev;CLI 完成校验并返回可用性与下一步")
|
|
19
|
+
.addHelpText("after", "\nnuma jev decide --file request.json --json\ncat request.json | numa jev decide --file - --json\n直接提交业务请求,无需先调用 capabilities、--check 或 MCP 探测。\n沿用当前平台配置和 numa login --platform 身份;不使用 MCP namespace/endpoint 参数。\ncapabilities 和 --check 仅供主动诊断,不自动重试付费推理,不接收 OpenRouter Key。\n");
|
|
20
|
+
jev.command("capabilities").description("可选诊断:读取 Jev 启用状态、模型和限制(view 权限)")
|
|
21
|
+
.action(async () => run(async () => {
|
|
22
|
+
const data = await client().capabilities();
|
|
23
|
+
return { ok: true, status: "succeeded", data,
|
|
24
|
+
availability: { state: data.enabled && data.configured ? "configured" : "not_configured", source: "capabilities", inference_verified: false } };
|
|
25
|
+
}));
|
|
26
|
+
jev.command("decide").description("一次提交业务判断并返回结果或可执行诊断(edit 权限,可能计费)")
|
|
27
|
+
.option("-f, --file <path|->", "UTF-8 JSON 文件;- 从标准输入读取,最大 256 KiB(必填)")
|
|
28
|
+
.option("--check", "可选离线校验;正常调用已包含此校验,不登录、不联网")
|
|
29
|
+
.action(async (options) => run(async () => {
|
|
30
|
+
if (!options.file)
|
|
31
|
+
throw new JevError("缺少 --file;提供 JSON 文件路径或使用 --file - 读取标准输入。", "JEV_INPUT_INVALID", undefined, "not_sent", { issues: [{ path: "file", message: "需要 --file <path|->。" }] });
|
|
32
|
+
const input = await readJevInput(options.file, dependencies.stdin);
|
|
33
|
+
if (options.check)
|
|
34
|
+
return { ok: true, status: "succeeded", data: { valid: true, questions: Object.keys(input.questions).length, sent: false }, availability: { state: "not_checked", source: "local_validation" } };
|
|
35
|
+
const data = await client().decide(input);
|
|
36
|
+
return { ok: true, status: "succeeded", data, availability: { state: "available", source: "decision" },
|
|
37
|
+
validation: { input: "passed", response: "passed" }, execution: { state: "completed", automatic_retry: false } };
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/jev/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type JevExecution = "not_sent" | "rejected" | "unknown";
|
|
2
|
+
export declare class JevError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly status?: number | undefined;
|
|
5
|
+
readonly execution: JevExecution;
|
|
6
|
+
readonly details?: unknown;
|
|
7
|
+
readonly requestId?: string | undefined;
|
|
8
|
+
constructor(message: string, code: string, status?: number | undefined, execution?: JevExecution, details?: unknown, requestId?: string | undefined);
|
|
9
|
+
}
|
|
10
|
+
export declare function jevExitCode(error: JevError): number;
|
|
11
|
+
/** Stable remediation is owned by the CLI, never copied from an upstream error body. */
|
|
12
|
+
export declare function jevFailure(error: JevError): {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
status: string;
|
|
15
|
+
availability: {
|
|
16
|
+
state: string;
|
|
17
|
+
source: string;
|
|
18
|
+
};
|
|
19
|
+
execution: {
|
|
20
|
+
state: JevExecution;
|
|
21
|
+
automatic_retry: boolean;
|
|
22
|
+
};
|
|
23
|
+
error: {
|
|
24
|
+
code: string;
|
|
25
|
+
message: string;
|
|
26
|
+
status: number | null;
|
|
27
|
+
details?: {} | null | undefined;
|
|
28
|
+
request_id?: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
next_action: {
|
|
31
|
+
type: string;
|
|
32
|
+
message: string;
|
|
33
|
+
command?: undefined;
|
|
34
|
+
} | {
|
|
35
|
+
type: string;
|
|
36
|
+
command: string;
|
|
37
|
+
message: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export class JevError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
status;
|
|
4
|
+
execution;
|
|
5
|
+
details;
|
|
6
|
+
requestId;
|
|
7
|
+
constructor(message, code, status, execution = "not_sent", details, requestId) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.execution = execution;
|
|
12
|
+
this.details = details;
|
|
13
|
+
this.requestId = requestId;
|
|
14
|
+
this.name = "JevError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function jevExitCode(error) {
|
|
18
|
+
return error.status === 401 ? 3 : error.status === 403 && error.code !== "JEV_UPSTREAM_FORBIDDEN" ? 4
|
|
19
|
+
: ["JEV_INPUT_INVALID", "JEV_INVALID_REQUEST", "JEV_REQUEST_TOO_LARGE", "JEV_UPSTREAM_REJECTED"].includes(error.code) ? 2 : 1;
|
|
20
|
+
}
|
|
21
|
+
/** Stable remediation is owned by the CLI, never copied from an upstream error body. */
|
|
22
|
+
export function jevFailure(error) {
|
|
23
|
+
const input = ["JEV_INPUT_INVALID", "JEV_INVALID_REQUEST", "JEV_REQUEST_TOO_LARGE", "JEV_UPSTREAM_REJECTED"].includes(error.code);
|
|
24
|
+
const machine = error.code === "JEV_MACHINE_AUTHENTICATION_REQUIRED";
|
|
25
|
+
const login = !machine && (error.code === "login_required" || error.status === 401);
|
|
26
|
+
const permission = error.status === 403 && error.code !== "JEV_UPSTREAM_FORBIDDEN";
|
|
27
|
+
const configuration = ["JEV_CONFIGURATION_REQUIRED", "JEV_IDENTITY_CONFIGURATION_INVALID"].includes(error.code);
|
|
28
|
+
const unknown = error.execution === "unknown";
|
|
29
|
+
return {
|
|
30
|
+
ok: false, status: unknown ? "pending" : input ? "needs_input" : "blocked",
|
|
31
|
+
availability: { state: input ? "not_checked" : login || machine ? "authentication_required" : permission ? "authorization_required"
|
|
32
|
+
: configuration ? "configuration_required" : error.code === "JEV_NOT_CONFIGURED" ? "not_configured"
|
|
33
|
+
: unknown ? "unknown" : "unavailable", source: "command" },
|
|
34
|
+
execution: { state: error.execution, automatic_retry: false },
|
|
35
|
+
error: { code: error.code, message: error.message, status: error.status ?? null,
|
|
36
|
+
...(error.details === undefined ? {} : { details: error.details }),
|
|
37
|
+
...(error.requestId ? { request_id: error.requestId } : {}) },
|
|
38
|
+
next_action: unknown ? { type: "review_unknown_result", message: "请求可能已计费;保留错误码和请求号,核对结果后再决定是否重新调用,不自动重放。" }
|
|
39
|
+
: input ? { type: "correct_input", message: "按字段诊断修正请求后直接重试,无需另做可用性测试。" }
|
|
40
|
+
: login ? { type: "login", command: "numa login --platform", message: "使用当前平台身份登录后重试。" }
|
|
41
|
+
: permission ? { type: "request_access", message: "请管理员授予当前身份 Jev 功能权限;不要更换身份或反复登录。" }
|
|
42
|
+
: configuration ? { type: "configure_platform", message: "修正当前平台 Profile/API 或进程身份配置后重试。" }
|
|
43
|
+
: machine || error.code === "JEV_IDENTITY_UNAVAILABLE" ? { type: "inspect_identity", message: "当前身份无法获取或使用凭据;核对受管身份配置与认证服务,不切换用户、读取秘密或改用浏览器登录。" }
|
|
44
|
+
: ["JEV_BUSY", "JEV_RATE_LIMITED"].includes(error.code) || error.status === 429 ? { type: "wait", message: "平台或上游限流;稍后按业务需要重新调用,CLI 未自动重试。" }
|
|
45
|
+
: { type: "contact_platform_admin", message: "将错误码交给平台管理员处理服务配置、代理或上游可用性;不要改走 MCP 或直连供应商。" },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/jev/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const MAX_REQUEST_BYTES: number;
|
|
2
|
+
export type JsonObject = Record<string, unknown>;
|
|
3
|
+
export interface JevInput extends JsonObject {
|
|
4
|
+
state: string | JsonObject | unknown[];
|
|
5
|
+
questions: Record<string, JsonObject>;
|
|
6
|
+
model?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function object(value: unknown): value is JsonObject;
|
|
9
|
+
/** Parse strictly before transport so duplicate question IDs cannot silently disappear. */
|
|
10
|
+
export declare function parseJevInput(text: string): JevInput;
|
|
11
|
+
export declare function readJevInput(path: string, stdin?: AsyncIterable<Uint8Array | string>): Promise<JevInput>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { open } from "node:fs/promises";
|
|
2
|
+
import { JevError } from "./errors.js";
|
|
3
|
+
export const MAX_REQUEST_BYTES = 256 * 1024;
|
|
4
|
+
export function object(value) { return value != null && typeof value === "object" && !Array.isArray(value); }
|
|
5
|
+
const invalid = (path = "input", message = "输入须为有效 UTF-8 JSON,且只包含 state、questions 和可选 model。") => new JevError("Jev 输入未通过校验;请求尚未发送。", "JEV_INPUT_INVALID", undefined, "not_sent", {
|
|
6
|
+
issues: [{ path, message }], limits: { maxRequestBytes: MAX_REQUEST_BYTES, maxQuestions: 16, maxChoiceOptions: 32, maxScoreLevels: 10 },
|
|
7
|
+
});
|
|
8
|
+
const id = (value) => value.trim().length > 0 && value.length <= 80 && !/[\p{Cc}]/u.test(value);
|
|
9
|
+
function description(value, nullable = false) {
|
|
10
|
+
return (nullable && value === null) || ((typeof value === "string" ? !!value.trim() : object(value) || Array.isArray(value))
|
|
11
|
+
&& Buffer.byteLength(JSON.stringify(value)) <= 8192);
|
|
12
|
+
}
|
|
13
|
+
/** Parse strictly before transport so duplicate question IDs cannot silently disappear. */
|
|
14
|
+
export function parseJevInput(text) {
|
|
15
|
+
try {
|
|
16
|
+
if (Buffer.byteLength(text) > MAX_REQUEST_BYTES)
|
|
17
|
+
throw invalid("input", "请求不能超过 256 KiB。");
|
|
18
|
+
const value = JSON.parse(text);
|
|
19
|
+
const stack = [];
|
|
20
|
+
for (const token of text.matchAll(/"(?:\\[\s\S]|[^"\\])*"|[{}\[\],:]/gu)) {
|
|
21
|
+
const t = token[0];
|
|
22
|
+
const frame = stack.at(-1);
|
|
23
|
+
if (t === "{" || t === "[") {
|
|
24
|
+
stack.push({ keys: t === "{" ? new Set() : undefined, expectingKey: true });
|
|
25
|
+
if (stack.length > 64)
|
|
26
|
+
throw invalid("input", "JSON 嵌套不能超过 64 层。");
|
|
27
|
+
}
|
|
28
|
+
else if (t === "}" || t === "]")
|
|
29
|
+
stack.pop();
|
|
30
|
+
else if (t === "," && frame)
|
|
31
|
+
frame.expectingKey = true;
|
|
32
|
+
else if (t === ":" && frame)
|
|
33
|
+
frame.expectingKey = false;
|
|
34
|
+
else if (t.startsWith('"') && frame?.keys && frame.expectingKey) {
|
|
35
|
+
const key = JSON.parse(t);
|
|
36
|
+
if (frame.keys.has(key))
|
|
37
|
+
throw invalid("input", "JSON 存在重复字段,不能静默覆盖。");
|
|
38
|
+
frame.keys.add(key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!object(value) || Object.keys(value).some(k => !["state", "questions", "model"].includes(k)))
|
|
42
|
+
throw invalid();
|
|
43
|
+
if (!(typeof value.state === "string" && !!value.state.trim()) && !object(value.state) && !Array.isArray(value.state))
|
|
44
|
+
throw invalid("state", "state 须为非空字符串、对象或数组。");
|
|
45
|
+
if (Object.hasOwn(value, "model") && (typeof value.model !== "string" || !["typesafe/jev-1.13", "~typesafe/jev-latest"].includes(value.model)))
|
|
46
|
+
throw invalid("model", "省略 model 使用平台默认值;显式模型须为 typesafe/jev-1.13 或 ~typesafe/jev-latest。");
|
|
47
|
+
if (!object(value.questions) || Object.keys(value.questions).length < 1 || Object.keys(value.questions).length > 16)
|
|
48
|
+
throw invalid("questions", "须包含 1–16 个独立问题。");
|
|
49
|
+
for (const [index, [key, q]] of Object.entries(value.questions).entries()) {
|
|
50
|
+
const field = `questions[${index}]`;
|
|
51
|
+
if (!id(key) || !object(q) || Object.keys(q).some(k => !["type", "instructions", "criteria"].includes(k)) || !description(q.instructions))
|
|
52
|
+
throw invalid(field, "问题 ID、字段或 instructions 无效;instructions 最多 8 KiB。");
|
|
53
|
+
if (q.type === "noul") {
|
|
54
|
+
if (Object.hasOwn(q, "criteria") && (!object(q.criteria) || Object.keys(q.criteria).some(k => !["true", "false"].includes(k)) || !Object.values(q.criteria).every(v => description(v, true))))
|
|
55
|
+
throw invalid(`${field}.criteria`, "noul criteria 只允许 true/false 描述。");
|
|
56
|
+
}
|
|
57
|
+
else if (q.type === "choice") {
|
|
58
|
+
if (!object(q.criteria) || Object.keys(q.criteria).length < 2 || Object.keys(q.criteria).length > 32
|
|
59
|
+
|| !Object.keys(q.criteria).every(id) || !Object.values(q.criteria).every(v => description(v, true)))
|
|
60
|
+
throw invalid(`${field}.criteria`, "choice 须包含 2–32 个有效候选。");
|
|
61
|
+
}
|
|
62
|
+
else if (q.type === "score") {
|
|
63
|
+
if (!Array.isArray(q.criteria) || q.criteria.length < 2 || q.criteria.length > 10 || !q.criteria.every(v => description(v, true)))
|
|
64
|
+
throw invalid(`${field}.criteria`, "score 须包含 2–10 个有序等级。");
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
throw invalid(`${field}.type`, "问题类型须为 noul、choice 或 score。");
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof JevError)
|
|
73
|
+
throw error;
|
|
74
|
+
throw invalid();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export async function readJevInput(path, stdin = process.stdin) {
|
|
78
|
+
try {
|
|
79
|
+
let bytes;
|
|
80
|
+
if (path === "-") {
|
|
81
|
+
if (stdin === process.stdin && process.stdin.isTTY)
|
|
82
|
+
throw invalid("file", "--file - 需要通过管道提供 JSON 标准输入。");
|
|
83
|
+
const chunks = [];
|
|
84
|
+
let size = 0;
|
|
85
|
+
for await (const chunk of stdin) {
|
|
86
|
+
const b = Buffer.from(chunk);
|
|
87
|
+
size += b.length;
|
|
88
|
+
if (size > MAX_REQUEST_BYTES)
|
|
89
|
+
throw invalid("input", "请求不能超过 256 KiB。");
|
|
90
|
+
chunks.push(b);
|
|
91
|
+
}
|
|
92
|
+
bytes = Buffer.concat(chunks);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const file = await open(path, "r");
|
|
96
|
+
try {
|
|
97
|
+
const stat = await file.stat();
|
|
98
|
+
if (!stat.isFile())
|
|
99
|
+
throw invalid("file", "输入路径须为普通文件。");
|
|
100
|
+
if (stat.size > MAX_REQUEST_BYTES)
|
|
101
|
+
throw invalid("input", "请求不能超过 256 KiB。");
|
|
102
|
+
bytes = Buffer.alloc(MAX_REQUEST_BYTES + 1);
|
|
103
|
+
let size = 0;
|
|
104
|
+
while (size < bytes.length) {
|
|
105
|
+
const read = await file.read(bytes, size, bytes.length - size, null);
|
|
106
|
+
if (!read.bytesRead)
|
|
107
|
+
break;
|
|
108
|
+
size += read.bytesRead;
|
|
109
|
+
}
|
|
110
|
+
if (size > MAX_REQUEST_BYTES)
|
|
111
|
+
throw invalid("input", "请求不能超过 256 KiB。");
|
|
112
|
+
bytes = bytes.subarray(0, size);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
await file.close();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return parseJevInput(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (error instanceof JevError)
|
|
122
|
+
throw error;
|
|
123
|
+
throw invalid("file", "无法读取 UTF-8 JSON 文件或标准输入;仅接受最多 256 KiB 的普通文件。");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/jev/input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { loadConfig } from "../config.js";
|
|
2
|
+
import { currentPlatformProfile } from "../platform-profile.js";
|
|
3
|
+
import { resolveBackendUrl } from "../backend.js";
|
|
4
|
+
import { JevError } from "./errors.js";
|
|
5
|
+
/** Shares the platform API and identity; no MCP descriptor, discovery or namespace login. */
|
|
6
|
+
export function platformJevTarget() {
|
|
7
|
+
const profile = currentPlatformProfile();
|
|
8
|
+
if (profile?.stale)
|
|
9
|
+
throw new JevError("平台 Profile 已过期,请恢复平台配置服务后重试。", "JEV_CONFIGURATION_REQUIRED");
|
|
10
|
+
const auth = loadConfig("platform");
|
|
11
|
+
if (!auth.discoveryUrl || !auth.clientId?.trim())
|
|
12
|
+
throw new JevError("缺少平台身份配置。", "JEV_CONFIGURATION_REQUIRED");
|
|
13
|
+
try {
|
|
14
|
+
resolveBackendUrl(auth, { method: "POST", path: "/api/v1/jev/decisions" });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new JevError("缺少有效的平台 API 地址。", "JEV_CONFIGURATION_REQUIRED");
|
|
18
|
+
}
|
|
19
|
+
if (auth.authenticationMode === "client_credentials"
|
|
20
|
+
? auth.clientId === "mcp-client" || Boolean(auth.clientSecret) === Boolean(auth.clientSecretFile)
|
|
21
|
+
: Boolean(auth.clientSecret || auth.clientSecretFile)) {
|
|
22
|
+
throw new JevError("进程身份配置冲突;机器身份需独立 Client 和单一 Secret 来源。", "JEV_IDENTITY_CONFIGURATION_INVALID");
|
|
23
|
+
}
|
|
24
|
+
return auth;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/jev/profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,6FAA6F;AAC7F,MAAM,UAAU,iBAAiB;IAC/B,MAAM,OAAO,GAAG,sBAAsB,EAAE,CAAC;IACzC,IAAI,OAAO,EAAE,KAAK;QAAE,MAAM,IAAI,QAAQ,CAAC,8BAA8B,EAAE,4BAA4B,CAAC,CAAC;IACrG,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,4BAA4B,CAAC,CAAC;IAChH,IAAI,CAAC;QAAC,iBAAiB,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAAC,CAAC;IACnF,MAAM,CAAC;QAAC,MAAM,IAAI,QAAQ,CAAC,iBAAiB,EAAE,4BAA4B,CAAC,CAAC;IAAC,CAAC;IAC9E,IAAI,IAAI,CAAC,kBAAkB,KAAK,oBAAoB;QAClD,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC;QACjG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,QAAQ,CAAC,wCAAwC,EAAE,oCAAoC,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JevError } from "./errors.js";
|
|
2
|
+
import { object } from "./input.js";
|
|
3
|
+
const number = (v, max) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= max;
|
|
4
|
+
/** Enforce the platform's response contract; callers need not recheck arithmetic or distributions. */
|
|
5
|
+
export function validateJevResponse(value, input) {
|
|
6
|
+
const fail = () => new JevError("Jev 返回的答案、概率分布或用量不符合契约;未自动重试。", "JEV_INVALID_RESPONSE", 502, "unknown");
|
|
7
|
+
if (typeof value.model !== "string" || !value.model.trim() || !object(value.usage) || !object(value.answers)
|
|
8
|
+
|| Object.keys(value.answers).length !== Object.keys(input.questions).length)
|
|
9
|
+
throw fail();
|
|
10
|
+
for (const key of ["input_tokens", "output_tokens"]) {
|
|
11
|
+
if (Object.hasOwn(value.usage, key) && (!Number.isSafeInteger(value.usage[key]) || Number(value.usage[key]) < 0))
|
|
12
|
+
throw fail();
|
|
13
|
+
}
|
|
14
|
+
if (Object.hasOwn(value.usage, "cost") && !number(value.usage.cost, Number.MAX_VALUE))
|
|
15
|
+
throw fail();
|
|
16
|
+
for (const [id, question] of Object.entries(input.questions)) {
|
|
17
|
+
const answer = value.answers[id];
|
|
18
|
+
if (!Object.hasOwn(value.answers, id) || !object(answer) || answer.type !== question.type)
|
|
19
|
+
throw fail();
|
|
20
|
+
if (question.type === "choice") {
|
|
21
|
+
if (typeof answer.choice !== "string" || !object(question.criteria) || !Object.hasOwn(question.criteria, answer.choice))
|
|
22
|
+
throw fail();
|
|
23
|
+
}
|
|
24
|
+
else if (!number(answer[String(question.type)], question.type === "noul" ? 1 : question.criteria.length - 1))
|
|
25
|
+
throw fail();
|
|
26
|
+
if (question.type !== "noul" && !number(answer.confidence, 1))
|
|
27
|
+
throw fail();
|
|
28
|
+
if (Object.hasOwn(answer, "probabilities")) {
|
|
29
|
+
const keys = Object.keys((question.criteria ?? {})), probabilities = answer.probabilities;
|
|
30
|
+
if (!object(probabilities) || keys.length !== Object.keys(probabilities).length
|
|
31
|
+
|| !keys.every(key => Object.hasOwn(probabilities, key) && number(probabilities[key], 1))
|
|
32
|
+
|| Math.abs(Object.values(probabilities).reduce((a, b) => a + Number(b), 0) - 1) > .001)
|
|
33
|
+
throw fail();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return Object.fromEntries(["model", "answers", "usage", "id", "provider"].filter(k => Object.hasOwn(value, k)).map(k => [k, value[k]]));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/jev/response.ts"],"names":[],"mappings":"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"}
|