refactor(workspace): rename @ktx/cli to @kaelio/ktx and pack it directly

Promote the CLI workspace package to the public name `@kaelio/ktx` and
drop the separate `scripts/build-public-npm-package.mjs` wrapper. The
CLI package is now publishable in place (`publishConfig.access: public`,
`provenance: true`), so artifact packing uses `pnpm pack` against
`packages/cli/` instead of assembling a parallel package tree.

Updates all workspace filter invocations, docs, tests, and release
readiness checks to reference the new package name, and folds the
tarball-name helper into `scripts/public-npm-release-metadata.mjs`.
This commit is contained in:
Andrey Avtomonov 2026-05-21 13:05:14 +02:00
parent 34d4a1e9e1
commit aa523f9ab3
31 changed files with 99 additions and 452 deletions

View file

@ -109,7 +109,7 @@ jobs:
run: pnpm run build
- name: Run slow CLI tests
run: pnpm --filter @ktx/cli run test:slow
run: pnpm --filter @kaelio/ktx run test:slow
cli-smoke-tests:
name: CLI smoke tests

View file

@ -89,7 +89,7 @@ pnpm run type-check
pnpm run test
pnpm run check
pnpm run dead-code
pnpm --filter @ktx/cli run smoke
pnpm --filter @kaelio/ktx run smoke
pnpm --filter './packages/*' run build
pnpm --filter './packages/*' run test
pnpm --filter './packages/*' run type-check

View file

@ -63,7 +63,7 @@ pnpm run build
This builds the TypeScript package. You can also build the package directly:
```bash
pnpm --filter @ktx/cli run build
pnpm --filter @kaelio/ktx run build
```
### Link the CLI for local testing
@ -110,16 +110,16 @@ resolution. The Python projects use `pyproject.toml` for dependency management.
pnpm run test
# Run tests for the TypeScript package
pnpm --filter @ktx/cli run test
pnpm --filter @kaelio/ktx run test
# Type-check all packages
pnpm run type-check
# Type-check the TypeScript package
pnpm --filter @ktx/cli run type-check
pnpm --filter @kaelio/ktx run type-check
# CLI smoke test
pnpm --filter @ktx/cli run smoke
pnpm --filter @kaelio/ktx run smoke
```
### Python
@ -204,9 +204,9 @@ it. Keep runtime loading dynamic when the driver is optional.
### Step 5: Test
```bash
pnpm --filter @ktx/cli run build
pnpm --filter @ktx/cli run type-check
pnpm --filter @ktx/cli run test
pnpm --filter @kaelio/ktx run build
pnpm --filter @kaelio/ktx run type-check
pnpm --filter @kaelio/ktx run test
```
Use `packages/cli/src/connectors/sqlite/` as a minimal reference and

View file

@ -58,7 +58,7 @@ Create a project and enable query history:
```bash
export WAREHOUSE_DATABASE_URL=postgresql://ktx_reader:ktx_reader@127.0.0.1:55432/analytics # pragma: allowlist secret
pnpm --filter @ktx/cli run build
pnpm --filter @kaelio/ktx run build
node packages/cli/dist/bin.js --project-dir /tmp/ktx-postgres-historic setup \
--new \
--skip-agents \

View file

@ -198,8 +198,7 @@ NODE
}
cd "$KTX_ROOT"
pnpm --filter @ktx/context run build
pnpm --filter @ktx/cli run build
pnpm --filter @kaelio/ktx run build
docker compose -f "$COMPOSE_FILE" up -d --wait
"$EXAMPLE_DIR/scripts/generate-workload.sh" base

View file

