@southwind-ai/shared 0.3.0 → 0.3.2
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/index.ts +15 -0
- package/package.json +3 -2
- package/src/agent-admin-catalog.ts +73 -0
- package/src/agent-skills.ts +68 -0
- package/src/agent-workbench.ts +100 -0
- package/src/agent-workflow-presets.ts +26 -0
- package/src/diagnostics.ts +13 -0
- package/src/rbac.ts +1 -0
- package/src/structured-log.ts +215 -0
package/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from "./src/audit.js";
|
|
|
2
2
|
export * from "./src/ai-operation-readiness.js";
|
|
3
3
|
// @windy-module system.agent begin
|
|
4
4
|
export * from "./src/ai-runtime-report.js";
|
|
5
|
+
export * from "./src/agent-admin-catalog.js";
|
|
6
|
+
export * from "./src/agent-workbench.js";
|
|
5
7
|
export * from "./src/agent-workflow.js";
|
|
6
8
|
export * from "./src/agent-workflow-events.js";
|
|
7
9
|
export * from "./src/agent-workflow-evaluation.js";
|
|
@@ -17,6 +19,7 @@ export * from "./src/migration-bundle-validation.js";
|
|
|
17
19
|
export * from "./src/mcp.js";
|
|
18
20
|
export * from "./src/module-configuration.js";
|
|
19
21
|
export * from "./src/correlation.js";
|
|
22
|
+
export * from "./src/structured-log.js";
|
|
20
23
|
export * from "./src/notification.js";
|
|
21
24
|
export * from "./src/password.js";
|
|
22
25
|
export * from "./src/platform-settings.js";
|
|
@@ -32,3 +35,15 @@ export * from "./src/stable-key.js";
|
|
|
32
35
|
export * from "./src/workflow.js";
|
|
33
36
|
|
|
34
37
|
export * from "./src/license-console-capabilities.js";
|
|
38
|
+
|
|
39
|
+
// @windy-module system.agent begin
|
|
40
|
+
export * from "./src/agent-skills.js";
|
|
41
|
+
// @windy-module system.agent end
|
|
42
|
+
|
|
43
|
+
// @windy-module system.audit begin
|
|
44
|
+
export * from "./src/diagnostics.js";
|
|
45
|
+
// @windy-module system.audit end
|
|
46
|
+
|
|
47
|
+
// @windy-module system.agent begin
|
|
48
|
+
export * from "./src/agent-workflow-presets.js";
|
|
49
|
+
// @windy-module system.agent end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@southwind-ai/shared",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit --project tsconfig.json"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
|
-
".": "./index.ts"
|
|
18
|
+
".": "./index.ts",
|
|
19
|
+
"./structured-log": "./src/structured-log.ts"
|
|
19
20
|
},
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { JsonObject } from "./json.js";
|
|
2
|
+
import type { CandidateRole } from "./workflow.js";
|
|
3
|
+
|
|
4
|
+
/** Public, bounded categories; never include raw Guard events or Provider bodies. */
|
|
5
|
+
export type AgentAdminAvailabilityReason =
|
|
6
|
+
| "allowed"
|
|
7
|
+
| "permission-denied"
|
|
8
|
+
| "feature-unavailable"
|
|
9
|
+
| "license-unavailable"
|
|
10
|
+
| "scope-unavailable"
|
|
11
|
+
| "runtime-unavailable"
|
|
12
|
+
| "workflow-only"
|
|
13
|
+
| "internal-operation"
|
|
14
|
+
| "unsupported-operation"
|
|
15
|
+
| "handler-unavailable"
|
|
16
|
+
| "governance-unavailable";
|
|
17
|
+
|
|
18
|
+
export interface AgentAdminToolCatalogItem {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
readonly label: string;
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly module: string;
|
|
23
|
+
readonly operation: "query" | "command" | "background";
|
|
24
|
+
readonly featureKey: string;
|
|
25
|
+
readonly permissionKey: string;
|
|
26
|
+
readonly auditAction: string;
|
|
27
|
+
readonly scopeMode: "platform" | "request";
|
|
28
|
+
readonly executionMode: "host" | "sandbox";
|
|
29
|
+
readonly dataClassification: "public" | "internal" | "sensitive";
|
|
30
|
+
readonly approval: "none" | "required";
|
|
31
|
+
readonly approvalRoles?: readonly CandidateRole[];
|
|
32
|
+
readonly approvalTimeoutSeconds?: number;
|
|
33
|
+
readonly idempotency: "none" | "supported" | "required";
|
|
34
|
+
readonly limits: AgentAdminCatalogLimits;
|
|
35
|
+
readonly inputSchemaKey: string;
|
|
36
|
+
readonly outputSchemaKey: string;
|
|
37
|
+
readonly inputSchema: JsonObject;
|
|
38
|
+
readonly outputSchema: JsonObject;
|
|
39
|
+
readonly allowedOperationKeys: readonly string[];
|
|
40
|
+
/** Current Guard + Scope eligibility, not an execution or approval grant. */
|
|
41
|
+
readonly allowed: boolean;
|
|
42
|
+
readonly availabilityReason: AgentAdminAvailabilityReason;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface AgentAdminCatalogLimits {
|
|
46
|
+
readonly maxDurationSeconds: number;
|
|
47
|
+
readonly maxInputBytes: number;
|
|
48
|
+
readonly maxOutputBytes: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface AgentAdminOperationCatalogItem {
|
|
52
|
+
readonly key: string;
|
|
53
|
+
readonly module: string;
|
|
54
|
+
readonly moduleLabel: string;
|
|
55
|
+
readonly kind: string;
|
|
56
|
+
readonly visibility: "public" | "internal";
|
|
57
|
+
readonly execution: string;
|
|
58
|
+
readonly featureKey: string;
|
|
59
|
+
readonly permissionKey: string;
|
|
60
|
+
readonly auditAction: string;
|
|
61
|
+
readonly dataClassification: "public" | "internal" | "sensitive";
|
|
62
|
+
readonly requiredCapabilities: readonly string[];
|
|
63
|
+
readonly inputModalities: readonly string[];
|
|
64
|
+
readonly inputSchemaKey: string;
|
|
65
|
+
readonly outputSchemaKey: string;
|
|
66
|
+
readonly inputSchema?: JsonObject;
|
|
67
|
+
readonly outputSchema?: JsonObject;
|
|
68
|
+
readonly allowedToolKeys: readonly string[];
|
|
69
|
+
readonly limits: AgentAdminCatalogLimits;
|
|
70
|
+
readonly launchAuthority: "direct" | "workflow-artifact";
|
|
71
|
+
readonly selectable: boolean;
|
|
72
|
+
readonly availabilityReason: AgentAdminAvailabilityReason;
|
|
73
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** DSH 技能是工作方法与文件包,不能授予业务权限或承载可执行授权。 */
|
|
2
|
+
export type AgentSkillScope = "personal" | "department" | "tenant";
|
|
3
|
+
export type AgentSkillOrigin = "builtin" | "shared";
|
|
4
|
+
export type AgentSkillStatus = "published" | "disabled";
|
|
5
|
+
export interface AgentSkillRequirement {
|
|
6
|
+
readonly toolKey: string;
|
|
7
|
+
readonly featureKey: string;
|
|
8
|
+
readonly permissionKey: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AgentSkillFile {
|
|
11
|
+
readonly path: string;
|
|
12
|
+
readonly contentBase64: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AgentSkillBundle {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly displayName: string;
|
|
17
|
+
readonly description: string;
|
|
18
|
+
readonly digest: string;
|
|
19
|
+
readonly files: readonly AgentSkillFile[];
|
|
20
|
+
}
|
|
21
|
+
export interface AgentSkillVersion {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
readonly version: number;
|
|
24
|
+
readonly tenantId: string;
|
|
25
|
+
readonly ownerActorId: string;
|
|
26
|
+
readonly departmentId?: string;
|
|
27
|
+
readonly scope: AgentSkillScope;
|
|
28
|
+
readonly origin: AgentSkillOrigin;
|
|
29
|
+
readonly status: AgentSkillStatus;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly description: string;
|
|
32
|
+
readonly digest: string;
|
|
33
|
+
readonly requirements: readonly AgentSkillRequirement[];
|
|
34
|
+
readonly safetyConstraints: readonly string[];
|
|
35
|
+
readonly createdAt: string;
|
|
36
|
+
readonly createdBy: string;
|
|
37
|
+
readonly restoredFromVersion?: number;
|
|
38
|
+
readonly source: string;
|
|
39
|
+
}
|
|
40
|
+
export interface AgentSkillGovernanceItem {
|
|
41
|
+
readonly skill: AgentSkillVersion;
|
|
42
|
+
readonly available: boolean;
|
|
43
|
+
readonly governable: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface ImportAgentSkillRequest {
|
|
46
|
+
readonly format: "markdown" | "zip";
|
|
47
|
+
/** SKILL.md 正文,或 ZIP 的标准 base64。 */
|
|
48
|
+
readonly content: string;
|
|
49
|
+
readonly scope: AgentSkillScope;
|
|
50
|
+
readonly expectedVersion: number;
|
|
51
|
+
readonly requirements?: readonly AgentSkillRequirement[];
|
|
52
|
+
readonly safetyConstraints?: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
export interface AgentSkillCommandRequest {
|
|
55
|
+
readonly expectedVersion: number;
|
|
56
|
+
readonly version?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface PublishedAgentSkillBundle {
|
|
59
|
+
readonly skill: AgentSkillVersion;
|
|
60
|
+
readonly bundle: AgentSkillBundle;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AgentSkillRuntimeBundle {
|
|
64
|
+
readonly key: string;
|
|
65
|
+
readonly version: number;
|
|
66
|
+
readonly digest: string;
|
|
67
|
+
readonly files: readonly AgentSkillFile[];
|
|
68
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { JsonValue } from "./json.js";
|
|
2
|
+
|
|
3
|
+
export type AgentWorkbenchRecordingSourceV1 = "harness" | "business";
|
|
4
|
+
export type AgentWorkbenchCorrelationStatusV1 =
|
|
5
|
+
| "complete"
|
|
6
|
+
| "partial"
|
|
7
|
+
| "missing";
|
|
8
|
+
|
|
9
|
+
export interface AgentWorkbenchEventV1 {
|
|
10
|
+
readonly sequence: number;
|
|
11
|
+
readonly type: string;
|
|
12
|
+
readonly summary: string;
|
|
13
|
+
readonly occurredAt?: string;
|
|
14
|
+
readonly runId?: string;
|
|
15
|
+
readonly turnId?: string;
|
|
16
|
+
readonly stepId?: string;
|
|
17
|
+
readonly modelAttemptId?: string;
|
|
18
|
+
readonly attemptSequence?: number;
|
|
19
|
+
readonly toolCallId?: string;
|
|
20
|
+
readonly toolKey?: string;
|
|
21
|
+
readonly workItemId?: string;
|
|
22
|
+
readonly artifactId?: string;
|
|
23
|
+
readonly workflowRunId?: string;
|
|
24
|
+
readonly nodeRunId?: string;
|
|
25
|
+
readonly outcome?: string;
|
|
26
|
+
readonly usage?: {
|
|
27
|
+
readonly inputTokens?: number;
|
|
28
|
+
readonly outputTokens?: number;
|
|
29
|
+
readonly totalTokens?: number;
|
|
30
|
+
readonly costMicros?: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AgentWorkbenchRunV1 {
|
|
35
|
+
readonly runId: string;
|
|
36
|
+
readonly operationKey: string;
|
|
37
|
+
readonly sessionId?: string;
|
|
38
|
+
readonly turnId?: string;
|
|
39
|
+
readonly messageSequence?: number;
|
|
40
|
+
readonly status: string;
|
|
41
|
+
readonly events: readonly AgentWorkbenchEventV1[];
|
|
42
|
+
readonly result?: { readonly output?: JsonValue; readonly status?: string };
|
|
43
|
+
readonly correlationStatus: AgentWorkbenchCorrelationStatusV1;
|
|
44
|
+
readonly missing: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AgentWorkbenchWorkflowV1 {
|
|
48
|
+
readonly workflowRunId: string;
|
|
49
|
+
readonly sessionId?: string;
|
|
50
|
+
readonly messageSequence?: number;
|
|
51
|
+
readonly workflowKey: string;
|
|
52
|
+
readonly title: string;
|
|
53
|
+
readonly status: string;
|
|
54
|
+
readonly parentRunId?: string;
|
|
55
|
+
readonly parentNodeRunId?: string;
|
|
56
|
+
readonly nodes: readonly {
|
|
57
|
+
readonly nodeRunId: string;
|
|
58
|
+
readonly nodeId: string;
|
|
59
|
+
readonly type: string;
|
|
60
|
+
readonly status: string;
|
|
61
|
+
readonly attempt: number;
|
|
62
|
+
readonly previousNodeRunId?: string;
|
|
63
|
+
readonly agentRunId?: string;
|
|
64
|
+
readonly workItemId?: string;
|
|
65
|
+
readonly selectedBranch?: string;
|
|
66
|
+
readonly artifactIds: readonly string[];
|
|
67
|
+
}[];
|
|
68
|
+
readonly events: readonly AgentWorkbenchEventV1[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface AgentWorkbenchRecordingSummaryV1 {
|
|
72
|
+
readonly source: AgentWorkbenchRecordingSourceV1;
|
|
73
|
+
readonly sessionId: string;
|
|
74
|
+
readonly title: string;
|
|
75
|
+
readonly updatedAt: string;
|
|
76
|
+
readonly operationKey?: string;
|
|
77
|
+
readonly sessionStatus?: "active" | "archived";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface AgentWorkbenchRecordingV1 extends AgentWorkbenchRecordingSummaryV1 {
|
|
81
|
+
readonly mode: "read-only";
|
|
82
|
+
readonly sideEffectPolicy: "no-reexecution";
|
|
83
|
+
readonly correlationStatus: AgentWorkbenchCorrelationStatusV1;
|
|
84
|
+
readonly messages: readonly {
|
|
85
|
+
readonly sequence: number;
|
|
86
|
+
readonly role: "user" | "assistant";
|
|
87
|
+
readonly content: string;
|
|
88
|
+
readonly createdAt: string;
|
|
89
|
+
}[];
|
|
90
|
+
readonly timeline: readonly AgentWorkbenchEventV1[];
|
|
91
|
+
/** Parent forks retain separate original ledger sequences, never renumbered. */
|
|
92
|
+
readonly segments?: readonly {
|
|
93
|
+
readonly sessionId: string;
|
|
94
|
+
readonly throughSequence: number;
|
|
95
|
+
readonly timeline: readonly AgentWorkbenchEventV1[];
|
|
96
|
+
}[];
|
|
97
|
+
readonly runs: readonly AgentWorkbenchRunV1[];
|
|
98
|
+
readonly workflows: readonly AgentWorkbenchWorkflowV1[];
|
|
99
|
+
readonly missing: readonly string[];
|
|
100
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentWorkflowBudget } from "./agent-workflow-run.js";
|
|
2
|
+
import type { AgentWorkflowNodeType } from "./agent-workflow-definition.js";
|
|
3
|
+
|
|
4
|
+
export interface AgentWorkflowPresetSummary {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly moduleKey: string;
|
|
7
|
+
readonly label: string;
|
|
8
|
+
readonly description: string;
|
|
9
|
+
readonly workflowKey: string;
|
|
10
|
+
readonly version: string;
|
|
11
|
+
readonly budget: AgentWorkflowBudget;
|
|
12
|
+
readonly nodes: readonly {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly type: AgentWorkflowNodeType;
|
|
16
|
+
}[];
|
|
17
|
+
readonly availability: "available" | "unavailable";
|
|
18
|
+
readonly safeCode?: string;
|
|
19
|
+
readonly blockedReason?: string;
|
|
20
|
+
/** 由所属模块注册的业务入口;目录本身不发布或启动 Workflow。 */
|
|
21
|
+
readonly entry?: { readonly path: string; readonly label: string };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AgentWorkflowPresetListResponse {
|
|
25
|
+
readonly items: readonly AgentWorkflowPresetSummary[];
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** 可向授权运维人员公开的失败索引,不包含业务正文、异常堆栈或身份凭据。 */
|
|
2
|
+
export interface DiagnosticRecord {
|
|
3
|
+
readonly diagnosticId: string;
|
|
4
|
+
readonly requestId: string;
|
|
5
|
+
readonly occurredAt: string;
|
|
6
|
+
readonly expiresAt: string;
|
|
7
|
+
readonly service: "server";
|
|
8
|
+
readonly stage: "http";
|
|
9
|
+
readonly method: string;
|
|
10
|
+
readonly route: string;
|
|
11
|
+
readonly statusCode: number;
|
|
12
|
+
readonly errorCode: string;
|
|
13
|
+
}
|
package/src/rbac.ts
CHANGED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/** 运行日志只承载排障线索;业务审计和 Agent Run 事实使用各自的持久化入口。 */
|
|
2
|
+
export const STRUCTURED_LOG_MAX_BYTES = 2048;
|
|
3
|
+
|
|
4
|
+
export type StructuredLogLevel = "info" | "warn" | "error";
|
|
5
|
+
export type StructuredLogOutcome = "success" | "failure" | "denied";
|
|
6
|
+
|
|
7
|
+
export interface StructuredLogInput {
|
|
8
|
+
event: string;
|
|
9
|
+
requestId?: string;
|
|
10
|
+
runId?: string;
|
|
11
|
+
jobId?: string;
|
|
12
|
+
traceId?: string;
|
|
13
|
+
diagnosticId?: string;
|
|
14
|
+
outcome?: StructuredLogOutcome;
|
|
15
|
+
errorCode?: string;
|
|
16
|
+
stageKey?: string;
|
|
17
|
+
handlerKey?: string;
|
|
18
|
+
queue?: string;
|
|
19
|
+
attemptNumber?: number;
|
|
20
|
+
method?: string;
|
|
21
|
+
/** 只接受框架登记的路由模板,不能传请求 URL 或实际对象路径。 */
|
|
22
|
+
route?: string;
|
|
23
|
+
statusCode?: number;
|
|
24
|
+
durationMs?: number;
|
|
25
|
+
actorType?: string;
|
|
26
|
+
authority?: string;
|
|
27
|
+
port?: number;
|
|
28
|
+
created?: number;
|
|
29
|
+
metadataUpdated?: number;
|
|
30
|
+
archived?: number;
|
|
31
|
+
unchanged?: number;
|
|
32
|
+
queued?: number;
|
|
33
|
+
dropped?: number;
|
|
34
|
+
completedUnits?: number;
|
|
35
|
+
totalUnits?: number;
|
|
36
|
+
count?: number;
|
|
37
|
+
/** 兼容现有 Logger 接口;未知字段在序列化时一律丢弃。 */
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface StructuredLogSink {
|
|
42
|
+
info(record: Record<string, unknown>): void;
|
|
43
|
+
warn(record: Record<string, unknown>): void;
|
|
44
|
+
error(record: Record<string, unknown>): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface StructuredLogger {
|
|
48
|
+
info(input: StructuredLogInput, message?: string): void;
|
|
49
|
+
warn(input: StructuredLogInput, message?: string): void;
|
|
50
|
+
error(input: StructuredLogInput, message?: string): void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const stableKey = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
|
|
54
|
+
const correlationKey = /^[A-Za-z0-9._-]{1,128}$/;
|
|
55
|
+
const errorKey = /^[A-Z][A-Z0-9_]{0,95}$/;
|
|
56
|
+
const methodKey = /^[A-Z]{1,12}$/;
|
|
57
|
+
const routeTemplate =
|
|
58
|
+
/^\/(?:[A-Za-z][A-Za-z0-9_-]*|:[A-Za-z][A-Za-z0-9_]*|\*)(?:\/(?:[A-Za-z][A-Za-z0-9_-]*|:[A-Za-z][A-Za-z0-9_]*|\*))*$/;
|
|
59
|
+
|
|
60
|
+
/** Adapter 可接 Pino、stdout 或测试 Sink;自由文本 message 不进入日志。 */
|
|
61
|
+
export function createStructuredLogger(
|
|
62
|
+
sink: StructuredLogSink,
|
|
63
|
+
component: string,
|
|
64
|
+
now: () => Date = () => new Date(),
|
|
65
|
+
): StructuredLogger {
|
|
66
|
+
if (!stableKey.test(component) || component.length > 64) {
|
|
67
|
+
throw new Error("运行日志 component 必须是有界稳定 Key");
|
|
68
|
+
}
|
|
69
|
+
const emit = (level: StructuredLogLevel, input: StructuredLogInput) => {
|
|
70
|
+
const record = structuredLogRecord(level, component, input, now());
|
|
71
|
+
try {
|
|
72
|
+
sink[level](record);
|
|
73
|
+
} catch {
|
|
74
|
+
// 排障日志是旁路输出;审计和业务结果由各自的可靠存储决定。
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
info: (input) => emit("info", input),
|
|
79
|
+
warn: (input) => emit("warn", input),
|
|
80
|
+
error: (input) => emit("error", input),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function structuredLogRecord(
|
|
85
|
+
level: StructuredLogLevel,
|
|
86
|
+
component: string,
|
|
87
|
+
input: StructuredLogInput,
|
|
88
|
+
at: Date,
|
|
89
|
+
): Record<string, unknown> {
|
|
90
|
+
const record: Record<string, unknown> = {
|
|
91
|
+
timestamp: at.toISOString(),
|
|
92
|
+
level,
|
|
93
|
+
component,
|
|
94
|
+
event:
|
|
95
|
+
stableKey.test(input.event) && input.event.length <= 96
|
|
96
|
+
? input.event
|
|
97
|
+
: "logging.event.invalid",
|
|
98
|
+
retentionClass: "operational",
|
|
99
|
+
};
|
|
100
|
+
for (const key of [
|
|
101
|
+
"requestId",
|
|
102
|
+
"runId",
|
|
103
|
+
"jobId",
|
|
104
|
+
"traceId",
|
|
105
|
+
"diagnosticId",
|
|
106
|
+
] as const) {
|
|
107
|
+
const value = input[key];
|
|
108
|
+
if (typeof value === "string" && correlationKey.test(value)) {
|
|
109
|
+
record[key] = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (
|
|
113
|
+
input.outcome === "success" ||
|
|
114
|
+
input.outcome === "failure" ||
|
|
115
|
+
input.outcome === "denied"
|
|
116
|
+
)
|
|
117
|
+
record.outcome = input.outcome;
|
|
118
|
+
if (typeof input.errorCode === "string" && errorKey.test(input.errorCode)) {
|
|
119
|
+
record.errorCode = input.errorCode;
|
|
120
|
+
}
|
|
121
|
+
for (const key of [
|
|
122
|
+
"stageKey",
|
|
123
|
+
"handlerKey",
|
|
124
|
+
"queue",
|
|
125
|
+
"actorType",
|
|
126
|
+
"authority",
|
|
127
|
+
] as const) {
|
|
128
|
+
const value = input[key];
|
|
129
|
+
if (
|
|
130
|
+
typeof value === "string" &&
|
|
131
|
+
value.length <= 64 &&
|
|
132
|
+
stableKey.test(value)
|
|
133
|
+
) {
|
|
134
|
+
record[key] = value;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (typeof input.method === "string" && methodKey.test(input.method)) {
|
|
138
|
+
record.method = input.method;
|
|
139
|
+
}
|
|
140
|
+
if (
|
|
141
|
+
typeof input.route === "string" &&
|
|
142
|
+
input.route.length <= 160 &&
|
|
143
|
+
(input.route === "/" || routeTemplate.test(input.route))
|
|
144
|
+
)
|
|
145
|
+
record.route = input.route;
|
|
146
|
+
if (validNumber(input.statusCode, 100, 599, true))
|
|
147
|
+
record.statusCode = input.statusCode;
|
|
148
|
+
if (validNumber(input.durationMs, 0, 3_600_000))
|
|
149
|
+
record.durationMs = input.durationMs;
|
|
150
|
+
if (validNumber(input.port, 1, 65535, true)) record.port = input.port;
|
|
151
|
+
if (validNumber(input.attemptNumber, 1, 1_000_000_000, true))
|
|
152
|
+
record.attemptNumber = input.attemptNumber;
|
|
153
|
+
for (const key of [
|
|
154
|
+
"created",
|
|
155
|
+
"metadataUpdated",
|
|
156
|
+
"archived",
|
|
157
|
+
"unchanged",
|
|
158
|
+
"queued",
|
|
159
|
+
"dropped",
|
|
160
|
+
"completedUnits",
|
|
161
|
+
"totalUnits",
|
|
162
|
+
"count",
|
|
163
|
+
] as const) {
|
|
164
|
+
const value = input[key];
|
|
165
|
+
if (validNumber(value, 0, 1_000_000_000, true)) record[key] = value;
|
|
166
|
+
}
|
|
167
|
+
for (const key of [
|
|
168
|
+
"dropped",
|
|
169
|
+
"completedUnits",
|
|
170
|
+
"totalUnits",
|
|
171
|
+
"count",
|
|
172
|
+
"route",
|
|
173
|
+
"queued",
|
|
174
|
+
"unchanged",
|
|
175
|
+
"archived",
|
|
176
|
+
"metadataUpdated",
|
|
177
|
+
"created",
|
|
178
|
+
"port",
|
|
179
|
+
"attemptNumber",
|
|
180
|
+
"queue",
|
|
181
|
+
"handlerKey",
|
|
182
|
+
"authority",
|
|
183
|
+
"actorType",
|
|
184
|
+
"stageKey",
|
|
185
|
+
"diagnosticId",
|
|
186
|
+
"traceId",
|
|
187
|
+
"jobId",
|
|
188
|
+
"runId",
|
|
189
|
+
"requestId",
|
|
190
|
+
] as const) {
|
|
191
|
+
if (
|
|
192
|
+
new TextEncoder().encode(JSON.stringify(record)).length <=
|
|
193
|
+
STRUCTURED_LOG_MAX_BYTES
|
|
194
|
+
)
|
|
195
|
+
break;
|
|
196
|
+
delete record[key];
|
|
197
|
+
record.truncated = true;
|
|
198
|
+
}
|
|
199
|
+
return record;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function validNumber(
|
|
203
|
+
value: unknown,
|
|
204
|
+
min: number,
|
|
205
|
+
max: number,
|
|
206
|
+
integer = false,
|
|
207
|
+
): value is number {
|
|
208
|
+
return (
|
|
209
|
+
typeof value === "number" &&
|
|
210
|
+
Number.isFinite(value) &&
|
|
211
|
+
value >= min &&
|
|
212
|
+
value <= max &&
|
|
213
|
+
(!integer || Number.isInteger(value))
|
|
214
|
+
);
|
|
215
|
+
}
|