Merge remote-tracking branch 'origin/main' into create-pr-v1

# Conflicts:
#	scripts/package-artifacts.mjs
This commit is contained in:
Andrey Avtomonov 2026-05-13 00:57:27 +02:00
commit a9d1d4091f
55 changed files with 1091 additions and 5038 deletions

View file

@ -619,8 +619,8 @@ try {
const missingProjectError = parseJsonFailure('ktx agent sl list missing project', missingProjectSearch);
assert.equal(missingProjectError.error.code, 'agent_sl_search_missing_project');
assert.deepEqual(missingProjectError.error.nextSteps, [
'ktx demo',
'ktx setup --project-dir ' + missingProjectDir,
'ktx status --project-dir ' + missingProjectDir,
'ktx ingest <connection>',
'ktx agent sl list --json --query "revenue" --project-dir ' + missingProjectDir,
]);
@ -676,8 +676,8 @@ try {
const emptySearchError = parseJsonFailure('ktx agent sl list no connections', emptySearch);
assert.equal(emptySearchError.error.code, 'agent_sl_search_no_connections');
assert.deepEqual(emptySearchError.error.nextSteps, [
'ktx demo',
'ktx setup --project-dir ' + emptyProjectDir,
'ktx status --project-dir ' + emptyProjectDir,
'ktx ingest <connection>',
'ktx agent sl list --json --query "revenue" --project-dir ' + emptyProjectDir,
]);
@ -767,8 +767,8 @@ try {
const noSourceSearchError = parseJsonFailure('ktx agent sl list no indexed sources', noSourceSearch);
assert.equal(noSourceSearchError.error.code, 'agent_sl_search_no_indexed_sources');
assert.deepEqual(noSourceSearchError.error.nextSteps, [
'ktx demo',
'ktx setup --project-dir ' + projectDir,
'ktx status --project-dir ' + projectDir,
'ktx ingest <connection>',
'ktx agent sl list --json --query "revenue" --project-dir ' + projectDir,
]);
@ -1005,7 +1005,7 @@ try {
`;
}
export function npmDemoSmokeSource() {
export function npmCliSmokeSource() {
return `
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
@ -1047,18 +1047,8 @@ function requireStdout(label, result, pattern) {
assert.match(result.stdout, pattern, label + ' stdout did not match ' + pattern);
}
function requireProjectStderr(label, result, projectDir) {
assert.equal(
result.code,
0,
label + ' failed with code ' + result.code + '\\nstdout:\\n' + result.stdout + '\\nstderr:\\n' + result.stderr,
);
assert.equal(result.stderr, 'Project: ' + projectDir + '\\n', label + ' wrote unexpected stderr');
}
const root = await mkdtemp(join(tmpdir(), 'ktx-packed-demo-smoke-'));
const root = await mkdtemp(join(tmpdir(), 'ktx-cli-smoke-'));
try {
const projectDir = join(root, 'demo-project');
const packageJson = JSON.parse(await readFile(join(process.cwd(), 'package.json'), 'utf8'));
assert.deepEqual(Object.keys(packageJson.dependencies), ['@kaelio/ktx']);
@ -1067,61 +1057,10 @@ try {
requireStdout('ktx --help', help, /Usage: ktx/);
requireStdout('ktx --help', help, /setup/);
const seeded = await run(
'pnpm',
['exec', 'ktx', 'setup', 'demo', '--project-dir', projectDir, '--no-input', '--plain'],
);
requireSuccess('ktx setup demo seeded', seeded);
requireStdout('ktx setup demo seeded', seeded, /Mode: seeded/);
requireStdout('ktx setup demo seeded', seeded, /Source: packaged demo project/);
requireStdout('ktx setup demo seeded', seeded, /LLM calls: none/);
requireStdout('ktx setup demo seeded', seeded, /ktx agent context --json/);
assert.doesNotMatch(seeded.stdout, new RegExp(['--mode', 'deterministic'].join(' ')));
assert.doesNotMatch(seeded.stdout, /KTX memory flow/);
requireProjectStderr('ktx setup demo seeded', seeded, projectDir);
const demoWikiSearch = await run('pnpm', [
'exec',
'ktx',
'agent',
'wiki',
'search',
'ARR contract',
'--json',
'--limit',
'5',
'--project-dir',
projectDir,
]);
requireSuccess('ktx seeded demo agent wiki search', demoWikiSearch);
const demoWikiSearchJson = JSON.parse(demoWikiSearch.stdout);
assert.ok(demoWikiSearchJson.totalFound > 0, 'seeded demo wiki search should find results');
assert.ok(
demoWikiSearchJson.results.some((result) => Array.isArray(result.matchReasons) && result.matchReasons.length > 0),
'seeded demo wiki search should expose match reasons',
);
process.stdout.write('ktx seeded demo agent wiki search verified\\n');
const demoSlSearch = await run('pnpm', [
'exec',
'ktx',
'agent',
'sl',
'list',
'--json',
'--query',
'ARR',
'--project-dir',
projectDir,
]);
requireSuccess('ktx seeded demo agent sl search', demoSlSearch);
const demoSlSearchJson = JSON.parse(demoSlSearch.stdout);
assert.ok(demoSlSearchJson.totalSources > 0, 'seeded demo semantic-layer search should find sources');
assert.ok(
demoSlSearchJson.sources.some((source) => Array.isArray(source.matchReasons) && source.matchReasons.length > 0),
'seeded demo semantic-layer search should expose match reasons',
);
process.stdout.write('ktx seeded demo agent sl search verified\\n');
const setupHelp = await run('pnpm', ['exec', 'ktx', 'setup', '--help']);
requireSuccess('ktx setup --help', setupHelp);
requireStdout('ktx setup --help', setupHelp, /Usage: ktx setup/);
requireStdout('ktx setup --help', setupHelp, /--no-input/);
const doctor = await run('pnpm', ['exec', 'ktx', 'status', '--no-input']);
assert.ok([0, 1].includes(doctor.code), 'ktx status setup exit code must be 0 or 1');
@ -1176,29 +1115,29 @@ async function verifyNpmArtifacts(layout, tmpRoot) {
await writeFile(join(projectDir, 'pnpm-workspace.yaml'), npmSmokePnpmWorkspaceYaml());
await writeFile(join(projectDir, 'verify-npm.mjs'), npmVerifySource());
await writeFile(join(projectDir, 'verify-installed-cli.mjs'), npmRuntimeSmokeSource());
await writeFile(join(projectDir, 'verify-installed-demo.mjs'), npmDemoSmokeSource());
await writeFile(join(projectDir, 'verify-installed-cli-commands.mjs'), npmCliSmokeSource());
await runCommand('pnpm', ['install'], { cwd: projectDir });
await runCommand('pnpm', ['rebuild', 'better-sqlite3'], { cwd: projectDir });
await runCommand('node', ['verify-npm.mjs'], { cwd: projectDir });
await runCommand('pnpm', ['exec', 'ktx', '--version'], { cwd: projectDir });
await runCommand('node', ['verify-installed-cli.mjs'], { cwd: projectDir });
await runCommand('node', ['verify-installed-demo.mjs'], { cwd: projectDir });
await runCommand('node', ['verify-installed-cli-commands.mjs'], { cwd: projectDir });
}
async function verifyNpmDemoArtifacts(layout, tmpRoot) {
async function verifyNpmCliArtifacts(layout, tmpRoot) {
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
await assertPathExists(layout.npmTarballs[packageInfo.name], `${packageInfo.name} tarball`);
}
const projectDir = join(tmpRoot, 'npm-demo-clean-install');
const projectDir = join(tmpRoot, 'npm-cli-clean-install');
await mkdir(projectDir, { recursive: true });
await writeFile(join(projectDir, 'package.json'), `${JSON.stringify(npmSmokePackageJson(layout), null, 2)}\n`);
await writeFile(join(projectDir, 'pnpm-workspace.yaml'), npmSmokePnpmWorkspaceYaml());
await writeFile(join(projectDir, 'verify-installed-demo.mjs'), npmDemoSmokeSource());
await writeFile(join(projectDir, 'verify-installed-cli-commands.mjs'), npmCliSmokeSource());
await runCommand('pnpm', ['install'], { cwd: projectDir });
await runCommand('node', ['verify-installed-demo.mjs'], { cwd: projectDir });
await runCommand('node', ['verify-installed-cli-commands.mjs'], { cwd: projectDir });
}
async function verifyArtifacts(layout) {
@ -1212,12 +1151,12 @@ async function verifyArtifacts(layout) {
}
}
async function verifyDemoArtifacts(layout) {
async function verifyCliArtifacts(layout) {
await verifyArtifactManifest(layout);
const tmpRoot = await mkdtemp(join(tmpdir(), 'ktx-demo-artifacts-'));
const tmpRoot = await mkdtemp(join(tmpdir(), 'ktx-cli-artifacts-'));
try {
await verifyNpmDemoArtifacts(layout, tmpRoot);
await verifyNpmCliArtifacts(layout, tmpRoot);
} finally {
await rm(tmpRoot, { recursive: true, force: true });
}
@ -1236,7 +1175,7 @@ async function main() {
return;
}
if (command === 'verify-demo') {
await verifyDemoArtifacts(layout);
await verifyCliArtifacts(layout);
return;
}
if (command === 'verify-manifest') {

View file

@ -16,7 +16,7 @@ import {
copyRuntimeWheelAssets,
findPythonArtifacts,
NPM_ARTIFACT_PACKAGES,
npmDemoSmokeSource,
npmCliSmokeSource,
npmRuntimeSmokeSource,
npmSmokePackageJson,
npmSmokePnpmWorkspaceYaml,
@ -387,9 +387,9 @@ describe('verifyNpmArtifacts', () => {
it('does not prepare an external Python environment for the npm smoke', async () => {
const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8');
const start = source.indexOf('async function verifyNpmArtifacts');
const end = source.indexOf('async function verifyNpmDemoArtifacts');
const end = source.indexOf('async function verifyNpmCliArtifacts');
assert.ok(start > 0, 'verifyNpmArtifacts function must exist');
assert.ok(end > start, 'verifyNpmDemoArtifacts must follow verifyNpmArtifacts');
assert.ok(end > start, 'verifyNpmCliArtifacts must follow verifyNpmArtifacts');
const body = source.slice(start, end);
assert.doesNotMatch(body, /uv', \['venv', '\.venv'\]/);
@ -513,21 +513,16 @@ describe('verification snippets', () => {
assert.match(source, /ktx dev ingest provider guard verified/);
});
describe('npmDemoSmokeSource', () => {
it('exercises the public packed-demo first-run contract', () => {
const source = npmDemoSmokeSource();
describe('npmCliSmokeSource', () => {
it('exercises supported public package CLI commands', () => {
const source = npmCliSmokeSource();
assert.match(source, /pnpm', \['exec', 'ktx', '--help'\]/);
assert.match(source, /'demo', '--project-dir', projectDir, '--no-input', '--plain'/);
assert.match(source, /Mode: seeded/);
assert.match(source, /Source: packaged demo project/);
assert.match(source, /LLM calls: none/);
assert.match(source, /ktx agent context --json/);
assert.match(source, /pnpm', \['exec', 'ktx', 'setup', '--help'\]/);
assert.match(source, /Usage: ktx setup/);
assert.doesNotMatch(source, new RegExp(["'demo'", "'--mode'", "'deterministic'"].join(', ')));
assert.match(source, /'status', '--no-input'/);
assert.match(source, /'--plain'/);
assert.match(source, /function requireProjectStderr/);
assert.match(source, /requireProjectStderr\('ktx setup demo seeded', seeded, projectDir\)/);
assert.doesNotMatch(source, /function requireProjectStderr/);
assert.match(source, /Object\.keys\(packageJson\.dependencies\)/);
assert.match(source, /'@kaelio\/ktx'/);
});

View file

@ -1,4 +1,3 @@
import { dirname, join } from 'node:path';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
@ -33,26 +32,6 @@ function registryEnv(config) {
return config.registry ? { npm_config_registry: config.registry } : {};
}
function runtimeCommandEnv(config, runtimeRoot) {
return { ...registryEnv(config), KTX_RUNTIME_ROOT: runtimeRoot };
}
function semanticQueryArgs(projectDir) {
return [
'sl',
'query',
'--project-dir',
projectDir,
'--connection-id',
'orbit_demo',
'--measure',
'contracts.contract_count',
'--format',
'sql',
'--yes',
];
}
function normalizePolicyConfig(policyConfig = {}) {
if (policyConfig === null || policyConfig === undefined) {
return { packageName: null, version: DEFAULT_VERSION_TAG, registry: null };
@ -150,24 +129,22 @@ export function buildPublishedPackageNpxCommand(config, args, label = 'published
export function buildPublishedPackageSmokeCommands(
config,
projectDir,
runtimeRoot = join(dirname(projectDir), 'managed-runtime'),
_projectDir,
) {
const runtimeEnv = runtimeCommandEnv(config, runtimeRoot);
const packageEnv = registryEnv(config);
const queryArgs = semanticQueryArgs(projectDir);
return [
buildPublishedPackageNpxCommand(config, ['--version'], 'published package npx version'),
buildPublishedPackageNpxCommand(
config,
['setup', 'demo', '--project-dir', projectDir, '--no-input', '--plain'],
'published package setup demo',
{ KTX_RUNTIME_ROOT: runtimeRoot },
['setup', '--help'],
'published package npx setup help',
),
buildPublishedPackageNpxCommand(
config,
['status', '--help'],
'published package npx status help',
),
buildPublishedPackageNpxCommand(config, queryArgs, 'published package npx sl query', {
KTX_RUNTIME_ROOT: runtimeRoot,
}),
{
label: 'published package local install',
command: 'pnpm',
@ -181,10 +158,10 @@ export function buildPublishedPackageSmokeCommands(
env: packageEnv,
},
{
label: 'published package local sl query',
label: 'published package local status help',
command: 'npx',
args: ['ktx', ...queryArgs],
env: runtimeEnv,
args: ['ktx', 'status', '--help'],
env: packageEnv,
},
{
label: 'published package global install',
@ -199,10 +176,10 @@ export function buildPublishedPackageSmokeCommands(
env: packageEnv,
},
{
label: 'published package global sl query',
label: 'published package global status help',
command: 'ktx',
args: queryArgs,
env: runtimeEnv,
args: ['status', '--help'],
env: packageEnv,
},
];
}

View file

@ -29,20 +29,10 @@ const VERSION_LABELS = new Set([
'published package global version',
]);
const SEMANTIC_QUERY_LABELS = new Set([
'published package npx sl query',
'published package local sl query',
'published package global sl query',
]);
export function isPublishedPackageVersionLabel(label) {
return VERSION_LABELS.has(label);
}
export function isPublishedPackageSemanticQueryLabel(label) {
return SEMANTIC_QUERY_LABELS.has(label);
}
function scriptRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
@ -100,10 +90,6 @@ export async function runPublishedPackageSmoke(config) {
if (isPublishedPackageVersionLabel(command.label)) {
assert.match(result.stdout, /@kaelio\/ktx /);
}
if (isPublishedPackageSemanticQueryLabel(command.label)) {
assert.match(result.stdout, /SELECT/i);
assert.match(result.stdout, /contracts/i);
}
}
process.stdout.write('published package invocation smoke verified\n');

View file

@ -5,7 +5,6 @@ import { describe, it } from 'node:test';
import {
buildPublishedPackageNpxCommand,
buildPublishedPackageSmokeCommands,
isPublishedPackageSemanticQueryLabel,
isPublishedPackageVersionLabel,
publishedPackageSpec,
readPublishedPackageSmokeConfig,
@ -149,16 +148,11 @@ describe('published package smoke config', () => {
});
describe('published package smoke output validation labels', () => {
it('classifies version and semantic query commands', () => {
it('classifies version commands', () => {
assert.equal(isPublishedPackageVersionLabel('published package npx version'), true);
assert.equal(isPublishedPackageVersionLabel('published package local version'), true);
assert.equal(isPublishedPackageVersionLabel('published package global version'), true);
assert.equal(isPublishedPackageVersionLabel('published package setup demo'), false);
assert.equal(isPublishedPackageSemanticQueryLabel('published package npx sl query'), true);
assert.equal(isPublishedPackageSemanticQueryLabel('published package local sl query'), true);
assert.equal(isPublishedPackageSemanticQueryLabel('published package global sl query'), true);
assert.equal(isPublishedPackageSemanticQueryLabel('published package local install'), false);
assert.equal(isPublishedPackageVersionLabel('published package npx setup help'), false);
});
});
@ -199,45 +193,16 @@ describe('published package smoke command construction', () => {
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package setup demo',
label: 'published package npx setup help',
command: 'npx',
args: [
'--yes',
'@kaelio/ktx@latest',
'setup',
'demo',
'--project-dir',
'/tmp/ktx-smoke/demo',
'--no-input',
'--plain',
],
env: {
npm_config_registry: 'https://registry.npmjs.org/',
KTX_RUNTIME_ROOT: '/tmp/ktx-smoke/managed-runtime',
},
args: ['--yes', '@kaelio/ktx@latest', 'setup', '--help'],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package npx sl query',
label: 'published package npx status help',
command: 'npx',
args: [
'--yes',
'@kaelio/ktx@latest',
'sl',
'query',
'--project-dir',
'/tmp/ktx-smoke/demo',
'--connection-id',
'orbit_demo',
'--measure',
'contracts.contract_count',
'--format',
'sql',
'--yes',
],
env: {
npm_config_registry: 'https://registry.npmjs.org/',
KTX_RUNTIME_ROOT: '/tmp/ktx-smoke/managed-runtime',
},
args: ['--yes', '@kaelio/ktx@latest', 'status', '--help'],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package local install',
@ -252,26 +217,10 @@ describe('published package smoke command construction', () => {
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package local sl query',
label: 'published package local status help',
command: 'npx',
args: [
'ktx',
'sl',
'query',
'--project-dir',
'/tmp/ktx-smoke/demo',
'--connection-id',
'orbit_demo',
'--measure',
'contracts.contract_count',
'--format',
'sql',
'--yes',
],
env: {
npm_config_registry: 'https://registry.npmjs.org/',
KTX_RUNTIME_ROOT: '/tmp/ktx-smoke/managed-runtime',
},
args: ['ktx', 'status', '--help'],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package global install',
@ -286,25 +235,10 @@ describe('published package smoke command construction', () => {
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
{
label: 'published package global sl query',
label: 'published package global status help',
command: 'ktx',
args: [
'sl',
'query',
'--project-dir',
'/tmp/ktx-smoke/demo',
'--connection-id',
'orbit_demo',
'--measure',
'contracts.contract_count',
'--format',
'sql',
'--yes',
],
env: {
npm_config_registry: 'https://registry.npmjs.org/',
KTX_RUNTIME_ROOT: '/tmp/ktx-smoke/managed-runtime',
},
args: ['status', '--help'],
env: { npm_config_registry: 'https://registry.npmjs.org/' },
},
],
);