@mytegroupinc/myte-core 0.0.53 → 0.0.55
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 +197 -184
- package/THIRD_PARTY_NOTICES.md +21 -21
- package/TRADEMARKS.md +10 -10
- package/cli.js +2518 -2441
- package/lib/certification-manifest.js +178 -178
- package/lib/mytecody-release-public-key.pem +11 -11
- package/lib/mytecody-splash.js +251 -251
- package/mytecody-cli.js +2040 -2040
- package/package.json +8 -8
- package/scripts/feedback-certification-cleanup.js +53 -53
- package/scripts/feedback-live-full-harness.js +1364 -1364
- package/scripts/project-assistant-live-full-harness.js +174 -0
- package/scripts/project-assistant-read-certification.js +470 -470
|
@@ -1,470 +1,470 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const os = require("node:os");
|
|
6
|
-
const path = require("node:path");
|
|
7
|
-
const { spawnSync } = require("node:child_process");
|
|
8
|
-
const { randomUUID } = require("node:crypto");
|
|
9
|
-
|
|
10
|
-
const CLI_PATH = path.resolve(__dirname, "..", "cli.js");
|
|
11
|
-
|
|
12
|
-
const ROUTE_MATRIX = [
|
|
13
|
-
{ id: "doctor", kind: "transport", mutation: "none" },
|
|
14
|
-
{ id: "config", kind: "read", mutation: "none" },
|
|
15
|
-
{ id: "bootstrap", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
16
|
-
{ id: "feedback-sync", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
17
|
-
{ id: "sync-qaqc", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
18
|
-
{ id: "suggestions-sync", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
19
|
-
{ id: "feedback-get", kind: "read", mutation: "none", conditional: "feedback_id" },
|
|
20
|
-
{ id: "feedback-history", kind: "read", mutation: "none", conditional: "feedback_id" },
|
|
21
|
-
{ id: "feedback-prd-versions", kind: "read", mutation: "none", conditional: "feedback_id" },
|
|
22
|
-
{ id: "feedback-prd-diff", kind: "read", mutation: "none", conditional: "version_id" },
|
|
23
|
-
{ id: "query", kind: "inference-job", mutation: "ephemeral_job", conditional: "--include-query" },
|
|
24
|
-
{
|
|
25
|
-
id: "query-with-diff",
|
|
26
|
-
kind: "inference-job",
|
|
27
|
-
mutation: "temporary_tracked_fixture_and_ephemeral_job",
|
|
28
|
-
conditional: "--include-diff-query",
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
function parseArgs(argv) {
|
|
33
|
-
const args = {};
|
|
34
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
35
|
-
const token = argv[index];
|
|
36
|
-
if (!token.startsWith("--")) continue;
|
|
37
|
-
const key = token.slice(2);
|
|
38
|
-
const next = argv[index + 1];
|
|
39
|
-
if (!next || next.startsWith("--")) {
|
|
40
|
-
args[key] = true;
|
|
41
|
-
} else {
|
|
42
|
-
args[key] = next;
|
|
43
|
-
index += 1;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return args;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function parseJsonOutput(result, command) {
|
|
50
|
-
const text = String(result.stdout || "").trim();
|
|
51
|
-
if (!text) return null;
|
|
52
|
-
try {
|
|
53
|
-
return JSON.parse(text);
|
|
54
|
-
} catch {
|
|
55
|
-
throw new Error(`${command} did not return JSON.`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function safeCommandLabel(args) {
|
|
60
|
-
if (args[0] === "feedback") {
|
|
61
|
-
return `myte feedback ${String(args[1] || "").trim() || "<subcommand>"}`;
|
|
62
|
-
}
|
|
63
|
-
if (args[0] === "suggestions") {
|
|
64
|
-
return `myte suggestions ${String(args[1] || "").trim() || "<subcommand>"}`;
|
|
65
|
-
}
|
|
66
|
-
if (args[0] === "query") {
|
|
67
|
-
return "myte query <redacted>";
|
|
68
|
-
}
|
|
69
|
-
return `myte ${String(args[0] || "").trim() || "<command>"}`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function runCli(args, { cwd, timeoutMs = 360000 } = {}) {
|
|
73
|
-
const started = Date.now();
|
|
74
|
-
const result = spawnSync(process.execPath, [CLI_PATH, ...args], {
|
|
75
|
-
cwd,
|
|
76
|
-
env: process.env,
|
|
77
|
-
encoding: "utf8",
|
|
78
|
-
timeout: timeoutMs,
|
|
79
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
80
|
-
});
|
|
81
|
-
const command = safeCommandLabel(args);
|
|
82
|
-
const parsed = parseJsonOutput(result, command);
|
|
83
|
-
return {
|
|
84
|
-
id: command.replace(/^myte\s+/, "").replace(/\s+/g, "-"),
|
|
85
|
-
command,
|
|
86
|
-
ok: result.status === 0,
|
|
87
|
-
exit_code: result.status,
|
|
88
|
-
duration_ms: Date.now() - started,
|
|
89
|
-
data: parsed,
|
|
90
|
-
error: result.status === 0
|
|
91
|
-
? null
|
|
92
|
-
: String(result.stderr || parsed?.message || "Command failed.").trim().slice(0, 2000),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function runGit(args, cwd) {
|
|
97
|
-
const result = spawnSync("git", args, {
|
|
98
|
-
cwd,
|
|
99
|
-
env: process.env,
|
|
100
|
-
encoding: "utf8",
|
|
101
|
-
timeout: 60000,
|
|
102
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
103
|
-
});
|
|
104
|
-
return {
|
|
105
|
-
ok: result.status === 0,
|
|
106
|
-
exit_code: result.status,
|
|
107
|
-
stdout: String(result.stdout || ""),
|
|
108
|
-
stderr: String(result.stderr || "").trim(),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function resolveDiffFixture(configData) {
|
|
113
|
-
const local = configData?.local && typeof configData.local === "object"
|
|
114
|
-
? configData.local
|
|
115
|
-
: {};
|
|
116
|
-
const root = String(local.root || "").trim();
|
|
117
|
-
const found = Array.isArray(local.found) ? local.found.map(String) : [];
|
|
118
|
-
if (!root || !found.length) {
|
|
119
|
-
throw new Error("Diff certification requires at least one resolved configured repository.");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
for (const repoName of found) {
|
|
123
|
-
const repoPath = path.resolve(root, repoName);
|
|
124
|
-
const status = runGit(["status", "--porcelain"], repoPath);
|
|
125
|
-
if (!status.ok || status.stdout.trim()) continue;
|
|
126
|
-
|
|
127
|
-
const tracked = runGit(["ls-files"], repoPath);
|
|
128
|
-
if (!tracked.ok) continue;
|
|
129
|
-
const candidates = tracked.stdout
|
|
130
|
-
.split(/\r?\n/)
|
|
131
|
-
.map((item) => item.trim())
|
|
132
|
-
.filter(Boolean)
|
|
133
|
-
.filter((item) => /(^|\/)readme(?:\.[a-z0-9]+)?$/i.test(item));
|
|
134
|
-
for (const relativePath of candidates) {
|
|
135
|
-
const filePath = path.resolve(repoPath, relativePath);
|
|
136
|
-
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
137
|
-
return {
|
|
138
|
-
repoName,
|
|
139
|
-
repoPath,
|
|
140
|
-
relativePath,
|
|
141
|
-
filePath,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
throw new Error(
|
|
147
|
-
"Diff certification requires a clean configured repository with a tracked README file.",
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function runDiffCertification({
|
|
152
|
-
args,
|
|
153
|
-
configData,
|
|
154
|
-
runId,
|
|
155
|
-
workspace,
|
|
156
|
-
shared,
|
|
157
|
-
}) {
|
|
158
|
-
const fixture = resolveDiffFixture(configData);
|
|
159
|
-
const marker = `MYTE_CERT_DIFF_${runId.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
160
|
-
const original = fs.readFileSync(fixture.filePath);
|
|
161
|
-
const originalStatus = runGit(["status", "--porcelain"], fixture.repoPath);
|
|
162
|
-
let contextCheck = null;
|
|
163
|
-
let queryCheck = null;
|
|
164
|
-
let restoreError = null;
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
const separator = original.length && !original.toString("utf8").endsWith("\n")
|
|
168
|
-
? "\n"
|
|
169
|
-
: "";
|
|
170
|
-
fs.appendFileSync(
|
|
171
|
-
fixture.filePath,
|
|
172
|
-
`${separator}\n<!-- ${marker} -->\n`,
|
|
173
|
-
"utf8",
|
|
174
|
-
);
|
|
175
|
-
const diffLimit = String(args["diff-limit"] || 500000);
|
|
176
|
-
const prompt = [
|
|
177
|
-
"Goal: certify project-scoped diff ingestion.",
|
|
178
|
-
`Evidence marker: ${marker}.`,
|
|
179
|
-
`Ask: return the exact marker and name ${fixture.repoName}/${fixture.relativePath}.`,
|
|
180
|
-
].join(" ");
|
|
181
|
-
const commonDiffArgs = [
|
|
182
|
-
"query",
|
|
183
|
-
prompt,
|
|
184
|
-
"--with-diff",
|
|
185
|
-
"--no-fetch",
|
|
186
|
-
"--diff-limit",
|
|
187
|
-
diffLimit,
|
|
188
|
-
...shared,
|
|
189
|
-
];
|
|
190
|
-
const rawContextCheck = runCli(
|
|
191
|
-
[...commonDiffArgs, "--print-context"],
|
|
192
|
-
{ cwd: workspace },
|
|
193
|
-
);
|
|
194
|
-
const contextText = Array.isArray(rawContextCheck.data?.additional_context)
|
|
195
|
-
? rawContextCheck.data.additional_context.join("\n")
|
|
196
|
-
: "";
|
|
197
|
-
const markerPresent = contextText.includes(marker);
|
|
198
|
-
const fetchDisabled =
|
|
199
|
-
rawContextCheck.data?.diff_diagnostics?.fetch_remote === false;
|
|
200
|
-
contextCheck = {
|
|
201
|
-
...rawContextCheck,
|
|
202
|
-
id: "query-with-diff-context",
|
|
203
|
-
command: "myte query <redacted> --with-diff --print-context",
|
|
204
|
-
ok: rawContextCheck.ok && markerPresent && fetchDisabled,
|
|
205
|
-
error: rawContextCheck.ok && markerPresent && fetchDisabled
|
|
206
|
-
? null
|
|
207
|
-
: rawContextCheck.error
|
|
208
|
-
|| "Diff context did not contain the marker or --no-fetch was not honored.",
|
|
209
|
-
data: {
|
|
210
|
-
marker_present: markerPresent,
|
|
211
|
-
fetch_remote: rawContextCheck.data?.diff_diagnostics?.fetch_remote,
|
|
212
|
-
truncated: rawContextCheck.data?.diff_diagnostics?.truncated,
|
|
213
|
-
fixture_repo: fixture.repoName,
|
|
214
|
-
fixture_file: fixture.relativePath,
|
|
215
|
-
},
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
if (contextCheck.ok) {
|
|
219
|
-
const rawQueryCheck = runCli(
|
|
220
|
-
[
|
|
221
|
-
...commonDiffArgs,
|
|
222
|
-
"--request-id",
|
|
223
|
-
`cert-diff-${runId}`.slice(0, 128),
|
|
224
|
-
"--json",
|
|
225
|
-
],
|
|
226
|
-
{ cwd: workspace },
|
|
227
|
-
);
|
|
228
|
-
const answer = String(rawQueryCheck.data?.answer || "");
|
|
229
|
-
const answerContainsMarker = answer.includes(marker);
|
|
230
|
-
queryCheck = {
|
|
231
|
-
...rawQueryCheck,
|
|
232
|
-
id: "query-with-diff",
|
|
233
|
-
command: "myte query <redacted> --with-diff",
|
|
234
|
-
ok: rawQueryCheck.ok && answerContainsMarker,
|
|
235
|
-
error: rawQueryCheck.ok && answerContainsMarker
|
|
236
|
-
? null
|
|
237
|
-
: rawQueryCheck.error
|
|
238
|
-
|| "Live diff-query answer did not contain the exact fixture marker.",
|
|
239
|
-
data: {
|
|
240
|
-
marker_present_in_answer: answerContainsMarker,
|
|
241
|
-
job_id: rawQueryCheck.data?.job_id || null,
|
|
242
|
-
request_id: rawQueryCheck.data?.request_id || null,
|
|
243
|
-
},
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
} finally {
|
|
247
|
-
try {
|
|
248
|
-
fs.writeFileSync(fixture.filePath, original);
|
|
249
|
-
const restoredStatus = runGit(["status", "--porcelain"], fixture.repoPath);
|
|
250
|
-
if (
|
|
251
|
-
!restoredStatus.ok
|
|
252
|
-
|| restoredStatus.stdout !== originalStatus.stdout
|
|
253
|
-
) {
|
|
254
|
-
restoreError = "Diff fixture repository did not return to its original clean status.";
|
|
255
|
-
}
|
|
256
|
-
} catch (error) {
|
|
257
|
-
restoreError = error?.message || String(error);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const checks = [];
|
|
262
|
-
if (contextCheck) checks.push(contextCheck);
|
|
263
|
-
if (queryCheck) checks.push(queryCheck);
|
|
264
|
-
checks.push({
|
|
265
|
-
id: "query-with-diff-fixture-restore",
|
|
266
|
-
command: "restore temporary tracked diff fixture",
|
|
267
|
-
ok: !restoreError,
|
|
268
|
-
exit_code: restoreError ? 1 : 0,
|
|
269
|
-
duration_ms: 0,
|
|
270
|
-
data: {
|
|
271
|
-
fixture_repo: fixture.repoName,
|
|
272
|
-
fixture_file: fixture.relativePath,
|
|
273
|
-
},
|
|
274
|
-
error: restoreError,
|
|
275
|
-
});
|
|
276
|
-
return checks;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function firstFeedbackId(feedbackPath) {
|
|
280
|
-
if (!fs.existsSync(feedbackPath)) return null;
|
|
281
|
-
const text = fs.readFileSync(feedbackPath, "utf8");
|
|
282
|
-
const match = text.match(/^\s*(?:-\s+)?feedback_id:\s*["']?([^"'\s]+)["']?\s*$/m);
|
|
283
|
-
return match ? match[1] : null;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function versionIdentifier(version) {
|
|
287
|
-
return String(version?.version_id || version?._id || "").trim() || null;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function versionDocumentIdentifier(version) {
|
|
291
|
-
const documentSet =
|
|
292
|
-
version?.prd_document_set && typeof version.prd_document_set === "object"
|
|
293
|
-
? version.prd_document_set
|
|
294
|
-
: null;
|
|
295
|
-
const documents = Array.isArray(version?.prd_documents)
|
|
296
|
-
? version.prd_documents
|
|
297
|
-
: Array.isArray(documentSet?.documents)
|
|
298
|
-
? documentSet.documents
|
|
299
|
-
: [];
|
|
300
|
-
return String(
|
|
301
|
-
version?.primary_document_id
|
|
302
|
-
|| documentSet?.primary_document_id
|
|
303
|
-
|| documents[0]?.document_id
|
|
304
|
-
|| "",
|
|
305
|
-
).trim() || null;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function baseArgs(args) {
|
|
309
|
-
const result = [];
|
|
310
|
-
if (args["base-url"]) result.push("--base-url", String(args["base-url"]));
|
|
311
|
-
result.push("--timeout-ms", String(args["timeout-ms"] || 300000));
|
|
312
|
-
return result;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function main() {
|
|
316
|
-
const args = parseArgs(process.argv.slice(2));
|
|
317
|
-
if (!args["confirm-read"]) {
|
|
318
|
-
console.log(JSON.stringify({
|
|
319
|
-
ok: true,
|
|
320
|
-
dry_run: true,
|
|
321
|
-
message: "No network or project mutation was performed.",
|
|
322
|
-
route_matrix: ROUTE_MATRIX,
|
|
323
|
-
mutation_harnesses: [
|
|
324
|
-
"feedback-live-full-harness.js",
|
|
325
|
-
"mission-live-full-harness.js",
|
|
326
|
-
"mission-live-disposable-harness.js",
|
|
327
|
-
],
|
|
328
|
-
}, null, 2));
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
if (!process.env.MYTE_API_KEY && !process.env.MYTE_PROJECT_API_KEY) {
|
|
332
|
-
throw new Error("Missing MYTE_API_KEY or MYTE_PROJECT_API_KEY.");
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const runId = String(args["run-id"] || randomUUID());
|
|
336
|
-
const workspace = path.resolve(
|
|
337
|
-
args.workspace || fs.mkdtempSync(path.join(os.tmpdir(), `myte-read-cert-${runId}-`)),
|
|
338
|
-
);
|
|
339
|
-
const outputDir = path.join(workspace, "MyteCommandCenter");
|
|
340
|
-
fs.mkdirSync(workspace, { recursive: true });
|
|
341
|
-
const shared = baseArgs(args);
|
|
342
|
-
const checks = [];
|
|
343
|
-
|
|
344
|
-
const doctor = runCli(["doctor", "--json", ...shared], { cwd: workspace, timeoutMs: 60000 });
|
|
345
|
-
checks.push({ ...doctor, data: doctor.data ? {
|
|
346
|
-
ok: doctor.data.ok,
|
|
347
|
-
failure_layer: doctor.data.failure_layer,
|
|
348
|
-
runtime: doctor.data.runtime,
|
|
349
|
-
api: doctor.data.api,
|
|
350
|
-
project_key: doctor.data.project_key,
|
|
351
|
-
proxy: doctor.data.proxy,
|
|
352
|
-
dns: doctor.data.dns,
|
|
353
|
-
direct_transport: doctor.data.direct_transport,
|
|
354
|
-
config_probe: doctor.data.config_probe,
|
|
355
|
-
} : null });
|
|
356
|
-
if (!doctor.ok) {
|
|
357
|
-
console.log(JSON.stringify({
|
|
358
|
-
ok: false,
|
|
359
|
-
run_id: runId,
|
|
360
|
-
workspace,
|
|
361
|
-
stopped_at: "doctor",
|
|
362
|
-
failure_layer: doctor.data?.failure_layer || "unknown",
|
|
363
|
-
checks,
|
|
364
|
-
}, null, 2));
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const config = runCli(["config", "--json", ...shared], { cwd: workspace });
|
|
369
|
-
checks.push(config);
|
|
370
|
-
checks.push(runCli(["bootstrap", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
371
|
-
checks.push(runCli(["feedback-sync", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
372
|
-
checks.push(runCli(["sync-qaqc", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
373
|
-
checks.push(runCli(["suggestions", "sync", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
374
|
-
|
|
375
|
-
const feedbackId = firstFeedbackId(path.join(outputDir, "data", "feedback.yml"));
|
|
376
|
-
if (feedbackId) {
|
|
377
|
-
checks.push(runCli(["feedback", "get", "--feedback-id", feedbackId, "--json", ...shared], { cwd: workspace }));
|
|
378
|
-
checks.push(runCli(["feedback", "history", "--feedback-id", feedbackId, "--json", ...shared], { cwd: workspace }));
|
|
379
|
-
const versionsCheck = runCli(
|
|
380
|
-
["feedback", "prd-versions", "--feedback-id", feedbackId, "--json", ...shared],
|
|
381
|
-
{ cwd: workspace },
|
|
382
|
-
);
|
|
383
|
-
checks.push(versionsCheck);
|
|
384
|
-
if (versionsCheck.ok) {
|
|
385
|
-
const versions = Array.isArray(versionsCheck.data?.versions)
|
|
386
|
-
? versionsCheck.data.versions
|
|
387
|
-
: [];
|
|
388
|
-
const activeVersionId = String(
|
|
389
|
-
versionsCheck.data?.active_prd_version_id || "",
|
|
390
|
-
).trim();
|
|
391
|
-
const targetVersion =
|
|
392
|
-
versions.find((version) => versionIdentifier(version) !== activeVersionId)
|
|
393
|
-
|| versions[0];
|
|
394
|
-
const targetVersionId = versionIdentifier(targetVersion);
|
|
395
|
-
if (targetVersionId) {
|
|
396
|
-
const diffArgs = [
|
|
397
|
-
"feedback",
|
|
398
|
-
"prd-diff",
|
|
399
|
-
"--feedback-id",
|
|
400
|
-
feedbackId,
|
|
401
|
-
"--version-id",
|
|
402
|
-
targetVersionId,
|
|
403
|
-
];
|
|
404
|
-
const documentId = versionDocumentIdentifier(targetVersion);
|
|
405
|
-
if (documentId) diffArgs.push("--document-id", documentId);
|
|
406
|
-
diffArgs.push("--json", ...shared);
|
|
407
|
-
checks.push(runCli(diffArgs, { cwd: workspace }));
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (args["include-query"]) {
|
|
413
|
-
checks.push(runCli([
|
|
414
|
-
"query",
|
|
415
|
-
"Goal: certify Project Assistant query transport. Ask: return the project title in one sentence.",
|
|
416
|
-
"--request-id",
|
|
417
|
-
`cert-read-${runId}`,
|
|
418
|
-
"--json",
|
|
419
|
-
...shared,
|
|
420
|
-
], { cwd: workspace }));
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (args["include-diff-query"]) {
|
|
424
|
-
if (!config.ok) {
|
|
425
|
-
checks.push({
|
|
426
|
-
id: "query-with-diff",
|
|
427
|
-
command: "myte query <redacted> --with-diff",
|
|
428
|
-
ok: false,
|
|
429
|
-
exit_code: 1,
|
|
430
|
-
duration_ms: 0,
|
|
431
|
-
data: null,
|
|
432
|
-
error: "Project config failed; diff certification could not resolve a repository.",
|
|
433
|
-
});
|
|
434
|
-
} else {
|
|
435
|
-
checks.push(...runDiffCertification({
|
|
436
|
-
args,
|
|
437
|
-
configData: config.data,
|
|
438
|
-
runId,
|
|
439
|
-
workspace,
|
|
440
|
-
shared,
|
|
441
|
-
}));
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const failed = checks.filter((check) => !check.ok);
|
|
446
|
-
console.log(JSON.stringify({
|
|
447
|
-
ok: failed.length === 0,
|
|
448
|
-
run_id: runId,
|
|
449
|
-
workspace,
|
|
450
|
-
local_artifacts_only: true,
|
|
451
|
-
live_business_mutations: false,
|
|
452
|
-
checks: checks.map((check) => ({
|
|
453
|
-
id: check.id,
|
|
454
|
-
command: check.command,
|
|
455
|
-
ok: check.ok,
|
|
456
|
-
exit_code: check.exit_code,
|
|
457
|
-
duration_ms: check.duration_ms,
|
|
458
|
-
error: check.error,
|
|
459
|
-
})),
|
|
460
|
-
failed_count: failed.length,
|
|
461
|
-
}, null, 2));
|
|
462
|
-
if (failed.length) process.exit(1);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
main();
|
|
467
|
-
} catch (error) {
|
|
468
|
-
console.error(JSON.stringify({ ok: false, message: error?.message || String(error) }, null, 2));
|
|
469
|
-
process.exit(1);
|
|
470
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const os = require("node:os");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const { spawnSync } = require("node:child_process");
|
|
8
|
+
const { randomUUID } = require("node:crypto");
|
|
9
|
+
|
|
10
|
+
const CLI_PATH = path.resolve(__dirname, "..", "cli.js");
|
|
11
|
+
|
|
12
|
+
const ROUTE_MATRIX = [
|
|
13
|
+
{ id: "doctor", kind: "transport", mutation: "none" },
|
|
14
|
+
{ id: "config", kind: "read", mutation: "none" },
|
|
15
|
+
{ id: "bootstrap", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
16
|
+
{ id: "feedback-sync", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
17
|
+
{ id: "sync-qaqc", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
18
|
+
{ id: "suggestions-sync", kind: "read/local-sync", mutation: "local_temp_only" },
|
|
19
|
+
{ id: "feedback-get", kind: "read", mutation: "none", conditional: "feedback_id" },
|
|
20
|
+
{ id: "feedback-history", kind: "read", mutation: "none", conditional: "feedback_id" },
|
|
21
|
+
{ id: "feedback-prd-versions", kind: "read", mutation: "none", conditional: "feedback_id" },
|
|
22
|
+
{ id: "feedback-prd-diff", kind: "read", mutation: "none", conditional: "version_id" },
|
|
23
|
+
{ id: "query", kind: "inference-job", mutation: "ephemeral_job", conditional: "--include-query" },
|
|
24
|
+
{
|
|
25
|
+
id: "query-with-diff",
|
|
26
|
+
kind: "inference-job",
|
|
27
|
+
mutation: "temporary_tracked_fixture_and_ephemeral_job",
|
|
28
|
+
conditional: "--include-diff-query",
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function parseArgs(argv) {
|
|
33
|
+
const args = {};
|
|
34
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
35
|
+
const token = argv[index];
|
|
36
|
+
if (!token.startsWith("--")) continue;
|
|
37
|
+
const key = token.slice(2);
|
|
38
|
+
const next = argv[index + 1];
|
|
39
|
+
if (!next || next.startsWith("--")) {
|
|
40
|
+
args[key] = true;
|
|
41
|
+
} else {
|
|
42
|
+
args[key] = next;
|
|
43
|
+
index += 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return args;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseJsonOutput(result, command) {
|
|
50
|
+
const text = String(result.stdout || "").trim();
|
|
51
|
+
if (!text) return null;
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(text);
|
|
54
|
+
} catch {
|
|
55
|
+
throw new Error(`${command} did not return JSON.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function safeCommandLabel(args) {
|
|
60
|
+
if (args[0] === "feedback") {
|
|
61
|
+
return `myte feedback ${String(args[1] || "").trim() || "<subcommand>"}`;
|
|
62
|
+
}
|
|
63
|
+
if (args[0] === "suggestions") {
|
|
64
|
+
return `myte suggestions ${String(args[1] || "").trim() || "<subcommand>"}`;
|
|
65
|
+
}
|
|
66
|
+
if (args[0] === "query") {
|
|
67
|
+
return "myte query <redacted>";
|
|
68
|
+
}
|
|
69
|
+
return `myte ${String(args[0] || "").trim() || "<command>"}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function runCli(args, { cwd, timeoutMs = 360000 } = {}) {
|
|
73
|
+
const started = Date.now();
|
|
74
|
+
const result = spawnSync(process.execPath, [CLI_PATH, ...args], {
|
|
75
|
+
cwd,
|
|
76
|
+
env: process.env,
|
|
77
|
+
encoding: "utf8",
|
|
78
|
+
timeout: timeoutMs,
|
|
79
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
80
|
+
});
|
|
81
|
+
const command = safeCommandLabel(args);
|
|
82
|
+
const parsed = parseJsonOutput(result, command);
|
|
83
|
+
return {
|
|
84
|
+
id: command.replace(/^myte\s+/, "").replace(/\s+/g, "-"),
|
|
85
|
+
command,
|
|
86
|
+
ok: result.status === 0,
|
|
87
|
+
exit_code: result.status,
|
|
88
|
+
duration_ms: Date.now() - started,
|
|
89
|
+
data: parsed,
|
|
90
|
+
error: result.status === 0
|
|
91
|
+
? null
|
|
92
|
+
: String(result.stderr || parsed?.message || "Command failed.").trim().slice(0, 2000),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function runGit(args, cwd) {
|
|
97
|
+
const result = spawnSync("git", args, {
|
|
98
|
+
cwd,
|
|
99
|
+
env: process.env,
|
|
100
|
+
encoding: "utf8",
|
|
101
|
+
timeout: 60000,
|
|
102
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
ok: result.status === 0,
|
|
106
|
+
exit_code: result.status,
|
|
107
|
+
stdout: String(result.stdout || ""),
|
|
108
|
+
stderr: String(result.stderr || "").trim(),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resolveDiffFixture(configData) {
|
|
113
|
+
const local = configData?.local && typeof configData.local === "object"
|
|
114
|
+
? configData.local
|
|
115
|
+
: {};
|
|
116
|
+
const root = String(local.root || "").trim();
|
|
117
|
+
const found = Array.isArray(local.found) ? local.found.map(String) : [];
|
|
118
|
+
if (!root || !found.length) {
|
|
119
|
+
throw new Error("Diff certification requires at least one resolved configured repository.");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const repoName of found) {
|
|
123
|
+
const repoPath = path.resolve(root, repoName);
|
|
124
|
+
const status = runGit(["status", "--porcelain"], repoPath);
|
|
125
|
+
if (!status.ok || status.stdout.trim()) continue;
|
|
126
|
+
|
|
127
|
+
const tracked = runGit(["ls-files"], repoPath);
|
|
128
|
+
if (!tracked.ok) continue;
|
|
129
|
+
const candidates = tracked.stdout
|
|
130
|
+
.split(/\r?\n/)
|
|
131
|
+
.map((item) => item.trim())
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.filter((item) => /(^|\/)readme(?:\.[a-z0-9]+)?$/i.test(item));
|
|
134
|
+
for (const relativePath of candidates) {
|
|
135
|
+
const filePath = path.resolve(repoPath, relativePath);
|
|
136
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
137
|
+
return {
|
|
138
|
+
repoName,
|
|
139
|
+
repoPath,
|
|
140
|
+
relativePath,
|
|
141
|
+
filePath,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
throw new Error(
|
|
147
|
+
"Diff certification requires a clean configured repository with a tracked README file.",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function runDiffCertification({
|
|
152
|
+
args,
|
|
153
|
+
configData,
|
|
154
|
+
runId,
|
|
155
|
+
workspace,
|
|
156
|
+
shared,
|
|
157
|
+
}) {
|
|
158
|
+
const fixture = resolveDiffFixture(configData);
|
|
159
|
+
const marker = `MYTE_CERT_DIFF_${runId.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
160
|
+
const original = fs.readFileSync(fixture.filePath);
|
|
161
|
+
const originalStatus = runGit(["status", "--porcelain"], fixture.repoPath);
|
|
162
|
+
let contextCheck = null;
|
|
163
|
+
let queryCheck = null;
|
|
164
|
+
let restoreError = null;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const separator = original.length && !original.toString("utf8").endsWith("\n")
|
|
168
|
+
? "\n"
|
|
169
|
+
: "";
|
|
170
|
+
fs.appendFileSync(
|
|
171
|
+
fixture.filePath,
|
|
172
|
+
`${separator}\n<!-- ${marker} -->\n`,
|
|
173
|
+
"utf8",
|
|
174
|
+
);
|
|
175
|
+
const diffLimit = String(args["diff-limit"] || 500000);
|
|
176
|
+
const prompt = [
|
|
177
|
+
"Goal: certify project-scoped diff ingestion.",
|
|
178
|
+
`Evidence marker: ${marker}.`,
|
|
179
|
+
`Ask: return the exact marker and name ${fixture.repoName}/${fixture.relativePath}.`,
|
|
180
|
+
].join(" ");
|
|
181
|
+
const commonDiffArgs = [
|
|
182
|
+
"query",
|
|
183
|
+
prompt,
|
|
184
|
+
"--with-diff",
|
|
185
|
+
"--no-fetch",
|
|
186
|
+
"--diff-limit",
|
|
187
|
+
diffLimit,
|
|
188
|
+
...shared,
|
|
189
|
+
];
|
|
190
|
+
const rawContextCheck = runCli(
|
|
191
|
+
[...commonDiffArgs, "--print-context"],
|
|
192
|
+
{ cwd: workspace },
|
|
193
|
+
);
|
|
194
|
+
const contextText = Array.isArray(rawContextCheck.data?.additional_context)
|
|
195
|
+
? rawContextCheck.data.additional_context.join("\n")
|
|
196
|
+
: "";
|
|
197
|
+
const markerPresent = contextText.includes(marker);
|
|
198
|
+
const fetchDisabled =
|
|
199
|
+
rawContextCheck.data?.diff_diagnostics?.fetch_remote === false;
|
|
200
|
+
contextCheck = {
|
|
201
|
+
...rawContextCheck,
|
|
202
|
+
id: "query-with-diff-context",
|
|
203
|
+
command: "myte query <redacted> --with-diff --print-context",
|
|
204
|
+
ok: rawContextCheck.ok && markerPresent && fetchDisabled,
|
|
205
|
+
error: rawContextCheck.ok && markerPresent && fetchDisabled
|
|
206
|
+
? null
|
|
207
|
+
: rawContextCheck.error
|
|
208
|
+
|| "Diff context did not contain the marker or --no-fetch was not honored.",
|
|
209
|
+
data: {
|
|
210
|
+
marker_present: markerPresent,
|
|
211
|
+
fetch_remote: rawContextCheck.data?.diff_diagnostics?.fetch_remote,
|
|
212
|
+
truncated: rawContextCheck.data?.diff_diagnostics?.truncated,
|
|
213
|
+
fixture_repo: fixture.repoName,
|
|
214
|
+
fixture_file: fixture.relativePath,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
if (contextCheck.ok) {
|
|
219
|
+
const rawQueryCheck = runCli(
|
|
220
|
+
[
|
|
221
|
+
...commonDiffArgs,
|
|
222
|
+
"--request-id",
|
|
223
|
+
`cert-diff-${runId}`.slice(0, 128),
|
|
224
|
+
"--json",
|
|
225
|
+
],
|
|
226
|
+
{ cwd: workspace },
|
|
227
|
+
);
|
|
228
|
+
const answer = String(rawQueryCheck.data?.answer || "");
|
|
229
|
+
const answerContainsMarker = answer.includes(marker);
|
|
230
|
+
queryCheck = {
|
|
231
|
+
...rawQueryCheck,
|
|
232
|
+
id: "query-with-diff",
|
|
233
|
+
command: "myte query <redacted> --with-diff",
|
|
234
|
+
ok: rawQueryCheck.ok && answerContainsMarker,
|
|
235
|
+
error: rawQueryCheck.ok && answerContainsMarker
|
|
236
|
+
? null
|
|
237
|
+
: rawQueryCheck.error
|
|
238
|
+
|| "Live diff-query answer did not contain the exact fixture marker.",
|
|
239
|
+
data: {
|
|
240
|
+
marker_present_in_answer: answerContainsMarker,
|
|
241
|
+
job_id: rawQueryCheck.data?.job_id || null,
|
|
242
|
+
request_id: rawQueryCheck.data?.request_id || null,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
} finally {
|
|
247
|
+
try {
|
|
248
|
+
fs.writeFileSync(fixture.filePath, original);
|
|
249
|
+
const restoredStatus = runGit(["status", "--porcelain"], fixture.repoPath);
|
|
250
|
+
if (
|
|
251
|
+
!restoredStatus.ok
|
|
252
|
+
|| restoredStatus.stdout !== originalStatus.stdout
|
|
253
|
+
) {
|
|
254
|
+
restoreError = "Diff fixture repository did not return to its original clean status.";
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
restoreError = error?.message || String(error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const checks = [];
|
|
262
|
+
if (contextCheck) checks.push(contextCheck);
|
|
263
|
+
if (queryCheck) checks.push(queryCheck);
|
|
264
|
+
checks.push({
|
|
265
|
+
id: "query-with-diff-fixture-restore",
|
|
266
|
+
command: "restore temporary tracked diff fixture",
|
|
267
|
+
ok: !restoreError,
|
|
268
|
+
exit_code: restoreError ? 1 : 0,
|
|
269
|
+
duration_ms: 0,
|
|
270
|
+
data: {
|
|
271
|
+
fixture_repo: fixture.repoName,
|
|
272
|
+
fixture_file: fixture.relativePath,
|
|
273
|
+
},
|
|
274
|
+
error: restoreError,
|
|
275
|
+
});
|
|
276
|
+
return checks;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function firstFeedbackId(feedbackPath) {
|
|
280
|
+
if (!fs.existsSync(feedbackPath)) return null;
|
|
281
|
+
const text = fs.readFileSync(feedbackPath, "utf8");
|
|
282
|
+
const match = text.match(/^\s*(?:-\s+)?feedback_id:\s*["']?([^"'\s]+)["']?\s*$/m);
|
|
283
|
+
return match ? match[1] : null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function versionIdentifier(version) {
|
|
287
|
+
return String(version?.version_id || version?._id || "").trim() || null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function versionDocumentIdentifier(version) {
|
|
291
|
+
const documentSet =
|
|
292
|
+
version?.prd_document_set && typeof version.prd_document_set === "object"
|
|
293
|
+
? version.prd_document_set
|
|
294
|
+
: null;
|
|
295
|
+
const documents = Array.isArray(version?.prd_documents)
|
|
296
|
+
? version.prd_documents
|
|
297
|
+
: Array.isArray(documentSet?.documents)
|
|
298
|
+
? documentSet.documents
|
|
299
|
+
: [];
|
|
300
|
+
return String(
|
|
301
|
+
version?.primary_document_id
|
|
302
|
+
|| documentSet?.primary_document_id
|
|
303
|
+
|| documents[0]?.document_id
|
|
304
|
+
|| "",
|
|
305
|
+
).trim() || null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function baseArgs(args) {
|
|
309
|
+
const result = [];
|
|
310
|
+
if (args["base-url"]) result.push("--base-url", String(args["base-url"]));
|
|
311
|
+
result.push("--timeout-ms", String(args["timeout-ms"] || 300000));
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function main() {
|
|
316
|
+
const args = parseArgs(process.argv.slice(2));
|
|
317
|
+
if (!args["confirm-read"]) {
|
|
318
|
+
console.log(JSON.stringify({
|
|
319
|
+
ok: true,
|
|
320
|
+
dry_run: true,
|
|
321
|
+
message: "No network or project mutation was performed.",
|
|
322
|
+
route_matrix: ROUTE_MATRIX,
|
|
323
|
+
mutation_harnesses: [
|
|
324
|
+
"feedback-live-full-harness.js",
|
|
325
|
+
"mission-live-full-harness.js",
|
|
326
|
+
"mission-live-disposable-harness.js",
|
|
327
|
+
],
|
|
328
|
+
}, null, 2));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (!process.env.MYTE_API_KEY && !process.env.MYTE_PROJECT_API_KEY) {
|
|
332
|
+
throw new Error("Missing MYTE_API_KEY or MYTE_PROJECT_API_KEY.");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const runId = String(args["run-id"] || randomUUID());
|
|
336
|
+
const workspace = path.resolve(
|
|
337
|
+
args.workspace || fs.mkdtempSync(path.join(os.tmpdir(), `myte-read-cert-${runId}-`)),
|
|
338
|
+
);
|
|
339
|
+
const outputDir = path.join(workspace, "MyteCommandCenter");
|
|
340
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
341
|
+
const shared = baseArgs(args);
|
|
342
|
+
const checks = [];
|
|
343
|
+
|
|
344
|
+
const doctor = runCli(["doctor", "--json", ...shared], { cwd: workspace, timeoutMs: 60000 });
|
|
345
|
+
checks.push({ ...doctor, data: doctor.data ? {
|
|
346
|
+
ok: doctor.data.ok,
|
|
347
|
+
failure_layer: doctor.data.failure_layer,
|
|
348
|
+
runtime: doctor.data.runtime,
|
|
349
|
+
api: doctor.data.api,
|
|
350
|
+
project_key: doctor.data.project_key,
|
|
351
|
+
proxy: doctor.data.proxy,
|
|
352
|
+
dns: doctor.data.dns,
|
|
353
|
+
direct_transport: doctor.data.direct_transport,
|
|
354
|
+
config_probe: doctor.data.config_probe,
|
|
355
|
+
} : null });
|
|
356
|
+
if (!doctor.ok) {
|
|
357
|
+
console.log(JSON.stringify({
|
|
358
|
+
ok: false,
|
|
359
|
+
run_id: runId,
|
|
360
|
+
workspace,
|
|
361
|
+
stopped_at: "doctor",
|
|
362
|
+
failure_layer: doctor.data?.failure_layer || "unknown",
|
|
363
|
+
checks,
|
|
364
|
+
}, null, 2));
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const config = runCli(["config", "--json", ...shared], { cwd: workspace });
|
|
369
|
+
checks.push(config);
|
|
370
|
+
checks.push(runCli(["bootstrap", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
371
|
+
checks.push(runCli(["feedback-sync", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
372
|
+
checks.push(runCli(["sync-qaqc", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
373
|
+
checks.push(runCli(["suggestions", "sync", "--output-dir", outputDir, "--json", ...shared], { cwd: workspace }));
|
|
374
|
+
|
|
375
|
+
const feedbackId = firstFeedbackId(path.join(outputDir, "data", "feedback.yml"));
|
|
376
|
+
if (feedbackId) {
|
|
377
|
+
checks.push(runCli(["feedback", "get", "--feedback-id", feedbackId, "--json", ...shared], { cwd: workspace }));
|
|
378
|
+
checks.push(runCli(["feedback", "history", "--feedback-id", feedbackId, "--json", ...shared], { cwd: workspace }));
|
|
379
|
+
const versionsCheck = runCli(
|
|
380
|
+
["feedback", "prd-versions", "--feedback-id", feedbackId, "--json", ...shared],
|
|
381
|
+
{ cwd: workspace },
|
|
382
|
+
);
|
|
383
|
+
checks.push(versionsCheck);
|
|
384
|
+
if (versionsCheck.ok) {
|
|
385
|
+
const versions = Array.isArray(versionsCheck.data?.versions)
|
|
386
|
+
? versionsCheck.data.versions
|
|
387
|
+
: [];
|
|
388
|
+
const activeVersionId = String(
|
|
389
|
+
versionsCheck.data?.active_prd_version_id || "",
|
|
390
|
+
).trim();
|
|
391
|
+
const targetVersion =
|
|
392
|
+
versions.find((version) => versionIdentifier(version) !== activeVersionId)
|
|
393
|
+
|| versions[0];
|
|
394
|
+
const targetVersionId = versionIdentifier(targetVersion);
|
|
395
|
+
if (targetVersionId) {
|
|
396
|
+
const diffArgs = [
|
|
397
|
+
"feedback",
|
|
398
|
+
"prd-diff",
|
|
399
|
+
"--feedback-id",
|
|
400
|
+
feedbackId,
|
|
401
|
+
"--version-id",
|
|
402
|
+
targetVersionId,
|
|
403
|
+
];
|
|
404
|
+
const documentId = versionDocumentIdentifier(targetVersion);
|
|
405
|
+
if (documentId) diffArgs.push("--document-id", documentId);
|
|
406
|
+
diffArgs.push("--json", ...shared);
|
|
407
|
+
checks.push(runCli(diffArgs, { cwd: workspace }));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (args["include-query"]) {
|
|
413
|
+
checks.push(runCli([
|
|
414
|
+
"query",
|
|
415
|
+
"Goal: certify Project Assistant query transport. Ask: return the project title in one sentence.",
|
|
416
|
+
"--request-id",
|
|
417
|
+
`cert-read-${runId}`,
|
|
418
|
+
"--json",
|
|
419
|
+
...shared,
|
|
420
|
+
], { cwd: workspace }));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (args["include-diff-query"]) {
|
|
424
|
+
if (!config.ok) {
|
|
425
|
+
checks.push({
|
|
426
|
+
id: "query-with-diff",
|
|
427
|
+
command: "myte query <redacted> --with-diff",
|
|
428
|
+
ok: false,
|
|
429
|
+
exit_code: 1,
|
|
430
|
+
duration_ms: 0,
|
|
431
|
+
data: null,
|
|
432
|
+
error: "Project config failed; diff certification could not resolve a repository.",
|
|
433
|
+
});
|
|
434
|
+
} else {
|
|
435
|
+
checks.push(...runDiffCertification({
|
|
436
|
+
args,
|
|
437
|
+
configData: config.data,
|
|
438
|
+
runId,
|
|
439
|
+
workspace,
|
|
440
|
+
shared,
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const failed = checks.filter((check) => !check.ok);
|
|
446
|
+
console.log(JSON.stringify({
|
|
447
|
+
ok: failed.length === 0,
|
|
448
|
+
run_id: runId,
|
|
449
|
+
workspace,
|
|
450
|
+
local_artifacts_only: true,
|
|
451
|
+
live_business_mutations: false,
|
|
452
|
+
checks: checks.map((check) => ({
|
|
453
|
+
id: check.id,
|
|
454
|
+
command: check.command,
|
|
455
|
+
ok: check.ok,
|
|
456
|
+
exit_code: check.exit_code,
|
|
457
|
+
duration_ms: check.duration_ms,
|
|
458
|
+
error: check.error,
|
|
459
|
+
})),
|
|
460
|
+
failed_count: failed.length,
|
|
461
|
+
}, null, 2));
|
|
462
|
+
if (failed.length) process.exit(1);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
main();
|
|
467
|
+
} catch (error) {
|
|
468
|
+
console.error(JSON.stringify({ ok: false, message: error?.message || String(error) }, null, 2));
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|