@ -40,12 +40,12 @@
"semantic-release": "semantic-release",
"semantic-release:debug": "semantic-release --dry-run --debug",
"semantic-release:dry-run": "semantic-release --dry-run --no-ci",
"smoke": "pnpm run build && pnpm --filter @ktx/cli run smoke",
"smoke": "pnpm run build && pnpm --filter @kaelio/ktx run smoke",
"test": "node --test scripts/*.test.mjs && pnpm --filter './packages/*' run test",
"test:coverage": "pnpm run test:coverage:ts && pnpm run test:coverage:py",
"test:coverage:py": "uv run pytest --cov=python/ktx-sl/semantic_layer --cov=python/ktx-daemon/src/ktx_daemon --cov-report=xml:coverage/python.xml --cov-report=term",
"test:coverage:ts": "pnpm --filter './packages/*' run build && pnpm --filter './packages/*' run test --coverage --coverage.reporter=lcov --coverage.exclude='dist/**' && node scripts/normalize-lcov-paths.mjs packages/*/coverage/lcov.info",
"test:slow": "pnpm --filter @ktx/cli run test:slow",
"test:slow": "pnpm --filter @kaelio/ktx run test:slow",
"type-check": "pnpm --filter './packages/*' run type-check"
},
"devDependencies": {

View file

@ -1,8 +1,7 @@
{
"name": "@ktx/cli",
"name": "@kaelio/ktx",
"version": "0.4.1",
"description": "Standalone ktx context layer CLI",
"private": true,
"description": "Standalone ktx context layer for database agents",
"type": "module",
"engines": {
"node": ">=22.0.0"
@ -24,6 +23,10 @@
"dist",
"assets"
],
"publishConfig": {
"access": "public",
"provenance": true
},
"scripts": {
"assets:demo": "node scripts/build-demo-assets.mjs",
"build": "tsc -p tsconfig.json && node scripts/copy-runtime-assets.mjs && node ../../scripts/prepare-cli-bin.mjs",
@ -89,11 +92,11 @@
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/kaelio/ktx.git",
"url": "https://github.com/Kaelio/ktx",
"directory": "packages/cli"
},
"bugs": {
"url": "https://github.com/kaelio/ktx/issues"
"url": "https://github.com/Kaelio/ktx/issues"
},
"homepage": "https://github.com/kaelio/ktx#readme"
"homepage": "https://github.com/Kaelio/ktx#readme"
}

View file

@ -5,7 +5,7 @@ import { describe, expect, it, vi } from 'vitest';
import { renderReindexJson, renderReindexPlain, reindexHasErrors } from './admin-reindex.js';
import { runKtxCli } from './index.js';
const cliVersion = (createRequire(import.meta.url)('@ktx/cli/package.json') as { version: string })
const cliVersion = (createRequire(import.meta.url)('@kaelio/ktx/package.json') as { version: string })
.version;
function makeIo(options: { stdoutIsTTY?: boolean } = {}) {

View file

@ -12,7 +12,7 @@ function stubIo(): KtxCliIo {
function stubPackageInfo(): KtxCliPackageInfo {
return {
name: '@ktx/cli',
name: '@kaelio/ktx',
version: '0.0.0-test',
};
}

View file

@ -11,7 +11,7 @@ function makeContext(overrides: Partial<KtxCliCommandContext> = {}): KtxCliComma
stderr: { write: vi.fn() },
},
deps: {},
packageInfo: { name: '@ktx/cli', version: '0.0.0-test' },
packageInfo: { name: '@kaelio/ktx', version: '0.0.0-test' },
setExitCode: (code) => {
exitCode = code;
},

View file

@ -11,7 +11,7 @@ function makeContext(overrides: Partial<KtxCliCommandContext> = {}): KtxCliComma
stderr: { write: vi.fn() },
},
deps: {},
packageInfo: { name: '@ktx/cli', version: '0.0.0-test' },
packageInfo: { name: '@kaelio/ktx', version: '0.0.0-test' },
setExitCode: (code) => {
exitCode = code;
},

View file

@ -106,7 +106,7 @@ describe('runSetupDoctorChecks', () => {
if (command === 'pnpm' && args[0] === '--version') return '10.28.0';
if (command === 'corepack' && args[0] === '--version') return '0.32.0';
if (command === 'uv' && args[0] === '--version') return 'uv 0.9.5';
if (command === process.execPath && args.includes('--version')) return '@ktx/cli 0.0.0-private';
if (command === process.execPath && args.includes('--version')) return '@kaelio/ktx 0.0.0-private';
throw new Error(`${command} ${args.join(' ')}`);
},
pathExists: async () => true,
@ -163,7 +163,7 @@ describe('runSetupDoctorChecks', () => {
if (command === 'pnpm' && args[0] === '--version') return '10.28.0';
if (command === 'corepack' && args[0] === '--version') throw new Error('spawn corepack ENOENT');
if (command === 'uv' && args[0] === '--version') return 'uv 0.9.5';
if (command === process.execPath && args.includes('--version')) return '@ktx/cli 0.0.0-private';
if (command === process.execPath && args.includes('--version')) return '@kaelio/ktx 0.0.0-private';
throw new Error(`${command} ${args.join(' ')}`);
},
pathExists: async () => true,

View file

@ -19,7 +19,7 @@ import {
const require = createRequire(import.meta.url);
const cliPackageJson = require('@ktx/cli/package.json') as { name: string; version: string };
const cliPackageJson = require('@kaelio/ktx/package.json') as { name: string; version: string };
const cliVersion = cliPackageJson.version;
function makeIo(options: { stdoutIsTty?: boolean } = {}) {
@ -47,16 +47,16 @@ function makeIo(options: { stdoutIsTty?: boolean } = {}) {
describe('getKtxCliPackageInfo', () => {
it('identifies the CLI package', () => {
expect(getKtxCliPackageInfo()).toEqual({
name: '@ktx/cli',
name: '@kaelio/ktx',
version: cliVersion,
});
});
it('exports package metadata for package managers and runtime diagnostics', () => {
const packageJson = require('@ktx/cli/package.json') as { name: string; version: string };
const packageJson = require('@kaelio/ktx/package.json') as { name: string; version: string };
expect(packageJson).toMatchObject({
name: '@ktx/cli',
name: '@kaelio/ktx',
version: cliVersion,
});
expect(cliVersion).toMatch(/^\d+\.\d+\.\d+/);
@ -116,7 +116,7 @@ describe('runKtxCli', () => {
await expect(runKtxCli(['--version'], testIo.io)).resolves.toBe(0);
expect(testIo.stdout()).toBe(`@ktx/cli ${cliVersion}\n`);
expect(testIo.stdout()).toBe(`@kaelio/ktx ${cliVersion}\n`);
expect(testIo.stderr()).toBe('');
});

View file

@ -12,7 +12,7 @@ function silentIo(): KtxCliIo {
function stubPackageInfo(): KtxCliPackageInfo {
return {
name: '@ktx/cli',
name: '@kaelio/ktx',
version: '0.0.0-docs',
};
}

View file

@ -4,7 +4,7 @@
It supports portable compute in two modes:
- One-shot commands, used by default by `@ktx/context`.
- One-shot commands, used by default by the `@kaelio/ktx` CLI.
- An explicit HTTP server for long-running local MCP sessions.
## One-shot semantic query

View file

@ -1,156 +0,0 @@
#!/usr/bin/env node
import { execFile } from 'node:child_process';
import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { promisify } from 'node:util';
import {
PUBLIC_NPM_PACKAGE_NAME,
publicNpmPackageVersion,
} from './public-npm-release-metadata.mjs';
const execFileAsync = promisify(execFile);
export const PUBLIC_NPM_PACKAGE_VERSION = publicNpmPackageVersion();
export { PUBLIC_NPM_PACKAGE_NAME };
export function publicNpmPackageTarballName(version = PUBLIC_NPM_PACKAGE_VERSION) {
return `kaelio-ktx-${version}.tgz`;
}
function scriptRootDir() {
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
}
export function publicNpmPackageLayout(rootDir = scriptRootDir(), version = PUBLIC_NPM_PACKAGE_VERSION) {
return {
rootDir,
packageVersion: version,
cliPackageRoot: join(rootDir, 'packages', 'cli'),
packRoot: join(rootDir, 'dist', 'public-npm-package'),
npmDir: join(rootDir, 'dist', 'artifacts', 'npm'),
tarballPath: join(rootDir, 'dist', 'artifacts', 'npm', publicNpmPackageTarballName(version)),
};
}
async function readJson(path) {
return JSON.parse(await readFile(path, 'utf8'));
}
async function writeJson(path, value) {
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
}
function sortedObject(entries) {
return Object.fromEntries([...entries].sort(([left], [right]) => left.localeCompare(right)));
}
function isWorkspacePackageName(name) {
return name.startsWith('@ktx/');
}
export function collectPublicDependencies(cliPackageJson) {
return sortedObject(
Object.entries(cliPackageJson.dependencies ?? {}).filter(([name]) => !isWorkspacePackageName(name)),
);
}
export function publicNpmPackageJson(cliPackageJson, dependencies, version = PUBLIC_NPM_PACKAGE_VERSION) {
return {
name: PUBLIC_NPM_PACKAGE_NAME,
version,
description: 'Standalone KTX context layer for database agents',
private: false,
type: 'module',
engines: cliPackageJson.engines ?? { node: '>=22.0.0' },
bin: { ktx: './dist/bin.js' },
main: cliPackageJson.main ?? 'dist/index.js',
types: cliPackageJson.types ?? 'dist/index.d.ts',
exports: cliPackageJson.exports ?? {
'.': {
types: './dist/index.d.ts',
import: './dist/index.js',
default: './dist/index.js',
},
'./package.json': './package.json',
},
files: ['dist', 'assets'],
dependencies,
license: cliPackageJson.license ?? 'Apache-2.0',
repository: {
type: 'git',
url: 'https://github.com/Kaelio/ktx',
},
bugs: {
url: 'https://github.com/Kaelio/ktx/issues',
},
homepage: 'https://github.com/Kaelio/ktx#readme',
};
}
async function copyPackageFileEntries(sourceRoot, targetRoot, packageJson) {
for (const entry of packageJson.files ?? ['dist']) {
await cp(join(sourceRoot, entry), join(targetRoot, entry), {
recursive: true,
force: true,
});
}
}
async function copyCliPackage(layout, cliPackageJson, dependencies) {
await copyPackageFileEntries(layout.cliPackageRoot, layout.packRoot, cliPackageJson);
await writeJson(
join(layout.packRoot, 'package.json'),
publicNpmPackageJson(cliPackageJson, dependencies, layout.packageVersion),
);
}
export async function createPublicNpmPackageTree(layout = publicNpmPackageLayout()) {
const cliPackageJson = await readJson(join(layout.cliPackageRoot, 'package.json'));
const dependencies = collectPublicDependencies(cliPackageJson);
await rm(layout.packRoot, { recursive: true, force: true });
await mkdir(layout.packRoot, { recursive: true });
await mkdir(layout.npmDir, { recursive: true });
await copyCliPackage(layout, cliPackageJson, dependencies);
return {
layout,
packageJson: publicNpmPackageJson(cliPackageJson, dependencies, layout.packageVersion),
};
}
export function publicNpmPackCommand(layout = publicNpmPackageLayout()) {
return {
command: 'pnpm',
args: ['--config.node-linker=hoisted', 'pack', '--out', layout.tarballPath],
cwd: layout.packRoot,
};
}
export async function buildPublicNpmPackage(layout = publicNpmPackageLayout()) {
await createPublicNpmPackageTree(layout);
const pack = publicNpmPackCommand(layout);
await execFileAsync(pack.command, pack.args, {
cwd: pack.cwd,
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
});
return layout.tarballPath;
}
async function main() {
const tarball = await buildPublicNpmPackage();
process.stdout.write(`Built ${PUBLIC_NPM_PACKAGE_NAME} package: ${tarball}\n`);
}
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
try {
await main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\n`);
process.exitCode = 1;
}
}

View file

@ -1,187 +0,0 @@
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, it } from 'node:test';
import {
PUBLIC_NPM_PACKAGE_NAME,
PUBLIC_NPM_PACKAGE_VERSION,
collectPublicDependencies,
createPublicNpmPackageTree,
publicNpmPackageJson,
publicNpmPackageLayout,
publicNpmPackageTarballName,
publicNpmPackCommand,
} from './build-public-npm-package.mjs';
async function writeJson(path, value) {
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
}
async function writePackage(root, packageRoot, packageJson, files = {}) {
const absoluteRoot = join(root, packageRoot);
await mkdir(absoluteRoot, { recursive: true });
await writeJson(join(absoluteRoot, 'package.json'), packageJson);
for (const [relativePath, contents] of Object.entries(files)) {
const target = join(absoluteRoot, relativePath);
await mkdir(join(target, '..'), { recursive: true });
await writeFile(target, contents);
}
}
async function writeWorkspaceFixture(root) {
await writePackage(
root,
'packages/cli',
{
name: '@ktx/cli',
version: '0.0.0-private',
description: 'CLI wrapper for KTX',
type: 'module',
engines: { node: '>=22.0.0' },
bin: { ktx: './dist/bin.js' },
main: 'dist/index.js',
types: 'dist/index.d.ts',
exports: {
'.': {
types: './dist/index.d.ts',
import: './dist/index.js',
default: './dist/index.js',
},
'./package.json': './package.json',
},
files: ['dist', 'assets'],
dependencies: {
'@clack/prompts': '1.3.0',
ai: '^6.0.168',
commander: '14.0.3',
yaml: '^2.8.2',
},
license: 'Apache-2.0',
repository: {
type: 'git',
url: 'git+https://github.com/kaelio/ktx.git',
directory: 'packages/cli',
},
},
{
'dist/bin.js': '#!/usr/bin/env node\n',
'dist/index.js': 'export const cli = true;\n',
'dist/index.d.ts': 'export declare const cli: true;\n',
'assets/python/manifest.json': '{"schemaVersion":1}\n',
},
);
}
describe('publicNpmPackageLayout', () => {
it('uses the public npm release version for the tarball name', () => {
const layout = publicNpmPackageLayout('/repo/ktx');
assert.match(PUBLIC_NPM_PACKAGE_VERSION, /^\d+\.\d+\.\d+/);
assert.equal(publicNpmPackageTarballName(), `kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`);
assert.equal(
layout.tarballPath,
`/repo/ktx/dist/artifacts/npm/kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`,
);
});
});
describe('collectPublicDependencies', () => {
it('returns CLI external runtime dependencies and omits workspace packages', () => {
assert.deepEqual(
collectPublicDependencies({
name: '@ktx/cli',
dependencies: {
'@ktx/internal-only': 'workspace:*',
commander: '14.0.3',
zod: '^4.4.3',
},
}),
{
commander: '14.0.3',
zod: '^4.4.3',
},
);
});
});
describe('publicNpmPackageJson', () => {
it('describes the public @kaelio/ktx binary package', () => {
const packageJson = publicNpmPackageJson(
{
name: '@ktx/cli',
version: '0.0.0-private',
engines: { node: '>=22.0.0' },
bin: { ktx: './dist/bin.js' },
main: 'dist/index.js',
types: 'dist/index.d.ts',
exports: { '.': './dist/index.js', './package.json': './package.json' },
license: 'Apache-2.0',
},
{ commander: '14.0.3' },
);
assert.equal(packageJson.name, PUBLIC_NPM_PACKAGE_NAME);
assert.equal(packageJson.version, PUBLIC_NPM_PACKAGE_VERSION);
assert.equal(packageJson.private, false);
assert.deepEqual(packageJson.bin, { ktx: './dist/bin.js' });
assert.deepEqual(packageJson.dependencies, { commander: '14.0.3' });
assert.deepEqual(packageJson.files, ['dist', 'assets']);
assert.deepEqual(packageJson.repository, {
type: 'git',
url: 'https://github.com/Kaelio/ktx',
});
assert.deepEqual(packageJson.bugs, {
url: 'https://github.com/Kaelio/ktx/issues',
});
assert.equal(packageJson.homepage, 'https://github.com/Kaelio/ktx#readme');
});
});
describe('createPublicNpmPackageTree', () => {
it('copies CLI files and assets without bundled internal workspace packages', async () => {
const root = await mkdtemp(join(tmpdir(), 'ktx-public-npm-test-'));
try {
await writeWorkspaceFixture(root);
const layout = publicNpmPackageLayout(root);
const result = await createPublicNpmPackageTree(layout);
assert.equal(result.packageJson.name, '@kaelio/ktx');
assert.equal(result.packageJson.dependencies.commander, '14.0.3');
assert.equal(result.packageJson.dependencies.yaml, '^2.8.2');
assert.equal(result.packageJson.dependencies.ai, '^6.0.168');
assert.equal(
await readFile(join(layout.packRoot, 'assets', 'python', 'manifest.json'), 'utf8'),
'{"schemaVersion":1}\n',
);
await assert.rejects(
() => readFile(join(layout.packRoot, 'node_modules', '@ktx', 'context', 'package.json'), 'utf8'),
/ENOENT/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe('publicNpmPackCommand', () => {
it('packs the assembled public package with pnpm', () => {
const layout = publicNpmPackageLayout('/repo/ktx');
assert.deepEqual(publicNpmPackCommand(layout), {
command: 'pnpm',
args: [
'--config.node-linker=hoisted',
'pack',
'--out',
`/repo/ktx/dist/artifacts/npm/kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`,
],
cwd: '/repo/ktx/dist/public-npm-package',
});
});
});

View file

@ -10,7 +10,7 @@ const identifierSkipPrefixes = ['docs/', 'docs-site/', 'examples/', 'python/ktx-
const identifierAllowPatterns = [
/^packages\/cli\/src\/(?:index|managed-local-embeddings|managed-python-command|managed-python-daemon|managed-python-runtime|release-version|runtime)(?:\.test)?\.ts$/,
/^python\/ktx-daemon\/src\/ktx_daemon\/__init__\.py$/,
/^scripts\/(?:build-public-npm-package|build-python-runtime-wheel|local-embeddings-runtime-smoke|package-artifacts|public-npm-release-metadata|published-package-smoke|release-readiness)(?:\.test)?\.mjs$/,
/^scripts\/(?:build-python-runtime-wheel|local-embeddings-runtime-smoke|package-artifacts|public-npm-release-metadata|published-package-smoke|release-readiness)(?:\.test)?\.mjs$/,
/^scripts\/semantic-release-config\.cjs$/,
];
const forbiddenIdentifierTerms = ['kae' + 'lio', 'Kae' + 'lio', 'KAE' + 'LIO_'];

View file

@ -13,7 +13,7 @@ test('linkDevCli writes a ktx-dev launcher by default', async () => {
execText: async (command, args) => {
assert.equal(command, 'ktx-dev');
assert.deepEqual(args, ['--version']);
return '@ktx/cli 0.0.0-private';
return '@kaelio/ktx 0.0.0-private';
},
writeFile: async (path, content) => writes.push({ path, content }),
chmod: async (path, mode) => chmods.push({ path, mode }),
@ -34,7 +34,7 @@ test('linkDevCli can explicitly write ktx when requested', async () => {
binaryName: 'ktx',
globalBin: '/pnpm/bin',
binPath: '/workspace/ktx/packages/cli/dist/bin.js',
execText: async () => '@ktx/cli 0.0.0-private',
execText: async () => '@kaelio/ktx 0.0.0-private',
writeFile: async (path, content) => writes.push({ path, content }),
chmod: async () => undefined,
access: async () => undefined,

View file

@ -8,7 +8,7 @@ import { promisify } from 'node:util';
import {
PUBLIC_NPM_PACKAGE_NAME,
PUBLIC_NPM_PACKAGE_VERSION,
} from './build-public-npm-package.mjs';
} from './public-npm-release-metadata.mjs';
import { npmSmokePnpmWorkspaceYaml } from './package-artifacts.mjs';
const execFileAsync = promisify(execFile);

View file

@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { describe, it } from 'node:test';
import { PUBLIC_NPM_PACKAGE_VERSION } from './build-public-npm-package.mjs';
import { PUBLIC_NPM_PACKAGE_VERSION } from './public-npm-release-metadata.mjs';
import {
buildLocalEmbeddingsSmokeEnv,
expectedPublicKtxVersionPattern,

View file

@ -15,8 +15,8 @@ import {
import {
PUBLIC_NPM_PACKAGE_NAME,
publicNpmPackageTarballName,
} from './build-public-npm-package.mjs';
import { publicNpmPackageVersion } from './public-npm-release-metadata.mjs';
publicNpmPackageVersion,
} from './public-npm-release-metadata.mjs';
export {
RUNTIME_WHEEL_DISTRIBUTION_NAME,
@ -24,10 +24,6 @@ export {
RUNTIME_WHEEL_PACKAGE_VERSION,
};
export const INTERNAL_NPM_WORKSPACE_PACKAGES = [
{ name: '@ktx/cli', packageRoot: 'packages/cli' },
];
export const NPM_ARTIFACT_PACKAGES = [{ name: PUBLIC_NPM_PACKAGE_NAME, packageRoot: 'packages/cli' }];
export const CLI_PYTHON_ASSET_MANIFEST = 'manifest.json';
@ -72,30 +68,22 @@ export function packageArtifactLayout(rootDir = scriptRootDir(), version = publi
}
export function buildArtifactCommands(layout) {
const npmBuildCommand = {
command: 'pnpm',
args: [
'--filter',
'@ktx/cli',
'run',
'build',
],
cwd: layout.rootDir,
};
const publicPackageCommand = {
command: process.execPath,
args: ['scripts/build-public-npm-package.mjs'],
cwd: layout.rootDir,
};
return [
npmBuildCommand,
{
command: 'pnpm',
args: ['--filter', PUBLIC_NPM_PACKAGE_NAME, 'run', 'build'],
cwd: layout.rootDir,
},
{
command: process.execPath,
args: ['scripts/build-python-runtime-wheel.mjs'],
cwd: layout.rootDir,
},
publicPackageCommand,
{
command: 'pnpm',
args: ['pack', '--out', layout.cliTarball],
cwd: join(layout.rootDir, 'packages', 'cli'),
},
];
}
@ -163,19 +151,17 @@ function releaseMetadataEntry({ ecosystem, packageName, packageRoot, packageVers
async function readNpmPackageMetadata(rootDir, packageInfo, version) {
const packageJson = await readJson(join(rootDir, packageInfo.packageRoot, 'package.json'));
const expectedSourceName = packageInfo.name === PUBLIC_NPM_PACKAGE_NAME ? '@ktx/cli' : packageInfo.name;
if (packageJson.name !== expectedSourceName) {
if (packageJson.name !== packageInfo.name) {
throw new Error(
`Unexpected package name in ${packageInfo.packageRoot}/package.json: expected ${expectedSourceName}, got ${packageJson.name}`,
`Unexpected package name in ${packageInfo.packageRoot}/package.json: expected ${packageInfo.name}, got ${packageJson.name}`,
);
}
const isPublicKtxPackage = packageInfo.name === PUBLIC_NPM_PACKAGE_NAME;
return releaseMetadataEntry({
ecosystem: 'npm',
packageName: packageInfo.name,
packageRoot: packageInfo.packageRoot,
packageVersion: isPublicKtxPackage ? version : packageJson.version,
privatePackage: isPublicKtxPackage ? false : packageJson.private === true,
packageVersion: version,
privatePackage: false,
});
}
@ -922,13 +908,13 @@ async function buildArtifacts(layout) {
await mkdir(layout.npmDir, { recursive: true });
await mkdir(layout.pythonDir, { recursive: true });
const [npmBuildCommand, wheelCommand, publicPackageCommand] = buildArtifactCommands(layout);
const [npmBuildCommand, wheelCommand, packCommand] = buildArtifactCommands(layout);
await runCommand(npmBuildCommand.command, npmBuildCommand.args, { cwd: npmBuildCommand.cwd });
await runCommand(wheelCommand.command, wheelCommand.args, { cwd: wheelCommand.cwd });
const pythonArtifacts = await findPythonArtifacts(layout.pythonDir);
await copyRuntimeWheelAssets(layout, pythonArtifacts);
await runCommand(publicPackageCommand.command, publicPackageCommand.args, { cwd: publicPackageCommand.cwd });
await runCommand(packCommand.command, packCommand.args, { cwd: packCommand.cwd });
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
await assertPathExists(layout.npmTarballs[packageInfo.name], `${packageInfo.name} tarball`);

View file

@ -5,10 +5,9 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, it } from 'node:test';
import { PUBLIC_NPM_PACKAGE_VERSION } from './build-public-npm-package.mjs';
import { PUBLIC_NPM_PACKAGE_VERSION } from './public-npm-release-metadata.mjs';
import {
CLI_PYTHON_ASSET_MANIFEST,
INTERNAL_NPM_WORKSPACE_PACKAGES,
RUNTIME_WHEEL_DISTRIBUTION_NAME,
RUNTIME_WHEEL_NORMALIZED_NAME,
RUNTIME_WHEEL_PACKAGE_VERSION,
@ -62,12 +61,11 @@ async function writeReleaseMetadataInputs(root) {
requiredBeforePublishing: ['Choose public release version.'],
});
for (const packageInfo of INTERNAL_NPM_WORKSPACE_PACKAGES) {
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
await mkdir(join(root, packageInfo.packageRoot), { recursive: true });
await writeJson(join(root, packageInfo.packageRoot, 'package.json'), {
name: packageInfo.name,
version: '0.0.0-private',
private: true,
version: PUBLIC_NPM_PACKAGE_VERSION,
});
}
}
@ -112,16 +110,20 @@ describe('packageArtifactLayout', () => {
});
describe('buildArtifactCommands', () => {
it('builds the CLI package, then the runtime wheel, then packs npm artifacts', () => {
it('builds the CLI package, then the runtime wheel, then packs the npm tarball directly', () => {
const layout = packageArtifactLayout('/repo/ktx', PUBLIC_NPM_PACKAGE_VERSION);
const commands = buildArtifactCommands(layout);
assert.deepEqual(
commands.map((command) => [command.command, command.args]),
commands.map((command) => [command.command, command.args, command.cwd]),
[
['pnpm', ['--filter', '@ktx/cli', 'run', 'build']],
[process.execPath, ['scripts/build-python-runtime-wheel.mjs']],
[process.execPath, ['scripts/build-public-npm-package.mjs']],
['pnpm', ['--filter', '@kaelio/ktx', 'run', 'build'], '/repo/ktx'],
[process.execPath, ['scripts/build-python-runtime-wheel.mjs'], '/repo/ktx'],
[
'pnpm',
['pack', '--out', `/repo/ktx/dist/artifacts/npm/kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`],
'/repo/ktx/packages/cli',
],
],
);
});

View file

@ -8,6 +8,10 @@ export const PUBLIC_NPM_PACKAGE_NAME = '@kaelio/ktx';
export const PUBLIC_NPM_RELEASE_TAGS = new Set(['latest', 'next']);
export const PUBLIC_NPM_BRANCH_RELEASE_TAG_PATTERN = /^branch-[a-z0-9]+(?:-[a-z0-9]+)*$/;
export function publicNpmPackageTarballName(version) {
return `kaelio-ktx-${version}.tgz`;
}
const SEMVER_PATTERN =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
const SEMVER_PARTS_PATTERN =
@ -82,6 +86,8 @@ export function publicNpmPackageVersion(rootDir = scriptRootDir()) {
return readPublicNpmReleaseMetadata(rootDir).version;
}
export const PUBLIC_NPM_PACKAGE_VERSION = publicNpmPackageVersion();
export function publicPythonRuntimePackageVersion(rootDir = scriptRootDir()) {
return publicNpmPackageVersionToPythonVersion(publicNpmPackageVersion(rootDir));
}

View file

@ -105,7 +105,7 @@ describe('published package smoke config', () => {
/Invalid KTX_PUBLISHED_KTX_PACKAGE/,
);
assert.throws(
() => readPublishedPackageSmokeConfig({ KTX_PUBLISHED_KTX_PACKAGE: '@ktx/cli public' }, []),
() => readPublishedPackageSmokeConfig({ KTX_PUBLISHED_KTX_PACKAGE: '@kaelio/ktx public' }, []),
/Invalid KTX_PUBLISHED_KTX_PACKAGE/,
);
assert.throws(
@ -114,7 +114,7 @@ describe('published package smoke config', () => {
{},
[],
{
packageName: '@ktx/cli public',
packageName: '@kaelio/ktx public',
version: 'latest',
registry: null,
},

View file

@ -228,20 +228,17 @@ function assertNonPublishingArtifactPolicy(policy, metadata, publicPackageVersio
if (entry.releaseMode !== CI_ARTIFACT_ONLY_RELEASE_MODE) {
throw new Error(`Package ${entry.packageName} releaseMode must remain ci-artifact-only`);
}
if (entry.ecosystem === 'npm') {
const isPublicKtxPackage = entry.packageName === '@kaelio/ktx';
if (isPublicKtxPackage) {
if (entry.private !== false) {
throw new Error(`${policyLabel} npm package @kaelio/ktx must be publishable when npm.publish is false`);
}
if (entry.packageVersion !== publicPackageVersion) {
throw new Error(`${policyLabel} npm package @kaelio/ktx must use public version ${publicPackageVersion}`);
}
} else if (entry.private !== true) {
throw new Error(`${policyLabel} npm package ${entry.packageName} must remain private`);
} else if (!entry.packageVersion.endsWith('-private')) {
throw new Error(`${policyLabel} npm package ${entry.packageName} must use a private version suffix`);
}
if (entry.ecosystem !== 'npm') {
continue;
}
if (entry.packageName !== '@kaelio/ktx') {
throw new Error(`${policyLabel} unexpected npm package ${entry.packageName}`);
}
if (entry.private !== false) {
throw new Error(`${policyLabel} npm package @kaelio/ktx must be publishable when npm.publish is false`);
}
if (entry.packageVersion !== publicPackageVersion) {
throw new Error(`${policyLabel} npm package @kaelio/ktx must use public version ${publicPackageVersion}`);
}
}
}

View file

@ -5,12 +5,11 @@ import { join } from 'node:path';
import { describe, it } from 'node:test';
import {
INTERNAL_NPM_WORKSPACE_PACKAGES,
NPM_ARTIFACT_PACKAGES,
packageArtifactLayout,
writeArtifactManifest,
} from './package-artifacts.mjs';
import { PUBLIC_NPM_PACKAGE_VERSION } from './build-public-npm-package.mjs';
import { PUBLIC_NPM_PACKAGE_VERSION } from './public-npm-release-metadata.mjs';
import { RUNTIME_WHEEL_PACKAGE_VERSION } from './build-python-runtime-wheel.mjs';
import { readReleasePolicy, releasePolicyPath, releaseReadinessReport } from './release-readiness.mjs';
@ -19,12 +18,11 @@ async function writeJson(path, value) {
}
async function writeReleaseMetadataInputs(root) {
for (const packageInfo of INTERNAL_NPM_WORKSPACE_PACKAGES) {
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
await mkdir(join(root, packageInfo.packageRoot), { recursive: true });
await writeJson(join(root, packageInfo.packageRoot, 'package.json'), {
name: packageInfo.name,
version: '0.0.0-private',
private: true,
version: PUBLIC_NPM_PACKAGE_VERSION,
});
}
}
@ -528,7 +526,7 @@ describe('release readiness policy', () => {
await writeReadyFixture(root, {
policy: releasePolicy({
publishedPackageSmoke: {
packageName: '@ktx/cli public',
packageName: '@kaelio/ktx public',
version: 'latest',
registry: null,
},

View file

@ -40,7 +40,7 @@ test('runWorkspaceKtx runs the built CLI when it already exists', async () => {
readdir: fs.readdir,
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd });
return { stdout: '@ktx/cli 0.0.0-private\n', stderr: '' };
return { stdout: '@kaelio/ktx 0.0.0-private\n', stderr: '' };
},
stdout: { write: (chunk) => logs.push(['stdout', chunk]) },
stderr: { write: (chunk) => logs.push(['stderr', chunk]) },
@ -54,7 +54,7 @@ test('runWorkspaceKtx runs the built CLI when it already exists', async () => {
cwd: '/workspace/ktx',
},
]);
assert.deepEqual(logs, [['stdout', '@ktx/cli 0.0.0-private\n']]);
assert.deepEqual(logs, [['stdout', '@kaelio/ktx 0.0.0-private\n']]);
});
test('runWorkspaceKtx forwards a caller-provided environment to buffered commands', async () => {
@ -69,7 +69,7 @@ test('runWorkspaceKtx forwards a caller-provided environment to buffered command
env: { PATH: '/bin', GIT_CEILING_DIRECTORIES: '/workspace/ktx/examples' },
execFile: async (command, args, options) => {
calls.push({ command, args, cwd: options.cwd, env: options.env });
return { stdout: '@ktx/cli 0.0.0-private\n', stderr: '' };
return { stdout: '@kaelio/ktx 0.0.0-private\n', stderr: '' };
},
stdout: { write: () => undefined },
stderr: { write: () => undefined },

View file

@ -40,7 +40,7 @@ describe('standalone KTX CI workflow', () => {
'pnpm install --frozen-lockfile',
'pnpm run check',
'pnpm run build',
'pnpm --filter @ktx/cli run test:slow',
'pnpm --filter @kaelio/ktx run test:slow',
'pnpm run smoke',
'actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405',
'python-version: "3.13"',

View file

@ -60,7 +60,7 @@ describe('test tiering', () => {
it('provides explicit slow package test scripts for CI', async () => {
const rootPackage = await readJson('../package.json');
const cliPackage = await readJson('../packages/cli/package.json');
assert.equal(rootPackage.scripts['test:slow'], 'pnpm --filter @ktx/cli run test:slow');
assert.equal(rootPackage.scripts['test:slow'], 'pnpm --filter @kaelio/ktx run test:slow');
assertScriptContainsAll(cliPackage.scripts['test:slow'], cliSlowTests);
assertScriptContainsAll(cliPackage.scripts['test:slow'], contextSlowTests);
assert.doesNotMatch(cliPackage.scripts['test:slow'], /relationship-benchmarks\.test\.ts/);

View file

@ -22,9 +22,8 @@ async function writeReleaseFixture(root) {
private: true,
});
await writeJson(join(root, 'packages', 'cli', 'package.json'), {
name: '@ktx/cli',
name: '@kaelio/ktx',
version: '0.0.0-private',
private: true,
});
await writeJson(join(root, 'release-policy.json'), {
schemaVersion: 1,