test: smoke installed npm package
Some checks are pending
Check / check (push) Waiting to run
Check / clawhub-dry-run (push) Waiting to run

This commit is contained in:
Vincent Koc 2026-04-29 13:29:10 -07:00
parent 7ca1d2c53e
commit 1e6474223f
No known key found for this signature in database
3 changed files with 141 additions and 2 deletions

View File

@ -30,7 +30,7 @@
},
"exports": {
".": "./src/index.js",
"./runtime": "./src/index.js",
"./runtime": "./src/kitchen-runtime.js",
"./scenarios": "./src/scenarios.js",
"./setup": "./src/setup.js"
},
@ -51,7 +51,8 @@
}
},
"scripts": {
"check": "npm run sync:surface -- --check && node scripts/check-sdk-surface.mjs && node scripts/check-kitchen-runtime.mjs && node scripts/check-kitchen-contract-probes.mjs && npm run plugin:inspect",
"acceptance:install": "node scripts/check-installed-package.mjs",
"check": "npm run sync:surface -- --check && node scripts/check-sdk-surface.mjs && node scripts/check-kitchen-runtime.mjs && node scripts/check-kitchen-contract-probes.mjs && npm run plugin:inspect && npm run acceptance:install",
"pack:check": "node scripts/check-pack-payload.mjs",
"plugin:inspect": "plugin-inspector check --config plugin-inspector.config.json --no-openclaw",
"plugin:inspect:runtime": "PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector check --config plugin-inspector.config.json --no-openclaw --runtime --mock-sdk",

View File

@ -0,0 +1,132 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
const repoRoot = process.cwd();
const tempRoot = mkdtempSync(path.join(tmpdir(), "kitchen-sink-install-"));
const keepTemp = process.env.KEEP_KITCHEN_INSTALL_SMOKE === "1";
let lastStdout = "";
try {
const packDir = path.join(tempRoot, "pack");
mkdirSync(packDir, { recursive: true });
run("npm", ["pack", "--json", "--pack-destination", packDir], { cwd: repoRoot });
const packOutput = JSON.parse(lastStdout);
const tarball = path.join(packDir, packOutput[0].filename);
const projectDir = path.join(tempRoot, "consumer");
mkdirSync(projectDir, { recursive: true });
run("npm", ["init", "-y"], { cwd: tempRoot });
run("npm", ["install", "--prefix", projectDir, "--package-lock=false", "--ignore-scripts", "--no-audit", "--no-fund", tarball], {
cwd: tempRoot,
});
const packageDir = path.join(projectDir, "node_modules", "@openclaw", "kitchen-sink");
const installedPackageJson = JSON.parse(readFileSync(path.join(packageDir, "package.json"), "utf8"));
assert.equal(installedPackageJson.name, "@openclaw/kitchen-sink");
assert.equal(installedPackageJson.version, JSON.parse(readFileSync("package.json", "utf8")).version);
const probeFile = path.join(projectDir, "probe.mjs");
writeFileSync(probeFile, consumerProbeSource());
run(process.execPath, [probeFile], { cwd: projectDir });
const inspectorBin = path.join(repoRoot, "node_modules", ".bin", "plugin-inspector");
run(inspectorBin, ["check", "--config", "plugin-inspector.config.json", "--no-openclaw", "--runtime", "--mock-sdk"], {
cwd: packageDir,
env: {
...process.env,
PLUGIN_INSPECTOR_EXECUTE_ISOLATED: "1",
},
});
console.log(`Installed package smoke OK: ${installedPackageJson.name}@${installedPackageJson.version}`);
} finally {
if (!keepTemp) {
rmSync(tempRoot, { recursive: true, force: true });
} else {
console.log(`Kept install smoke temp dir: ${tempRoot}`);
}
}
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: options.cwd,
env: options.env || process.env,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
lastStdout = result.stdout;
if (result.status !== 0) {
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
process.exit(result.status ?? 1);
}
}
function consumerProbeSource() {
return String.raw`
import assert from "node:assert/strict";
import { plugin } from "@openclaw/kitchen-sink";
import { createKitchenSinkRuntime } from "@openclaw/kitchen-sink/runtime";
import { createKitchenSinkImageAsset, kitchenPromptGuidance } from "@openclaw/kitchen-sink/scenarios";
import setup from "@openclaw/kitchen-sink/setup";
const registrations = {};
const api = new Proxy(
{ id: "consumer-install-smoke", registrationMode: "full", config: {}, logger: console },
{
get(target, property) {
if (property in target) return target[property];
if (property === "on") {
return (...args) => {
registrations.on ??= [];
registrations.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) return undefined;
return (...args) => {
registrations[property] ??= [];
registrations[property].push(args);
};
},
},
);
plugin.register(api);
assert.equal(plugin.id, "openclaw-kitchen-sink-fixture");
assert.ok(registrations.registerImageGenerationProvider?.some(([provider]) => provider.id === "kitchen-sink-image"));
assert.ok(registrations.registerProvider?.some(([provider]) => provider.id === "kitchen-sink-llm"));
assert.ok(registrations.registerWebSearchProvider?.some(([provider]) => provider.id === "kitchen-sink-search"));
assert.ok(registrations.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"));
const runtime = createKitchenSinkRuntime({
delayMs: 10_000,
sleep: async () => {},
now: (() => {
let tick = 0;
return () => new Date(Date.UTC(2026, 3, 29, 12, 0, tick++));
})(),
});
const image = await runtime.runImageJob({ prompt: "generate an image with kitchen sink" });
assert.equal(image.job.status, "completed");
assert.equal(image.image.metadata.assetName, "kitchen_sink_office.png");
assert.equal(image.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
const directImage = createKitchenSinkImageAsset({
prompt: "consumer import smoke",
jobId: "ks_consumer_install_smoke",
});
assert.equal(directImage.mimeType, "image/png");
assert.ok(directImage.dataUrl.startsWith("data:image/png;base64,"));
assert.ok(kitchenPromptGuidance().some((line) => line.includes("kitchen_sink_image_job")));
assert.equal(setup.id, "openclaw-kitchen-sink-setup");
assert.equal(typeof setup.setup, "function");
const setupResult = await setup.setup({ config: {} });
assert.equal(setupResult.configured, true);
`;
}

View File

@ -94,6 +94,12 @@ if (packageJson.dependencies?.openclaw !== buildOpenClawVersion) {
if (!packageJson.files?.includes("src/")) {
issues.push("package files must include src/");
}
if (packageJson.exports?.["./runtime"] !== "./src/kitchen-runtime.js") {
issues.push('package exports "./runtime" must point at "./src/kitchen-runtime.js"');
}
if (packageJson.exports?.["./scenarios"] !== "./src/scenarios.js") {
issues.push('package exports "./scenarios" must point at "./src/scenarios.js"');
}
if (issues.length > 0) {
console.error(`Package payload check failed:\n- ${issues.join("\n- ")}`);