@native-knowledge/cli 0.1.0-rc.1 → 0.1.0-rc.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/LICENSE +202 -21
- package/NOTICE +29 -0
- package/README.md +26 -19
- package/dist/agent-entry-cli.js +88 -0
- package/dist/auth-commands.js +102 -0
- package/dist/cli.js +101 -4874
- package/dist/commands.js +17 -0
- package/dist/download.js +45 -0
- package/dist/errors.js +7 -0
- package/dist/input.js +89 -0
- package/dist/internal/contracts/artifact.js +103 -0
- package/dist/internal/contracts/collection-control.js +69 -0
- package/dist/internal/contracts/collection.js +174 -0
- package/dist/internal/contracts/common.js +16 -0
- package/dist/internal/contracts/currentness.js +221 -0
- package/dist/internal/contracts/event.js +35 -0
- package/dist/internal/contracts/evidence.js +171 -0
- package/dist/internal/contracts/index.js +54 -0
- package/dist/internal/contracts/material-capability.js +8 -0
- package/dist/internal/contracts/material-carrier.js +15 -0
- package/dist/internal/contracts/material-context.js +9 -0
- package/dist/internal/contracts/material-read.js +12 -0
- package/dist/internal/contracts/observation.js +39 -0
- package/dist/internal/contracts/persistence.js +239 -0
- package/dist/internal/contracts/project-intent.js +121 -0
- package/dist/internal/contracts/projection.js +194 -0
- package/dist/internal/contracts/public-reference.js +87 -0
- package/dist/internal/contracts/reference-record.js +55 -0
- package/dist/internal/contracts/remote-file.js +159 -0
- package/dist/internal/contracts/resource-scope.js +38 -0
- package/dist/internal/contracts/review-packet.js +61 -0
- package/dist/internal/contracts/review-plane.js +89 -0
- package/dist/internal/contracts/rich-document.js +184 -0
- package/dist/internal/contracts/runtime-compatibility.js +55 -0
- package/dist/internal/contracts/runtime.js +68 -0
- package/dist/internal/contracts/scheduler.js +256 -0
- package/dist/internal/contracts/signal.js +77 -0
- package/dist/internal/contracts/source-governance.js +194 -0
- package/dist/internal/contracts/source.js +32 -0
- package/dist/internal/contracts/tenant-budget.js +34 -0
- package/dist/internal/contracts/tenant-collection-request.js +58 -0
- package/dist/internal/contracts/tenant-collection.js +66 -0
- package/dist/internal/contracts/tenant-currentness.js +26 -0
- package/dist/internal/contracts/tenant-management.js +27 -0
- package/dist/internal/contracts/tenant-material-api.js +46 -0
- package/dist/internal/contracts/tenant-material-json-schema.js +16 -0
- package/dist/internal/contracts/tenant-pdf-text.js +13 -0
- package/dist/internal/contracts/tenant-project-control.js +19 -0
- package/dist/internal/contracts/tenant-public-reference.js +26 -0
- package/dist/internal/contracts/tenant-resource-api.js +70 -0
- package/dist/internal/contracts/tenant-resource.js +118 -0
- package/dist/internal/contracts/tenant-rich-document.js +17 -0
- package/dist/internal/contracts/tenant-source-binding.js +33 -0
- package/dist/internal/contracts/tenant-source-bridge.js +9 -0
- package/dist/internal/contracts/tenant-source-cutover.js +26 -0
- package/dist/internal/contracts/tenant-source-document.js +38 -0
- package/dist/internal/contracts/tenant-source-lease.js +5 -0
- package/dist/internal/contracts/tenant-source.js +83 -0
- package/dist/internal/contracts/tenant-web-collection.js +118 -0
- package/dist/internal/contracts/tenant-web-content-plan.js +39 -0
- package/dist/internal/contracts/tenant-web-content-result.js +15 -0
- package/dist/internal/contracts/tenant-web-content.js +16 -0
- package/dist/internal/contracts/tenant-web-crawl.js +55 -0
- package/dist/internal/contracts/tenant-web-dynamic.js +68 -0
- package/dist/internal/contracts/tenant-web-results.js +41 -0
- package/dist/internal/contracts/tenant-web-workflow.js +29 -0
- package/dist/internal/serving-contracts/agent-catalog.js +56 -0
- package/dist/internal/serving-contracts/agent-entry.js +57 -0
- package/dist/internal/serving-contracts/agent-guidance-shapes.js +12 -0
- package/dist/internal/serving-contracts/agent-input-hints.js +104 -0
- package/dist/internal/serving-contracts/body-search.js +16 -0
- package/dist/internal/serving-contracts/consumer.js +45 -0
- package/dist/internal/serving-contracts/core.js +76 -0
- package/dist/internal/serving-contracts/http.js +64 -0
- package/dist/internal/serving-contracts/index.js +25 -0
- package/dist/internal/serving-contracts/management.js +15 -0
- package/dist/internal/serving-contracts/material-capabilities.js +14 -0
- package/dist/internal/serving-contracts/material-index-status.js +5 -0
- package/dist/internal/serving-contracts/mcp.js +140 -0
- package/dist/internal/serving-contracts/tenant-governance.js +13 -0
- package/dist/internal/serving-contracts/tenant-management.js +89 -0
- package/dist/internal/serving-contracts/tenant-project.js +39 -0
- package/dist/internal/serving-contracts/tenant-runtime.js +37 -0
- package/dist/internal/serving-contracts/tenant-session.js +8 -0
- package/dist/internal/serving-contracts/tenant-web-content.js +20 -0
- package/dist/internal/serving-contracts/tenant-web-crawl.js +13 -0
- package/dist/internal/serving-contracts/tenant-web-dynamic.js +20 -0
- package/dist/internal/serving-contracts/tenant-web-workflow.js +36 -0
- package/dist/internal/serving-contracts/tenant-web.js +53 -0
- package/dist/internal/serving-contracts/tenant-workspace.js +28 -0
- package/dist/internal/serving-contracts/workflow.js +37 -0
- package/dist/profile-store.js +142 -0
- package/package.json +9 -13
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { idSchema, isoDateTimeSchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
|
|
3
|
+
export const runtimeCapabilitySchema = z.string().trim().min(1).max(128);
|
|
4
|
+
export const runtimeNodeSchema = z.object({
|
|
5
|
+
schemaVersion: schemaVersionSchema,
|
|
6
|
+
nodeId: idSchema,
|
|
7
|
+
capabilities: z.array(runtimeCapabilitySchema).max(128),
|
|
8
|
+
codeRevision: z.string().trim().min(7).max(64),
|
|
9
|
+
observedAt: isoDateTimeSchema,
|
|
10
|
+
}).strict();
|
|
11
|
+
export const workAssignmentSchema = z.object({
|
|
12
|
+
schemaVersion: schemaVersionSchema,
|
|
13
|
+
assignmentId: idSchema,
|
|
14
|
+
workloadId: idSchema,
|
|
15
|
+
runtimeNodeId: idSchema,
|
|
16
|
+
projectRef: stableUriSchema.optional(),
|
|
17
|
+
revision: z.number().int().positive(),
|
|
18
|
+
enabled: z.boolean(),
|
|
19
|
+
effectiveFrom: isoDateTimeSchema.optional(),
|
|
20
|
+
effectiveUntil: isoDateTimeSchema.optional(),
|
|
21
|
+
}).strict().superRefine((value, ctx) => {
|
|
22
|
+
if (value.effectiveFrom && value.effectiveUntil && Date.parse(value.effectiveUntil) <= Date.parse(value.effectiveFrom)) {
|
|
23
|
+
ctx.addIssue({
|
|
24
|
+
code: 'custom',
|
|
25
|
+
path: ['effectiveUntil'],
|
|
26
|
+
message: 'effectiveUntil must be after effectiveFrom',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
export const runStatusSchema = z.enum(['SUCCEEDED', 'FAILED', 'PARTIAL', 'SKIPPED', 'BLOCKED']);
|
|
31
|
+
/** Fixed numeric diagnostics only; not a response body, URL or new finality authority. */
|
|
32
|
+
export function nativeAcquisitionHttpStatus(errorCode, value) {
|
|
33
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 100 || value > 599)
|
|
34
|
+
return undefined;
|
|
35
|
+
const terminal = errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' && (value < 200 || value >= 300 && value < 500 && ![401, 403, 429].includes(value));
|
|
36
|
+
const controlled = errorCode === 'TENANT_SOURCE_ACCESS_CONTROLLED' && [401, 403].includes(value);
|
|
37
|
+
return terminal || controlled ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
export const runReceiptSchema = z.object({
|
|
40
|
+
schemaVersion: schemaVersionSchema,
|
|
41
|
+
runId: idSchema,
|
|
42
|
+
workloadId: idSchema,
|
|
43
|
+
runtimeNodeId: idSchema,
|
|
44
|
+
projectRef: stableUriSchema.optional(),
|
|
45
|
+
projectIntentRef: stableUriSchema.optional(),
|
|
46
|
+
collectionBatchRef: stableUriSchema.optional(),
|
|
47
|
+
assignmentRevision: z.number().int().positive(),
|
|
48
|
+
codeRevision: z.string().trim().min(7).max(64),
|
|
49
|
+
startedAt: isoDateTimeSchema,
|
|
50
|
+
finishedAt: isoDateTimeSchema,
|
|
51
|
+
status: runStatusSchema,
|
|
52
|
+
inputFingerprint: sha256Schema.optional(),
|
|
53
|
+
outputFingerprint: sha256Schema.optional(),
|
|
54
|
+
evidenceCount: z.number().int().nonnegative(),
|
|
55
|
+
observationCount: z.number().int().nonnegative(),
|
|
56
|
+
errorCode: z.string().trim().min(1).max(128).optional(),
|
|
57
|
+
acquisitionHttpStatus: z.number().int().min(100).max(599).optional(),
|
|
58
|
+
}).strict().superRefine((value, ctx) => {
|
|
59
|
+
if (Date.parse(value.finishedAt) < Date.parse(value.startedAt)) {
|
|
60
|
+
ctx.addIssue({ code: 'custom', path: ['finishedAt'], message: 'finishedAt must not precede startedAt' });
|
|
61
|
+
}
|
|
62
|
+
if (value.status === 'FAILED' && !value.errorCode) {
|
|
63
|
+
ctx.addIssue({ code: 'custom', path: ['errorCode'], message: 'FAILED runs require errorCode' });
|
|
64
|
+
}
|
|
65
|
+
if (value.acquisitionHttpStatus !== undefined && (!value.collectionBatchRef || nativeAcquisitionHttpStatus(value.errorCode, value.acquisitionHttpStatus) === undefined || (value.errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' ? value.status !== 'FAILED' : value.status !== 'BLOCKED'))) {
|
|
66
|
+
ctx.addIssue({ code: 'custom', path: ['acquisitionHttpStatus'], message: 'HTTP diagnostic must match the native acquisition failure class' });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema } from './common.js';
|
|
3
|
+
import { signalProfileNameSchema } from './signal.js';
|
|
4
|
+
import { runtimeCapabilitySchema } from './runtime.js';
|
|
5
|
+
export const TIPP_UNIVERSE_MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
|
|
6
|
+
const deploymentPathSchema = z.string().trim().min(1).max(4096).refine((value) => !value.includes('\0'), 'path must not contain NUL');
|
|
7
|
+
const scheduledWorkloadBaseSchema = z.object({
|
|
8
|
+
workloadId: idSchema,
|
|
9
|
+
projectKey: projectKeySchema,
|
|
10
|
+
priority: z.number().int().min(0).max(1000),
|
|
11
|
+
intervalSeconds: z.number().int().min(60).max(7 * 24 * 60 * 60),
|
|
12
|
+
timeoutSeconds: z.number().int().min(30).max(2 * 60 * 60).default(15 * 60),
|
|
13
|
+
assignmentFile: deploymentPathSchema,
|
|
14
|
+
initialDueAt: isoDateTimeSchema.optional(),
|
|
15
|
+
}).strict();
|
|
16
|
+
const acquisitionInvocationSchema = z.object({
|
|
17
|
+
command: z.literal('acquisition-run'),
|
|
18
|
+
programDirectory: deploymentPathSchema,
|
|
19
|
+
fileRootsFile: deploymentPathSchema.optional(),
|
|
20
|
+
}).strict();
|
|
21
|
+
const reviewPacketInvocationSchema = z.object({
|
|
22
|
+
command: z.literal('review-packet-project'),
|
|
23
|
+
}).strict();
|
|
24
|
+
// 自动发布只消费已持久化的事件和 Agent 输入,不触发任何来源采集。
|
|
25
|
+
const autoPublicationInvocationSchema = z.object({
|
|
26
|
+
command: z.literal('auto-publication-tick'),
|
|
27
|
+
// 同项目发布并发有固定上限;旧 manifest 无此字段时采用 Runner 默认值。
|
|
28
|
+
concurrency: z.number().int().min(1).max(4).optional(),
|
|
29
|
+
}).strict();
|
|
30
|
+
const publicReferenceInvocationSchema = z.object({ command: z.literal('public-reference-tick'), policyFile: deploymentPathSchema, identityDatabase: deploymentPathSchema }).strict();
|
|
31
|
+
const feedProjectInvocationSchema = z.object({
|
|
32
|
+
command: z.literal('feed-project'),
|
|
33
|
+
}).strict();
|
|
34
|
+
const procurementProjectInvocationSchema = z.object({
|
|
35
|
+
command: z.literal('procurement-project'),
|
|
36
|
+
}).strict();
|
|
37
|
+
const pkValuationStatusProjectInvocationSchema = z.object({
|
|
38
|
+
command: z.literal('pk-valuation-status-project'),
|
|
39
|
+
}).strict();
|
|
40
|
+
const pkStandardsProjectInvocationSchema = z.object({
|
|
41
|
+
command: z.literal('pk-standards-project'),
|
|
42
|
+
}).strict();
|
|
43
|
+
const cpecProjectInvocationSchema = z.object({
|
|
44
|
+
command: z.literal('cpec-project'),
|
|
45
|
+
}).strict();
|
|
46
|
+
const tippUniverseProjectInvocationSchema = z.object({
|
|
47
|
+
command: z.literal('tipp-universe-project'),
|
|
48
|
+
chapterBatch: z.number().int().min(1).max(96).default(4),
|
|
49
|
+
maxResourceBytes: z.number().int().min(64 * 1024).max(16 * 1024 * 1024).default(TIPP_UNIVERSE_MAX_RESOURCE_BYTES),
|
|
50
|
+
}).strict();
|
|
51
|
+
const tippDetailProjectInvocationSchema = z.object({
|
|
52
|
+
command: z.literal('tipp-detail-project'),
|
|
53
|
+
detailBatch: z.number().int().min(1).max(1000).default(50),
|
|
54
|
+
detailConcurrency: z.number().int().min(1).max(4).optional(),
|
|
55
|
+
maxResourceBytes: z.number().int().min(64 * 1024).max(16 * 1024 * 1024).default(4 * 1024 * 1024),
|
|
56
|
+
}).strict();
|
|
57
|
+
const tippTariffProjectInvocationSchema = z.object({
|
|
58
|
+
command: z.literal('tipp-tariff-project'),
|
|
59
|
+
}).strict();
|
|
60
|
+
const tippRegulatoryProjectInvocationSchema = z.object({
|
|
61
|
+
command: z.literal('tipp-regulatory-project'),
|
|
62
|
+
}).strict();
|
|
63
|
+
const pkTariffReconciliationProjectInvocationSchema = z.object({
|
|
64
|
+
command: z.literal('pk-tariff-reconciliation-project'),
|
|
65
|
+
}).strict();
|
|
66
|
+
const collectionInvocationSchema = z.object({
|
|
67
|
+
command: z.literal('collection-tick'),
|
|
68
|
+
maxResourceBytes: z.number().int().min(1024).max(64 * 1024 * 1024).default(16 * 1024 * 1024),
|
|
69
|
+
batchLimit: z.number().int().min(1).max(10_000).default(100),
|
|
70
|
+
fileRootsFile: deploymentPathSchema.optional(),
|
|
71
|
+
}).strict();
|
|
72
|
+
const collectionProjectionInvocationSchema = z.object({ command: z.literal('collection-projection-tick'), limit: z.number().int().min(1).max(100).default(25) }).strict();
|
|
73
|
+
const signalCollectInvocationSchema = z.object({
|
|
74
|
+
command: z.literal('signal-collect'),
|
|
75
|
+
programFile: deploymentPathSchema,
|
|
76
|
+
profile: signalProfileNameSchema,
|
|
77
|
+
}).strict();
|
|
78
|
+
const signalComposeInvocationSchema = z.object({
|
|
79
|
+
command: z.literal('signal-compose'),
|
|
80
|
+
programFile: deploymentPathSchema,
|
|
81
|
+
}).strict();
|
|
82
|
+
const customsCorpusInvocationSchema = z.object({
|
|
83
|
+
command: z.literal('customs-corpus-run'),
|
|
84
|
+
programFile: deploymentPathSchema,
|
|
85
|
+
adapter: z.enum(['us-hts', 'us-cross', 'ca-tariff', 'ca-rulings', 'eu-ebti', 'pk-tariff', 'pk-import-sro', 'pk-tax-authorities', 'pk-classification', 'pk-valuation', 'za-determinations', 'eg-nafeza', 'ae-tariff', 'sa-tariff', 'ru-eaeu']),
|
|
86
|
+
refreshStoredDocuments: z.boolean().optional(),
|
|
87
|
+
// GCC 重新访问官方来源必须显式启用,历史文件导入不触发网络。
|
|
88
|
+
refreshRemoteBundle: z.boolean().optional(),
|
|
89
|
+
}).strict();
|
|
90
|
+
const locationCorpusInvocationSchema = z.object({
|
|
91
|
+
command: z.literal('location-corpus-run'),
|
|
92
|
+
programFile: deploymentPathSchema,
|
|
93
|
+
adapter: z.literal('unlocode'),
|
|
94
|
+
}).strict();
|
|
95
|
+
export const schedulerInvocationSchema = z.discriminatedUnion('command', [
|
|
96
|
+
z.object({ command: z.literal('business-information-collect'), maxSources: z.number().int().min(1).max(32).default(8), maxResourceBytes: z.number().int().min(1024).max(16 * 1024 * 1024).default(2 * 1024 * 1024), fileRootsFile: deploymentPathSchema.optional() }).strict(),
|
|
97
|
+
z.object({ command: z.literal('pk-import-rules-project'), acquireLinkedDocuments: z.boolean().default(false), documentBatch: z.number().int().min(1).max(50).default(20), maxDocumentBytes: z.number().int().min(1024).max(32 * 1024 * 1024).default(16 * 1024 * 1024) }).strict(),
|
|
98
|
+
acquisitionInvocationSchema,
|
|
99
|
+
collectionInvocationSchema,
|
|
100
|
+
collectionProjectionInvocationSchema,
|
|
101
|
+
z.object({ command: z.literal('project-projection-tick'),
|
|
102
|
+
maxRows: z.number().int().min(1).max(100).optional(),
|
|
103
|
+
maxDependencyRows: z.number().int().min(1).max(10000).optional(),
|
|
104
|
+
maxBytes: z.number().int().min(1).max(16 * 1024 ** 3).optional(),
|
|
105
|
+
maxMs: z.number().int().min(1).max(600000).optional(),
|
|
106
|
+
}).strict(),
|
|
107
|
+
reviewPacketInvocationSchema,
|
|
108
|
+
autoPublicationInvocationSchema,
|
|
109
|
+
publicReferenceInvocationSchema,
|
|
110
|
+
feedProjectInvocationSchema,
|
|
111
|
+
procurementProjectInvocationSchema,
|
|
112
|
+
pkValuationStatusProjectInvocationSchema,
|
|
113
|
+
pkStandardsProjectInvocationSchema,
|
|
114
|
+
cpecProjectInvocationSchema,
|
|
115
|
+
tippUniverseProjectInvocationSchema,
|
|
116
|
+
tippDetailProjectInvocationSchema,
|
|
117
|
+
tippTariffProjectInvocationSchema,
|
|
118
|
+
tippRegulatoryProjectInvocationSchema,
|
|
119
|
+
pkTariffReconciliationProjectInvocationSchema,
|
|
120
|
+
signalCollectInvocationSchema,
|
|
121
|
+
signalComposeInvocationSchema,
|
|
122
|
+
customsCorpusInvocationSchema,
|
|
123
|
+
locationCorpusInvocationSchema,
|
|
124
|
+
]);
|
|
125
|
+
export const scheduledWorkloadSchema = scheduledWorkloadBaseSchema.extend({
|
|
126
|
+
invocation: schedulerInvocationSchema,
|
|
127
|
+
}).strict().superRefine((value, ctx) => {
|
|
128
|
+
let expected;
|
|
129
|
+
switch (value.invocation.command) {
|
|
130
|
+
case 'acquisition-run':
|
|
131
|
+
expected = `project:${value.projectKey}:acquisition`;
|
|
132
|
+
break;
|
|
133
|
+
case 'collection-projection-tick':
|
|
134
|
+
expected = `project:${value.projectKey}:material-projection`;
|
|
135
|
+
break;
|
|
136
|
+
case 'project-projection-tick':
|
|
137
|
+
expected = `project:${value.projectKey}:catalog-projection`;
|
|
138
|
+
break;
|
|
139
|
+
case 'collection-tick':
|
|
140
|
+
expected = `project:${value.projectKey}:collection`;
|
|
141
|
+
break;
|
|
142
|
+
case 'review-packet-project':
|
|
143
|
+
expected = `project:${value.projectKey}:review-packets`;
|
|
144
|
+
break;
|
|
145
|
+
case 'auto-publication-tick':
|
|
146
|
+
expected = `project:${value.projectKey}:auto-publication`;
|
|
147
|
+
break;
|
|
148
|
+
case 'public-reference-tick':
|
|
149
|
+
expected = `project:${value.projectKey}:public-reference-publication`;
|
|
150
|
+
break;
|
|
151
|
+
case 'feed-project':
|
|
152
|
+
expected = `project:${value.projectKey}:feed-project`;
|
|
153
|
+
break;
|
|
154
|
+
case 'procurement-project':
|
|
155
|
+
expected = `project:${value.projectKey}:procurement-project`;
|
|
156
|
+
break;
|
|
157
|
+
case 'pk-valuation-status-project':
|
|
158
|
+
expected = `project:${value.projectKey}:pk-valuation-status-project`;
|
|
159
|
+
break;
|
|
160
|
+
case 'pk-standards-project':
|
|
161
|
+
expected = `project:${value.projectKey}:pk-standards-project`;
|
|
162
|
+
break;
|
|
163
|
+
case 'pk-import-rules-project':
|
|
164
|
+
expected = `project:${value.projectKey}:pk-import-rules-project`;
|
|
165
|
+
break;
|
|
166
|
+
case 'cpec-project':
|
|
167
|
+
expected = `project:${value.projectKey}:cpec-project`;
|
|
168
|
+
break;
|
|
169
|
+
case 'business-information-collect':
|
|
170
|
+
expected = `project:${value.projectKey}:business-information-collect`;
|
|
171
|
+
break;
|
|
172
|
+
case 'tipp-universe-project':
|
|
173
|
+
expected = `project:${value.projectKey}:tipp-universe-project`;
|
|
174
|
+
break;
|
|
175
|
+
case 'tipp-detail-project':
|
|
176
|
+
expected = `project:${value.projectKey}:tipp-detail-project`;
|
|
177
|
+
break;
|
|
178
|
+
case 'tipp-tariff-project':
|
|
179
|
+
expected = `project:${value.projectKey}:tipp-tariff-project`;
|
|
180
|
+
break;
|
|
181
|
+
case 'tipp-regulatory-project':
|
|
182
|
+
expected = `project:${value.projectKey}:tipp-regulatory-project`;
|
|
183
|
+
break;
|
|
184
|
+
case 'pk-tariff-reconciliation-project':
|
|
185
|
+
expected = `project:${value.projectKey}:pk-tariff-reconciliation-project`;
|
|
186
|
+
break;
|
|
187
|
+
case 'signal-collect':
|
|
188
|
+
expected = `project:${value.projectKey}:signal:${value.invocation.profile}`;
|
|
189
|
+
break;
|
|
190
|
+
case 'signal-compose':
|
|
191
|
+
expected = `project:${value.projectKey}:signal:compose`;
|
|
192
|
+
break;
|
|
193
|
+
case 'customs-corpus-run':
|
|
194
|
+
expected = `project:${value.projectKey}:customs:${value.invocation.adapter}`;
|
|
195
|
+
break;
|
|
196
|
+
case 'location-corpus-run':
|
|
197
|
+
expected = `project:${value.projectKey}:locations:${value.invocation.adapter}`;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (value.workloadId !== expected) {
|
|
201
|
+
ctx.addIssue({ code: 'custom', path: ['workloadId'], message: `workloadId must be ${expected}` });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
export const nodeSchedulerManifestSchema = z.object({
|
|
205
|
+
schemaVersion: schemaVersionSchema,
|
|
206
|
+
nodeId: idSchema,
|
|
207
|
+
capabilities: z.array(runtimeCapabilitySchema).max(128).default(['network:http']),
|
|
208
|
+
maxWorkloadsPerTick: z.number().int().min(1).max(32).default(8),
|
|
209
|
+
workloads: z.array(scheduledWorkloadSchema).max(256),
|
|
210
|
+
}).strict().superRefine((value, ctx) => {
|
|
211
|
+
const seen = new Set();
|
|
212
|
+
const capabilities = new Set(value.capabilities);
|
|
213
|
+
value.workloads.forEach((workload, index) => {
|
|
214
|
+
if (seen.has(workload.workloadId))
|
|
215
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'workloadId'], message: 'duplicate workloadId' });
|
|
216
|
+
seen.add(workload.workloadId);
|
|
217
|
+
if (workload.invocation.command === 'business-information-collect' && !capabilities.has('network:http'))
|
|
218
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'public business collection requires network:http capability' });
|
|
219
|
+
if ((workload.invocation.command === 'collection-tick' || workload.invocation.command === 'collection-projection-tick') && !capabilities.has('network:http')) {
|
|
220
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'collection tick requires network:http capability' });
|
|
221
|
+
}
|
|
222
|
+
if (workload.invocation.command === 'signal-collect') {
|
|
223
|
+
if (!capabilities.has('network:http')) {
|
|
224
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'signal collection requires network:http capability' });
|
|
225
|
+
}
|
|
226
|
+
if (['tiktok', 'douyin', 'meta', 'deep-social'].includes(workload.invocation.profile) && !capabilities.has('browser:playwright')) {
|
|
227
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: `profile ${workload.invocation.profile} requires browser:playwright capability` });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (workload.invocation.command === 'location-corpus-run' && !capabilities.has('network:http')) {
|
|
231
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'reference corpus collection requires network:http capability' });
|
|
232
|
+
}
|
|
233
|
+
if (workload.invocation.command === 'tipp-universe-project' && !capabilities.has('network:http')) {
|
|
234
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'TIPP universe collection requires network:http capability' });
|
|
235
|
+
}
|
|
236
|
+
if (workload.invocation.command === 'tipp-detail-project' && !capabilities.has('network:http')) {
|
|
237
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'TIPP detail collection requires network:http capability' });
|
|
238
|
+
}
|
|
239
|
+
if (workload.invocation.command === 'customs-corpus-run') {
|
|
240
|
+
if (workload.invocation.refreshRemoteBundle) {
|
|
241
|
+
if (!['ae-tariff', 'sa-tariff'].includes(workload.invocation.adapter))
|
|
242
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation', 'refreshRemoteBundle'], message: 'GCC refresh requires ae-tariff or sa-tariff adapter' });
|
|
243
|
+
if (!capabilities.has('network:http'))
|
|
244
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: 'GCC refresh requires network:http capability' });
|
|
245
|
+
}
|
|
246
|
+
if (workload.invocation.refreshStoredDocuments && !['pk-import-sro', 'ru-eaeu'].includes(workload.invocation.adapter)) {
|
|
247
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation', 'refreshStoredDocuments'], message: 'stored document refresh requires pk-import-sro or ru-eaeu adapter' });
|
|
248
|
+
}
|
|
249
|
+
const localVerifiedBundleAdapter = ['pk-valuation', 'za-determinations', 'ae-tariff', 'sa-tariff'].includes(workload.invocation.adapter);
|
|
250
|
+
if (!localVerifiedBundleAdapter && !capabilities.has('network:http')) {
|
|
251
|
+
ctx.addIssue({ code: 'custom', path: ['workloads', index, 'invocation'], message: `customs adapter ${workload.invocation.adapter} requires network:http capability` });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
export const scheduledWorkerCommands = schedulerInvocationSchema.options.map(option => option.shape.command.value);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
|
|
3
|
+
export const signalProfileNameSchema = z.enum([
|
|
4
|
+
'trends',
|
|
5
|
+
'hn',
|
|
6
|
+
'github',
|
|
7
|
+
'autocomplete',
|
|
8
|
+
'youtube',
|
|
9
|
+
'tiktok',
|
|
10
|
+
'douyin',
|
|
11
|
+
'meta',
|
|
12
|
+
'deep-social',
|
|
13
|
+
]);
|
|
14
|
+
export const signalSourceHealthSchema = z.object({
|
|
15
|
+
ok: z.boolean(),
|
|
16
|
+
items: z.number().int().nonnegative().optional(),
|
|
17
|
+
state: z.string().trim().min(1).max(128).optional(),
|
|
18
|
+
note: z.string().trim().max(2000).optional(),
|
|
19
|
+
error: z.string().trim().max(2000).optional(),
|
|
20
|
+
}).catchall(z.unknown());
|
|
21
|
+
export const signalSourceSnapshotSchema = z.object({
|
|
22
|
+
schemaVersion: schemaVersionSchema,
|
|
23
|
+
projectKey: projectKeySchema,
|
|
24
|
+
projectRef: stableUriSchema.optional(),
|
|
25
|
+
sourceRef: stableUriSchema.optional(),
|
|
26
|
+
sourceRevisionRef: stableUriSchema.optional(),
|
|
27
|
+
sourceKey: idSchema,
|
|
28
|
+
profile: signalProfileNameSchema,
|
|
29
|
+
collectedAt: isoDateTimeSchema,
|
|
30
|
+
contentHash: sha256Schema,
|
|
31
|
+
evidenceId: idSchema,
|
|
32
|
+
signalCount: z.number().int().nonnegative(),
|
|
33
|
+
health: signalSourceHealthSchema,
|
|
34
|
+
}).strict();
|
|
35
|
+
export const signalSourceAttemptSchema = z.object({
|
|
36
|
+
schemaVersion: schemaVersionSchema,
|
|
37
|
+
projectKey: projectKeySchema,
|
|
38
|
+
projectRef: stableUriSchema.optional(),
|
|
39
|
+
sourceRef: stableUriSchema.optional(),
|
|
40
|
+
sourceRevisionRef: stableUriSchema.optional(),
|
|
41
|
+
sourceKey: idSchema,
|
|
42
|
+
profile: signalProfileNameSchema,
|
|
43
|
+
lastAttemptAt: isoDateTimeSchema,
|
|
44
|
+
lastSuccessAt: isoDateTimeSchema.nullable(),
|
|
45
|
+
success: z.boolean(),
|
|
46
|
+
durationMs: z.number().int().nonnegative(),
|
|
47
|
+
state: z.string().trim().min(1).max(128).nullable(),
|
|
48
|
+
error: z.string().trim().max(2000).nullable(),
|
|
49
|
+
}).strict();
|
|
50
|
+
export const signalGenerationSchema = z.object({
|
|
51
|
+
schemaVersion: schemaVersionSchema,
|
|
52
|
+
projectKey: projectKeySchema,
|
|
53
|
+
projectRef: stableUriSchema.optional(),
|
|
54
|
+
generationKey: sha256Schema,
|
|
55
|
+
scoringProfile: z.string().trim().min(1).max(128),
|
|
56
|
+
generatedAt: isoDateTimeSchema,
|
|
57
|
+
snapshotManifest: z.record(idSchema, sha256Schema),
|
|
58
|
+
historyBasis: z.array(sha256Schema).max(512).default([]),
|
|
59
|
+
contentHash: sha256Schema,
|
|
60
|
+
candidateCount: z.number().int().nonnegative(),
|
|
61
|
+
observationCount: z.number().int().nonnegative(),
|
|
62
|
+
}).strict();
|
|
63
|
+
export const signalHistoryCandidateSchema = z.object({
|
|
64
|
+
query: z.string().trim().min(1).max(1000),
|
|
65
|
+
score: z.number().finite(),
|
|
66
|
+
sources: z.array(idSchema).max(64),
|
|
67
|
+
}).strict();
|
|
68
|
+
export const signalHistorySeedSchema = z.object({
|
|
69
|
+
schemaVersion: schemaVersionSchema,
|
|
70
|
+
projectKey: projectKeySchema,
|
|
71
|
+
projectRef: stableUriSchema.optional(),
|
|
72
|
+
historyKey: sha256Schema,
|
|
73
|
+
generatedAt: isoDateTimeSchema,
|
|
74
|
+
scoringVersion: z.number().int().positive(),
|
|
75
|
+
origin: z.string().trim().min(1).max(128),
|
|
76
|
+
candidates: z.array(signalHistoryCandidateSchema).max(120),
|
|
77
|
+
}).strict();
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, stableUriSchema, } from './common.js';
|
|
3
|
+
export const sourceExpectationSchema = z.enum(['REQUIRED', 'OPTIONAL', 'CONDITIONAL']);
|
|
4
|
+
export const sourceDeclaredStateSchema = z.enum([
|
|
5
|
+
'CONFIGURED',
|
|
6
|
+
'PENDING_DISCOVERY',
|
|
7
|
+
'KNOWN_MISSING',
|
|
8
|
+
'ACCESS_CONTROLLED',
|
|
9
|
+
'NEED_AUTHORIZED_SOURCE',
|
|
10
|
+
'DEFERRED',
|
|
11
|
+
'RETIRED',
|
|
12
|
+
]);
|
|
13
|
+
export const sourceCoverageStateSchema = z.enum([
|
|
14
|
+
'ACQUIRED',
|
|
15
|
+
'CONFIGURED_UNOBSERVED',
|
|
16
|
+
'PARTIAL',
|
|
17
|
+
'BLOCKED',
|
|
18
|
+
'ACCESS_CONTROLLED',
|
|
19
|
+
'PENDING_DISCOVERY',
|
|
20
|
+
'KNOWN_MISSING',
|
|
21
|
+
'NEED_AUTHORIZED_SOURCE',
|
|
22
|
+
'DEFERRED',
|
|
23
|
+
'RETIRED',
|
|
24
|
+
]);
|
|
25
|
+
const shortText = z.string().trim().min(1).max(512);
|
|
26
|
+
const roleSchema = z.string().trim().min(1).max(256);
|
|
27
|
+
export const sourceDiscoveryPolicySchema = z.object({
|
|
28
|
+
hostMode: z.enum(['ALLOWLIST', 'EVIDENCE_OBSERVED']).default('ALLOWLIST'),
|
|
29
|
+
allowedHosts: z.array(z.string().trim().toLowerCase().min(1).max(253)).max(256).default([]),
|
|
30
|
+
forbiddenHostSuffixes: z.array(z.string().trim().toLowerCase().min(1).max(253)).max(256).default([]),
|
|
31
|
+
allowedPathPrefixes: z.array(z.string().trim().startsWith('/').max(2048)).min(1).max(128),
|
|
32
|
+
}).strict().superRefine((value, ctx) => {
|
|
33
|
+
if (value.hostMode === 'ALLOWLIST' && !value.allowedHosts.length)
|
|
34
|
+
ctx.addIssue({ code: 'custom', path: ['allowedHosts'], message: 'ALLOWLIST discovery requires at least one allowed host' });
|
|
35
|
+
const allowed = new Set(value.allowedHosts);
|
|
36
|
+
for (const suffix of value.forbiddenHostSuffixes)
|
|
37
|
+
if (allowed.has(suffix))
|
|
38
|
+
ctx.addIssue({ code: 'custom', path: ['forbiddenHostSuffixes'], message: `Host cannot be both allowed and forbidden: ${suffix}` });
|
|
39
|
+
});
|
|
40
|
+
export const sourceUniverseEntrySchema = z.object({
|
|
41
|
+
sourceId: idSchema,
|
|
42
|
+
familyId: idSchema,
|
|
43
|
+
acquisitionSourceIds: z.array(idSchema).max(64).default([]),
|
|
44
|
+
expectation: sourceExpectationSchema,
|
|
45
|
+
authority: shortText,
|
|
46
|
+
access: accessClassificationSchema,
|
|
47
|
+
freshness: shortText,
|
|
48
|
+
semanticRoles: z.array(roleSchema).max(64).default([]),
|
|
49
|
+
locator: stableUriSchema.nullable().default(null),
|
|
50
|
+
declaredState: sourceDeclaredStateSchema,
|
|
51
|
+
expectedOwner: shortText.nullable().default(null),
|
|
52
|
+
requiredFor: z.array(idSchema).max(128).default([]),
|
|
53
|
+
discoveryPolicy: sourceDiscoveryPolicySchema.nullable().default(null),
|
|
54
|
+
notes: z.string().trim().max(2000).nullable().default(null),
|
|
55
|
+
}).strict().superRefine((value, ctx) => {
|
|
56
|
+
if (value.declaredState === 'CONFIGURED' && value.locator === null) {
|
|
57
|
+
ctx.addIssue({ code: 'custom', path: ['locator'], message: 'CONFIGURED source requires locator' });
|
|
58
|
+
}
|
|
59
|
+
if (['PENDING_DISCOVERY', 'KNOWN_MISSING', 'ACCESS_CONTROLLED', 'NEED_AUTHORIZED_SOURCE'].includes(value.declaredState) && value.expectation === 'OPTIONAL' && !value.requiredFor.length) {
|
|
60
|
+
ctx.addIssue({ code: 'custom', path: ['requiredFor'], message: 'unresolved optional source must declare why it is tracked' });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
export const sourceUniverseSchema = z.object({
|
|
64
|
+
schemaVersion: schemaVersionSchema,
|
|
65
|
+
projectKey: projectKeySchema,
|
|
66
|
+
asOf: isoDateTimeSchema,
|
|
67
|
+
sources: z.array(sourceUniverseEntrySchema).min(1).max(5000),
|
|
68
|
+
}).strict().superRefine((value, ctx) => {
|
|
69
|
+
const ids = new Set();
|
|
70
|
+
for (let i = 0; i < value.sources.length; i += 1) {
|
|
71
|
+
const id = value.sources[i].sourceId;
|
|
72
|
+
if (ids.has(id))
|
|
73
|
+
ctx.addIssue({ code: 'custom', path: ['sources', i, 'sourceId'], message: 'sourceId must be unique' });
|
|
74
|
+
ids.add(id);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
export const sourceCoverageEntrySchema = z.object({
|
|
78
|
+
sourceId: idSchema,
|
|
79
|
+
familyId: idSchema,
|
|
80
|
+
expectation: sourceExpectationSchema,
|
|
81
|
+
declaredState: sourceDeclaredStateSchema,
|
|
82
|
+
coverageState: sourceCoverageStateSchema,
|
|
83
|
+
configuredItemCount: z.number().int().nonnegative(),
|
|
84
|
+
observedItemCount: z.number().int().nonnegative(),
|
|
85
|
+
acquiredItemCount: z.number().int().nonnegative(),
|
|
86
|
+
blockedItemCount: z.number().int().nonnegative(),
|
|
87
|
+
accessControlledItemCount: z.number().int().nonnegative(),
|
|
88
|
+
evidenceRefs: z.array(stableUriSchema).max(2048),
|
|
89
|
+
blockers: z.array(shortText).max(256),
|
|
90
|
+
}).strict();
|
|
91
|
+
export const sourceCoverageAssessmentSchema = z.object({
|
|
92
|
+
schemaVersion: schemaVersionSchema,
|
|
93
|
+
projectKey: projectKeySchema,
|
|
94
|
+
assessedAt: isoDateTimeSchema,
|
|
95
|
+
complete: z.boolean(),
|
|
96
|
+
requiredSourceCount: z.number().int().nonnegative(),
|
|
97
|
+
coveredRequiredSourceCount: z.number().int().nonnegative(),
|
|
98
|
+
entries: z.array(sourceCoverageEntrySchema).max(5000),
|
|
99
|
+
blockers: z.array(shortText).max(1000),
|
|
100
|
+
}).strict();
|
|
101
|
+
export const sourceGapStateSchema = z.enum([
|
|
102
|
+
'PENDING_DISCOVERY',
|
|
103
|
+
'KNOWN_MISSING',
|
|
104
|
+
'ACCESS_CONTROLLED',
|
|
105
|
+
'NEED_AUTHORIZED_SOURCE',
|
|
106
|
+
'DEFERRED',
|
|
107
|
+
]);
|
|
108
|
+
export const sourceGapSchema = z.object({
|
|
109
|
+
schemaVersion: schemaVersionSchema,
|
|
110
|
+
gapId: idSchema,
|
|
111
|
+
projectKey: projectKeySchema,
|
|
112
|
+
sourceId: idSchema,
|
|
113
|
+
familyId: idSchema,
|
|
114
|
+
state: sourceGapStateSchema,
|
|
115
|
+
access: accessClassificationSchema,
|
|
116
|
+
expectedOwner: shortText.nullable(),
|
|
117
|
+
reason: z.string().trim().min(1).max(2000),
|
|
118
|
+
requiredFor: z.array(idSchema).max(128),
|
|
119
|
+
evidenceRefs: z.array(stableUriSchema).max(256),
|
|
120
|
+
recordedAt: isoDateTimeSchema,
|
|
121
|
+
}).strict();
|
|
122
|
+
const discoveryLinkIdSchema = z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
|
|
123
|
+
const discoveredLinkSchema = z.object({
|
|
124
|
+
linkId: discoveryLinkIdSchema,
|
|
125
|
+
url: z.url(),
|
|
126
|
+
locator: z.string().trim().min(1).max(2048),
|
|
127
|
+
parentEvidenceRef: stableUriSchema,
|
|
128
|
+
title: z.string().trim().min(1).max(500).optional(),
|
|
129
|
+
}).strict();
|
|
130
|
+
export const sourceDiscoveryProposalSchema = z.object({
|
|
131
|
+
schemaVersion: schemaVersionSchema,
|
|
132
|
+
projectKey: projectKeySchema,
|
|
133
|
+
proposalId: idSchema,
|
|
134
|
+
familyId: idSchema,
|
|
135
|
+
createdAt: isoDateTimeSchema,
|
|
136
|
+
observedLinks: z.array(discoveredLinkSchema).min(1).max(1000),
|
|
137
|
+
selectedLinkIds: z.array(discoveryLinkIdSchema).max(1000),
|
|
138
|
+
completeness: z.object({
|
|
139
|
+
claim: z.enum(['COMPLETE', 'INCOMPLETE']),
|
|
140
|
+
missing: z.array(z.string().trim().min(1).max(500)).max(1000),
|
|
141
|
+
notes: z.string().trim().max(4000).nullable().default(null),
|
|
142
|
+
}).strict(),
|
|
143
|
+
}).strict();
|
|
144
|
+
export const sourceDiscoveryValidationSchema = z.object({
|
|
145
|
+
schemaVersion: schemaVersionSchema,
|
|
146
|
+
projectKey: projectKeySchema,
|
|
147
|
+
proposalId: idSchema,
|
|
148
|
+
familyId: idSchema,
|
|
149
|
+
proposalHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
150
|
+
valid: z.boolean(),
|
|
151
|
+
selectedSources: z.array(z.object({ linkId: idSchema, url: z.url(), parentEvidenceRef: stableUriSchema }).strict()).max(1000),
|
|
152
|
+
completeness: z.object({ claim: z.enum(['COMPLETE', 'INCOMPLETE']), missing: z.array(z.string()).max(1000), notes: z.string().nullable() }).strict(),
|
|
153
|
+
evidenceRefs: z.array(stableUriSchema).max(1000),
|
|
154
|
+
errors: z.array(shortText).max(1000),
|
|
155
|
+
warnings: z.array(shortText).max(1000),
|
|
156
|
+
}).strict();
|
|
157
|
+
export const sourceDiscoveryReceiptSchema = z.object({
|
|
158
|
+
schemaVersion: schemaVersionSchema,
|
|
159
|
+
projectKey: projectKeySchema,
|
|
160
|
+
proposalId: idSchema,
|
|
161
|
+
familyId: idSchema,
|
|
162
|
+
proposalHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
163
|
+
state: z.enum(['COMPLETE', 'REVIEWED_PARTIAL']),
|
|
164
|
+
proposalRef: stableUriSchema,
|
|
165
|
+
reviewRef: stableUriSchema,
|
|
166
|
+
reviewedAt: isoDateTimeSchema,
|
|
167
|
+
selectedSources: z.array(z.object({ linkId: idSchema, url: z.url(), parentEvidenceRef: stableUriSchema }).strict()).max(1000),
|
|
168
|
+
completeness: z.object({ claim: z.enum(['COMPLETE', 'INCOMPLETE']), missing: z.array(z.string()).max(1000), notes: z.string().nullable() }).strict(),
|
|
169
|
+
evidenceRefs: z.array(stableUriSchema).max(1000),
|
|
170
|
+
configMutationPerformed: z.literal(false),
|
|
171
|
+
}).strict().superRefine((value, ctx) => {
|
|
172
|
+
if (value.state === 'COMPLETE' && (value.completeness.claim !== 'COMPLETE' || value.completeness.missing.length))
|
|
173
|
+
ctx.addIssue({ code: 'custom', path: ['state'], message: 'COMPLETE receipt requires complete proposal without missing sources' });
|
|
174
|
+
if (value.state === 'REVIEWED_PARTIAL' && value.completeness.claim !== 'INCOMPLETE')
|
|
175
|
+
ctx.addIssue({ code: 'custom', path: ['state'], message: 'REVIEWED_PARTIAL receipt requires INCOMPLETE proposal' });
|
|
176
|
+
});
|
|
177
|
+
export const sourceAdmissionMatchSchema = z.object({
|
|
178
|
+
url: z.url(),
|
|
179
|
+
status: z.enum(['ADMITTED', 'BLOCKED', 'MISSING', 'DUPLICATE']),
|
|
180
|
+
universeSourceId: idSchema.nullable(),
|
|
181
|
+
acquisitionSourceId: idSchema.nullable(),
|
|
182
|
+
concreteSourceId: idSchema.nullable(),
|
|
183
|
+
issues: z.array(shortText).max(128),
|
|
184
|
+
}).strict();
|
|
185
|
+
export const sourceAdmissionAssessmentSchema = z.object({
|
|
186
|
+
schemaVersion: schemaVersionSchema,
|
|
187
|
+
projectKey: projectKeySchema,
|
|
188
|
+
familyId: idSchema,
|
|
189
|
+
state: z.enum(['ADMITTED', 'BLOCKED']),
|
|
190
|
+
selectedSourceCount: z.number().int().nonnegative(),
|
|
191
|
+
matches: z.array(sourceAdmissionMatchSchema).max(1000),
|
|
192
|
+
blockers: z.array(shortText).max(1000),
|
|
193
|
+
configMutationPerformed: z.literal(false),
|
|
194
|
+
}).strict();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { accessClassificationSchema, idSchema, projectKeySchema, schemaVersionSchema, stableUriSchema, } from './common.js';
|
|
3
|
+
export const sourceKindSchema = z.enum([
|
|
4
|
+
'WEB',
|
|
5
|
+
'API',
|
|
6
|
+
'FILE',
|
|
7
|
+
'REPOSITORY',
|
|
8
|
+
'FEED',
|
|
9
|
+
'SOCIAL',
|
|
10
|
+
'BROWSER',
|
|
11
|
+
'MANUAL',
|
|
12
|
+
'OTHER',
|
|
13
|
+
]);
|
|
14
|
+
export const sourceDefinitionSchema = z.object({
|
|
15
|
+
schemaVersion: schemaVersionSchema,
|
|
16
|
+
sourceId: idSchema,
|
|
17
|
+
projectKey: projectKeySchema.optional(),
|
|
18
|
+
kind: sourceKindSchema,
|
|
19
|
+
authority: z.string().trim().min(1).max(256),
|
|
20
|
+
access: accessClassificationSchema,
|
|
21
|
+
canonicalUri: stableUriSchema.optional(),
|
|
22
|
+
refreshPolicy: z.string().trim().min(1).max(256),
|
|
23
|
+
enabled: z.boolean(),
|
|
24
|
+
tags: z.array(z.string().trim().min(1).max(128)).max(64).default([]),
|
|
25
|
+
}).strict();
|
|
26
|
+
export const collectorDefinitionSchema = z.object({
|
|
27
|
+
schemaVersion: schemaVersionSchema,
|
|
28
|
+
collectorId: idSchema,
|
|
29
|
+
version: z.string().trim().min(1).max(128),
|
|
30
|
+
supportedSourceKinds: z.array(sourceKindSchema).min(1),
|
|
31
|
+
capabilities: z.array(z.string().trim().min(1).max(128)).max(64),
|
|
32
|
+
}).strict();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Host-authored admission accounting, independent of business authorization and receipt schemas. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { materialTenantIdSchema } from './material-context.js';
|
|
5
|
+
import { isoDateTimeSchema } from './common.js';
|
|
6
|
+
import { parseTenantCollectionBatchRef } from './tenant-collection.js';
|
|
7
|
+
import { tenantStorageKey } from './resource-scope.js';
|
|
8
|
+
const positive = (max) => z.number().int().min(1).max(max);
|
|
9
|
+
export const tenantBudgetPolicySchema = z.object({ tenantId: materialTenantIdSchema, maxInFlight: positive(32), maxWorkUnits: positive(1_000_000), maxRuntimeMs: positive(86_400_000), weight: positive(8), paused: z.boolean() }).strict();
|
|
10
|
+
export const tenantBudgetConfigSchema = z.object({ schemaVersion: z.literal(1), windowSeconds: z.number().int().min(60).max(86400), maxInFlight: positive(128), maxWaitRounds: positive(2048), tenants: z.array(tenantBudgetPolicySchema).min(1).max(128) }).strict().superRefine((value, ctx) => {
|
|
11
|
+
if (new Set(value.tenants.map(t => t.tenantId)).size !== value.tenants.length || value.tenants.some(t => t.maxInFlight > value.maxInFlight) || value.tenants.reduce((sum, t) => sum + t.weight, 0) > value.maxWaitRounds)
|
|
12
|
+
ctx.addIssue({ code: 'custom', message: 'Unique Tenants, Host limits and weighted waiting bound are required' });
|
|
13
|
+
});
|
|
14
|
+
export const tenantBudgetCandidateSchema = z.object({ key: z.string().regex(/^[a-f0-9]{64}$/), tenantId: materialTenantIdSchema, workUnits: positive(100_000), runtimeMs: z.number().int().min(1000).max(7_200_000) }).strict();
|
|
15
|
+
export const tenantBudgetReservationSchema = tenantBudgetCandidateSchema.extend({ admissionId: z.string().uuid(), nodeId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/), startedAt: isoDateTimeSchema, expiresAt: isoDateTimeSchema, windowStart: isoDateTimeSchema }).strict();
|
|
16
|
+
export const tenantBudgetReasonSchema = z.enum(['READY', 'TENANT_UNCONFIGURED', 'PAUSED', 'HOST_IN_FLIGHT', 'TENANT_IN_FLIGHT', 'WORK_BUDGET', 'TIME_BUDGET', 'WORK_ACTIVE']);
|
|
17
|
+
/** 两小时是整个准入的硬上限;短任务保留结算余量,合法的上限任务不因此被排除。 */
|
|
18
|
+
export function tenantLegacyAdmissionRuntimeMs(timeoutSeconds) {
|
|
19
|
+
if (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 30 || timeoutSeconds > 7200)
|
|
20
|
+
throw new Error('TENANT_LEGACY_TIMEOUT_INVALID');
|
|
21
|
+
return Math.min(7_200_000, timeoutSeconds * 1000 + 2000);
|
|
22
|
+
}
|
|
23
|
+
export function tenantCollectionAdmissionKey(tenantId, batchRef) {
|
|
24
|
+
const batch = parseTenantCollectionBatchRef(batchRef);
|
|
25
|
+
if (!batch || batch.storageKey !== tenantStorageKey(tenantId))
|
|
26
|
+
throw new Error('TENANT_BUDGET_WORK_SCOPE_INVALID');
|
|
27
|
+
return createHash('sha256').update(JSON.stringify(['TENANT_COLLECTION_ADMISSION_V1', tenantId, batchRef])).digest('hex');
|
|
28
|
+
}
|
|
29
|
+
export function tenantLegacyAdmissionKey(tenantId, workloadId) {
|
|
30
|
+
materialTenantIdSchema.parse(tenantId);
|
|
31
|
+
if (typeof workloadId !== 'string' || workloadId.length > 256 || !/^project:[A-Za-z0-9][A-Za-z0-9._-]{0,127}:.+$/.test(workloadId))
|
|
32
|
+
throw new Error('TENANT_BUDGET_WORK_SCOPE_INVALID');
|
|
33
|
+
return createHash('sha256').update(JSON.stringify(['TENANT_LEGACY_ADMISSION_V1', tenantId, workloadId])).digest('hex');
|
|
34
|
+
}
|