@mytegroupinc/myte-core 0.0.47 → 0.0.49

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.
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+
3
+ const { randomUUID } = require("node:crypto");
4
+
5
+ const SCHEMA_VERSION = 1;
6
+ const ARTIFACT_KINDS = new Set([
7
+ "feedback",
8
+ "review_request",
9
+ "feedback_event",
10
+ "comment",
11
+ "notification",
12
+ "object",
13
+ "query_job",
14
+ ]);
15
+ const SENSITIVE_KEY = /(api[_-]?key|authorization|bearer|password|secret|token|credential)/i;
16
+
17
+ function cleanText(value, label, maxLength = 500) {
18
+ const text = String(value || "").trim();
19
+ if (!text) throw new Error(`${label} is required.`);
20
+ if (text.length > maxLength) throw new Error(`${label} is too long.`);
21
+ return text;
22
+ }
23
+
24
+ function createCertificationManifest({
25
+ runId = randomUUID(),
26
+ namespace,
27
+ projectId,
28
+ environment = "dev",
29
+ createdAt = new Date().toISOString(),
30
+ } = {}) {
31
+ const cleanRunId = cleanText(runId, "run_id", 128);
32
+ const cleanNamespace = cleanText(
33
+ namespace || `MYTE_TEST_${cleanRunId.replace(/[^A-Za-z0-9_-]/g, "_")}`,
34
+ "namespace",
35
+ 180,
36
+ );
37
+ return {
38
+ schema_version: SCHEMA_VERSION,
39
+ kind: "myte_project_assistant_certification",
40
+ run_id: cleanRunId,
41
+ namespace: cleanNamespace,
42
+ project_id: projectId ? cleanText(projectId, "project_id", 128) : null,
43
+ environment: cleanText(environment, "environment", 40),
44
+ created_at: createdAt,
45
+ updated_at: createdAt,
46
+ state: "planned",
47
+ artifacts: [],
48
+ checks: [],
49
+ };
50
+ }
51
+
52
+ function assertManifestSafe(value, path = "manifest") {
53
+ if (Array.isArray(value)) {
54
+ value.forEach((item, index) => assertManifestSafe(item, `${path}[${index}]`));
55
+ return;
56
+ }
57
+ if (!value || typeof value !== "object") return;
58
+ for (const [key, item] of Object.entries(value)) {
59
+ if (SENSITIVE_KEY.test(key)) {
60
+ throw new Error(`Sensitive field is not allowed in certification manifests: ${path}.${key}`);
61
+ }
62
+ assertManifestSafe(item, `${path}.${key}`);
63
+ }
64
+ }
65
+
66
+ function validateCertificationManifest(manifest, { expectedRunId } = {}) {
67
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
68
+ throw new Error("Certification manifest must be an object.");
69
+ }
70
+ if (Number(manifest.schema_version) !== SCHEMA_VERSION) {
71
+ throw new Error(`Unsupported certification manifest schema: ${manifest.schema_version}`);
72
+ }
73
+ if (manifest.kind !== "myte_project_assistant_certification") {
74
+ throw new Error("Unexpected certification manifest kind.");
75
+ }
76
+ const runId = cleanText(manifest.run_id, "run_id", 128);
77
+ if (expectedRunId && runId !== String(expectedRunId).trim()) {
78
+ throw new Error("Certification run_id does not match the requested cleanup run.");
79
+ }
80
+ cleanText(manifest.namespace, "namespace", 180);
81
+ if (!Array.isArray(manifest.artifacts)) {
82
+ throw new Error("Certification manifest artifacts must be an array.");
83
+ }
84
+ const seen = new Set();
85
+ for (const artifact of manifest.artifacts) {
86
+ if (!artifact || typeof artifact !== "object") {
87
+ throw new Error("Certification artifact entries must be objects.");
88
+ }
89
+ const kind = cleanText(artifact.kind, "artifact.kind", 80);
90
+ const id = cleanText(artifact.id, "artifact.id", 500);
91
+ if (!ARTIFACT_KINDS.has(kind)) {
92
+ throw new Error(`Unsupported certification artifact kind: ${kind}`);
93
+ }
94
+ if (String(artifact.run_id || "") !== runId) {
95
+ throw new Error(`Artifact ${kind}:${id} does not belong to run ${runId}.`);
96
+ }
97
+ const identity = `${kind}:${id}`;
98
+ if (seen.has(identity)) {
99
+ throw new Error(`Duplicate certification artifact: ${identity}`);
100
+ }
101
+ seen.add(identity);
102
+ }
103
+ assertManifestSafe(manifest);
104
+ return manifest;
105
+ }
106
+
107
+ function recordCertificationArtifact(manifest, { kind, id, metadata = {} } = {}) {
108
+ validateCertificationManifest(manifest);
109
+ const cleanKind = cleanText(kind, "artifact.kind", 80);
110
+ const cleanId = cleanText(id, "artifact.id", 500);
111
+ if (!ARTIFACT_KINDS.has(cleanKind)) {
112
+ throw new Error(`Unsupported certification artifact kind: ${cleanKind}`);
113
+ }
114
+ assertManifestSafe(metadata, "artifact.metadata");
115
+ const identity = `${cleanKind}:${cleanId}`;
116
+ if (manifest.artifacts.some((artifact) => `${artifact.kind}:${artifact.id}` === identity)) {
117
+ return manifest;
118
+ }
119
+ manifest.artifacts.push({
120
+ kind: cleanKind,
121
+ id: cleanId,
122
+ run_id: manifest.run_id,
123
+ namespace: manifest.namespace,
124
+ metadata: { ...metadata },
125
+ recorded_at: new Date().toISOString(),
126
+ });
127
+ manifest.updated_at = new Date().toISOString();
128
+ return manifest;
129
+ }
130
+
131
+ function buildCertificationCleanupPlan(manifest, { expectedRunId } = {}) {
132
+ validateCertificationManifest(manifest, { expectedRunId });
133
+ const actions = [];
134
+ for (const artifact of manifest.artifacts) {
135
+ if (artifact.kind === "feedback") {
136
+ actions.push({
137
+ action: "archive_feedback",
138
+ id: artifact.id,
139
+ run_id: manifest.run_id,
140
+ namespace: manifest.namespace,
141
+ });
142
+ } else if (artifact.kind === "review_request") {
143
+ actions.push({
144
+ action: "cancel_review_request_if_active",
145
+ id: artifact.id,
146
+ run_id: manifest.run_id,
147
+ namespace: manifest.namespace,
148
+ });
149
+ } else {
150
+ actions.push({
151
+ action: "retain_audit_evidence",
152
+ artifact_kind: artifact.kind,
153
+ id: artifact.id,
154
+ run_id: manifest.run_id,
155
+ namespace: manifest.namespace,
156
+ });
157
+ }
158
+ }
159
+ return {
160
+ schema_version: SCHEMA_VERSION,
161
+ dry_run: true,
162
+ run_id: manifest.run_id,
163
+ namespace: manifest.namespace,
164
+ project_id: manifest.project_id || null,
165
+ action_count: actions.length,
166
+ actions,
167
+ };
168
+ }
169
+
170
+ module.exports = {
171
+ ARTIFACT_KINDS,
172
+ SCHEMA_VERSION,
173
+ assertManifestSafe,
174
+ buildCertificationCleanupPlan,
175
+ createCertificationManifest,
176
+ recordCertificationArtifact,
177
+ validateCertificationManifest,
178
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mytegroupinc/myte-core",
3
- "version": "0.0.47",
3
+ "version": "0.0.49",
4
4
  "description": "Myte CLI core implementation.",
5
5
  "type": "commonjs",
6
6
  "main": "cli.js",
@@ -18,9 +18,12 @@
18
18
  "test": "node --test"
19
19
  },
