* test(clients): verify native Command Code workflows * docs: synchronize Italian provider guide links * fix(contracts): make ADB identifier patterns portable * fix(contracts): preserve portable ADB patterns after formatting
453 lines
15 KiB
JavaScript
453 lines
15 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
import { homedir } from "node:os";
|
|
import { isAbsolute, join, relative } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
import {
|
|
TOOL_CONTRACTS,
|
|
toolContract,
|
|
} from "../dist/contracts/toolContracts.js";
|
|
import { exec } from "./lib/verify-package-core.mjs";
|
|
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
|
import { createOpenAiModelFixture } from "./verify/clients/model-fixture.mjs";
|
|
|
|
assert(
|
|
process.platform !== "win32",
|
|
"This verifier requires a disposable POSIX account; Windows is unverified",
|
|
);
|
|
const account = process.env.REA_VERIFY_COMMANDCODE_ACCOUNT_HOME;
|
|
assert(
|
|
account && isAbsolute(account) && account === homedir(),
|
|
"Run as a disposable account whose actual OS home matches REA_VERIFY_COMMANDCODE_ACCOUNT_HOME; do not override HOME",
|
|
);
|
|
assert.equal(
|
|
await readFile(join(account, ".rea-client-verification"), "utf8"),
|
|
"Disposable REA client verification account\n",
|
|
);
|
|
const repo = fileURLToPath(new URL("..", import.meta.url));
|
|
const runtimeRoot = process.env.REA_VERIFY_RUNTIME_ROOT ?? repo;
|
|
const command = process.env.REA_VERIFY_COMMANDCODE_COMMAND ?? "cmd";
|
|
const mode = process.argv[2] ?? "call";
|
|
assert(
|
|
["chat", "call"].includes(mode),
|
|
"Usage: verify-commandcode-client.mjs [chat|call]",
|
|
);
|
|
const verifier = createVerifierRun();
|
|
const lab = await mkdtemp(join(account, "rea-commandcode-client-"));
|
|
const workspace = join(lab, "workspace");
|
|
const target = join(workspace, "fixture α测试");
|
|
const profile = join(account, ".commandcode");
|
|
const configPath = join(profile, "mcp.json");
|
|
const skillDirectory = join(
|
|
account,
|
|
".agents",
|
|
"skills",
|
|
"reverse-engineer-anything",
|
|
);
|
|
await Promise.all([
|
|
mkdir(target, { recursive: true }),
|
|
mkdir(profile, { recursive: true }),
|
|
mkdir(join(lab, "tmp")),
|
|
]);
|
|
await exec("git", ["init", "--quiet", workspace], { timeout: 30_000 });
|
|
await writeFile(
|
|
join(target, "app.js"),
|
|
'export function result() { return { clientCompatibility: "REA_CLIENT_FIXTURE", count: 7 }; }\n',
|
|
);
|
|
const environment = {
|
|
PATH: process.env.PATH,
|
|
NODE_OPTIONS: process.env.NODE_OPTIONS,
|
|
USERPROFILE: account,
|
|
TMPDIR: join(lab, "tmp"),
|
|
CMD_LOCAL_ONLY: "1",
|
|
// Native print mode checks for an account key even with a keyless BYOK model.
|
|
// This synthetic value is never a real credential; local-only refuses hosted API calls.
|
|
COMMAND_CODE_API_KEY: "local-fixture-only",
|
|
COMMANDCODE_SKIP_UPDATES: "1",
|
|
COMMANDCODE_DISABLE_CRON: "1",
|
|
OTEL_SDK_DISABLED: "true",
|
|
REA_PROCESS_RUN_ID: verifier.run_id,
|
|
};
|
|
const expectedNames = TOOL_CONTRACTS.map(
|
|
(tool) => "mcp__rea__" + tool.name,
|
|
).sort();
|
|
const schemaValidator = new Ajv2020({ strict: false, validateFormats: false });
|
|
const textValues = (value) =>
|
|
typeof value === "string"
|
|
? [value]
|
|
: value !== null && typeof value === "object"
|
|
? Object.values(value).flatMap(textValues)
|
|
: [];
|
|
let clientVersion;
|
|
let expectedSkill;
|
|
let inventoryChecked = false;
|
|
let skillLoaded = false;
|
|
let schemasChecked = 0;
|
|
let catalogArtifact;
|
|
let catalogDigest;
|
|
let catalogProjection;
|
|
let artifactProjectionSeen = false;
|
|
let evidenceSeen = false;
|
|
let evidenceBytes;
|
|
let finalSeen = false;
|
|
let stage = 0;
|
|
let failure;
|
|
const call = (name, args) => ({
|
|
role: "assistant",
|
|
content: null,
|
|