@mytegroupinc/myte-core 0.0.54 → 0.0.56
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 +211 -182
- package/THIRD_PARTY_NOTICES.md +21 -21
- package/TRADEMARKS.md +10 -10
- package/cli.js +2822 -2489
- package/lib/certification-manifest.js +180 -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 +1509 -1364
- package/scripts/mission-live-full-harness.js +10 -9
- package/scripts/project-assistant-live-full-harness.js +208 -0
- package/scripts/project-assistant-read-certification.js +470 -470
|
@@ -8,6 +8,7 @@ const { spawnSync } = require("node:child_process");
|
|
|
8
8
|
|
|
9
9
|
const CLI_PATH = path.resolve(__dirname, "..", "cli.js");
|
|
10
10
|
const DEFAULT_BASE_URL = "https://api.myte.dev/api";
|
|
11
|
+
const PACKAGE_VERSION = require("../package.json").version;
|
|
11
12
|
|
|
12
13
|
function parseArgs(argv) {
|
|
13
14
|
const args = { _: [] };
|
|
@@ -285,7 +286,7 @@ class Harness {
|
|
|
285
286
|
this.key = getProjectKey();
|
|
286
287
|
this.actorBase = String(args["actor-scope"] || `mission-live-full-${nowId()}`);
|
|
287
288
|
this.cliMode = String(args.cli || "local").toLowerCase();
|
|
288
|
-
this.packageVersion = String(args["package-version"] ||
|
|
289
|
+
this.packageVersion = String(args["package-version"] || PACKAGE_VERSION);
|
|
289
290
|
this.batchSize = Math.max(1, Math.min(10, Number(args["batch-size"] || 10)));
|
|
290
291
|
this.includeQaqcRun = args["include-qaqc-run"] === true;
|
|
291
292
|
this.workspace = path.resolve(
|
|
@@ -433,7 +434,7 @@ class Harness {
|
|
|
433
434
|
change_set: { description: "Harness proposed description. This should be rejected during cleanup." },
|
|
434
435
|
}],
|
|
435
436
|
});
|
|
436
|
-
const first = this.cli(["suggestions", "create", "--file", createOne, "--no-sync", "--json"], { actorScope: actor });
|
|
437
|
+
const first = this.cli(["suggestions", "create", "--file", createOne, "--confirm-write", "--approval-artifact", createOne, "--no-sync", "--json"], { actorScope: actor });
|
|
437
438
|
const suggestionId = pickSuggestionId(first);
|
|
438
439
|
assertCondition(suggestionId, "Update suggestion create did not return suggestion_id.");
|
|
439
440
|
this.openSuggestions.add(suggestionId);
|
|
@@ -446,7 +447,7 @@ class Harness {
|
|
|
446
447
|
change_set: { acceptance_criteria: ["Harness proposed acceptance criterion. This should be rejected."] },
|
|
447
448
|
}],
|
|
448
449
|
});
|
|
449
|
-
const second = this.cli(["suggestions", "create", "--file", createTwo, "--no-sync", "--json"], { actorScope: actor });
|
|
450
|
+
const second = this.cli(["suggestions", "create", "--file", createTwo, "--confirm-write", "--approval-artifact", createTwo, "--no-sync", "--json"], { actorScope: actor });
|
|
450
451
|
const appendedId = pickSuggestionId(second);
|
|
451
452
|
assertCondition(appendedId === suggestionId, `Expected append to same suggestion ${suggestionId}, got ${appendedId || "(none)"}.`);
|
|
452
453
|
|
|
@@ -457,7 +458,7 @@ class Harness {
|
|
|
457
458
|
change_set: { technical_requirements: ["Harness proposed requirement. This should be rejected."] },
|
|
458
459
|
}],
|
|
459
460
|
});
|
|
460
|
-
const revised = this.cli(["suggestions", "revise", "--file", revise, "--no-sync", "--json"], { actorScope: actor });
|
|
461
|
+
const revised = this.cli(["suggestions", "revise", "--file", revise, "--confirm-write", "--approval-artifact", revise, "--no-sync", "--json"], { actorScope: actor });
|
|
461
462
|
assertCondition((revised.revised_count || 0) >= 1, "Update suggestion revise did not revise the thread.");
|
|
462
463
|
|
|
463
464
|
const requestChanges = writeJson(this.payloadDir, "update-loop-request-changes.json", {
|
|
@@ -473,7 +474,7 @@ class Harness {
|
|
|
473
474
|
change_set: { labels: ["harness-proposed-update"] },
|
|
474
475
|
}],
|
|
475
476
|
});
|
|
476
|
-
const revisedAgain = this.cli(["suggestions", "revise", "--file", reviseAfterRequest, "--no-sync", "--json"], { actorScope: actor });
|
|
477
|
+
const revisedAgain = this.cli(["suggestions", "revise", "--file", reviseAfterRequest, "--confirm-write", "--approval-artifact", reviseAfterRequest, "--no-sync", "--json"], { actorScope: actor });
|
|
477
478
|
assertCondition((revisedAgain.revised_count || 0) >= 1, "Update suggestion revise after request_changes did not process.");
|
|
478
479
|
|
|
479
480
|
const reject = writeJson(this.payloadDir, "update-loop-reject.json", {
|
|
@@ -504,7 +505,7 @@ class Harness {
|
|
|
504
505
|
},
|
|
505
506
|
}],
|
|
506
507
|
});
|
|
507
|
-
const created = this.cli(["suggestions", "create", "--file", createFile, "--no-sync", "--json"], { actorScope: actor });
|
|
508
|
+
const created = this.cli(["suggestions", "create", "--file", createFile, "--confirm-write", "--approval-artifact", createFile, "--no-sync", "--json"], { actorScope: actor });
|
|
508
509
|
const suggestionId = pickSuggestionId(created);
|
|
509
510
|
assertCondition(suggestionId, "Create suggestion did not return suggestion_id.");
|
|
510
511
|
this.openSuggestions.add(suggestionId);
|
|
@@ -522,7 +523,7 @@ class Harness {
|
|
|
522
523
|
change_set: { description: "Harness revised proposal. This should still be rejected." },
|
|
523
524
|
}],
|
|
524
525
|
});
|
|
525
|
-
const revised = this.cli(["suggestions", "revise", "--file", reviseFile, "--no-sync", "--json"], { actorScope: actor });
|
|
526
|
+
const revised = this.cli(["suggestions", "revise", "--file", reviseFile, "--confirm-write", "--approval-artifact", reviseFile, "--no-sync", "--json"], { actorScope: actor });
|
|
526
527
|
assertCondition((revised.revised_count || 0) >= 1, "Create suggestion revise did not process.");
|
|
527
528
|
|
|
528
529
|
const requestChanges = writeJson(this.payloadDir, "create-reject-request-changes.json", {
|
|
@@ -579,7 +580,7 @@ class Harness {
|
|
|
579
580
|
},
|
|
580
581
|
}],
|
|
581
582
|
});
|
|
582
|
-
const created = this.cli(["suggestions", "create", "--file", createFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
|
|
583
|
+
const created = this.cli(["suggestions", "create", "--file", createFile, "--confirm-write", "--approval-artifact", createFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
|
|
583
584
|
const suggestionId = pickSuggestionId(created);
|
|
584
585
|
assertCondition(suggestionId, "Approved mission create did not return suggestion_id.");
|
|
585
586
|
this.openSuggestions.add(suggestionId);
|
|
@@ -633,7 +634,7 @@ class Harness {
|
|
|
633
634
|
change_set: { description: "Harness pending suggestion should be hidden from actionable queue while archived." },
|
|
634
635
|
}],
|
|
635
636
|
});
|
|
636
|
-
const pending = this.cli(["suggestions", "create", "--file", pendingFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
|
|
637
|
+
const pending = this.cli(["suggestions", "create", "--file", pendingFile, "--confirm-write", "--approval-artifact", pendingFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
|
|
637
638
|
pendingSuggestionId = pickSuggestionId(pending);
|
|
638
639
|
assertCondition(pendingSuggestionId, "Pending update suggestion for approved mission was not created.");
|
|
639
640
|
this.openSuggestions.add(pendingSuggestionId);
|
|
@@ -0,0 +1,208 @@
|
|
|
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 { randomUUID } = require("node:crypto");
|
|
8
|
+
const { spawn, spawnSync } = require("node:child_process");
|
|
9
|
+
|
|
10
|
+
const ROOT = path.resolve(__dirname, "..");
|
|
11
|
+
const CLI = path.join(ROOT, "cli.js");
|
|
12
|
+
const DEFAULT_BASE_URL = "https://api.myte.dev/api";
|
|
13
|
+
const GAAHFIELD_PROJECT_ALIASES = new Set(["gaahfield", "gaahfield marketplace"]);
|
|
14
|
+
const ROUTES = [
|
|
15
|
+
"POST /query", "GET /query/<job_id>", "GET /config", "GET /bootstrap",
|
|
16
|
+
"GET /qaqc-sync", "POST /run-qaqc", "GET /run-qaqc/<batch_id>",
|
|
17
|
+
"POST /mission-status-update", "POST /mission-archive", "GET /feedback-sync",
|
|
18
|
+
"GET /feedback/<feedback_id>", "POST /feedback/<feedback_id>/refinement/validate",
|
|
19
|
+
"POST /feedback/<feedback_id>/refinement/requests",
|
|
20
|
+
"POST /feedback-review-requests/<request_id>/revise",
|
|
21
|
+
"POST /feedback/<feedback_id>/refinement/apply",
|
|
22
|
+
"GET /feedback/<feedback_id>/refinement/history", "GET /feedback-review-requests",
|
|
23
|
+
"GET /feedback-review-requests/<request_id>",
|
|
24
|
+
"POST /feedback-review-requests/<request_id>/review",
|
|
25
|
+
"POST /feedback-review-requests/batch-review",
|
|
26
|
+
"POST /feedback-review-requests/<request_id>/request-changes",
|
|
27
|
+
"POST /feedback-review-requests/<request_id>/cancel",
|
|
28
|
+
"POST /feedback/<feedback_id>/board-move", "POST /feedback/batch-board-move",
|
|
29
|
+
"POST /feedback/<feedback_id>/comments", "GET /feedback/<feedback_id>/events",
|
|
30
|
+
"GET /feedback/<feedback_id>/tickets", "POST /feedback/<feedback_id>/tickets",
|
|
31
|
+
"POST /feedback/<feedback_id>/tickets/batch", "POST /feedback/<feedback_id>/tickets/batch/validate",
|
|
32
|
+
"PATCH /feedback/<feedback_id>/tickets/<ticket_id>", "PATCH /feedback/<feedback_id>/tickets/batch",
|
|
33
|
+
"POST /feedback/<feedback_id>/tickets/batch-update/validate",
|
|
34
|
+
"PATCH /feedback/<feedback_id>/tickets/<ticket_id>/prd-coverage",
|
|
35
|
+
"POST /feedback/<feedback_id>/events/<event_id>/undo",
|
|
36
|
+
"GET /feedback/<feedback_id>/prd/versions",
|
|
37
|
+
"GET /feedback/<feedback_id>/prd/versions/<version_id>/diff",
|
|
38
|
+
"POST /project-comment", "POST /update-owner", "POST /client-update-drafts",
|
|
39
|
+
"POST /create-prd", "POST /create-prds", "GET /suggestions", "POST /suggestions",
|
|
40
|
+
"POST /suggestions/revise", "POST /suggestions/review",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const COVERAGE = {
|
|
44
|
+
read: ROUTES.filter((route) => route.startsWith("GET ") || route.startsWith("POST /query")),
|
|
45
|
+
mission: ROUTES.filter((route) => /mission|qaqc|suggestion/.test(route)),
|
|
46
|
+
feedback: ROUTES.filter((route) => /feedback|create-prd/.test(route)),
|
|
47
|
+
communications: ROUTES.filter((route) => /project-comment|update-owner|client-update/.test(route)),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function parseArgs(argv) {
|
|
51
|
+
const args = {};
|
|
52
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
53
|
+
const token = argv[index];
|
|
54
|
+
if (!token.startsWith("--")) continue;
|
|
55
|
+
const next = argv[index + 1];
|
|
56
|
+
if (!next || next.startsWith("--")) args[token.slice(2)] = true;
|
|
57
|
+
else { args[token.slice(2)] = next; index += 1; }
|
|
58
|
+
}
|
|
59
|
+
return args;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeProjectTitle(value) {
|
|
63
|
+
return String(value || "").trim().toLowerCase().replace(/\s+/g, " ");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isGaahFieldProject(value) {
|
|
67
|
+
return GAAHFIELD_PROJECT_ALIASES.has(normalizeProjectTitle(value));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function routeCoverage() {
|
|
71
|
+
const accounted = new Set(Object.values(COVERAGE).flat());
|
|
72
|
+
const missing = ROUTES.filter((route) => !accounted.has(route));
|
|
73
|
+
if (ROUTES.length !== 46 || new Set(ROUTES).size !== 46 || missing.length) {
|
|
74
|
+
throw new Error(`Route manifest is incomplete: ${missing.join(", ") || "count/duplicate mismatch"}`);
|
|
75
|
+
}
|
|
76
|
+
return { implemented_project_key_routes: 46, accounted_routes: accounted.size, missing };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function runNode(script, args, cwd) {
|
|
80
|
+
const result = spawnSync(process.execPath, [script, ...args], {
|
|
81
|
+
cwd, env: process.env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 1_800_000,
|
|
82
|
+
});
|
|
83
|
+
let output = null;
|
|
84
|
+
try { output = JSON.parse(String(result.stdout || "").trim() || "null"); } catch {}
|
|
85
|
+
return {
|
|
86
|
+
command: path.basename(script), ok: result.status === 0,
|
|
87
|
+
exit_code: result.status, output,
|
|
88
|
+
error: result.status === 0 ? null : String(result.stderr || result.stdout || "failed").trim().slice(0, 2000),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function runCli(args, cwd) {
|
|
93
|
+
return runNode(CLI, args, cwd);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function runNodeAsync(script, args, cwd) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
const child = spawn(process.execPath, [script, ...args], { cwd, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
99
|
+
let stdout = "";
|
|
100
|
+
let stderr = "";
|
|
101
|
+
child.stdout.on("data", (chunk) => { stdout += chunk; });
|
|
102
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
103
|
+
child.on("close", (code) => {
|
|
104
|
+
let output = null;
|
|
105
|
+
try { output = JSON.parse(stdout.trim() || "null"); } catch {}
|
|
106
|
+
resolve({ ok: code === 0, exit_code: code, output, error: code === 0 ? null : (stderr || stdout).trim().slice(0, 2000) });
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isIdempotencyInProgress(result) {
|
|
112
|
+
return !result?.ok && /idempotency key is still in progress/i.test(String(result?.error || ""));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function settleIdempotentReplay(script, args, cwd, initialResult) {
|
|
116
|
+
let result = initialResult;
|
|
117
|
+
for (let attempt = 0; attempt < 6 && isIdempotencyInProgress(result); attempt += 1) {
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(250 * (2 ** attempt), 2000)));
|
|
119
|
+
result = await runNodeAsync(script, args, cwd);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function assertExpectedProject(baseUrl, expectedProject) {
|
|
125
|
+
const key = process.env.MYTE_API_KEY || process.env.MYTE_PROJECT_API_KEY;
|
|
126
|
+
if (!key) throw new Error("Missing MYTE_API_KEY or MYTE_PROJECT_API_KEY.");
|
|
127
|
+
const response = await fetch(`${String(baseUrl).replace(/\/+$/, "")}/project-assistant/bootstrap`, {
|
|
128
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
129
|
+
});
|
|
130
|
+
const body = await response.json();
|
|
131
|
+
const title = String(body?.data?.project?.title || "").trim();
|
|
132
|
+
if (!response.ok || body?.status !== "success") throw new Error("Project bootstrap preflight failed.");
|
|
133
|
+
if (!isGaahFieldProject(expectedProject) || !isGaahFieldProject(title)) {
|
|
134
|
+
throw new Error(`Live target mismatch: expected ${expectedProject}, received ${title || "unknown"}.`);
|
|
135
|
+
}
|
|
136
|
+
return { project_id: body.data.project.id, title };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function main() {
|
|
140
|
+
const args = parseArgs(process.argv.slice(2));
|
|
141
|
+
const coverage = routeCoverage();
|
|
142
|
+
if (!args["confirm-live"]) {
|
|
143
|
+
console.log(JSON.stringify({
|
|
144
|
+
ok: true, dry_run: true, network_requests: 0, live_mutations: 0, coverage,
|
|
145
|
+
stages: ["read", "mission", "feedback", "communications", "concurrent-idempotency", "exact-id-cleanup"],
|
|
146
|
+
required_live_flags: ["--confirm-live", "--expected-project GaahField", "--include-communications"],
|
|
147
|
+
owner_email_requires: "--include-owner-email",
|
|
148
|
+
}, null, 2));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const expectedProject = String(args["expected-project"] || "").trim();
|
|
152
|
+
if (!isGaahFieldProject(expectedProject)) throw new Error("Live certification is pinned to --expected-project GaahField.");
|
|
153
|
+
if (!args["include-communications"]) throw new Error("Full live certification requires --include-communications.");
|
|
154
|
+
const baseUrl = String(args["base-url"] || process.env.MYTE_API_BASE || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
155
|
+
const project = await assertExpectedProject(baseUrl, expectedProject);
|
|
156
|
+
const runId = String(args["run-id"] || randomUUID());
|
|
157
|
+
const workspace = path.resolve(args.workspace || fs.mkdtempSync(path.join(os.tmpdir(), `myte-full-${runId}-`)));
|
|
158
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
159
|
+
const common = ["--base-url", baseUrl, "--run-id", runId];
|
|
160
|
+
const results = [];
|
|
161
|
+
results.push(runNode(path.join(__dirname, "project-assistant-read-certification.js"), ["--confirm-read", "--include-query", ...common], workspace));
|
|
162
|
+
results.push(runNode(path.join(__dirname, "mission-live-full-harness.js"), ["--confirm-live", ...common], workspace));
|
|
163
|
+
results.push(runNode(path.join(__dirname, "feedback-live-full-harness.js"), ["--confirm-live", ...common], workspace));
|
|
164
|
+
|
|
165
|
+
const team = path.join(workspace, `team-${runId}.md`);
|
|
166
|
+
const client = path.join(workspace, `client-${runId}.md`);
|
|
167
|
+
fs.writeFileSync(team, `Project Assistant certification ${runId}: team update route verified.\n`, "utf8");
|
|
168
|
+
fs.writeFileSync(client, `Project Assistant certification ${runId}: client draft route verified; do not send.\n`, "utf8");
|
|
169
|
+
const teamArgs = ["update-team", `Certification ${runId}`, "--confirm-write", "--approval-artifact", team, "--idempotency-key", `cert-team-${runId}`, "--json", "--base-url", baseUrl];
|
|
170
|
+
const concurrentTeamInitial = await Promise.all(Array.from({ length: 10 }, () => runNodeAsync(CLI, teamArgs, workspace)));
|
|
171
|
+
const transientInProgress = concurrentTeamInitial.filter(isIdempotencyInProgress).length;
|
|
172
|
+
const concurrentTeam = [];
|
|
173
|
+
for (const result of concurrentTeamInitial) {
|
|
174
|
+
concurrentTeam.push(await settleIdempotentReplay(CLI, teamArgs, workspace, result));
|
|
175
|
+
}
|
|
176
|
+
const teamFingerprints = new Set(
|
|
177
|
+
concurrentTeam.filter((item) => item.ok).map((item) => JSON.stringify(item.output?.data || item.output || null)),
|
|
178
|
+
);
|
|
179
|
+
results.push({
|
|
180
|
+
command: "concurrent-idempotency:update-team",
|
|
181
|
+
ok: concurrentTeam.every((item) => item.ok) && teamFingerprints.size === 1,
|
|
182
|
+
attempts: concurrentTeam.length,
|
|
183
|
+
transient_in_progress: transientInProgress,
|
|
184
|
+
unique_outcomes: teamFingerprints.size,
|
|
185
|
+
error: concurrentTeam.find((item) => !item.ok)?.error || (teamFingerprints.size === 1 ? null : "Concurrent replays returned different mutation outcomes."),
|
|
186
|
+
});
|
|
187
|
+
results.push(runCli(["update-client", "--subject", `Certification ${runId}`, "--body-file", client, "--confirm-write", "--approval-artifact", client, "--idempotency-key", `cert-client-${runId}`, "--json", "--base-url", baseUrl], workspace));
|
|
188
|
+
if (args["include-owner-email"]) {
|
|
189
|
+
const owner = path.join(workspace, `owner-${runId}.md`);
|
|
190
|
+
fs.writeFileSync(owner, `Project Assistant certification ${runId}: owner delivery route verified.\n`, "utf8");
|
|
191
|
+
results.push(runCli(["update-owner", "--subject", `Certification ${runId}`, "--body-file", owner, "--confirm-write", "--approval-artifact", owner, "--idempotency-key", `cert-owner-${runId}`, "--json", "--base-url", baseUrl], workspace));
|
|
192
|
+
}
|
|
193
|
+
const failed = results.filter((result) => !result.ok);
|
|
194
|
+
const report = { ok: failed.length === 0, run_id: runId, project, coverage, workspace, results, failed_count: failed.length };
|
|
195
|
+
const reportPath = path.join(workspace, `project-assistant-certification-${runId}.json`);
|
|
196
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");
|
|
197
|
+
console.log(JSON.stringify({ ...report, report_path: reportPath }, null, 2));
|
|
198
|
+
if (failed.length) process.exitCode = 1;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (require.main === module) {
|
|
202
|
+
main().catch((error) => {
|
|
203
|
+
console.error(JSON.stringify({ ok: false, message: error?.message || String(error) }, null, 2));
|
|
204
|
+
process.exit(1);
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
module.exports = { COVERAGE, DEFAULT_BASE_URL, ROUTES, isGaahFieldProject, isIdempotencyInProgress, routeCoverage };
|
|
208
|
+
}
|