20
20
  "license": "MIT",
21
- "engines": {
22
- "node": ">=18"
23
- },
21
+ "engines": {
22
+ "node": ">=18.17"
23
+ },
24
+ "dependencies": {
25
+ "undici": "6.21.3"
26
+ },
24
27
  "publishConfig": {
25
28
  "access": "public"
26
29
  },
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("node:fs");
5
+ const path = require("node:path");
6
+
7
+ const {
8
+ buildCertificationCleanupPlan,
9
+ validateCertificationManifest,
10
+ } = require("../lib/certification-manifest");
11
+
12
+ function parseArgs(argv) {
13
+ const args = {};
14
+ for (let index = 0; index < argv.length; index += 1) {
15
+ const token = argv[index];
16
+ if (!token.startsWith("--")) continue;
17
+ const key = token.slice(2);
18
+ const next = argv[index + 1];
19
+ if (!next || next.startsWith("--")) {
20
+ args[key] = true;
21
+ } else {
22
+ args[key] = next;
23
+ index += 1;
24
+ }
25
+ }
26
+ return args;
27
+ }
28
+
29
+ function main() {
30
+ const args = parseArgs(process.argv.slice(2));
31
+ const manifestArg = String(args.manifest || "").trim();
32
+ const expectedRunId = String(args["run-id"] || "").trim();
33
+ if (!manifestArg || !expectedRunId) {
34
+ throw new Error("Usage: feedback-certification-cleanup --manifest <path> --run-id <id> [--json]");
35
+ }
36
+ if (args.execute || args["confirm-live"]) {
37
+ throw new Error(
38
+ "This command is intentionally dry-run only. Execute the reviewed exact-ID actions through the live Feedback harness after deployment.",
39
+ );
40
+ }
41
+ const manifestPath = path.resolve(process.cwd(), manifestArg);
42
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
43
+ validateCertificationManifest(manifest, { expectedRunId });
44
+ const plan = buildCertificationCleanupPlan(manifest, { expectedRunId });
45
+ console.log(JSON.stringify({ ok: true, manifest_path: manifestPath, ...plan }, null, 2));
46
+ }
47
+
48
+ try {
49
+ main();
50
+ } catch (error) {
51
+ console.error(JSON.stringify({ ok: false, message: error?.message || String(error) }, null, 2));
52
+ process.exit(1);
53
+ }