mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-13 08:15:14 +02:00
rename klo to ktx
This commit is contained in:
parent
1a42152e6f
commit
3ce510b55b
704 changed files with 10205 additions and 10255 deletions
|
|
@ -6,7 +6,7 @@ import { describe, it } from 'node:test';
|
|||
import { acquirePublicBenchmarkFixtures } from './acquire-public-benchmark-fixtures.mjs';
|
||||
|
||||
function tempRoot() {
|
||||
return mkdtempSync(path.join(tmpdir(), 'klo-acquire-'));
|
||||
return mkdtempSync(path.join(tmpdir(), 'ktx-acquire-'));
|
||||
}
|
||||
|
||||
function writeManifest(dir, fixtures) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
|
|||
import { readdir, readFile } from 'node:fs/promises';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
const KLO_ROOT = new URL('../', import.meta.url);
|
||||
const KTX_ROOT = new URL('../', import.meta.url);
|
||||
|
||||
const RELATIONSHIP_RUNTIME_SOURCES = Object.freeze([
|
||||
'packages/context/src/scan/relationship-benchmarks.ts',
|
||||
|
|
@ -19,7 +19,7 @@ const RELATIONSHIP_RUNTIME_SOURCES = Object.freeze([
|
|||
]);
|
||||
|
||||
async function checkedInFixtureIds() {
|
||||
const fixtureRoot = new URL('packages/context/test/fixtures/relationship-benchmarks/', KLO_ROOT);
|
||||
const fixtureRoot = new URL('packages/context/test/fixtures/relationship-benchmarks/', KTX_ROOT);
|
||||
const entries = await readdir(fixtureRoot, { withFileTypes: true });
|
||||
return entries
|
||||
.filter((entry) => entry.isDirectory())
|
||||
|
|
@ -31,7 +31,7 @@ async function readRuntimeSources() {
|
|||
return Promise.all(
|
||||
RELATIONSHIP_RUNTIME_SOURCES.map(async (relativePath) => ({
|
||||
relativePath,
|
||||
source: await readFile(new URL(relativePath, KLO_ROOT), 'utf8'),
|
||||
source: await readFile(new URL(relativePath, KTX_ROOT), 'utf8'),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,16 +216,16 @@ export async function buildAdventureWorksOltpFixture(input) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const url = process.env.KLO_ADVENTUREWORKS_SQLSERVER_URL;
|
||||
const url = process.env.KTX_ADVENTUREWORKS_SQLSERVER_URL;
|
||||
if (!url) {
|
||||
throw new Error(
|
||||
'Set KLO_ADVENTUREWORKS_SQLSERVER_URL to a read-only SQL Server URL for a full AdventureWorks OLTP database before running this script.',
|
||||
'Set KTX_ADVENTUREWORKS_SQLSERVER_URL to a read-only SQL Server URL for a full AdventureWorks OLTP database before running this script.',
|
||||
);
|
||||
}
|
||||
|
||||
const source = JSON.parse(readFileSync(path.join(scriptDir, 'adventureworks-oltp-source.json'), 'utf8'));
|
||||
const { KloSqlServerScanConnector } = await import('../packages/connector-sqlserver/dist/index.js');
|
||||
const connector = new KloSqlServerScanConnector({
|
||||
const { KtxSqlServerScanConnector } = await import('../packages/connector-sqlserver/dist/index.js');
|
||||
const connector = new KtxSqlServerScanConnector({
|
||||
connectionId: fixtureId,
|
||||
connection: {
|
||||
driver: 'sqlserver',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const require = createRequire(new URL('../packages/context/package.json', import
|
|||
const Database = require('better-sqlite3');
|
||||
|
||||
describe('buildBenchmarkSnapshot', () => {
|
||||
it('emits a KloSchemaSnapshot-shaped object plus expected-links from declared FKs', () => {
|
||||
it('emits a KtxSchemaSnapshot-shaped object plus expected-links from declared FKs', () => {
|
||||
const db = new Database(':memory:');
|
||||
db.exec(`
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import { gzipSync } from 'node:zlib';
|
|||
import { buildBenchmarkSnapshot, writeFixtureFiles } from './build-benchmark-snapshot.mjs';
|
||||
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const kloRoot = path.resolve(scriptDir, '..');
|
||||
const fixtureRoot = path.join(kloRoot, 'packages', 'context', 'test', 'fixtures', 'relationship-benchmarks');
|
||||
const ktxRoot = path.resolve(scriptDir, '..');
|
||||
const fixtureRoot = path.join(ktxRoot, 'packages', 'context', 'test', 'fixtures', 'relationship-benchmarks');
|
||||
const require = createRequire(new URL('../packages/context/package.json', import.meta.url));
|
||||
const Database = require('better-sqlite3');
|
||||
const { stringify: yamlStringify } = require('yaml');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|||
|
||||
const codeExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py']);
|
||||
const runtimeAssetPatterns = [/^packages\/[^/]+\/prompts\/.+\.md$/, /^packages\/[^/]+\/skills\/.+\.md$/];
|
||||
const identifierSkipPrefixes = ['docs/', 'examples/', 'python/klo-sl/plans/', 'python/klo-sl/openspec/'];
|
||||
const identifierSkipPrefixes = ['docs/', 'examples/', 'python/ktx-sl/plans/', 'python/ktx-sl/openspec/'];
|
||||
const forbiddenIdentifierTerms = ['kae' + 'lio', 'Kae' + 'lio', 'KAE' + 'LIO_'];
|
||||
|
||||
const appImportPatterns = [
|
||||
|
|
@ -47,7 +47,7 @@ const llmBoundaryPatterns = [
|
|||
},
|
||||
{
|
||||
label: 'legacy scan LLM provider port',
|
||||
pattern: /\bKloScanLlmPort\b/,
|
||||
pattern: /\bKtxScanLlmPort\b/,
|
||||
},
|
||||
{
|
||||
label: 'legacy gateway LLM provider helper',
|
||||
|
|
@ -120,7 +120,7 @@ export function scanFileContent(relativePath, content) {
|
|||
violations.push({
|
||||
file: normalizedPath,
|
||||
kind: 'llm-boundary',
|
||||
message: `Forbidden ${llmBoundaryPattern.label}; use @klo/llm`,
|
||||
message: `Forbidden ${llmBoundaryPattern.label}; use @ktx/llm`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ export function scanFileContent(relativePath, content) {
|
|||
violations.push({
|
||||
file: normalizedPath,
|
||||
kind: 'llm-boundary',
|
||||
message: `Forbidden ${llmBoundaryPattern.label}; use getModel(role) inside @klo/context`,
|
||||
message: `Forbidden ${llmBoundaryPattern.label}; use getModel(role) inside @ktx/context`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -197,7 +197,7 @@ async function main() {
|
|||
const violations = await collectViolations(rootDir);
|
||||
|
||||
if (violations.length === 0) {
|
||||
process.stdout.write('klo boundary check passed\n');
|
||||
process.stdout.write('ktx boundary check passed\n');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,22 +61,22 @@ describe('scanFileContent', () => {
|
|||
|
||||
assert.equal(scanFileContent('docs/transition.md', name).length, 0);
|
||||
assert.equal(scanFileContent('examples/transition.md', name).length, 0);
|
||||
assert.equal(scanFileContent('python/klo-sl/plans/brainstorm.md', name).length, 0);
|
||||
assert.equal(scanFileContent('python/klo-sl/openspec/specs/semantic-layer/spec.md', name).length, 0);
|
||||
assert.equal(scanFileContent('python/ktx-sl/plans/brainstorm.md', name).length, 0);
|
||||
assert.equal(scanFileContent('python/ktx-sl/openspec/specs/semantic-layer/spec.md', name).length, 0);
|
||||
});
|
||||
|
||||
it('allows clean source files and clean runtime prompt assets', () => {
|
||||
assert.deepEqual(
|
||||
scanFileContent('packages/context/src/index.ts', "export const packageName = '@klo/context';"),
|
||||
scanFileContent('packages/context/src/index.ts', "export const packageName = '@ktx/context';"),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(
|
||||
scanFileContent('packages/context/prompts/memory_agent_bundle_ingest_work_unit.md', 'Write output for KLO.'),
|
||||
scanFileContent('packages/context/prompts/memory_agent_bundle_ingest_work_unit.md', 'Write output for KTX.'),
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects context-owned LLM provider construction after @klo/llm migration', () => {
|
||||
it('rejects context-owned LLM provider construction after @ktx/llm migration', () => {
|
||||
const violations = [
|
||||
...scanFileContent(
|
||||
'packages/context/src/agent/local-llm-provider.ts',
|
||||
|
|
@ -92,10 +92,10 @@ describe('scanFileContent', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('rejects old KLO LLM port declarations in context', () => {
|
||||
it('rejects old KTX LLM port declarations in context', () => {
|
||||
const violations = [
|
||||
...scanFileContent('packages/context/src/agent/agent-runner.service.ts', 'export interface LlmProviderPort {}'),
|
||||
...scanFileContent('packages/context/src/scan/types.ts', 'export interface KloScanLlmPort {}'),
|
||||
...scanFileContent('packages/context/src/scan/types.ts', 'export interface KtxScanLlmPort {}'),
|
||||
...scanFileContent('packages/context/src/agent/gateway-llm-provider.ts', 'export function createGatewayLlmProvider() {}'),
|
||||
];
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ describe('scanFileContent', () => {
|
|||
assert.equal(violations[0]?.kind, 'llm-boundary');
|
||||
assert.equal(
|
||||
violations[0]?.message,
|
||||
'Forbidden context getModelByName call; use getModel(role) inside @klo/context',
|
||||
'Forbidden context getModelByName call; use getModel(role) inside @ktx/context',
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ async function readCiWorkflowOrSkip(testContext) {
|
|||
await access(ciWorkflowPath);
|
||||
} catch (error) {
|
||||
if (error && error.code === 'ENOENT') {
|
||||
testContext.skip('root CI workflow is absent from sparse klo checkout');
|
||||
testContext.skip('root CI workflow is absent from sparse ktx checkout');
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
|
|
@ -20,8 +20,8 @@ async function readCiWorkflowOrSkip(testContext) {
|
|||
return readFile(ciWorkflowPath, 'utf-8');
|
||||
}
|
||||
|
||||
describe('KLO CI artifact upload contract', () => {
|
||||
it('uploads verified KLO package artifacts from check-klo-subtree', async (testContext) => {
|
||||
describe('KTX CI artifact upload contract', () => {
|
||||
it('uploads verified KTX package artifacts from check-ktx-subtree', async (testContext) => {
|
||||
const workflow = await readCiWorkflowOrSkip(testContext);
|
||||
if (workflow === null) {
|
||||
return;
|
||||
|
|
@ -29,14 +29,14 @@ describe('KLO CI artifact upload contract', () => {
|
|||
|
||||
assert.match(
|
||||
workflow,
|
||||
/name: Build klo package artifacts and verify public smoke\s+run: cd klo && pnpm run artifacts:build && pnpm run artifacts:verify-manifest && pnpm run artifacts:verify-demo\s+- name: Upload klo package artifacts/s,
|
||||
/name: Build ktx package artifacts and verify public smoke\s+run: cd ktx && pnpm run artifacts:build && pnpm run artifacts:verify-manifest && pnpm run artifacts:verify-demo\s+- name: Upload ktx package artifacts/s,
|
||||
);
|
||||
assert.match(workflow, /uses: actions\/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f/);
|
||||
assert.match(workflow, /name: klo-package-artifacts-\$\{\{ github\.sha \}\}/);
|
||||
assert.match(workflow, /klo\/dist\/artifacts\/manifest\.json/);
|
||||
assert.match(workflow, /klo\/dist\/artifacts\/npm\/\*\.tgz/);
|
||||
assert.match(workflow, /klo\/dist\/artifacts\/python\/\*\.whl/);
|
||||
assert.match(workflow, /klo\/dist\/artifacts\/python\/\*\.tar\.gz/);
|
||||
assert.match(workflow, /name: ktx-package-artifacts-\$\{\{ github\.sha \}\}/);
|
||||
assert.match(workflow, /ktx\/dist\/artifacts\/manifest\.json/);
|
||||
assert.match(workflow, /ktx\/dist\/artifacts\/npm\/\*\.tgz/);
|
||||
assert.match(workflow, /ktx\/dist\/artifacts\/python\/\*\.whl/);
|
||||
assert.match(workflow, /ktx\/dist\/artifacts\/python\/\*\.tar\.gz/);
|
||||
assert.match(workflow, /if-no-files-found: error/);
|
||||
assert.match(workflow, /retention-days: 7/);
|
||||
});
|
||||
|
|
@ -47,11 +47,11 @@ describe('KLO CI artifact upload contract', () => {
|
|||
return;
|
||||
}
|
||||
|
||||
assert.match(workflow, /check-klo-packed-demo:/);
|
||||
assert.match(workflow, /check-ktx-packed-demo:/);
|
||||
assert.match(workflow, /matrix:\s+os: \[ubuntu-latest, macos-latest\]/s);
|
||||
assert.match(workflow, /name: Download klo package artifacts/);
|
||||
assert.match(workflow, /path: klo\/dist\/artifacts/);
|
||||
assert.match(workflow, /run: cd klo && pnpm run artifacts:verify-demo/);
|
||||
assert.match(workflow, /name: Download ktx package artifacts/);
|
||||
assert.match(workflow, /path: ktx\/dist\/artifacts/);
|
||||
assert.match(workflow, /run: cd ktx && pnpm run artifacts:verify-demo/);
|
||||
});
|
||||
|
||||
it('includes packed demo artifact smoke in ci-success', async (testContext) => {
|
||||
|
|
@ -62,9 +62,9 @@ describe('KLO CI artifact upload contract', () => {
|
|||
|
||||
assert.match(
|
||||
workflow,
|
||||
/needs: \[check-klo-subtree, check-klo-packed-demo, build-python-service, test-server, build-frontend, run-pre-commit, build-docker-images\]/,
|
||||
/needs: \[check-ktx-subtree, check-ktx-packed-demo, build-python-service, test-server, build-frontend, run-pre-commit, build-docker-images\]/,
|
||||
);
|
||||
assert.match(workflow, /needs\.check-klo-packed-demo\.result.*== "failure"/);
|
||||
assert.match(workflow, /needs\.check-klo-packed-demo\.result.*== "cancelled"/);
|
||||
assert.match(workflow, /needs\.check-ktx-packed-demo\.result.*== "failure"/);
|
||||
assert.match(workflow, /needs\.check-ktx-packed-demo\.result.*== "cancelled"/);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ describe('standalone example docs', () => {
|
|||
it('documents the Orbit relationship verification example project', async () => {
|
||||
const examples = await readText('examples/README.md');
|
||||
const readme = await readText('examples/orbit-relationship-verification/README.md');
|
||||
const config = await readText('examples/orbit-relationship-verification/klo.yaml');
|
||||
const config = await readText('examples/orbit-relationship-verification/ktx.yaml');
|
||||
|
||||
assert.match(examples, /orbit-relationship-verification/);
|
||||
assert.match(examples, /relationships:verify-orbit/);
|
||||
|
|
@ -51,14 +51,14 @@ describe('standalone example docs', () => {
|
|||
assert.match(examples, /pg_stat_statements/);
|
||||
assert.match(readme, /--enable-historic-sql/);
|
||||
assert.match(readme, /--historic-sql-min-calls 2/);
|
||||
assert.match(readme, /klo dev doctor --project-dir/);
|
||||
assert.match(readme, /ktx dev doctor --project-dir/);
|
||||
assert.match(readme, /Postgres Historic SQL/);
|
||||
assert.match(readme, /dev ingest run/);
|
||||
assert.match(compose, /postgres:14/);
|
||||
assert.match(compose, /shared_preload_libraries=pg_stat_statements/);
|
||||
assert.match(compose, /pg_stat_statements.track=top/);
|
||||
assert.match(initSql, /CREATE EXTENSION IF NOT EXISTS pg_stat_statements/);
|
||||
assert.match(initSql, /GRANT pg_read_all_stats TO klo_reader/);
|
||||
assert.match(initSql, /GRANT pg_read_all_stats TO ktx_reader/);
|
||||
assert.match(workload, /JOIN customers/);
|
||||
assert.match(workload, /app_user/);
|
||||
assert.match(workload, /etl_user/);
|
||||
|
|
@ -81,8 +81,8 @@ describe('standalone example docs', () => {
|
|||
assert.match(rootReadme, /`packages\/connector-snowflake`/);
|
||||
assert.match(rootReadme, /`packages\/connector-sqlite`/);
|
||||
assert.match(rootReadme, /`packages\/connector-sqlserver`/);
|
||||
assert.match(rootReadme, /`python\/klo-sl`/);
|
||||
assert.match(rootReadme, /`python\/klo-daemon`/);
|
||||
assert.match(rootReadme, /`python\/ktx-sl`/);
|
||||
assert.match(rootReadme, /`python\/ktx-daemon`/);
|
||||
});
|
||||
|
||||
it('documents every standalone MCP tool that the CLI server exposes', async () => {
|
||||
|
|
@ -103,7 +103,7 @@ describe('standalone example docs', () => {
|
|||
assert.match(rootReadme, /`ingest_replay`/);
|
||||
});
|
||||
|
||||
it('walks through klo connection list and klo connection test in the README quickstart', async () => {
|
||||
it('walks through ktx connection list and ktx connection test in the README quickstart', async () => {
|
||||
const rootReadme = await readText('README.md');
|
||||
|
||||
assert.match(rootReadme, /connection list --project-dir/);
|
||||
|
|
@ -112,7 +112,7 @@ describe('standalone example docs', () => {
|
|||
assert.match(rootReadme, /Tables: 1/);
|
||||
});
|
||||
|
||||
it('replaces the fake-ingest smoke with a klo scan walkthrough in the README', async () => {
|
||||
it('replaces the fake-ingest smoke with a ktx scan walkthrough in the README', async () => {
|
||||
const rootReadme = await readText('README.md');
|
||||
|
||||
assert.match(rootReadme, /### Scan the demo warehouse/);
|
||||
|
|
@ -121,15 +121,15 @@ describe('standalone example docs', () => {
|
|||
assert.match(rootReadme, /scan report --project-dir/);
|
||||
assert.match(rootReadme, /raw-sources\/warehouse\/live-database/);
|
||||
assert.doesNotMatch(rootReadme, /Run a local ingest smoke test/);
|
||||
assert.doesNotMatch(rootReadme, /klo dev ingest run --project-dir/);
|
||||
assert.doesNotMatch(rootReadme, /klo ingest status --project-dir/);
|
||||
assert.doesNotMatch(rootReadme, /ktx dev ingest run --project-dir/);
|
||||
assert.doesNotMatch(rootReadme, /ktx ingest status --project-dir/);
|
||||
});
|
||||
|
||||
it('documents pnpm setup as a prerequisite when optional dev linking fails', async () => {
|
||||
const rootReadme = await readText('README.md');
|
||||
|
||||
assert.match(rootReadme, /pnpm run link:dev/);
|
||||
assert.match(rootReadme, /klo-dev --help/);
|
||||
assert.match(rootReadme, /ktx-dev --help/);
|
||||
assert.doesNotMatch(
|
||||
rootReadme,
|
||||
/If the setup command reports that pnpm's global bin directory is not on your\n`PATH`, add the printed directory to your shell profile/,
|
||||
|
|
@ -144,7 +144,7 @@ describe('standalone example docs', () => {
|
|||
});
|
||||
|
||||
it('documents daemon HTTP database, source generation, LookML, embedding, and code execution support', async () => {
|
||||
const readme = await readText('python/klo-daemon/README.md');
|
||||
const readme = await readText('python/ktx-daemon/README.md');
|
||||
|
||||
assert.match(readme, /semantic-generate-sources/);
|
||||
assert.match(readme, /database-introspect/);
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import {
|
|||
pythonArtifactInstallArgs,
|
||||
} from './package-artifacts.mjs';
|
||||
|
||||
const POSTGRES_IMAGE = process.env.KLO_ARTIFACT_POSTGRES_IMAGE ?? 'postgres:16-alpine';
|
||||
const POSTGRES_USER = 'klo';
|
||||
const POSTGRES_IMAGE = process.env.KTX_ARTIFACT_POSTGRES_IMAGE ?? 'postgres:16-alpine';
|
||||
const POSTGRES_USER = 'ktx';
|
||||
const POSTGRES_PASSWORD = 'postgres'; // pragma: allowlist secret
|
||||
const POSTGRES_DB = 'warehouse';
|
||||
|
||||
export function smokeContainerName(pid = process.pid, now = Date.now()) {
|
||||
return `klo-live-db-smoke-${pid}-${now}`;
|
||||
return `ktx-live-db-smoke-${pid}-${now}`;
|
||||
}
|
||||
|
||||
export function buildPostgresUrl(hostPort) {
|
||||
|
|
@ -88,7 +88,7 @@ export function buildSeedSql() {
|
|||
].join('\n');
|
||||
}
|
||||
|
||||
export function buildKloYaml(postgresUrl) {
|
||||
export function buildKtxYaml(postgresUrl) {
|
||||
return [
|
||||
'project: artifact-live-database',
|
||||
'connections:',
|
||||
|
|
@ -109,7 +109,7 @@ export function buildKloYaml(postgresUrl) {
|
|||
export function buildLiveDatabaseIngestArgs(projectDir, databaseIntrospectionUrl) {
|
||||
return [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'dev',
|
||||
'ingest',
|
||||
'run',
|
||||
|
|
@ -125,7 +125,7 @@ export function buildLiveDatabaseIngestArgs(projectDir, databaseIntrospectionUrl
|
|||
}
|
||||
|
||||
export function buildLiveDatabaseStatusArgs(projectDir, runId) {
|
||||
return ['exec', 'klo', 'ingest', 'status', '--project-dir', projectDir, runId];
|
||||
return ['exec', 'ktx', 'ingest', 'status', '--project-dir', projectDir, runId];
|
||||
}
|
||||
|
||||
async function run(command, args, options = {}) {
|
||||
|
|
@ -283,12 +283,12 @@ async function waitForHttpHealth(url, daemon) {
|
|||
if (daemon.error()) {
|
||||
const output = daemon.output();
|
||||
throw new Error(
|
||||
`Failed to start klo-daemon: ${daemon.error().message}\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`,
|
||||
`Failed to start ktx-daemon: ${daemon.error().message}\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`,
|
||||
);
|
||||
}
|
||||
if (daemon.child.exitCode !== null || daemon.child.signalCode !== null) {
|
||||
const output = daemon.output();
|
||||
throw new Error(`klo-daemon exited before health check passed\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`);
|
||||
throw new Error(`ktx-daemon exited before health check passed\nstdout:\n${output.stdout}\nstderr:\n${output.stderr}`);
|
||||
}
|
||||
try {
|
||||
if (await httpGetOk(url)) {
|
||||
|
|
@ -306,7 +306,7 @@ async function waitForHttpHealth(url, daemon) {
|
|||
|
||||
async function startDaemon(port, cleanInstallDir) {
|
||||
const daemon = spawnLogged(
|
||||
'klo-daemon',
|
||||
'ktx-daemon',
|
||||
['serve-http', '--host', '127.0.0.1', '--port', String(port), '--log-level', 'warning'],
|
||||
{ cwd: cleanInstallDir, env: npmSmokePythonEnv(cleanInstallDir) },
|
||||
);
|
||||
|
|
@ -337,8 +337,8 @@ async function assertPathExists(path, label) {
|
|||
|
||||
async function prepareCleanInstall(layout, cleanInstallDir) {
|
||||
const pythonArtifacts = await findPythonArtifacts(layout.pythonDir);
|
||||
await assertPathExists(layout.contextTarball, '@klo/context tarball');
|
||||
await assertPathExists(layout.cliTarball, '@klo/cli tarball');
|
||||
await assertPathExists(layout.contextTarball, '@ktx/context tarball');
|
||||
await assertPathExists(layout.cliTarball, '@ktx/cli tarball');
|
||||
await mkdir(cleanInstallDir, { recursive: true });
|
||||
await writeFile(join(cleanInstallDir, 'package.json'), `${JSON.stringify(npmSmokePackageJson(layout), null, 2)}\n`);
|
||||
await run('pnpm', ['install'], { cwd: cleanInstallDir, timeout: 120_000 }).then((result) =>
|
||||
|
|
@ -362,7 +362,7 @@ async function prepareCleanInstall(layout, cleanInstallDir) {
|
|||
|
||||
async function main() {
|
||||
const layout = packageArtifactLayout();
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-live-db-artifact-smoke-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-live-db-artifact-smoke-'));
|
||||
const containerName = smokeContainerName();
|
||||
let daemon;
|
||||
try {
|
||||
|
|
@ -379,12 +379,12 @@ async function main() {
|
|||
await prepareCleanInstall(layout, cleanInstallDir);
|
||||
|
||||
await mkdir(projectDir, { recursive: true });
|
||||
const init = await run('pnpm', ['exec', 'klo', 'init', projectDir, '--name', 'artifact-live-database'], {
|
||||
const init = await run('pnpm', ['exec', 'ktx', 'init', projectDir, '--name', 'artifact-live-database'], {
|
||||
cwd: cleanInstallDir,
|
||||
timeout: 30_000,
|
||||
});
|
||||
requireSuccess('klo init', init);
|
||||
await writeFile(join(projectDir, 'klo.yaml'), buildKloYaml(postgresUrl), 'utf8');
|
||||
requireSuccess('ktx init', init);
|
||||
await writeFile(join(projectDir, 'ktx.yaml'), buildKtxYaml(postgresUrl), 'utf8');
|
||||
|
||||
daemon = await startDaemon(daemonPort, cleanInstallDir);
|
||||
|
||||
|
|
@ -393,12 +393,12 @@ async function main() {
|
|||
env: npmSmokePythonEnv(cleanInstallDir),
|
||||
timeout: 120_000,
|
||||
});
|
||||
requireSuccess('klo dev ingest run live-database', ingestRun);
|
||||
requireOutput('klo dev ingest run live-database', ingestRun, /Status: done/);
|
||||
requireOutput('klo dev ingest run live-database', ingestRun, /Adapter: live-database/);
|
||||
requireOutput('klo dev ingest run live-database', ingestRun, /Diff: \+4\/~0\/-0\/=0/);
|
||||
requireOutput('klo dev ingest run live-database', ingestRun, /Raw files: 4/);
|
||||
requireOutput('klo dev ingest run live-database', ingestRun, /Work units: 2/);
|
||||
requireSuccess('ktx dev ingest run live-database', ingestRun);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Status: done/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Adapter: live-database/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Diff: \+4\/~0\/-0\/=0/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Raw files: 4/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Work units: 2/);
|
||||
|
||||
const runId = getRunId(ingestRun.stdout);
|
||||
const ingestStatus = await run('pnpm', buildLiveDatabaseStatusArgs(projectDir, runId), {
|
||||
|
|
@ -406,12 +406,12 @@ async function main() {
|
|||
env: npmSmokePythonEnv(cleanInstallDir),
|
||||
timeout: 30_000,
|
||||
});
|
||||
requireSuccess('klo ingest status live-database', ingestStatus);
|
||||
requireOutput('klo ingest status live-database', ingestStatus, new RegExp(`Run: ${runId}`));
|
||||
requireOutput('klo ingest status live-database', ingestStatus, /Status: done/);
|
||||
requireOutput('klo ingest status live-database', ingestStatus, /Raw files: 4/);
|
||||
requireOutput('klo ingest status live-database', ingestStatus, /Work units: 2/);
|
||||
await assertPathExists(join(projectDir, '.klo', 'db.sqlite'), 'SQLite local ingest state');
|
||||
requireSuccess('ktx ingest status live-database', ingestStatus);
|
||||
requireOutput('ktx ingest status live-database', ingestStatus, new RegExp(`Run: ${runId}`));
|
||||
requireOutput('ktx ingest status live-database', ingestStatus, /Status: done/);
|
||||
requireOutput('ktx ingest status live-database', ingestStatus, /Raw files: 4/);
|
||||
requireOutput('ktx ingest status live-database', ingestStatus, /Work units: 2/);
|
||||
await assertPathExists(join(projectDir, '.ktx', 'db.sqlite'), 'SQLite local ingest state');
|
||||
process.stdout.write(`Installed live-database artifact smoke passed: ${runId}\n`);
|
||||
} finally {
|
||||
if (daemon) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { describe, it } from 'node:test';
|
|||
|
||||
import {
|
||||
buildDockerRunArgs,
|
||||
buildKloYaml,
|
||||
buildKtxYaml,
|
||||
buildLiveDatabaseIngestArgs,
|
||||
buildLiveDatabaseStatusArgs,
|
||||
buildPostgresUrl,
|
||||
|
|
@ -16,7 +16,7 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
it('builds a deterministic disposable Postgres container command', () => {
|
||||
assert.deepEqual(
|
||||
buildDockerRunArgs({
|
||||
containerName: 'klo-live-db-smoke-test',
|
||||
containerName: 'ktx-live-db-smoke-test',
|
||||
hostPort: 15432,
|
||||
image: 'postgres:16-alpine',
|
||||
}),
|
||||
|
|
@ -25,11 +25,11 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
'--rm',
|
||||
'-d',
|
||||
'--name',
|
||||
'klo-live-db-smoke-test',
|
||||
'ktx-live-db-smoke-test',
|
||||
'-e',
|
||||
'POSTGRES_PASSWORD=postgres', // pragma: allowlist secret
|
||||
'-e',
|
||||
'POSTGRES_USER=klo',
|
||||
'POSTGRES_USER=ktx',
|
||||
'-e',
|
||||
'POSTGRES_DB=warehouse',
|
||||
'-p',
|
||||
|
|
@ -40,25 +40,25 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
});
|
||||
|
||||
it('uses a collision-resistant Docker container name prefix', () => {
|
||||
assert.match(smokeContainerName(1234, 5678), /^klo-live-db-smoke-1234-5678$/);
|
||||
assert.match(smokeContainerName(1234, 5678), /^ktx-live-db-smoke-1234-5678$/);
|
||||
});
|
||||
|
||||
it('builds the Postgres URL used by klo.yaml and daemon introspection', () => {
|
||||
it('builds the Postgres URL used by ktx.yaml and daemon introspection', () => {
|
||||
assert.equal(
|
||||
buildPostgresUrl(15432),
|
||||
'postgresql://klo:postgres@127.0.0.1:15432/warehouse', // pragma: allowlist secret
|
||||
'postgresql://ktx:postgres@127.0.0.1:15432/warehouse', // pragma: allowlist secret
|
||||
);
|
||||
});
|
||||
|
||||
it('writes a live-database-only KLO project config with SQLite local state', () => {
|
||||
it('writes a live-database-only KTX project config with SQLite local state', () => {
|
||||
assert.equal(
|
||||
buildKloYaml('postgresql://klo:postgres@127.0.0.1:15432/warehouse'), // pragma: allowlist secret
|
||||
buildKtxYaml('postgresql://ktx:postgres@127.0.0.1:15432/warehouse'), // pragma: allowlist secret
|
||||
[
|
||||
'project: artifact-live-database',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
' url: "postgresql://klo:postgres@127.0.0.1:15432/warehouse"', // pragma: allowlist secret
|
||||
' url: "postgresql://ktx:postgres@127.0.0.1:15432/warehouse"', // pragma: allowlist secret
|
||||
' readonly: true',
|
||||
'storage:',
|
||||
' state: sqlite',
|
||||
|
|
@ -83,12 +83,12 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
});
|
||||
|
||||
it('waits for a real SQL connection to the target Postgres database', () => {
|
||||
assert.deepEqual(buildPostgresReadyArgs('klo-live-db-smoke-test'), [
|
||||
assert.deepEqual(buildPostgresReadyArgs('ktx-live-db-smoke-test'), [
|
||||
'exec',
|
||||
'klo-live-db-smoke-test',
|
||||
'ktx-live-db-smoke-test',
|
||||
'psql',
|
||||
'-U',
|
||||
'klo',
|
||||
'ktx',
|
||||
'-d',
|
||||
'warehouse',
|
||||
'-v',
|
||||
|
|
@ -101,7 +101,7 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
it('builds installed CLI live-database ingest and status commands', () => {
|
||||
assert.deepEqual(buildLiveDatabaseIngestArgs('/tmp/project', 'http://127.0.0.1:8765'), [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'dev',
|
||||
'ingest',
|
||||
'run',
|
||||
|
|
@ -117,7 +117,7 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
|
||||
assert.deepEqual(buildLiveDatabaseStatusArgs('/tmp/project', 'local-run-1'), [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'ingest',
|
||||
'status',
|
||||
'--project-dir',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { access as fsAccess, chmod as fsChmod, writeFile as fsWriteFile } from '
|
|||
import { delimiter, join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { promisify } from 'node:util';
|
||||
import { ensureCliBinExecutable, kloRootDir } from './prepare-cli-bin.mjs';
|
||||
import { ensureCliBinExecutable, ktxRootDir } from './prepare-cli-bin.mjs';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ async function writePinnedPosixLauncher(globalBin, binPath, binaryName, writeFil
|
|||
const launcherPath = join(globalBin, binaryName);
|
||||
const script = [
|
||||
'#!/bin/sh',
|
||||
'# Generated by `pnpm run link:dev` in the KLO workspace.',
|
||||
'# Generated by `pnpm run link:dev` in the KTX workspace.',
|
||||
'# Keep this launcher pinned to the Node binary that built native dependencies.',
|
||||
`exec ${shellDoubleQuote(process.execPath)} ${shellDoubleQuote(binPath)} "$@"`,
|
||||
'',
|
||||
|
|
@ -88,7 +88,7 @@ async function writePinnedWindowsLauncher(globalBin, binPath, binaryName, writeF
|
|||
const launcherPath = join(globalBin, `${binaryName}.cmd`);
|
||||
const script = [
|
||||
'@echo off',
|
||||
'REM Generated by `pnpm run link:dev` in the KLO workspace.',
|
||||
'REM Generated by `pnpm run link:dev` in the KTX workspace.',
|
||||
`"${process.execPath}" "${binPath}" %*`,
|
||||
'',
|
||||
].join('\r\n');
|
||||
|
|
@ -134,8 +134,8 @@ async function assertBuiltCli(rootDir, access, binPathOverride) {
|
|||
}
|
||||
|
||||
export async function linkDevCli(options = {}) {
|
||||
const rootDir = options.rootDir ?? kloRootDir();
|
||||
const binaryName = options.binaryName ?? 'klo-dev';
|
||||
const rootDir = options.rootDir ?? ktxRootDir();
|
||||
const binaryName = options.binaryName ?? 'ktx-dev';
|
||||
const access = options.access ?? fsAccess;
|
||||
const chmod = options.chmod ?? fsChmod;
|
||||
const writeFile = options.writeFile ?? fsWriteFile;
|
||||
|
|
@ -177,9 +177,9 @@ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|||
try {
|
||||
const result = await linkDevCli({
|
||||
checkOnly: hasFlag('--check-only'),
|
||||
binaryName: optionValue('--name', 'klo-dev'),
|
||||
binaryName: optionValue('--name', 'ktx-dev'),
|
||||
});
|
||||
process.stdout.write(`KLO CLI bin: ${result.binPath}\n`);
|
||||
process.stdout.write(`KTX CLI bin: ${result.binPath}\n`);
|
||||
if (result.linked) {
|
||||
process.stdout.write(`Linked binary: ${result.binaryName}\n`);
|
||||
process.stdout.write(`Verified: ${result.verification.output}\n`);
|
||||
|
|
|
|||
|
|
@ -2,44 +2,44 @@ import assert from 'node:assert/strict';
|
|||
import { test } from 'node:test';
|
||||
import { linkDevCli } from './link-dev-cli.mjs';
|
||||
|
||||
test('linkDevCli writes a klo-dev launcher by default', async () => {
|
||||
test('linkDevCli writes a ktx-dev launcher by default', async () => {
|
||||
const writes = [];
|
||||
const chmods = [];
|
||||
|
||||
const result = await linkDevCli({
|
||||
rootDir: '/workspace/klo',
|
||||
rootDir: '/workspace/ktx',
|
||||
globalBin: '/pnpm/bin',
|
||||
binPath: '/workspace/klo/packages/cli/dist/bin.js',
|
||||
binPath: '/workspace/ktx/packages/cli/dist/bin.js',
|
||||
execText: async (command, args) => {
|
||||
assert.equal(command, 'klo-dev');
|
||||
assert.equal(command, 'ktx-dev');
|
||||
assert.deepEqual(args, ['--version']);
|
||||
return '@klo/cli 0.0.0-private';
|
||||
return '@ktx/cli 0.0.0-private';
|
||||
},
|
||||
writeFile: async (path, content) => writes.push({ path, content }),
|
||||
chmod: async (path, mode) => chmods.push({ path, mode }),
|
||||
access: async () => undefined,
|
||||
});
|
||||
|
||||
assert.equal(result.binaryName, 'klo-dev');
|
||||
assert.equal(writes[0].path, '/pnpm/bin/klo-dev');
|
||||
assert.equal(result.binaryName, 'ktx-dev');
|
||||
assert.equal(writes[0].path, '/pnpm/bin/ktx-dev');
|
||||
assert.match(writes[0].content, /packages\/cli\/dist\/bin.js/);
|
||||
assert.deepEqual(chmods, [{ path: '/pnpm/bin/klo-dev', mode: 0o755 }]);
|
||||
assert.deepEqual(chmods, [{ path: '/pnpm/bin/ktx-dev', mode: 0o755 }]);
|
||||
});
|
||||
|
||||
test('linkDevCli can explicitly write klo when requested', async () => {
|
||||
test('linkDevCli can explicitly write ktx when requested', async () => {
|
||||
const writes = [];
|
||||
|
||||
const result = await linkDevCli({
|
||||
rootDir: '/workspace/klo',
|
||||
binaryName: 'klo',
|
||||
rootDir: '/workspace/ktx',
|
||||
binaryName: 'ktx',
|
||||
globalBin: '/pnpm/bin',
|
||||
binPath: '/workspace/klo/packages/cli/dist/bin.js',
|
||||
execText: async () => '@klo/cli 0.0.0-private',
|
||||
binPath: '/workspace/ktx/packages/cli/dist/bin.js',
|
||||
execText: async () => '@ktx/cli 0.0.0-private',
|
||||
writeFile: async (path, content) => writes.push({ path, content }),
|
||||
chmod: async () => undefined,
|
||||
access: async () => undefined,
|
||||
});
|
||||
|
||||
assert.equal(result.binaryName, 'klo');
|
||||
assert.equal(writes[0].path, '/pnpm/bin/klo');
|
||||
assert.equal(result.binaryName, 'ktx');
|
||||
assert.equal(writes[0].path, '/pnpm/bin/ktx');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,22 +11,22 @@ const PACKAGE_VERSION = '0.0.0-private';
|
|||
const PYTHON_PACKAGE_VERSION = '0.1.0';
|
||||
|
||||
export const NPM_ARTIFACT_PACKAGES = [
|
||||
{ name: '@klo/context', packageRoot: 'packages/context' },
|
||||
{ name: '@klo/llm', packageRoot: 'packages/llm' },
|
||||
{ name: '@klo/connector-bigquery', packageRoot: 'packages/connector-bigquery' },
|
||||
{ name: '@klo/connector-clickhouse', packageRoot: 'packages/connector-clickhouse' },
|
||||
{ name: '@klo/connector-mysql', packageRoot: 'packages/connector-mysql' },
|
||||
{ name: '@klo/connector-postgres', packageRoot: 'packages/connector-postgres' },
|
||||
{ name: '@klo/connector-posthog', packageRoot: 'packages/connector-posthog' },
|
||||
{ name: '@klo/connector-snowflake', packageRoot: 'packages/connector-snowflake' },
|
||||
{ name: '@klo/connector-sqlite', packageRoot: 'packages/connector-sqlite' },
|
||||
{ name: '@klo/connector-sqlserver', packageRoot: 'packages/connector-sqlserver' },
|
||||
{ name: '@klo/cli', packageRoot: 'packages/cli' },
|
||||
{ name: '@ktx/context', packageRoot: 'packages/context' },
|
||||
{ name: '@ktx/llm', packageRoot: 'packages/llm' },
|
||||
{ name: '@ktx/connector-bigquery', packageRoot: 'packages/connector-bigquery' },
|
||||
{ name: '@ktx/connector-clickhouse', packageRoot: 'packages/connector-clickhouse' },
|
||||
{ name: '@ktx/connector-mysql', packageRoot: 'packages/connector-mysql' },
|
||||
{ name: '@ktx/connector-postgres', packageRoot: 'packages/connector-postgres' },
|
||||
{ name: '@ktx/connector-posthog', packageRoot: 'packages/connector-posthog' },
|
||||
{ name: '@ktx/connector-snowflake', packageRoot: 'packages/connector-snowflake' },
|
||||
{ name: '@ktx/connector-sqlite', packageRoot: 'packages/connector-sqlite' },
|
||||
{ name: '@ktx/connector-sqlserver', packageRoot: 'packages/connector-sqlserver' },
|
||||
{ name: '@ktx/cli', packageRoot: 'packages/cli' },
|
||||
];
|
||||
|
||||
const CONNECTOR_PACKAGE_NAMES = NPM_ARTIFACT_PACKAGES
|
||||
.map((packageInfo) => packageInfo.name)
|
||||
.filter((packageName) => packageName.startsWith('@klo/connector-'));
|
||||
.filter((packageName) => packageName.startsWith('@ktx/connector-'));
|
||||
|
||||
const ordersSource = {
|
||||
name: 'orders',
|
||||
|
|
@ -46,7 +46,7 @@ function scriptRootDir() {
|
|||
}
|
||||
|
||||
function npmPackageTarballName(packageName) {
|
||||
return `${packageName.replace('@klo/', 'klo-')}-${PACKAGE_VERSION}.tgz`;
|
||||
return `${packageName.replace('@ktx/', 'ktx-')}-${PACKAGE_VERSION}.tgz`;
|
||||
}
|
||||
|
||||
function npmPackageTarballs(npmDir) {
|
||||
|
|
@ -67,8 +67,8 @@ export function packageArtifactLayout(rootDir = scriptRootDir()) {
|
|||
npmDir,
|
||||
pythonDir,
|
||||
npmTarballs,
|
||||
contextTarball: npmTarballs['@klo/context'],
|
||||
cliTarball: npmTarballs['@klo/cli'],
|
||||
contextTarball: npmTarballs['@ktx/context'],
|
||||
cliTarball: npmTarballs['@ktx/cli'],
|
||||
connectorTarballs: Object.fromEntries(
|
||||
CONNECTOR_PACKAGE_NAMES.map((packageName) => [packageName, npmTarballs[packageName]]),
|
||||
),
|
||||
|
|
@ -93,12 +93,12 @@ export function buildArtifactCommands(layout) {
|
|||
...npmPackCommands,
|
||||
{
|
||||
command: 'uv',
|
||||
args: ['build', '--package', 'klo-sl', '--out-dir', layout.pythonDir],
|
||||
args: ['build', '--package', 'ktx-sl', '--out-dir', layout.pythonDir],
|
||||
cwd: layout.rootDir,
|
||||
},
|
||||
{
|
||||
command: 'uv',
|
||||
args: ['build', '--package', 'klo-daemon', '--out-dir', layout.pythonDir],
|
||||
args: ['build', '--package', 'ktx-daemon', '--out-dir', layout.pythonDir],
|
||||
cwd: layout.rootDir,
|
||||
},
|
||||
];
|
||||
|
|
@ -136,10 +136,10 @@ export async function findPythonArtifacts(pythonDir) {
|
|||
const files = await readdir(pythonDir);
|
||||
|
||||
return {
|
||||
kloSlWheel: findOne(files, 'klo-sl', '.whl', 'klo-sl wheel', pythonDir),
|
||||
kloSlSdist: findOne(files, 'klo-sl', '.tar.gz', 'klo-sl source distribution', pythonDir),
|
||||
kloDaemonWheel: findOne(files, 'klo-daemon', '.whl', 'klo-daemon wheel', pythonDir),
|
||||
kloDaemonSdist: findOne(files, 'klo-daemon', '.tar.gz', 'klo-daemon source distribution', pythonDir),
|
||||
ktxSlWheel: findOne(files, 'ktx-sl', '.whl', 'ktx-sl wheel', pythonDir),
|
||||
ktxSlSdist: findOne(files, 'ktx-sl', '.tar.gz', 'ktx-sl source distribution', pythonDir),
|
||||
ktxDaemonWheel: findOne(files, 'ktx-daemon', '.whl', 'ktx-daemon wheel', pythonDir),
|
||||
ktxDaemonSdist: findOne(files, 'ktx-daemon', '.tar.gz', 'ktx-daemon source distribution', pythonDir),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -223,23 +223,23 @@ export async function packageReleaseMetadata(rootDir = scriptRootDir()) {
|
|||
const npmPackages = await Promise.all(
|
||||
NPM_ARTIFACT_PACKAGES.map((packageInfo) => readNpmPackageMetadata(rootDir, packageInfo)),
|
||||
);
|
||||
const kloSlPackage = await readPyprojectMetadata(join(rootDir, 'python', 'klo-sl', 'pyproject.toml'));
|
||||
const kloDaemonPackage = await readPyprojectMetadata(join(rootDir, 'python', 'klo-daemon', 'pyproject.toml'));
|
||||
const ktxSlPackage = await readPyprojectMetadata(join(rootDir, 'python', 'ktx-sl', 'pyproject.toml'));
|
||||
const ktxDaemonPackage = await readPyprojectMetadata(join(rootDir, 'python', 'ktx-daemon', 'pyproject.toml'));
|
||||
|
||||
return [
|
||||
...npmPackages,
|
||||
releaseMetadataEntry({
|
||||
ecosystem: 'python',
|
||||
packageName: kloSlPackage.name,
|
||||
packageRoot: 'python/klo-sl',
|
||||
packageVersion: kloSlPackage.version,
|
||||
packageName: ktxSlPackage.name,
|
||||
packageRoot: 'python/ktx-sl',
|
||||
packageVersion: ktxSlPackage.version,
|
||||
privatePackage: false,
|
||||
}),
|
||||
releaseMetadataEntry({
|
||||
ecosystem: 'python',
|
||||
packageName: kloDaemonPackage.name,
|
||||
packageRoot: 'python/klo-daemon',
|
||||
packageVersion: kloDaemonPackage.version,
|
||||
packageName: ktxDaemonPackage.name,
|
||||
packageRoot: 'python/ktx-daemon',
|
||||
packageVersion: ktxDaemonPackage.version,
|
||||
privatePackage: false,
|
||||
}),
|
||||
];
|
||||
|
|
@ -269,23 +269,23 @@ function artifactPackageRecords(layout, pythonArtifacts, packages) {
|
|||
...npmRecords,
|
||||
{
|
||||
artifactKind: 'wheel',
|
||||
artifactPath: pythonArtifacts.kloSlWheel,
|
||||
metadata: requirePackageMetadata(packagesByName, 'klo-sl'),
|
||||
artifactPath: pythonArtifacts.ktxSlWheel,
|
||||
metadata: requirePackageMetadata(packagesByName, 'ktx-sl'),
|
||||
},
|
||||
{
|
||||
artifactKind: 'sdist',
|
||||
artifactPath: pythonArtifacts.kloSlSdist,
|
||||
metadata: requirePackageMetadata(packagesByName, 'klo-sl'),
|
||||
artifactPath: pythonArtifacts.ktxSlSdist,
|
||||
metadata: requirePackageMetadata(packagesByName, 'ktx-sl'),
|
||||
},
|
||||
{
|
||||
artifactKind: 'wheel',
|
||||
artifactPath: pythonArtifacts.kloDaemonWheel,
|
||||
metadata: requirePackageMetadata(packagesByName, 'klo-daemon'),
|
||||
artifactPath: pythonArtifacts.ktxDaemonWheel,
|
||||
metadata: requirePackageMetadata(packagesByName, 'ktx-daemon'),
|
||||
},
|
||||
{
|
||||
artifactKind: 'sdist',
|
||||
artifactPath: pythonArtifacts.kloDaemonSdist,
|
||||
metadata: requirePackageMetadata(packagesByName, 'klo-daemon'),
|
||||
artifactPath: pythonArtifacts.ktxDaemonSdist,
|
||||
metadata: requirePackageMetadata(packagesByName, 'ktx-daemon'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -399,7 +399,7 @@ export async function verifyArtifactManifest(layout, options = {}) {
|
|||
const manifest = await readJson(artifactManifestPath(layout));
|
||||
assertManifestShape(manifest);
|
||||
|
||||
const expectedSourceRevision = options.expectedSourceRevision ?? process.env.KLO_EXPECTED_SOURCE_REVISION;
|
||||
const expectedSourceRevision = options.expectedSourceRevision ?? process.env.KTX_EXPECTED_SOURCE_REVISION;
|
||||
if (expectedSourceRevision !== undefined && manifest.sourceRevision !== expectedSourceRevision) {
|
||||
throw new Error(
|
||||
`Artifact manifest sourceRevision mismatch: expected ${expectedSourceRevision}, got ${manifest.sourceRevision}`,
|
||||
|
|
@ -435,8 +435,8 @@ export function pythonArtifactInstallArgs(python, pythonArtifacts) {
|
|||
'install',
|
||||
'--python',
|
||||
python,
|
||||
pythonArtifacts.kloSlWheel,
|
||||
pythonArtifacts.kloDaemonWheel,
|
||||
pythonArtifacts.ktxSlWheel,
|
||||
pythonArtifacts.ktxDaemonWheel,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -486,7 +486,7 @@ function npmTarballDependencyEntries(layout) {
|
|||
export function npmSmokePackageJson(layout) {
|
||||
const npmTarballDependencies = npmTarballDependencyEntries(layout);
|
||||
return {
|
||||
name: 'klo-artifact-npm-smoke',
|
||||
name: 'ktx-artifact-npm-smoke',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
type: 'module',
|
||||
|
|
@ -503,53 +503,53 @@ export function npmSmokePackageJson(layout) {
|
|||
|
||||
export function npmVerifySource() {
|
||||
return `
|
||||
const context = await import('@klo/context');
|
||||
const project = await import('@klo/context/project');
|
||||
const mcp = await import('@klo/context/mcp');
|
||||
const memory = await import('@klo/context/memory');
|
||||
const daemon = await import('@klo/context/daemon');
|
||||
const ingest = await import('@klo/context/ingest');
|
||||
const search = await import('@klo/context/search');
|
||||
const llm = await import('@klo/llm');
|
||||
const cli = await import('@klo/cli');
|
||||
const bigqueryConnector = await import('@klo/connector-bigquery');
|
||||
const clickhouseConnector = await import('@klo/connector-clickhouse');
|
||||
const mysqlConnector = await import('@klo/connector-mysql');
|
||||
const postgresConnector = await import('@klo/connector-postgres');
|
||||
const posthogConnector = await import('@klo/connector-posthog');
|
||||
const snowflakeConnector = await import('@klo/connector-snowflake');
|
||||
const sqliteConnector = await import('@klo/connector-sqlite');
|
||||
const sqlserverConnector = await import('@klo/connector-sqlserver');
|
||||
const context = await import('@ktx/context');
|
||||
const project = await import('@ktx/context/project');
|
||||
const mcp = await import('@ktx/context/mcp');
|
||||
const memory = await import('@ktx/context/memory');
|
||||
const daemon = await import('@ktx/context/daemon');
|
||||
const ingest = await import('@ktx/context/ingest');
|
||||
const search = await import('@ktx/context/search');
|
||||
const llm = await import('@ktx/llm');
|
||||
const cli = await import('@ktx/cli');
|
||||
const bigqueryConnector = await import('@ktx/connector-bigquery');
|
||||
const clickhouseConnector = await import('@ktx/connector-clickhouse');
|
||||
const mysqlConnector = await import('@ktx/connector-mysql');
|
||||
const postgresConnector = await import('@ktx/connector-postgres');
|
||||
const posthogConnector = await import('@ktx/connector-posthog');
|
||||
const snowflakeConnector = await import('@ktx/connector-snowflake');
|
||||
const sqliteConnector = await import('@ktx/connector-sqlite');
|
||||
const sqlserverConnector = await import('@ktx/connector-sqlserver');
|
||||
|
||||
if (context.kloContextPackageInfo.name !== '@klo/context') {
|
||||
throw new Error('Unexpected @klo/context package info');
|
||||
if (context.ktxContextPackageInfo.name !== '@ktx/context') {
|
||||
throw new Error('Unexpected @ktx/context package info');
|
||||
}
|
||||
if (typeof llm.createKloLlmProvider !== 'function') {
|
||||
throw new Error('Missing createKloLlmProvider export');
|
||||
if (typeof llm.createKtxLlmProvider !== 'function') {
|
||||
throw new Error('Missing createKtxLlmProvider export');
|
||||
}
|
||||
if (typeof llm.KloMessageBuilder !== 'function') {
|
||||
throw new Error('Missing KloMessageBuilder export');
|
||||
if (typeof llm.KtxMessageBuilder !== 'function') {
|
||||
throw new Error('Missing KtxMessageBuilder export');
|
||||
}
|
||||
if (typeof llm.createKloEmbeddingProvider !== 'function') {
|
||||
throw new Error('Missing createKloEmbeddingProvider export');
|
||||
if (typeof llm.createKtxEmbeddingProvider !== 'function') {
|
||||
throw new Error('Missing createKtxEmbeddingProvider export');
|
||||
}
|
||||
if (typeof project.initKloProject !== 'function') {
|
||||
throw new Error('Missing initKloProject export');
|
||||
if (typeof project.initKtxProject !== 'function') {
|
||||
throw new Error('Missing initKtxProject export');
|
||||
}
|
||||
if (typeof mcp.createDefaultKloMcpServer !== 'function') {
|
||||
throw new Error('Missing createDefaultKloMcpServer export');
|
||||
if (typeof mcp.createDefaultKtxMcpServer !== 'function') {
|
||||
throw new Error('Missing createDefaultKtxMcpServer export');
|
||||
}
|
||||
if (typeof memory.createLocalProjectMemoryCapture !== 'function') {
|
||||
throw new Error('Missing createLocalProjectMemoryCapture export');
|
||||
}
|
||||
if (typeof search.HybridSearchCore !== 'function') {
|
||||
throw new Error('Missing HybridSearchCore export from @klo/context/search');
|
||||
throw new Error('Missing HybridSearchCore export from @ktx/context/search');
|
||||
}
|
||||
if (typeof search.assertSearchBackendConformanceCase !== 'function') {
|
||||
throw new Error('Missing assertSearchBackendConformanceCase export from @klo/context/search');
|
||||
throw new Error('Missing assertSearchBackendConformanceCase export from @ktx/context/search');
|
||||
}
|
||||
if (typeof search.assertSearchBackendCapabilities !== 'function') {
|
||||
throw new Error('Missing assertSearchBackendCapabilities export from @klo/context/search');
|
||||
throw new Error('Missing assertSearchBackendCapabilities export from @ktx/context/search');
|
||||
}
|
||||
if (typeof daemon.createPythonSemanticLayerComputePort !== 'function') {
|
||||
throw new Error('Missing createPythonSemanticLayerComputePort export');
|
||||
|
|
@ -576,21 +576,21 @@ const metricflowConfig = ingest.parseMetricflowPullConfig({
|
|||
repoUrl: 'https://example.com/acme/analytics.git',
|
||||
});
|
||||
if (metricflowConfig.branch !== 'main' || metricflowConfig.path !== null) {
|
||||
throw new Error('Unexpected MetricFlow pull-config defaults from installed @klo/context/ingest');
|
||||
throw new Error('Unexpected MetricFlow pull-config defaults from installed @ktx/context/ingest');
|
||||
}
|
||||
if (cli.getKloCliPackageInfo().name !== '@klo/cli') {
|
||||
throw new Error('Unexpected @klo/cli package info');
|
||||
if (cli.getKtxCliPackageInfo().name !== '@ktx/cli') {
|
||||
throw new Error('Unexpected @ktx/cli package info');
|
||||
}
|
||||
|
||||
const connectorExports = [
|
||||
['@klo/connector-bigquery', bigqueryConnector.KloBigQueryScanConnector, bigqueryConnector.KloBigQueryDialect],
|
||||
['@klo/connector-clickhouse', clickhouseConnector.KloClickHouseScanConnector, clickhouseConnector.KloClickHouseDialect],
|
||||
['@klo/connector-mysql', mysqlConnector.KloMysqlScanConnector, mysqlConnector.KloMysqlDialect],
|
||||
['@klo/connector-postgres', postgresConnector.KloPostgresScanConnector, postgresConnector.KloPostgresDialect],
|
||||
['@klo/connector-posthog', posthogConnector.KloPostHogScanConnector, posthogConnector.KloPostHogDialect],
|
||||
['@klo/connector-snowflake', snowflakeConnector.KloSnowflakeScanConnector, snowflakeConnector.KloSnowflakeDialect],
|
||||
['@klo/connector-sqlite', sqliteConnector.KloSqliteScanConnector, sqliteConnector.KloSqliteDialect],
|
||||
['@klo/connector-sqlserver', sqlserverConnector.KloSqlServerScanConnector, sqlserverConnector.KloSqlServerDialect],
|
||||
['@ktx/connector-bigquery', bigqueryConnector.KtxBigQueryScanConnector, bigqueryConnector.KtxBigQueryDialect],
|
||||
['@ktx/connector-clickhouse', clickhouseConnector.KtxClickHouseScanConnector, clickhouseConnector.KtxClickHouseDialect],
|
||||
['@ktx/connector-mysql', mysqlConnector.KtxMysqlScanConnector, mysqlConnector.KtxMysqlDialect],
|
||||
['@ktx/connector-postgres', postgresConnector.KtxPostgresScanConnector, postgresConnector.KtxPostgresDialect],
|
||||
['@ktx/connector-posthog', posthogConnector.KtxPostHogScanConnector, posthogConnector.KtxPostHogDialect],
|
||||
['@ktx/connector-snowflake', snowflakeConnector.KtxSnowflakeScanConnector, snowflakeConnector.KtxSnowflakeDialect],
|
||||
['@ktx/connector-sqlite', sqliteConnector.KtxSqliteScanConnector, sqliteConnector.KtxSqliteDialect],
|
||||
['@ktx/connector-sqlserver', sqlserverConnector.KtxSqlServerScanConnector, sqlserverConnector.KtxSqlServerDialect],
|
||||
];
|
||||
|
||||
for (const [packageName, ScanConnector, Dialect] of connectorExports) {
|
||||
|
|
@ -621,11 +621,11 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|||
import {
|
||||
createDaemonLookerTableIdentifierParser,
|
||||
LocalLookerRuntimeStore,
|
||||
} from '@klo/context/ingest';
|
||||
} from '@ktx/context/ingest';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const require = createRequire(import.meta.url);
|
||||
const contextPackageRoot = dirname(require.resolve('@klo/context/package.json'));
|
||||
const contextPackageRoot = dirname(require.resolve('@ktx/context/package.json'));
|
||||
|
||||
async function requireContextRuntimeAsset(relativePath) {
|
||||
await access(join(contextPackageRoot, relativePath));
|
||||
|
|
@ -760,7 +760,7 @@ async function waitForHttpHealth(url, daemon) {
|
|||
if (daemon.error()) {
|
||||
const output = daemon.output();
|
||||
throw new Error(
|
||||
'Failed to start klo-daemon serve-http: ' +
|
||||
'Failed to start ktx-daemon serve-http: ' +
|
||||
daemon.error().message +
|
||||
'\\nstdout:\\n' +
|
||||
output.stdout +
|
||||
|
|
@ -771,7 +771,7 @@ async function waitForHttpHealth(url, daemon) {
|
|||
if (daemon.child.exitCode !== null || daemon.child.signalCode !== null) {
|
||||
const output = daemon.output();
|
||||
throw new Error(
|
||||
'klo-daemon serve-http exited before health check passed\\nstdout:\\n' +
|
||||
'ktx-daemon serve-http exited before health check passed\\nstdout:\\n' +
|
||||
output.stdout +
|
||||
'\\nstderr:\\n' +
|
||||
output.stderr,
|
||||
|
|
@ -792,7 +792,7 @@ async function waitForHttpHealth(url, daemon) {
|
|||
}
|
||||
|
||||
async function startSemanticDaemon(port) {
|
||||
const daemon = spawnLogged('klo-daemon', [
|
||||
const daemon = spawnLogged('ktx-daemon', [
|
||||
'serve-http',
|
||||
'--host',
|
||||
'127.0.0.1',
|
||||
|
|
@ -847,7 +847,7 @@ await requireContextRuntimeAsset('prompts/skills/page_triage_classifier.md');
|
|||
await requireContextRuntimeAsset('prompts/skills/light_extraction.md');
|
||||
process.stdout.write('packaged ingest runtime assets verified\\n');
|
||||
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-installed-cli-smoke-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-installed-cli-smoke-'));
|
||||
try {
|
||||
const projectDir = join(root, 'project');
|
||||
const sourceDir = join(root, 'source');
|
||||
|
|
@ -856,7 +856,7 @@ try {
|
|||
await mkdir(missingProjectDir, { recursive: true });
|
||||
const missingProjectSearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -866,19 +866,19 @@ try {
|
|||
'--project-dir',
|
||||
missingProjectDir,
|
||||
]);
|
||||
const missingProjectError = parseJsonFailure('klo agent sl list missing project', missingProjectSearch);
|
||||
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, [
|
||||
'klo demo',
|
||||
'klo setup --project-dir ' + missingProjectDir,
|
||||
'klo ingest <connection>',
|
||||
'klo agent sl list --json --query "revenue" --project-dir ' + missingProjectDir,
|
||||
'ktx demo',
|
||||
'ktx setup --project-dir ' + missingProjectDir,
|
||||
'ktx ingest <connection>',
|
||||
'ktx agent sl list --json --query "revenue" --project-dir ' + missingProjectDir,
|
||||
]);
|
||||
process.stdout.write('klo agent sl list missing project guidance verified\\n');
|
||||
process.stdout.write('ktx agent sl list missing project guidance verified\\n');
|
||||
|
||||
const init = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'setup',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
|
|
@ -891,13 +891,13 @@ try {
|
|||
'--skip-sources',
|
||||
'--skip-agents',
|
||||
]);
|
||||
requireSuccess('klo setup', init);
|
||||
requireOutput('klo setup', init, /Project: /);
|
||||
requireSuccess('ktx setup', init);
|
||||
requireOutput('ktx setup', init, /Project: /);
|
||||
|
||||
const emptyProjectDir = join(root, 'empty-project');
|
||||
const emptyInit = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'setup',
|
||||
'--project-dir',
|
||||
emptyProjectDir,
|
||||
|
|
@ -910,10 +910,10 @@ try {
|
|||
'--skip-sources',
|
||||
'--skip-agents',
|
||||
]);
|
||||
requireSuccess('klo setup empty project', emptyInit);
|
||||
requireSuccess('ktx setup empty project', emptyInit);
|
||||
const emptySearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -923,18 +923,18 @@ try {
|
|||
'--project-dir',
|
||||
emptyProjectDir,
|
||||
]);
|
||||
const emptySearchError = parseJsonFailure('klo agent sl list no connections', emptySearch);
|
||||
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, [
|
||||
'klo demo',
|
||||
'klo setup --project-dir ' + emptyProjectDir,
|
||||
'klo ingest <connection>',
|
||||
'klo agent sl list --json --query "revenue" --project-dir ' + emptyProjectDir,
|
||||
'ktx demo',
|
||||
'ktx setup --project-dir ' + emptyProjectDir,
|
||||
'ktx ingest <connection>',
|
||||
'ktx agent sl list --json --query "revenue" --project-dir ' + emptyProjectDir,
|
||||
]);
|
||||
process.stdout.write('klo agent sl list no connections guidance verified\\n');
|
||||
process.stdout.write('ktx agent sl list no connections guidance verified\\n');
|
||||
|
||||
await writeFile(
|
||||
join(projectDir, 'klo.yaml'),
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
|
|
@ -958,7 +958,7 @@ try {
|
|||
);
|
||||
await writeSqliteWarehouse(projectDir);
|
||||
|
||||
const lookerStore = new LocalLookerRuntimeStore({ dbPath: join(projectDir, '.klo', 'db.sqlite') });
|
||||
const lookerStore = new LocalLookerRuntimeStore({ dbPath: join(projectDir, '.ktx', 'db.sqlite') });
|
||||
await lookerStore.setCursors('prod-looker', {
|
||||
dashboardsLastSyncedAt: null,
|
||||
looksLastSyncedAt: null,
|
||||
|
|
@ -966,12 +966,12 @@ try {
|
|||
await lookerStore.upsertConnectionMapping({
|
||||
lookerConnectionId: 'prod-looker',
|
||||
lookerConnectionName: 'analytics',
|
||||
kloConnectionId: 'warehouse',
|
||||
ktxConnectionId: 'warehouse',
|
||||
source: 'cli',
|
||||
});
|
||||
const lookerMappings = await lookerStore.readMappings('prod-looker');
|
||||
assert.equal(lookerMappings.length, 1);
|
||||
assert.equal(lookerMappings[0].kloConnectionId, 'warehouse');
|
||||
assert.equal(lookerMappings[0].ktxConnectionId, 'warehouse');
|
||||
process.stdout.write('Looker local runtime store verified\\n');
|
||||
|
||||
await mkdir(join(projectDir, 'knowledge', 'global'), { recursive: true });
|
||||
|
|
@ -995,7 +995,7 @@ try {
|
|||
|
||||
const agentWikiSearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'wiki',
|
||||
'search',
|
||||
|
|
@ -1006,19 +1006,19 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
const agentWikiSearchJson = parseJsonResult('klo agent wiki search', agentWikiSearch);
|
||||
const agentWikiSearchJson = parseJsonResult('ktx agent wiki search', agentWikiSearch);
|
||||
assert.equal(agentWikiSearchJson.totalFound, 1);
|
||||
assert.equal(agentWikiSearchJson.results[0].key, 'revenue');
|
||||
assert.equal(agentWikiSearchJson.results[0].path, 'knowledge/global/revenue.md');
|
||||
assert.equal(typeof agentWikiSearchJson.results[0].score, 'number');
|
||||
requireIncludes(agentWikiSearchJson.results[0].matchReasons, 'lexical', 'agent wiki search match reasons');
|
||||
process.stdout.write('klo agent wiki search hybrid metadata verified\\n');
|
||||
await access(join(projectDir, '.klo', 'db.sqlite'));
|
||||
process.stdout.write('SQLite knowledge index: ' + join(projectDir, '.klo', 'db.sqlite') + '\\n');
|
||||
process.stdout.write('ktx agent wiki search hybrid metadata verified\\n');
|
||||
await access(join(projectDir, '.ktx', 'db.sqlite'));
|
||||
process.stdout.write('SQLite knowledge index: ' + join(projectDir, '.ktx', 'db.sqlite') + '\\n');
|
||||
|
||||
const noSourceSearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -1030,15 +1030,15 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
const noSourceSearchError = parseJsonFailure('klo agent sl list no indexed sources', noSourceSearch);
|
||||
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, [
|
||||
'klo demo',
|
||||
'klo setup --project-dir ' + projectDir,
|
||||
'klo ingest <connection>',
|
||||
'klo agent sl list --json --query "revenue" --project-dir ' + projectDir,
|
||||
'ktx demo',
|
||||
'ktx setup --project-dir ' + projectDir,
|
||||
'ktx ingest <connection>',
|
||||
'ktx agent sl list --json --query "revenue" --project-dir ' + projectDir,
|
||||
]);
|
||||
process.stdout.write('klo agent sl list no indexed sources guidance verified\\n');
|
||||
process.stdout.write('ktx agent sl list no indexed sources guidance verified\\n');
|
||||
|
||||
const slYaml = [
|
||||
'name: orders',
|
||||
|
|
@ -1062,7 +1062,7 @@ try {
|
|||
|
||||
const agentSlSearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -1074,18 +1074,18 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
const agentSlSearchJson = parseJsonResult('klo agent sl list', agentSlSearch);
|
||||
const agentSlSearchJson = parseJsonResult('ktx agent sl list', agentSlSearch);
|
||||
assert.equal(agentSlSearchJson.totalSources, 1);
|
||||
assert.equal(agentSlSearchJson.sources[0].connectionId, 'warehouse');
|
||||
assert.equal(agentSlSearchJson.sources[0].name, 'orders');
|
||||
assert.equal(typeof agentSlSearchJson.sources[0].score, 'number');
|
||||
requireIncludes(agentSlSearchJson.sources[0].matchReasons, 'lexical', 'agent sl search match reasons');
|
||||
process.stdout.write('klo agent sl list hybrid metadata verified\\n');
|
||||
process.stdout.write('ktx agent sl list hybrid metadata verified\\n');
|
||||
|
||||
const slQueryFile = join(projectDir, 'sl-query.json');
|
||||
await writeFile(slQueryFile, '{"measures":["orders.order_count"],"dimensions":[]}\\n', 'utf-8');
|
||||
|
||||
const slQuery = await run('pnpm', ['exec', 'klo', 'agent', 'sl', 'query',
|
||||
const slQuery = await run('pnpm', ['exec', 'ktx', 'agent', 'sl', 'query',
|
||||
'--json',
|
||||
'--connection-id',
|
||||
'warehouse',
|
||||
|
|
@ -1094,11 +1094,11 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
requireSuccess('klo agent sl query', slQuery);
|
||||
requireOutput('klo agent sl query', slQuery, /"mode": "compile_only"/);
|
||||
requireOutput('klo agent sl query', slQuery, /orders/);
|
||||
requireSuccess('ktx agent sl query', slQuery);
|
||||
requireOutput('ktx agent sl query', slQuery, /"mode": "compile_only"/);
|
||||
requireOutput('ktx agent sl query', slQuery, /orders/);
|
||||
|
||||
const sqliteSlQuery = await run('pnpm', ['exec', 'klo', 'agent', 'sl', 'query',
|
||||
const sqliteSlQuery = await run('pnpm', ['exec', 'ktx', 'agent', 'sl', 'query',
|
||||
'--json',
|
||||
'--connection-id',
|
||||
'warehouse',
|
||||
|
|
@ -1110,40 +1110,40 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
requireSuccess('klo agent sl query sqlite execute', sqliteSlQuery);
|
||||
requireOutput('klo agent sl query sqlite execute', sqliteSlQuery, /"dialect": "sqlite"/);
|
||||
requireOutput('klo agent sl query sqlite execute', sqliteSlQuery, /"mode": "executed"/);
|
||||
requireOutput('klo agent sl query sqlite execute', sqliteSlQuery, /"driver": "sqlite"/);
|
||||
requireOutput('klo agent sl query sqlite execute', sqliteSlQuery, /"rows": \\[\\s*\\[\\s*3\\s*\\]\\s*\\]/);
|
||||
process.stdout.write('klo agent sl query sqlite execute verified\\n');
|
||||
requireSuccess('ktx agent sl query sqlite execute', sqliteSlQuery);
|
||||
requireOutput('ktx agent sl query sqlite execute', sqliteSlQuery, /"dialect": "sqlite"/);
|
||||
requireOutput('ktx agent sl query sqlite execute', sqliteSlQuery, /"mode": "executed"/);
|
||||
requireOutput('ktx agent sl query sqlite execute', sqliteSlQuery, /"driver": "sqlite"/);
|
||||
requireOutput('ktx agent sl query sqlite execute', sqliteSlQuery, /"rows": \\[\\s*\\[\\s*3\\s*\\]\\s*\\]/);
|
||||
process.stdout.write('ktx agent sl query sqlite execute verified\\n');
|
||||
|
||||
const structuralScan = await run('pnpm', ['exec', 'klo', 'dev', 'scan', 'warehouse',
|
||||
const structuralScan = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'warehouse',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
requireSuccess('klo scan structural', structuralScan);
|
||||
requireOutput('klo scan structural', structuralScan, /Status: done/);
|
||||
requireOutput('klo scan structural', structuralScan, /Mode: structural/);
|
||||
requireOutput('klo scan structural', structuralScan, /Needs attention\\s+None/);
|
||||
requireSuccess('ktx scan structural', structuralScan);
|
||||
requireOutput('ktx scan structural', structuralScan, /Status: done/);
|
||||
requireOutput('ktx scan structural', structuralScan, /Mode: structural/);
|
||||
requireOutput('ktx scan structural', structuralScan, /Needs attention\\s+None/);
|
||||
const structuralScanRunId = getRunId(structuralScan.stdout);
|
||||
|
||||
const scanStatus = await run('pnpm', ['exec', 'klo', 'dev', 'scan', 'status',
|
||||
const scanStatus = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'status',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
structuralScanRunId,
|
||||
]);
|
||||
requireSuccess('klo scan status', scanStatus);
|
||||
requireOutput('klo scan status', scanStatus, new RegExp('Run: ' + structuralScanRunId));
|
||||
requireOutput('klo scan status', scanStatus, /Status: done/);
|
||||
requireOutput('klo scan status', scanStatus, /Mode: structural/);
|
||||
requireSuccess('ktx scan status', scanStatus);
|
||||
requireOutput('ktx scan status', scanStatus, new RegExp('Run: ' + structuralScanRunId));
|
||||
requireOutput('ktx scan status', scanStatus, /Status: done/);
|
||||
requireOutput('ktx scan status', scanStatus, /Mode: structural/);
|
||||
|
||||
const scanReport = await run('pnpm', ['exec', 'klo', 'dev', 'scan', 'report',
|
||||
const scanReport = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'report',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--json',
|
||||
structuralScanRunId,
|
||||
]);
|
||||
requireSuccess('klo scan report', scanReport);
|
||||
requireSuccess('ktx scan report', scanReport);
|
||||
const scanReportJson = JSON.parse(scanReport.stdout);
|
||||
assert.equal(scanReportJson.mode, 'structural');
|
||||
assert.equal(scanReportJson.connectionId, 'warehouse');
|
||||
|
|
@ -1151,35 +1151,35 @@ try {
|
|||
assert.deepEqual(scanReportJson.artifactPaths.enrichmentArtifacts, []);
|
||||
assert.deepEqual(scanReportJson.artifactPaths.manifestShards, ['semantic-layer/warehouse/_schema/public.yaml']);
|
||||
await access(join(projectDir, 'semantic-layer', 'warehouse', '_schema', 'public.yaml'));
|
||||
process.stdout.write('klo scan structural verified: ' + structuralScanRunId + '\\n');
|
||||
process.stdout.write('ktx scan structural verified: ' + structuralScanRunId + '\\n');
|
||||
|
||||
const enrichedScan = await run('pnpm', ['exec', 'klo', 'dev', 'scan', 'warehouse',
|
||||
const enrichedScan = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'warehouse',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--mode',
|
||||
'enriched',
|
||||
]);
|
||||
requireSuccess('klo scan enriched', enrichedScan);
|
||||
requireOutput('klo scan enriched', enrichedScan, /Status: done/);
|
||||
requireOutput('klo scan enriched', enrichedScan, /Mode: enriched/);
|
||||
requireSuccess('ktx scan enriched', enrichedScan);
|
||||
requireOutput('ktx scan enriched', enrichedScan, /Status: done/);
|
||||
requireOutput('ktx scan enriched', enrichedScan, /Mode: enriched/);
|
||||
const enrichedScanRunId = getRunId(enrichedScan.stdout);
|
||||
const enrichedScanReport = await run('pnpm', ['exec', 'klo', 'dev', 'scan', 'report',
|
||||
const enrichedScanReport = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'report',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--json',
|
||||
enrichedScanRunId,
|
||||
]);
|
||||
requireSuccess('klo scan enriched report', enrichedScanReport);
|
||||
requireSuccess('ktx scan enriched report', enrichedScanReport);
|
||||
const enrichedScanReportJson = JSON.parse(enrichedScanReport.stdout);
|
||||
assert.equal(enrichedScanReportJson.mode, 'enriched');
|
||||
assert.ok(enrichedScanReportJson.artifactPaths.enrichmentArtifacts.length > 0);
|
||||
assert.deepEqual(enrichedScanReportJson.artifactPaths.manifestShards, ['semantic-layer/warehouse/_schema/public.yaml']);
|
||||
process.stdout.write('klo scan enriched verified: ' + enrichedScanRunId + '\\n');
|
||||
process.stdout.write('ktx scan enriched verified: ' + enrichedScanRunId + '\\n');
|
||||
|
||||
await mkdir(join(sourceDir, 'orders'), { recursive: true });
|
||||
await writeFile(join(sourceDir, 'orders', 'orders.json'), '{"name":"orders"}\\n', 'utf-8');
|
||||
|
||||
const ingestRun = await run('pnpm', ['exec', 'klo', 'dev', 'ingest', 'run',
|
||||
const ingestRun = await run('pnpm', ['exec', 'ktx', 'dev', 'ingest', 'run',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--connection-id',
|
||||
|
|
@ -1189,17 +1189,17 @@ try {
|
|||
'--source-dir',
|
||||
sourceDir,
|
||||
]);
|
||||
assert.equal(ingestRun.code, 1, 'klo dev ingest run without an LLM provider must fail');
|
||||
assert.equal(ingestRun.code, 1, 'ktx dev ingest run without an LLM provider must fail');
|
||||
assert.match(
|
||||
ingestRun.stderr,
|
||||
/klo dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway, or an injected agentRunner/,
|
||||
/ktx dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway, or an injected agentRunner/,
|
||||
);
|
||||
|
||||
await access(join(projectDir, '.klo', 'db.sqlite'));
|
||||
process.stdout.write('klo dev ingest provider guard verified\\n');
|
||||
await access(join(projectDir, '.ktx', 'db.sqlite'));
|
||||
process.stdout.write('ktx dev ingest provider guard verified\\n');
|
||||
|
||||
await writeFile(
|
||||
join(projectDir, 'klo.yaml'),
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
|
|
@ -1231,7 +1231,7 @@ try {
|
|||
|
||||
const daemonPort = await getAvailablePort();
|
||||
const semanticComputeUrl = 'http://127.0.0.1:' + daemonPort;
|
||||
process.stdout.write('klo-daemon serve-http --host 127.0.0.1 --port ' + daemonPort + '\\n');
|
||||
process.stdout.write('ktx-daemon serve-http --host 127.0.0.1 --port ' + daemonPort + '\\n');
|
||||
const daemon = await startSemanticDaemon(daemonPort);
|
||||
const lookerParser = createDaemonLookerTableIdentifierParser({ baseUrl: semanticComputeUrl });
|
||||
const parsedLookerTables = await lookerParser.parse([
|
||||
|
|
@ -1241,13 +1241,13 @@ try {
|
|||
assert.equal(parsedLookerTables.orders.name, 'orders');
|
||||
assert.equal(parsedLookerTables.orders.canonical_table, 'orders');
|
||||
process.stdout.write('Looker daemon table identifier parser verified\\n');
|
||||
const client = new Client({ name: 'klo-artifact-smoke-client', version: '0.0.0' });
|
||||
process.stdout.write('klo serve --mcp stdio --semantic-compute-url ' + semanticComputeUrl + ' --execute-queries\\n');
|
||||
const client = new Client({ name: 'ktx-artifact-smoke-client', version: '0.0.0' });
|
||||
process.stdout.write('ktx serve --mcp stdio --semantic-compute-url ' + semanticComputeUrl + ' --execute-queries\\n');
|
||||
const transport = new StdioClientTransport({
|
||||
command: 'pnpm',
|
||||
args: [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'serve', '--mcp', 'stdio',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
|
|
@ -1379,7 +1379,7 @@ try {
|
|||
} catch (error) {
|
||||
const stderr = Buffer.concat(mcpServerStderr).toString('utf8');
|
||||
if (stderr) {
|
||||
error.message += '\\nklo serve stderr:\\n' + stderr;
|
||||
error.message += '\\nktx serve stderr:\\n' + stderr;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
|
|
@ -1434,31 +1434,31 @@ function requireStdout(label, result, pattern) {
|
|||
assert.match(result.stdout, pattern, label + ' stdout did not match ' + pattern);
|
||||
}
|
||||
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-packed-demo-smoke-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-packed-demo-smoke-'));
|
||||
try {
|
||||
const projectDir = join(root, 'demo-project');
|
||||
|
||||
const help = await run('pnpm', ['exec', 'klo', '--help']);
|
||||
requireSuccess('klo --help', help);
|
||||
requireStdout('klo --help', help, /Usage: klo/);
|
||||
requireStdout('klo --help', help, /setup/);
|
||||
const help = await run('pnpm', ['exec', 'ktx', '--help']);
|
||||
requireSuccess('ktx --help', help);
|
||||
requireStdout('ktx --help', help, /Usage: ktx/);
|
||||
requireStdout('ktx --help', help, /setup/);
|
||||
|
||||
const seeded = await run(
|
||||
'pnpm',
|
||||
['exec', 'klo', 'setup', 'demo', '--project-dir', projectDir, '--no-input', '--plain'],
|
||||
['exec', 'ktx', 'setup', 'demo', '--project-dir', projectDir, '--no-input', '--plain'],
|
||||
);
|
||||
requireSuccess('klo setup demo seeded', seeded);
|
||||
requireStdout('klo setup demo seeded', seeded, /Mode: seeded/);
|
||||
requireStdout('klo setup demo seeded', seeded, /Source: packaged demo project/);
|
||||
requireStdout('klo setup demo seeded', seeded, /LLM calls: none/);
|
||||
requireStdout('klo setup demo seeded', seeded, /klo serve --mcp stdio/);
|
||||
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 serve --mcp stdio/);
|
||||
assert.doesNotMatch(seeded.stdout, new RegExp(['--mode', 'deterministic'].join(' ')));
|
||||
assert.doesNotMatch(seeded.stdout, /KLO memory flow/);
|
||||
assert.equal(seeded.stderr, '', 'klo setup demo seeded wrote unexpected stderr');
|
||||
assert.doesNotMatch(seeded.stdout, /KTX memory flow/);
|
||||
assert.equal(seeded.stderr, '', 'ktx setup demo seeded wrote unexpected stderr');
|
||||
|
||||
const demoWikiSearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'wiki',
|
||||
'search',
|
||||
|
|
@ -1469,18 +1469,18 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
requireSuccess('klo seeded demo agent wiki search', demoWikiSearch);
|
||||
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('klo seeded demo agent wiki search verified\\n');
|
||||
process.stdout.write('ktx seeded demo agent wiki search verified\\n');
|
||||
|
||||
const demoSlSearch = await run('pnpm', [
|
||||
'exec',
|
||||
'klo',
|
||||
'ktx',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -1490,20 +1490,20 @@ try {
|
|||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
requireSuccess('klo seeded demo agent sl search', demoSlSearch);
|
||||
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('klo seeded demo agent sl search verified\\n');
|
||||
process.stdout.write('ktx seeded demo agent sl search verified\\n');
|
||||
|
||||
const doctor = await run('pnpm', ['exec', 'klo', 'dev', 'doctor', 'setup', '--no-input']);
|
||||
assert.ok([0, 1].includes(doctor.code), 'klo dev doctor setup exit code must be 0 or 1');
|
||||
requireStdout('klo dev doctor setup', doctor, /KLO setup doctor/);
|
||||
requireStdout('klo dev doctor setup', doctor, /Node 22\\+/);
|
||||
assert.equal(doctor.stderr, '', 'klo dev doctor setup wrote unexpected stderr');
|
||||
const doctor = await run('pnpm', ['exec', 'ktx', 'dev', 'doctor', 'setup', '--no-input']);
|
||||
assert.ok([0, 1].includes(doctor.code), 'ktx dev doctor setup exit code must be 0 or 1');
|
||||
requireStdout('ktx dev doctor setup', doctor, /KTX setup doctor/);
|
||||
requireStdout('ktx dev doctor setup', doctor, /Node 22\\+/);
|
||||
assert.equal(doctor.stderr, '', 'ktx dev doctor setup wrote unexpected stderr');
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
|
|
@ -1513,13 +1513,13 @@ try {
|
|||
export function pythonVerifySource() {
|
||||
return `
|
||||
import importlib.metadata
|
||||
import klo_daemon
|
||||
import ktx_daemon
|
||||
import semantic_layer
|
||||
|
||||
assert importlib.metadata.version("klo-sl") == "0.1.0"
|
||||
assert importlib.metadata.version("klo-daemon") == "0.1.0"
|
||||
assert importlib.metadata.version("ktx-sl") == "0.1.0"
|
||||
assert importlib.metadata.version("ktx-daemon") == "0.1.0"
|
||||
assert semantic_layer is not None
|
||||
assert klo_daemon.PACKAGE_NAME == "klo-daemon"
|
||||
assert ktx_daemon.PACKAGE_NAME == "ktx-daemon"
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
@ -1580,7 +1580,7 @@ async function verifyNpmArtifacts(layout, tmpRoot) {
|
|||
cwd: projectDir,
|
||||
});
|
||||
await runCommand('node', ['verify-npm.mjs'], { cwd: projectDir });
|
||||
await runCommand('pnpm', ['exec', 'klo', '--version'], { cwd: projectDir });
|
||||
await runCommand('pnpm', ['exec', 'ktx', '--version'], { cwd: projectDir });
|
||||
await runCommand('node', ['verify-installed-cli.mjs'], {
|
||||
cwd: projectDir,
|
||||
env: npmSmokePythonEnv(projectDir),
|
||||
|
|
@ -1618,7 +1618,7 @@ async function verifyPythonArtifacts(layout, tmpRoot) {
|
|||
cwd: projectDir,
|
||||
});
|
||||
await runCommand(python, ['verify_python.py'], { cwd: projectDir });
|
||||
await runCommand(python, ['-m', 'klo_daemon', 'semantic-validate'], {
|
||||
await runCommand(python, ['-m', 'ktx_daemon', 'semantic-validate'], {
|
||||
cwd: projectDir,
|
||||
input: `${JSON.stringify({ sources: [ordersSource], dialect: 'postgres' })}\n`,
|
||||
});
|
||||
|
|
@ -1627,7 +1627,7 @@ async function verifyPythonArtifacts(layout, tmpRoot) {
|
|||
async function verifyArtifacts(layout) {
|
||||
await verifyArtifactManifest(layout);
|
||||
|
||||
const tmpRoot = await mkdtemp(join(tmpdir(), 'klo-artifacts-'));
|
||||
const tmpRoot = await mkdtemp(join(tmpdir(), 'ktx-artifacts-'));
|
||||
try {
|
||||
await verifyNpmArtifacts(layout, tmpRoot);
|
||||
await verifyPythonArtifacts(layout, tmpRoot);
|
||||
|
|
@ -1639,7 +1639,7 @@ async function verifyArtifacts(layout) {
|
|||
async function verifyDemoArtifacts(layout) {
|
||||
await verifyArtifactManifest(layout);
|
||||
|
||||
const tmpRoot = await mkdtemp(join(tmpdir(), 'klo-demo-artifacts-'));
|
||||
const tmpRoot = await mkdtemp(join(tmpdir(), 'ktx-demo-artifacts-'));
|
||||
try {
|
||||
await verifyNpmDemoArtifacts(layout, tmpRoot);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -30,29 +30,29 @@ async function writeJson(path, value) {
|
|||
}
|
||||
|
||||
const CONNECTOR_PACKAGE_NAMES = [
|
||||
'@klo/connector-bigquery',
|
||||
'@klo/connector-clickhouse',
|
||||
'@klo/connector-mysql',
|
||||
'@klo/connector-postgres',
|
||||
'@klo/connector-posthog',
|
||||
'@klo/connector-snowflake',
|
||||
'@klo/connector-sqlite',
|
||||
'@klo/connector-sqlserver',
|
||||
'@ktx/connector-bigquery',
|
||||
'@ktx/connector-clickhouse',
|
||||
'@ktx/connector-mysql',
|
||||
'@ktx/connector-postgres',
|
||||
'@ktx/connector-posthog',
|
||||
'@ktx/connector-snowflake',
|
||||
'@ktx/connector-sqlite',
|
||||
'@ktx/connector-sqlserver',
|
||||
];
|
||||
|
||||
function packageRootForName(packageName) {
|
||||
return `packages/${packageName.replace('@klo/', '')}`;
|
||||
return `packages/${packageName.replace('@ktx/', '')}`;
|
||||
}
|
||||
|
||||
function expectedNpmArtifactPath(packageName) {
|
||||
return `npm/${packageName.replace('@klo/', 'klo-')}-0.0.0-private.tgz`;
|
||||
return `npm/${packageName.replace('@ktx/', 'ktx-')}-0.0.0-private.tgz`;
|
||||
}
|
||||
|
||||
async function writeReleaseMetadataInputs(root) {
|
||||
const npmPackages = ['@klo/context', '@klo/llm', ...CONNECTOR_PACKAGE_NAMES, '@klo/cli'];
|
||||
const npmPackages = ['@ktx/context', '@ktx/llm', ...CONNECTOR_PACKAGE_NAMES, '@ktx/cli'];
|
||||
|
||||
for (const packageName of npmPackages) {
|
||||
const packageRoot = packageName === '@klo/context' ? 'packages/context' : packageRootForName(packageName);
|
||||
const packageRoot = packageName === '@ktx/context' ? 'packages/context' : packageRootForName(packageName);
|
||||
await mkdir(join(root, packageRoot), { recursive: true });
|
||||
await writeJson(join(root, packageRoot, 'package.json'), {
|
||||
name: packageName,
|
||||
|
|
@ -61,15 +61,15 @@ async function writeReleaseMetadataInputs(root) {
|
|||
});
|
||||
}
|
||||
|
||||
await mkdir(join(root, 'python', 'klo-sl'), { recursive: true });
|
||||
await mkdir(join(root, 'python', 'klo-daemon'), { recursive: true });
|
||||
await mkdir(join(root, 'python', 'ktx-sl'), { recursive: true });
|
||||
await mkdir(join(root, 'python', 'ktx-daemon'), { recursive: true });
|
||||
await writeFile(
|
||||
join(root, 'python', 'klo-sl', 'pyproject.toml'),
|
||||
['[project]', 'name = "klo-sl"', 'version = "0.1.0"', ''].join('\n'),
|
||||
join(root, 'python', 'ktx-sl', 'pyproject.toml'),
|
||||
['[project]', 'name = "ktx-sl"', 'version = "0.1.0"', ''].join('\n'),
|
||||
);
|
||||
await writeFile(
|
||||
join(root, 'python', 'klo-daemon', 'pyproject.toml'),
|
||||
['[project]', 'name = "klo-daemon"', 'version = "0.1.0"', ''].join('\n'),
|
||||
join(root, 'python', 'ktx-daemon', 'pyproject.toml'),
|
||||
['[project]', 'name = "ktx-daemon"', 'version = "0.1.0"', ''].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -82,10 +82,10 @@ async function writeUploadableArtifactFixtures(layout) {
|
|||
layout.npmTarballs[packageInfo.name],
|
||||
`${packageInfo.name}-tarball`,
|
||||
]),
|
||||
[join(layout.pythonDir, 'klo_sl-0.1.0-py3-none-any.whl'), 'klo-sl-wheel'],
|
||||
[join(layout.pythonDir, 'klo_sl-0.1.0.tar.gz'), 'klo-sl-sdist'],
|
||||
[join(layout.pythonDir, 'klo_daemon-0.1.0-py3-none-any.whl'), 'klo-daemon-wheel'],
|
||||
[join(layout.pythonDir, 'klo_daemon-0.1.0.tar.gz'), 'klo-daemon-sdist'],
|
||||
[join(layout.pythonDir, 'ktx_sl-0.1.0-py3-none-any.whl'), 'ktx-sl-wheel'],
|
||||
[join(layout.pythonDir, 'ktx_sl-0.1.0.tar.gz'), 'ktx-sl-sdist'],
|
||||
[join(layout.pythonDir, 'ktx_daemon-0.1.0-py3-none-any.whl'), 'ktx-daemon-wheel'],
|
||||
[join(layout.pythonDir, 'ktx_daemon-0.1.0.tar.gz'), 'ktx-daemon-sdist'],
|
||||
]);
|
||||
|
||||
for (const [path, contents] of fileContents) {
|
||||
|
|
@ -94,21 +94,21 @@ async function writeUploadableArtifactFixtures(layout) {
|
|||
}
|
||||
|
||||
describe('packageArtifactLayout', () => {
|
||||
it('uses stable artifact paths under klo/dist/artifacts', () => {
|
||||
const layout = packageArtifactLayout('/repo/klo');
|
||||
it('uses stable artifact paths under ktx/dist/artifacts', () => {
|
||||
const layout = packageArtifactLayout('/repo/ktx');
|
||||
|
||||
assert.equal(layout.artifactDir, '/repo/klo/dist/artifacts');
|
||||
assert.equal(layout.npmDir, '/repo/klo/dist/artifacts/npm');
|
||||
assert.equal(layout.pythonDir, '/repo/klo/dist/artifacts/python');
|
||||
assert.equal(layout.contextTarball, '/repo/klo/dist/artifacts/npm/klo-context-0.0.0-private.tgz');
|
||||
assert.equal(layout.cliTarball, '/repo/klo/dist/artifacts/npm/klo-cli-0.0.0-private.tgz');
|
||||
assert.equal(layout.artifactDir, '/repo/ktx/dist/artifacts');
|
||||
assert.equal(layout.npmDir, '/repo/ktx/dist/artifacts/npm');
|
||||
assert.equal(layout.pythonDir, '/repo/ktx/dist/artifacts/python');
|
||||
assert.equal(layout.contextTarball, '/repo/ktx/dist/artifacts/npm/ktx-context-0.0.0-private.tgz');
|
||||
assert.equal(layout.cliTarball, '/repo/ktx/dist/artifacts/npm/ktx-cli-0.0.0-private.tgz');
|
||||
assert.equal(
|
||||
layout.connectorTarballs['@klo/connector-sqlite'],
|
||||
'/repo/klo/dist/artifacts/npm/klo-connector-sqlite-0.0.0-private.tgz',
|
||||
layout.connectorTarballs['@ktx/connector-sqlite'],
|
||||
'/repo/ktx/dist/artifacts/npm/ktx-connector-sqlite-0.0.0-private.tgz',
|
||||
);
|
||||
assert.equal(
|
||||
layout.connectorTarballs['@klo/connector-postgres'],
|
||||
'/repo/klo/dist/artifacts/npm/klo-connector-postgres-0.0.0-private.tgz',
|
||||
layout.connectorTarballs['@ktx/connector-postgres'],
|
||||
'/repo/ktx/dist/artifacts/npm/ktx-connector-postgres-0.0.0-private.tgz',
|
||||
);
|
||||
assert.deepEqual(
|
||||
Object.keys(layout.npmTarballs),
|
||||
|
|
@ -119,7 +119,7 @@ describe('packageArtifactLayout', () => {
|
|||
|
||||
describe('buildArtifactCommands', () => {
|
||||
it('builds all TypeScript packages before packing npm artifacts and builds both Python packages', () => {
|
||||
const layout = packageArtifactLayout('/repo/klo');
|
||||
const layout = packageArtifactLayout('/repo/ktx');
|
||||
const commands = buildArtifactCommands(layout);
|
||||
|
||||
assert.deepEqual(
|
||||
|
|
@ -138,8 +138,8 @@ describe('buildArtifactCommands', () => {
|
|||
assert.deepEqual(
|
||||
commands.slice(NPM_ARTIFACT_PACKAGES.length * 2).map((command) => [command.command, command.args]),
|
||||
[
|
||||
['uv', ['build', '--package', 'klo-sl', '--out-dir', '/repo/klo/dist/artifacts/python']],
|
||||
['uv', ['build', '--package', 'klo-daemon', '--out-dir', '/repo/klo/dist/artifacts/python']],
|
||||
['uv', ['build', '--package', 'ktx-sl', '--out-dir', '/repo/ktx/dist/artifacts/python']],
|
||||
['uv', ['build', '--package', 'ktx-daemon', '--out-dir', '/repo/ktx/dist/artifacts/python']],
|
||||
],
|
||||
);
|
||||
});
|
||||
|
|
@ -147,7 +147,7 @@ describe('buildArtifactCommands', () => {
|
|||
|
||||
describe('packageReleaseMetadata', () => {
|
||||
it('reads package identities and versions from package manifests', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-metadata-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-metadata-test-'));
|
||||
try {
|
||||
await writeReleaseMetadataInputs(root);
|
||||
|
||||
|
|
@ -162,16 +162,16 @@ describe('packageReleaseMetadata', () => {
|
|||
})),
|
||||
{
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-sl',
|
||||
packageRoot: 'python/klo-sl',
|
||||
packageName: 'ktx-sl',
|
||||
packageRoot: 'python/ktx-sl',
|
||||
packageVersion: '0.1.0',
|
||||
private: false,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
},
|
||||
{
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-daemon',
|
||||
packageRoot: 'python/klo-daemon',
|
||||
packageName: 'ktx-daemon',
|
||||
packageRoot: 'python/ktx-daemon',
|
||||
packageVersion: '0.1.0',
|
||||
private: false,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
|
|
@ -185,18 +185,18 @@ describe('packageReleaseMetadata', () => {
|
|||
|
||||
describe('findPythonArtifacts', () => {
|
||||
it('finds one wheel and one source distribution for each Python package', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-test-'));
|
||||
try {
|
||||
await writeFile(join(root, 'klo_sl-0.1.0-py3-none-any.whl'), '');
|
||||
await writeFile(join(root, 'klo_sl-0.1.0.tar.gz'), '');
|
||||
await writeFile(join(root, 'klo_daemon-0.1.0-py3-none-any.whl'), '');
|
||||
await writeFile(join(root, 'klo_daemon-0.1.0.tar.gz'), '');
|
||||
await writeFile(join(root, 'ktx_sl-0.1.0-py3-none-any.whl'), '');
|
||||
await writeFile(join(root, 'ktx_sl-0.1.0.tar.gz'), '');
|
||||
await writeFile(join(root, 'ktx_daemon-0.1.0-py3-none-any.whl'), '');
|
||||
await writeFile(join(root, 'ktx_daemon-0.1.0.tar.gz'), '');
|
||||
|
||||
assert.deepEqual(await findPythonArtifacts(root), {
|
||||
kloSlWheel: join(root, 'klo_sl-0.1.0-py3-none-any.whl'),
|
||||
kloSlSdist: join(root, 'klo_sl-0.1.0.tar.gz'),
|
||||
kloDaemonWheel: join(root, 'klo_daemon-0.1.0-py3-none-any.whl'),
|
||||
kloDaemonSdist: join(root, 'klo_daemon-0.1.0.tar.gz'),
|
||||
ktxSlWheel: join(root, 'ktx_sl-0.1.0-py3-none-any.whl'),
|
||||
ktxSlSdist: join(root, 'ktx_sl-0.1.0.tar.gz'),
|
||||
ktxDaemonWheel: join(root, 'ktx_daemon-0.1.0-py3-none-any.whl'),
|
||||
ktxDaemonSdist: join(root, 'ktx_daemon-0.1.0.tar.gz'),
|
||||
});
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
|
|
@ -204,9 +204,9 @@ describe('findPythonArtifacts', () => {
|
|||
});
|
||||
|
||||
it('throws when a required Python artifact is missing', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-test-'));
|
||||
try {
|
||||
await assert.rejects(() => findPythonArtifacts(root), /Missing Python artifact: klo-sl wheel/);
|
||||
await assert.rejects(() => findPythonArtifacts(root), /Missing Python artifact: ktx-sl wheel/);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
|
|
@ -215,7 +215,7 @@ describe('findPythonArtifacts', () => {
|
|||
|
||||
describe('artifact manifest', () => {
|
||||
it('writes release metadata, source revision, checksums, and byte counts for every uploadable artifact', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-manifest-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-manifest-test-'));
|
||||
const layout = packageArtifactLayout(root);
|
||||
try {
|
||||
await writeReleaseMetadataInputs(root);
|
||||
|
|
@ -245,16 +245,16 @@ describe('artifact manifest', () => {
|
|||
[
|
||||
{
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-sl',
|
||||
packageRoot: 'python/klo-sl',
|
||||
packageName: 'ktx-sl',
|
||||
packageRoot: 'python/ktx-sl',
|
||||
packageVersion: '0.1.0',
|
||||
private: false,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
},
|
||||
{
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-daemon',
|
||||
packageRoot: 'python/klo-daemon',
|
||||
packageName: 'ktx-daemon',
|
||||
packageRoot: 'python/ktx-daemon',
|
||||
packageVersion: '0.1.0',
|
||||
private: false,
|
||||
releaseMode: 'ci-artifact-only',
|
||||
|
|
@ -294,38 +294,38 @@ describe('artifact manifest', () => {
|
|||
{
|
||||
artifactKind: 'wheel',
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-daemon',
|
||||
packageName: 'ktx-daemon',
|
||||
packageVersion: '0.1.0',
|
||||
path: 'python/klo_daemon-0.1.0-py3-none-any.whl',
|
||||
path: 'python/ktx_daemon-0.1.0-py3-none-any.whl',
|
||||
},
|
||||
{
|
||||
artifactKind: 'sdist',
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-daemon',
|
||||
packageName: 'ktx-daemon',
|
||||
packageVersion: '0.1.0',
|
||||
path: 'python/klo_daemon-0.1.0.tar.gz',
|
||||
path: 'python/ktx_daemon-0.1.0.tar.gz',
|
||||
},
|
||||
{
|
||||
artifactKind: 'wheel',
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-sl',
|
||||
packageName: 'ktx-sl',
|
||||
packageVersion: '0.1.0',
|
||||
path: 'python/klo_sl-0.1.0-py3-none-any.whl',
|
||||
path: 'python/ktx_sl-0.1.0-py3-none-any.whl',
|
||||
},
|
||||
{
|
||||
artifactKind: 'sdist',
|
||||
ecosystem: 'python',
|
||||
packageName: 'klo-sl',
|
||||
packageName: 'ktx-sl',
|
||||
packageVersion: '0.1.0',
|
||||
path: 'python/klo_sl-0.1.0.tar.gz',
|
||||
path: 'python/ktx_sl-0.1.0.tar.gz',
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const sqliteEntry = manifest.files.find((file) => file.path === 'npm/klo-connector-sqlite-0.0.0-private.tgz');
|
||||
const sqliteEntry = manifest.files.find((file) => file.path === 'npm/ktx-connector-sqlite-0.0.0-private.tgz');
|
||||
assert.ok(sqliteEntry);
|
||||
assert.equal(sqliteEntry.bytes, Buffer.byteLength('@klo/connector-sqlite-tarball'));
|
||||
assert.equal(sqliteEntry.sha256, createHash('sha256').update('@klo/connector-sqlite-tarball').digest('hex'));
|
||||
assert.equal(sqliteEntry.bytes, Buffer.byteLength('@ktx/connector-sqlite-tarball'));
|
||||
assert.equal(sqliteEntry.sha256, createHash('sha256').update('@ktx/connector-sqlite-tarball').digest('hex'));
|
||||
|
||||
const writtenManifest = JSON.parse(await readFile(artifactManifestPath(layout), 'utf-8'));
|
||||
assert.deepEqual(writtenManifest, manifest);
|
||||
|
|
@ -337,7 +337,7 @@ describe('artifact manifest', () => {
|
|||
|
||||
describe('verifyArtifactManifest', () => {
|
||||
it('accepts a schema version 2 manifest that matches the artifact directory', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-verify-manifest-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-verify-manifest-test-'));
|
||||
const layout = packageArtifactLayout(root);
|
||||
try {
|
||||
await writeReleaseMetadataInputs(root);
|
||||
|
|
@ -359,7 +359,7 @@ describe('verifyArtifactManifest', () => {
|
|||
});
|
||||
|
||||
it('rejects a manifest when a file checksum has drifted', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-checksum-drift-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-checksum-drift-test-'));
|
||||
const layout = packageArtifactLayout(root);
|
||||
try {
|
||||
await writeReleaseMetadataInputs(root);
|
||||
|
|
@ -379,7 +379,7 @@ describe('verifyArtifactManifest', () => {
|
|||
});
|
||||
|
||||
it('rejects a manifest with an unsafe artifact path', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-path-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-path-test-'));
|
||||
const layout = packageArtifactLayout(root);
|
||||
try {
|
||||
await writeReleaseMetadataInputs(root);
|
||||
|
|
@ -397,7 +397,7 @@ describe('verifyArtifactManifest', () => {
|
|||
});
|
||||
|
||||
it('rejects a manifest from the wrong source revision when one is required', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-artifacts-revision-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-revision-test-'));
|
||||
const layout = packageArtifactLayout(root);
|
||||
try {
|
||||
await writeReleaseMetadataInputs(root);
|
||||
|
|
@ -422,10 +422,10 @@ describe('verifyArtifactManifest', () => {
|
|||
describe('pythonArtifactInstallArgs', () => {
|
||||
it('installs the built Python wheels by artifact path', () => {
|
||||
const args = pythonArtifactInstallArgs('/tmp/smoke/.venv/bin/python', {
|
||||
kloSlWheel: '/repo/klo/dist/artifacts/python/klo_sl-0.1.0-py3-none-any.whl',
|
||||
kloSlSdist: '/repo/klo/dist/artifacts/python/klo_sl-0.1.0.tar.gz',
|
||||
kloDaemonWheel: '/repo/klo/dist/artifacts/python/klo_daemon-0.1.0-py3-none-any.whl',
|
||||
kloDaemonSdist: '/repo/klo/dist/artifacts/python/klo_daemon-0.1.0.tar.gz',
|
||||
ktxSlWheel: '/repo/ktx/dist/artifacts/python/ktx_sl-0.1.0-py3-none-any.whl',
|
||||
ktxSlSdist: '/repo/ktx/dist/artifacts/python/ktx_sl-0.1.0.tar.gz',
|
||||
ktxDaemonWheel: '/repo/ktx/dist/artifacts/python/ktx_daemon-0.1.0-py3-none-any.whl',
|
||||
ktxDaemonSdist: '/repo/ktx/dist/artifacts/python/ktx_daemon-0.1.0.tar.gz',
|
||||
});
|
||||
|
||||
assert.deepEqual(args, [
|
||||
|
|
@ -433,26 +433,26 @@ describe('pythonArtifactInstallArgs', () => {
|
|||
'install',
|
||||
'--python',
|
||||
'/tmp/smoke/.venv/bin/python',
|
||||
'/repo/klo/dist/artifacts/python/klo_sl-0.1.0-py3-none-any.whl',
|
||||
'/repo/klo/dist/artifacts/python/klo_daemon-0.1.0-py3-none-any.whl',
|
||||
'/repo/ktx/dist/artifacts/python/ktx_sl-0.1.0-py3-none-any.whl',
|
||||
'/repo/ktx/dist/artifacts/python/ktx_daemon-0.1.0-py3-none-any.whl',
|
||||
]);
|
||||
assert.equal(args.includes('klo-daemon'), false);
|
||||
assert.equal(args.includes('ktx-daemon'), false);
|
||||
assert.equal(args.includes('--find-links'), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('npmSmokePythonEnv', () => {
|
||||
it('prepends the npm smoke virtualenv bin directory to PATH', () => {
|
||||
const env = npmSmokePythonEnv('/tmp/klo-npm-smoke', { PATH: '/usr/bin' });
|
||||
const env = npmSmokePythonEnv('/tmp/ktx-npm-smoke', { PATH: '/usr/bin' });
|
||||
|
||||
assert.match(env.PATH, /^\/tmp\/klo-npm-smoke\/\.venv\/(bin|Scripts)/);
|
||||
assert.match(env.PATH, /^\/tmp\/ktx-npm-smoke\/\.venv\/(bin|Scripts)/);
|
||||
assert.match(env.PATH, /\/usr\/bin$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verification snippets', () => {
|
||||
it('pins smoke dependencies and connector packages to clean-install-safe artifacts', () => {
|
||||
const layout = packageArtifactLayout('/repo/klo');
|
||||
const layout = packageArtifactLayout('/repo/ktx');
|
||||
const packageJson = npmSmokePackageJson(layout);
|
||||
|
||||
for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
|
||||
|
|
@ -473,10 +473,10 @@ describe('verification snippets', () => {
|
|||
assert.equal(packageJson.scripts['artifacts:verify-manifest'], 'node scripts/package-artifacts.mjs verify-manifest');
|
||||
});
|
||||
|
||||
it('verifies installed dbt extraction exports from @klo/context/ingest', () => {
|
||||
it('verifies installed dbt extraction exports from @ktx/context/ingest', () => {
|
||||
const source = npmVerifySource();
|
||||
|
||||
assert.match(source, /const ingest = await import\('@klo\/context\/ingest'\);/);
|
||||
assert.match(source, /const ingest = await import\('@ktx\/context\/ingest'\);/);
|
||||
assert.match(source, /const dbtExtractionExports = \[/);
|
||||
assert.match(source, /throw new Error\('Missing dbt extraction export: ' \+ exportName\);/);
|
||||
|
||||
|
|
@ -498,26 +498,26 @@ describe('verification snippets', () => {
|
|||
it('asserts the public npm and connector entry points that clean installs must expose', () => {
|
||||
const source = npmVerifySource();
|
||||
|
||||
assert.match(source, /@klo\/context/);
|
||||
assert.match(source, /@klo\/context\/project/);
|
||||
assert.match(source, /@klo\/context\/mcp/);
|
||||
assert.match(source, /@klo\/context\/memory/);
|
||||
assert.match(source, /@klo\/context\/daemon/);
|
||||
assert.match(source, /@klo\/cli/);
|
||||
assert.match(source, /@klo\/llm/);
|
||||
assert.match(source, /createKloLlmProvider/);
|
||||
assert.match(source, /KloMessageBuilder/);
|
||||
assert.match(source, /createKloEmbeddingProvider/);
|
||||
assert.match(source, /@ktx\/context/);
|
||||
assert.match(source, /@ktx\/context\/project/);
|
||||
assert.match(source, /@ktx\/context\/mcp/);
|
||||
assert.match(source, /@ktx\/context\/memory/);
|
||||
assert.match(source, /@ktx\/context\/daemon/);
|
||||
assert.match(source, /@ktx\/cli/);
|
||||
assert.match(source, /@ktx\/llm/);
|
||||
assert.match(source, /createKtxLlmProvider/);
|
||||
assert.match(source, /KtxMessageBuilder/);
|
||||
assert.match(source, /createKtxEmbeddingProvider/);
|
||||
assert.doesNotMatch(source, /createGatewayLlmProvider/);
|
||||
assert.match(source, /createLocalProjectMemoryCapture/);
|
||||
for (const packageName of CONNECTOR_PACKAGE_NAMES) {
|
||||
assert.match(source, new RegExp(packageName.replace('/', '\\/')));
|
||||
}
|
||||
assert.match(source, /KloSqliteScanConnector/);
|
||||
assert.match(source, /KloPostgresScanConnector/);
|
||||
assert.match(source, /KloBigQueryScanConnector/);
|
||||
assert.match(source, /KloSnowflakeScanConnector/);
|
||||
assert.match(source, /KloPostHogScanConnector/);
|
||||
assert.match(source, /KtxSqliteScanConnector/);
|
||||
assert.match(source, /KtxPostgresScanConnector/);
|
||||
assert.match(source, /KtxBigQueryScanConnector/);
|
||||
assert.match(source, /KtxSnowflakeScanConnector/);
|
||||
assert.match(source, /KtxPostHogScanConnector/);
|
||||
});
|
||||
|
||||
it('asserts installed hybrid search exports and CLI smoke coverage', () => {
|
||||
|
|
@ -525,19 +525,19 @@ describe('verification snippets', () => {
|
|||
const runtimeSource = npmRuntimeSmokeSource();
|
||||
const demoSource = npmDemoSmokeSource();
|
||||
|
||||
assert.match(verifySource, /const search = await import\('@klo\/context\/search'\);/);
|
||||
assert.match(verifySource, /const search = await import\('@ktx\/context\/search'\);/);
|
||||
assert.match(verifySource, /HybridSearchCore/);
|
||||
assert.match(verifySource, /assertSearchBackendConformanceCase/);
|
||||
assert.match(verifySource, /assertSearchBackendCapabilities/);
|
||||
|
||||
assert.match(runtimeSource, /klo agent wiki search hybrid metadata verified/);
|
||||
assert.match(runtimeSource, /klo agent sl list hybrid metadata verified/);
|
||||
assert.match(runtimeSource, /ktx agent wiki search hybrid metadata verified/);
|
||||
assert.match(runtimeSource, /ktx agent sl list hybrid metadata verified/);
|
||||
assert.match(runtimeSource, /agent_sl_search_missing_project/);
|
||||
assert.match(runtimeSource, /agent_sl_search_no_connections/);
|
||||
assert.match(runtimeSource, /agent_sl_search_no_indexed_sources/);
|
||||
|
||||
assert.match(demoSource, /klo seeded demo agent wiki search verified/);
|
||||
assert.match(demoSource, /klo seeded demo agent sl search verified/);
|
||||
assert.match(demoSource, /ktx seeded demo agent wiki search verified/);
|
||||
assert.match(demoSource, /ktx seeded demo agent sl search verified/);
|
||||
});
|
||||
|
||||
it('runs installed CLI commands and MCP through an installed daemon HTTP server', () => {
|
||||
|
|
@ -552,7 +552,7 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /startSemanticDaemon/);
|
||||
assert.match(source, /waitForHttpHealth/);
|
||||
assert.match(source, /stopSemanticDaemon/);
|
||||
assert.match(source, /'klo-daemon'/);
|
||||
assert.match(source, /'ktx-daemon'/);
|
||||
assert.match(source, /'serve-http'/);
|
||||
assert.match(source, /'--host'/);
|
||||
assert.match(source, /'127\.0\.0\.1'/);
|
||||
|
|
@ -564,12 +564,12 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /Looker daemon table identifier parser verified/);
|
||||
assert.match(source, /Looker local runtime store verified/);
|
||||
assert.match(source, /semanticComputeUrl/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'setup'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'setup'/);
|
||||
assert.match(source, /knowledge', 'global', 'revenue\.md'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'agent',\s*'wiki',\s*'search'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'agent',\s*'wiki',\s*'search'/);
|
||||
assert.match(source, /semantic-layer', 'warehouse', 'orders\.yaml'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'agent',\s*'sl',\s*'list'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'agent',\s*'sl',\s*'query'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'agent',\s*'sl',\s*'list'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'agent',\s*'sl',\s*'query'/);
|
||||
assert.match(source, /orders\.order_count/);
|
||||
assert.match(source, /sqlite3/);
|
||||
assert.match(source, /driver: sqlite/);
|
||||
|
|
@ -582,12 +582,12 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /slQueryResult\.plan\.execution\.driver, 'sqlite'/);
|
||||
assert.match(source, /"mode": "compile_only"/);
|
||||
assert.match(source, /"mode": "executed"/);
|
||||
assert.match(source, /klo agent sl query sqlite execute/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'klo',\s*'dev',\s*'scan',\s*'warehouse'/);
|
||||
assert.match(source, /ktx agent sl query sqlite execute/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'dev',\s*'scan',\s*'warehouse'/);
|
||||
assert.match(source, /'--mode',\s*'enriched'/);
|
||||
assert.doesNotMatch(source, /'--enrich'/);
|
||||
assert.match(source, /klo scan structural verified/);
|
||||
assert.match(source, /klo scan enriched verified/);
|
||||
assert.match(source, /ktx scan structural verified/);
|
||||
assert.match(source, /ktx scan enriched verified/);
|
||||
assert.match(source, /scanReportJson\.artifactPaths\.manifestShards/);
|
||||
assert.match(source, /scanReportJson\.artifactPaths\.enrichmentArtifacts/);
|
||||
assert.match(source, /enrichment:/);
|
||||
|
|
@ -596,12 +596,12 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /models:/);
|
||||
assert.match(source, /default: smoke\/provider/);
|
||||
assert.match(source, /api_key: env:AI_GATEWAY_API_KEY/);
|
||||
assert.match(source, /run\('pnpm', \['exec', 'klo', 'dev', 'ingest', 'run'/);
|
||||
assert.match(source, /run\('pnpm', \['exec', 'ktx', 'dev', 'ingest', 'run'/);
|
||||
assert.match(source, /'serve', '--mcp', 'stdio'/);
|
||||
assert.doesNotMatch(source, /'--semantic-compute',\n\s*'--execute-queries'/);
|
||||
assert.match(source, /'--memory-capture', '--memory-model', 'smoke\/provider'/);
|
||||
assert.match(source, /mcpServerStderr/);
|
||||
assert.match(source, /klo serve stderr/);
|
||||
assert.match(source, /ktx serve stderr/);
|
||||
assert.match(source, /sl_validate/);
|
||||
assert.match(source, /sl_query/);
|
||||
assert.match(source, /memory_capture/);
|
||||
|
|
@ -614,26 +614,26 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /scan_read_artifact/);
|
||||
assert.match(source, /mcpScanArtifacts\.artifacts\.find/);
|
||||
assert.match(source, /AI_GATEWAY_API_KEY/);
|
||||
assert.match(source, /access\(join\(projectDir, '\.klo', 'db\.sqlite'\)\)/);
|
||||
assert.match(source, /access\(join\(projectDir, '\.ktx', 'db\.sqlite'\)\)/);
|
||||
assert.match(source, /SQLite knowledge index/);
|
||||
assert.match(source, /klo dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway/);
|
||||
assert.match(source, /klo dev ingest provider guard verified/);
|
||||
assert.match(source, /ktx dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway/);
|
||||
assert.match(source, /ktx dev ingest provider guard verified/);
|
||||
});
|
||||
|
||||
describe('npmDemoSmokeSource', () => {
|
||||
it('exercises the public packed-demo first-run contract', () => {
|
||||
const source = npmDemoSmokeSource();
|
||||
|
||||
assert.match(source, /pnpm', \['exec', 'klo', '--help'\]/);
|
||||
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, /klo serve --mcp stdio/);
|
||||
assert.match(source, /ktx serve --mcp stdio/);
|
||||
assert.doesNotMatch(source, new RegExp(["'demo'", "'--mode'", "'deterministic'"].join(', ')));
|
||||
assert.match(source, /'dev', 'doctor', 'setup', '--no-input'/);
|
||||
assert.match(source, /'--plain'/);
|
||||
assert.match(source, /klo setup demo seeded wrote unexpected stderr/);
|
||||
assert.match(source, /ktx setup demo seeded wrote unexpected stderr/);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -649,7 +649,7 @@ describe('verification snippets', () => {
|
|||
const source = pythonVerifySource();
|
||||
|
||||
assert.match(source, /semantic_layer/);
|
||||
assert.match(source, /klo_daemon/);
|
||||
assert.match(source, /ktx_daemon/);
|
||||
assert.match(source, /importlib.metadata/);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { dirname, join, relative, sep } from 'node:path';
|
|||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const scriptPath = fileURLToPath(import.meta.url);
|
||||
const kloRoot = dirname(dirname(scriptPath));
|
||||
const repoRoot = dirname(kloRoot);
|
||||
const ktxRoot = dirname(dirname(scriptPath));
|
||||
const repoRoot = dirname(ktxRoot);
|
||||
|
||||
const packageNameByDir = new Map(
|
||||
[
|
||||
|
|
@ -22,7 +22,7 @@ const packageNameByDir = new Map(
|
|||
'context',
|
||||
'llm',
|
||||
].map((packageDir) => {
|
||||
const manifestPath = join(kloRoot, 'packages', packageDir, 'package.json');
|
||||
const manifestPath = join(ktxRoot, 'packages', packageDir, 'package.json');
|
||||
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
||||
return [packageDir, manifest.name];
|
||||
}),
|
||||
|
|
@ -31,8 +31,8 @@ const packageNameByDir = new Map(
|
|||
const packageCodePattern = /\.(?:ts|tsx|js|jsx|json)$/;
|
||||
const scriptPattern = /\.(?:mjs|js|json)$/;
|
||||
const pythonPackageTests = new Map([
|
||||
['klo-sl', 'python/klo-sl/tests'],
|
||||
['klo-daemon', 'python/klo-daemon/tests'],
|
||||
['ktx-sl', 'python/ktx-sl/tests'],
|
||||
['ktx-daemon', 'python/ktx-daemon/tests'],
|
||||
]);
|
||||
|
||||
function normalizeFilePath(filePath) {
|
||||
|
|
@ -57,7 +57,7 @@ function maybeScriptTest(scriptFile) {
|
|||
}
|
||||
|
||||
const testFile = scriptFile.replace(/\.mjs$/, '.test.mjs');
|
||||
return existsSync(join(kloRoot, testFile)) ? testFile : null;
|
||||
return existsSync(join(ktxRoot, testFile)) ? testFile : null;
|
||||
}
|
||||
|
||||
export function planChecks(files) {
|
||||
|
|
@ -71,14 +71,14 @@ export function planChecks(files) {
|
|||
for (const rawFile of files) {
|
||||
const file = normalizeFilePath(rawFile);
|
||||
|
||||
if (!file.startsWith('klo/')) {
|
||||
if (!file.startsWith('ktx/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const kloFile = file.slice('klo/'.length);
|
||||
const ktxFile = file.slice('ktx/'.length);
|
||||
|
||||
if (kloFile.startsWith('packages/')) {
|
||||
const [, packageDir, ...rest] = kloFile.split('/');
|
||||
if (ktxFile.startsWith('packages/')) {
|
||||
const [, packageDir, ...rest] = ktxFile.split('/');
|
||||
const packageName = packageNameByDir.get(packageDir);
|
||||
const packageFile = rest.join('/');
|
||||
|
||||
|
|
@ -90,8 +90,8 @@ export function planChecks(files) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (kloFile.startsWith('scripts/') && scriptPattern.test(kloFile)) {
|
||||
const testFile = maybeScriptTest(kloFile);
|
||||
if (ktxFile.startsWith('scripts/') && scriptPattern.test(ktxFile)) {
|
||||
const testFile = maybeScriptTest(ktxFile);
|
||||
|
||||
if (testFile) {
|
||||
stablePush(commands, `script-test:${testFile}`, 'node', ['--test', testFile]);
|
||||
|
|
@ -100,8 +100,8 @@ export function planChecks(files) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (kloFile.startsWith('python/')) {
|
||||
const [, packageDir] = kloFile.split('/');
|
||||
if (ktxFile.startsWith('python/')) {
|
||||
const [, packageDir] = ktxFile.split('/');
|
||||
|
||||
if (pythonPackageTests.has(packageDir)) {
|
||||
pythonPackages.add(packageDir);
|
||||
|
|
@ -112,7 +112,7 @@ export function planChecks(files) {
|
|||
|
||||
if (
|
||||
['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'release-policy.json', 'tsconfig.base.json'].includes(
|
||||
kloFile,
|
||||
ktxFile,
|
||||
)
|
||||
) {
|
||||
runBoundaryCheck = true;
|
||||
|
|
@ -120,7 +120,7 @@ export function planChecks(files) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (['pyproject.toml', 'uv.lock', 'uv.toml'].includes(kloFile)) {
|
||||
if (['pyproject.toml', 'uv.lock', 'uv.toml'].includes(ktxFile)) {
|
||||
runAllPythonTests = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ export function runChecks(files) {
|
|||
const commands = planChecks(files);
|
||||
|
||||
if (commands.length === 0) {
|
||||
console.log('No KLO package checks needed for these files.');
|
||||
console.log('No KTX package checks needed for these files.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ export function runChecks(files) {
|
|||
printCommand(command);
|
||||
|
||||
const result = spawnSync(command.cmd, command.args, {
|
||||
cwd: kloRoot,
|
||||
cwd: ktxRoot,
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
});
|
||||
|
|
@ -190,6 +190,6 @@ export function runChecks(files) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (process.argv[1] && relative(repoRoot, process.argv[1]).split(sep).join('/') === 'klo/scripts/precommit-check.mjs') {
|
||||
if (process.argv[1] && relative(repoRoot, process.argv[1]).split(sep).join('/') === 'ktx/scripts/precommit-check.mjs') {
|
||||
process.exitCode = runChecks(process.argv.slice(2));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,26 +8,26 @@ function commandKeys(files) {
|
|||
}
|
||||
|
||||
describe('precommit-check', () => {
|
||||
it('skips files outside klo', () => {
|
||||
it('skips files outside ktx', () => {
|
||||
assert.deepEqual(commandKeys(['server/src/app.ts']), []);
|
||||
});
|
||||
|
||||
it('runs only the touched package checks for package code', () => {
|
||||
assert.deepEqual(commandKeys(['klo/packages/cli/src/index.ts']), [
|
||||
assert.deepEqual(commandKeys(['ktx/packages/cli/src/index.ts']), [
|
||||
'boundary-check',
|
||||
'type-check:@klo/cli',
|
||||
'build:@klo/cli',
|
||||
'test:@klo/cli',
|
||||
'type-check:@ktx/cli',
|
||||
'build:@ktx/cli',
|
||||
'test:@ktx/cli',
|
||||
]);
|
||||
});
|
||||
|
||||
it('runs the matching script test when a script changes', () => {
|
||||
assert.deepEqual(commandKeys(['klo/scripts/check-boundaries.mjs']), [
|
||||
assert.deepEqual(commandKeys(['ktx/scripts/check-boundaries.mjs']), [
|
||||
'script-test:scripts/check-boundaries.test.mjs',
|
||||
]);
|
||||
});
|
||||
|
||||
it('runs the touched python package tests', () => {
|
||||
assert.deepEqual(commandKeys(['klo/python/klo-sl/semantic_layer/parser.py']), ['pytest:klo-sl']);
|
||||
assert.deepEqual(commandKeys(['ktx/python/ktx-sl/semantic_layer/parser.py']), ['pytest:ktx-sl']);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import { access, chmod } from 'node:fs/promises';
|
|||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
export function kloRootDir() {
|
||||
export function ktxRootDir() {
|
||||
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
}
|
||||
|
||||
export function cliBinPath(rootDir = kloRootDir()) {
|
||||
export function cliBinPath(rootDir = ktxRootDir()) {
|
||||
return resolve(rootDir, 'packages', 'cli', 'dist', 'bin.js');
|
||||
}
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ async function canExecute(path) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function ensureCliBinExecutable(rootDir = kloRootDir()) {
|
||||
export async function ensureCliBinExecutable(rootDir = ktxRootDir()) {
|
||||
const binPath = cliBinPath(rootDir);
|
||||
await access(binPath, constants.R_OK);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ export async function ensureCliBinExecutable(rootDir = kloRootDir()) {
|
|||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
try {
|
||||
const binPath = await ensureCliBinExecutable();
|
||||
process.stdout.write(`Prepared KLO CLI bin: ${binPath}\n`);
|
||||
process.stdout.write(`Prepared KTX CLI bin: ${binPath}\n`);
|
||||
} catch (error) {
|
||||
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
||||
process.exitCode = 1;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises';
|
|||
|
||||
export const DEFAULT_VERSION_TAG = 'latest';
|
||||
export const NO_PACKAGE_REASON =
|
||||
'Set KLO_PUBLISHED_KLO_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.';
|
||||
'Set KTX_PUBLISHED_KTX_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.';
|
||||
|
||||
function optionalTrimmedString(value) {
|
||||
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
|
||||
|
|
@ -53,7 +53,7 @@ export function readPublishedPackageSmokeConfig(env = process.env, args = proces
|
|||
const requireConfig = args.includes('--require-config');
|
||||
const policy = normalizePolicyConfig(policyConfig);
|
||||
|
||||
const envPackageName = optionalTrimmedString(env.KLO_PUBLISHED_KLO_PACKAGE);
|
||||
const envPackageName = optionalTrimmedString(env.KTX_PUBLISHED_KTX_PACKAGE);
|
||||
const packageName = envPackageName ?? policy.packageName;
|
||||
|
||||
if (!packageName) {
|
||||
|
|
@ -68,24 +68,24 @@ export function readPublishedPackageSmokeConfig(env = process.env, args = proces
|
|||
assertSafePackageName(
|
||||
packageName,
|
||||
configSource === 'environment'
|
||||
? 'KLO_PUBLISHED_KLO_PACKAGE'
|
||||
? 'KTX_PUBLISHED_KTX_PACKAGE'
|
||||
: 'release-policy.json publishedPackageSmoke.packageName',
|
||||
);
|
||||
|
||||
const packageVersion = optionalTrimmedString(env.KLO_PUBLISHED_KLO_VERSION) ?? policy.version;
|
||||
const packageVersion = optionalTrimmedString(env.KTX_PUBLISHED_KTX_VERSION) ?? policy.version;
|
||||
assertSafeVersionTag(
|
||||
packageVersion,
|
||||
optionalTrimmedString(env.KLO_PUBLISHED_KLO_VERSION)
|
||||
? 'KLO_PUBLISHED_KLO_VERSION'
|
||||
optionalTrimmedString(env.KTX_PUBLISHED_KTX_VERSION)
|
||||
? 'KTX_PUBLISHED_KTX_VERSION'
|
||||
: 'release-policy.json publishedPackageSmoke.version',
|
||||
);
|
||||
|
||||
const registry = optionalTrimmedString(env.KLO_PUBLISHED_KLO_REGISTRY) ?? policy.registry;
|
||||
const registry = optionalTrimmedString(env.KTX_PUBLISHED_KTX_REGISTRY) ?? policy.registry;
|
||||
if (registry) {
|
||||
assertHttpRegistry(
|
||||
registry,
|
||||
optionalTrimmedString(env.KLO_PUBLISHED_KLO_REGISTRY)
|
||||
? 'KLO_PUBLISHED_KLO_REGISTRY'
|
||||
optionalTrimmedString(env.KTX_PUBLISHED_KTX_REGISTRY)
|
||||
? 'KTX_PUBLISHED_KTX_REGISTRY'
|
||||
: 'release-policy.json publishedPackageSmoke.registry',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,19 +94,19 @@ function assertMissingProjectReadiness(result, emptyProjectDir) {
|
|||
ok: false,
|
||||
error: {
|
||||
code: 'agent_sl_search_missing_project',
|
||||
message: `Semantic-layer search needs an initialized KLO project at ${emptyProjectDir}.`,
|
||||
message: `Semantic-layer search needs an initialized KTX project at ${emptyProjectDir}.`,
|
||||
nextSteps: [
|
||||
'klo demo',
|
||||
`klo setup --project-dir ${emptyProjectDir}`,
|
||||
'klo ingest <connection>',
|
||||
`klo agent sl list --json --query "revenue" --project-dir ${emptyProjectDir}`,
|
||||
'ktx demo',
|
||||
`ktx setup --project-dir ${emptyProjectDir}`,
|
||||
'ktx ingest <connection>',
|
||||
`ktx agent sl list --json --query "revenue" --project-dir ${emptyProjectDir}`,
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function runPublishedPackageSmoke(config) {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-published-package-smoke-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-published-package-smoke-'));
|
||||
try {
|
||||
const projectDir = join(root, 'demo-project');
|
||||
const emptyProjectDir = join(root, 'empty-project');
|
||||
|
|
@ -147,7 +147,7 @@ async function main() {
|
|||
if (config.requireConfig) {
|
||||
throw new Error(config.reason);
|
||||
}
|
||||
process.stdout.write(`Published KLO package smoke skipped: ${config.reason}\n`);
|
||||
process.stdout.write(`Published KTX package smoke skipped: ${config.reason}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ describe('published package smoke config', () => {
|
|||
enabled: false,
|
||||
requireConfig: false,
|
||||
reason:
|
||||
'Set KLO_PUBLISHED_KLO_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.',
|
||||
'Set KTX_PUBLISHED_KTX_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ describe('published package smoke config', () => {
|
|||
enabled: false,
|
||||
requireConfig: true,
|
||||
reason:
|
||||
'Set KLO_PUBLISHED_KLO_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.',
|
||||
'Set KTX_PUBLISHED_KTX_PACKAGE or release-policy.json publishedPackageSmoke.packageName to the published npm package name after the release decision.',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -32,9 +32,9 @@ describe('published package smoke config', () => {
|
|||
assert.deepEqual(
|
||||
readPublishedPackageSmokeConfig(
|
||||
{
|
||||
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-public',
|
||||
KLO_PUBLISHED_KLO_VERSION: 'latest',
|
||||
KLO_PUBLISHED_KLO_REGISTRY: 'https://registry.npmjs.org/',
|
||||
KTX_PUBLISHED_KTX_PACKAGE: '@ktx/cli-public',
|
||||
KTX_PUBLISHED_KTX_VERSION: 'latest',
|
||||
KTX_PUBLISHED_KTX_REGISTRY: 'https://registry.npmjs.org/',
|
||||
},
|
||||
[],
|
||||
),
|
||||
|
|
@ -42,7 +42,7 @@ describe('published package smoke config', () => {
|
|||
enabled: true,
|
||||
requireConfig: false,
|
||||
configSource: 'environment',
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
packageVersion: 'latest',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -55,7 +55,7 @@ describe('published package smoke config', () => {
|
|||
{},
|
||||
[],
|
||||
{
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
version: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -64,7 +64,7 @@ describe('published package smoke config', () => {
|
|||
enabled: true,
|
||||
requireConfig: false,
|
||||
configSource: 'release-policy',
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
packageVersion: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -75,12 +75,12 @@ describe('published package smoke config', () => {
|
|||
assert.deepEqual(
|
||||
readPublishedPackageSmokeConfig(
|
||||
{
|
||||
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-from-env',
|
||||
KLO_PUBLISHED_KLO_VERSION: 'latest',
|
||||
KTX_PUBLISHED_KTX_PACKAGE: '@ktx/cli-from-env',
|
||||
KTX_PUBLISHED_KTX_VERSION: 'latest',
|
||||
},
|
||||
[],
|
||||
{
|
||||
packageName: '@klo/cli-from-policy',
|
||||
packageName: '@ktx/cli-from-policy',
|
||||
version: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -89,7 +89,7 @@ describe('published package smoke config', () => {
|
|||
enabled: true,
|
||||
requireConfig: false,
|
||||
configSource: 'environment',
|
||||
packageName: '@klo/cli-from-env',
|
||||
packageName: '@ktx/cli-from-env',
|
||||
packageVersion: 'latest',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -98,12 +98,12 @@ describe('published package smoke config', () => {
|
|||
|
||||
it('rejects package names that would be unsafe as npx package specs', () => {
|
||||
assert.throws(
|
||||
() => readPublishedPackageSmokeConfig({ KLO_PUBLISHED_KLO_PACKAGE: '--package=@evil/pkg' }, []),
|
||||
/Invalid KLO_PUBLISHED_KLO_PACKAGE/,
|
||||
() => readPublishedPackageSmokeConfig({ KTX_PUBLISHED_KTX_PACKAGE: '--package=@evil/pkg' }, []),
|
||||
/Invalid KTX_PUBLISHED_KTX_PACKAGE/,
|
||||
);
|
||||
assert.throws(
|
||||
() => readPublishedPackageSmokeConfig({ KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli public' }, []),
|
||||
/Invalid KLO_PUBLISHED_KLO_PACKAGE/,
|
||||
() => readPublishedPackageSmokeConfig({ KTX_PUBLISHED_KTX_PACKAGE: '@ktx/cli public' }, []),
|
||||
/Invalid KTX_PUBLISHED_KTX_PACKAGE/,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
|
|
@ -111,7 +111,7 @@ describe('published package smoke config', () => {
|
|||
{},
|
||||
[],
|
||||
{
|
||||
packageName: '@klo/cli public',
|
||||
packageName: '@ktx/cli public',
|
||||
version: 'latest',
|
||||
registry: null,
|
||||
},
|
||||
|
|
@ -125,23 +125,23 @@ describe('published package smoke config', () => {
|
|||
() =>
|
||||
readPublishedPackageSmokeConfig(
|
||||
{
|
||||
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-public',
|
||||
KLO_PUBLISHED_KLO_VERSION: '--tag latest',
|
||||
KTX_PUBLISHED_KTX_PACKAGE: '@ktx/cli-public',
|
||||
KTX_PUBLISHED_KTX_VERSION: '--tag latest',
|
||||
},
|
||||
[],
|
||||
),
|
||||
/Invalid KLO_PUBLISHED_KLO_VERSION/,
|
||||
/Invalid KTX_PUBLISHED_KTX_VERSION/,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
readPublishedPackageSmokeConfig(
|
||||
{
|
||||
KLO_PUBLISHED_KLO_PACKAGE: '@klo/cli-public',
|
||||
KLO_PUBLISHED_KLO_REGISTRY: 'file:///tmp/npm',
|
||||
KTX_PUBLISHED_KTX_PACKAGE: '@ktx/cli-public',
|
||||
KTX_PUBLISHED_KTX_REGISTRY: 'file:///tmp/npm',
|
||||
},
|
||||
[],
|
||||
),
|
||||
/KLO_PUBLISHED_KLO_REGISTRY must be an http\(s\) URL/,
|
||||
/KTX_PUBLISHED_KTX_REGISTRY must be an http\(s\) URL/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -150,30 +150,30 @@ describe('published package smoke command construction', () => {
|
|||
const config = {
|
||||
enabled: true,
|
||||
requireConfig: false,
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
packageVersion: 'latest',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
};
|
||||
|
||||
it('builds the npx package spec from package name and version tag', () => {
|
||||
assert.equal(publishedPackageSpec(config), '@klo/cli-public@latest');
|
||||
assert.equal(publishedPackageSpec(config), '@ktx/cli-public@latest');
|
||||
});
|
||||
|
||||
it('builds npx commands with a registry env patch instead of shell interpolation', () => {
|
||||
assert.deepEqual(buildPublishedPackageNpxCommand(config, ['--version']), {
|
||||
label: 'published package command',
|
||||
command: 'npx',
|
||||
args: ['--yes', '@klo/cli-public@latest', '--version'],
|
||||
args: ['--yes', '@ktx/cli-public@latest', '--version'],
|
||||
env: { npm_config_registry: 'https://registry.npmjs.org/' },
|
||||
});
|
||||
});
|
||||
|
||||
it('builds the full hybrid-search smoke command list', () => {
|
||||
assert.deepEqual(buildPublishedPackageSmokeCommands(config, '/tmp/klo-smoke/demo', '/tmp/klo-smoke/empty'), [
|
||||
assert.deepEqual(buildPublishedPackageSmokeCommands(config, '/tmp/ktx-smoke/demo', '/tmp/ktx-smoke/empty'), [
|
||||
{
|
||||
label: 'published package version',
|
||||
command: 'npx',
|
||||
args: ['--yes', '@klo/cli-public@latest', '--version'],
|
||||
args: ['--yes', '@ktx/cli-public@latest', '--version'],
|
||||
env: { npm_config_registry: 'https://registry.npmjs.org/' },
|
||||
},
|
||||
{
|
||||
|
|
@ -181,10 +181,10 @@ describe('published package smoke command construction', () => {
|
|||
command: 'npx',
|
||||
args: [
|
||||
'--yes',
|
||||
'@klo/cli-public@latest',
|
||||
'@ktx/cli-public@latest',
|
||||
'demo',
|
||||
'--project-dir',
|
||||
'/tmp/klo-smoke/demo',
|
||||
'/tmp/ktx-smoke/demo',
|
||||
'--no-input',
|
||||
'--plain',
|
||||
],
|
||||
|
|
@ -195,7 +195,7 @@ describe('published package smoke command construction', () => {
|
|||
command: 'npx',
|
||||
args: [
|
||||
'--yes',
|
||||
'@klo/cli-public@latest',
|
||||
'@ktx/cli-public@latest',
|
||||
'agent',
|
||||
'wiki',
|
||||
'search',
|
||||
|
|
@ -204,7 +204,7 @@ describe('published package smoke command construction', () => {
|
|||
'--limit',
|
||||
'5',
|
||||
'--project-dir',
|
||||
'/tmp/klo-smoke/demo',
|
||||
'/tmp/ktx-smoke/demo',
|
||||
],
|
||||
env: { npm_config_registry: 'https://registry.npmjs.org/' },
|
||||
},
|
||||
|
|
@ -213,7 +213,7 @@ describe('published package smoke command construction', () => {
|
|||
command: 'npx',
|
||||
args: [
|
||||
'--yes',
|
||||
'@klo/cli-public@latest',
|
||||
'@ktx/cli-public@latest',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -221,7 +221,7 @@ describe('published package smoke command construction', () => {
|
|||
'--query',
|
||||
'ARR',
|
||||
'--project-dir',
|
||||
'/tmp/klo-smoke/demo',
|
||||
'/tmp/ktx-smoke/demo',
|
||||
],
|
||||
env: { npm_config_registry: 'https://registry.npmjs.org/' },
|
||||
},
|
||||
|
|
@ -230,7 +230,7 @@ describe('published package smoke command construction', () => {
|
|||
command: 'npx',
|
||||
args: [
|
||||
'--yes',
|
||||
'@klo/cli-public@latest',
|
||||
'@ktx/cli-public@latest',
|
||||
'agent',
|
||||
'sl',
|
||||
'list',
|
||||
|
|
@ -238,7 +238,7 @@ describe('published package smoke command construction', () => {
|
|||
'--query',
|
||||
'revenue',
|
||||
'--project-dir',
|
||||
'/tmp/klo-smoke/empty',
|
||||
'/tmp/ktx-smoke/empty',
|
||||
],
|
||||
env: { npm_config_registry: 'https://registry.npmjs.org/' },
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ import { execFile as childExecFile } from 'node:child_process';
|
|||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { promisify } from 'node:util';
|
||||
import { runWorkspaceKlo } from './run-klo.mjs';
|
||||
import { runWorkspaceKtx } from './run-ktx.mjs';
|
||||
|
||||
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
||||
const kloRootDir = resolve(scriptDir, '..');
|
||||
const repoRootDir = resolve(kloRootDir, '..');
|
||||
const defaultProjectDir = resolve(kloRootDir, 'examples/orbit-relationship-verification');
|
||||
const ktxRootDir = resolve(scriptDir, '..');
|
||||
const repoRootDir = resolve(ktxRootDir, '..');
|
||||
const defaultProjectDir = resolve(ktxRootDir, 'examples/orbit-relationship-verification');
|
||||
const defaultReportPath = resolve(
|
||||
kloRootDir,
|
||||
ktxRootDir,
|
||||
'examples/orbit-relationship-verification/reports/orbit-verification.md',
|
||||
);
|
||||
const defaultExecFile = promisify(childExecFile);
|
||||
|
|
@ -43,7 +43,7 @@ export function defaultOrbitVerificationProjectDir() {
|
|||
}
|
||||
|
||||
function shellCommand(argv) {
|
||||
return ['pnpm', 'run', 'klo', '--', ...argv].join(' ');
|
||||
return ['pnpm', 'run', 'ktx', '--', ...argv].join(' ');
|
||||
}
|
||||
|
||||
function firstNonEmptyLine(...values) {
|
||||
|
|
@ -61,8 +61,8 @@ function firstNonEmptyLine(...values) {
|
|||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
connectionId: process.env.KLO_ORBIT_CONNECTION_ID ?? 'orbit',
|
||||
projectDir: process.env.KLO_ORBIT_PROJECT_DIR ?? defaultProjectDir,
|
||||
connectionId: process.env.KTX_ORBIT_CONNECTION_ID ?? 'orbit',
|
||||
projectDir: process.env.KTX_ORBIT_PROJECT_DIR ?? defaultProjectDir,
|
||||
reportPath: defaultReportPath,
|
||||
};
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ function formatBlocked(result) {
|
|||
|
||||
export function formatOrbitVerificationMarkdown(result) {
|
||||
const lines = [
|
||||
'# KLO Relationship Discovery Orbit Verification',
|
||||
'# KTX Relationship Discovery Orbit Verification',
|
||||
'',
|
||||
`Date: ${result.date}`,
|
||||
'',
|
||||
|
|
@ -219,7 +219,7 @@ export function formatOrbitVerificationMarkdown(result) {
|
|||
return `${lines.join('\n')}\n`;
|
||||
}
|
||||
|
||||
async function runBufferedWorkspaceKlo(runner, argv, rootDir, execFile) {
|
||||
async function runBufferedWorkspaceKtx(runner, argv, rootDir, execFile) {
|
||||
const stdout = new BufferWriter();
|
||||
const stderr = new BufferWriter();
|
||||
const exitCode = await runner(argv, { rootDir, execFile, stdout, stderr });
|
||||
|
|
@ -241,11 +241,11 @@ function orbitVerificationEnv(projectDir) {
|
|||
}
|
||||
|
||||
export async function runOrbitVerification(options = {}) {
|
||||
const connectionId = options.connectionId ?? process.env.KLO_ORBIT_CONNECTION_ID ?? 'orbit';
|
||||
const projectDir = options.projectDir ?? process.env.KLO_ORBIT_PROJECT_DIR ?? defaultProjectDir;
|
||||
const connectionId = options.connectionId ?? process.env.KTX_ORBIT_CONNECTION_ID ?? 'orbit';
|
||||
const projectDir = options.projectDir ?? process.env.KTX_ORBIT_PROJECT_DIR ?? defaultProjectDir;
|
||||
const reportPath = options.reportPath ?? defaultReportPath;
|
||||
const rootDir = options.rootDir ?? kloRootDir;
|
||||
const runner = options.runWorkspaceKlo ?? runWorkspaceKlo;
|
||||
const rootDir = options.rootDir ?? ktxRootDir;
|
||||
const runner = options.runWorkspaceKtx ?? runWorkspaceKtx;
|
||||
const execFile = options.execFile ?? defaultExecFile;
|
||||
const now = options.now ?? (() => new Date());
|
||||
const mkdir = options.mkdir ?? fsMkdir;
|
||||
|
|
@ -255,7 +255,7 @@ export async function runOrbitVerification(options = {}) {
|
|||
const runWithEnv = (argv, runnerOptions) => runner(argv, { ...runnerOptions, env });
|
||||
|
||||
const scanArgv = buildOrbitScanArgv({ connectionId, projectDir });
|
||||
const scan = await runBufferedWorkspaceKlo(runWithEnv, scanArgv, rootDir, execFile);
|
||||
const scan = await runBufferedWorkspaceKtx(runWithEnv, scanArgv, rootDir, execFile);
|
||||
let result;
|
||||
|
||||
if (scan.exitCode !== 0) {
|
||||
|
|
@ -280,13 +280,13 @@ export async function runOrbitVerification(options = {}) {
|
|||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
scanExitCode: scan.exitCode,
|
||||
blocker: 'KLO scan completed without printing a Run id',
|
||||
blocker: 'KTX scan completed without printing a Run id',
|
||||
scanStdout: scan.stdout,
|
||||
scanStderr: scan.stderr,
|
||||
};
|
||||
} else {
|
||||
const reportArgv = buildOrbitReportArgv({ projectDir, runId });
|
||||
const reportOutput = await runBufferedWorkspaceKlo(runWithEnv, reportArgv, rootDir, execFile);
|
||||
const reportOutput = await runBufferedWorkspaceKtx(runWithEnv, reportArgv, rootDir, execFile);
|
||||
if (reportOutput.exitCode !== 0) {
|
||||
result = {
|
||||
status: 'blocked',
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ function successReportJson() {
|
|||
}
|
||||
|
||||
describe('relationship Orbit verification helper', () => {
|
||||
it('exposes the Orbit verification command from the KLO workspace package', async () => {
|
||||
it('exposes the Orbit verification command from the KTX workspace package', async () => {
|
||||
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
||||
|
||||
assert.equal(
|
||||
|
|
@ -59,7 +59,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('builds the current KLO launcher arguments for scan and JSON report commands', () => {
|
||||
it('builds the current KTX launcher arguments for scan and JSON report commands', () => {
|
||||
assert.deepEqual(buildOrbitScanArgv({ connectionId: 'orbit', projectDir: '/tmp/orbit-project' }), [
|
||||
'dev',
|
||||
'scan',
|
||||
|
|
@ -92,14 +92,14 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async (path, content) => {
|
||||
writes.push({ path, content });
|
||||
},
|
||||
runWorkspaceKlo: async (argv, options) => {
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
envs.push(options.env);
|
||||
if (argv[2] === 'report') {
|
||||
options.stdout.write(successReportJson());
|
||||
return 0;
|
||||
}
|
||||
options.stdout.write('KLO scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
|
|
@ -116,8 +116,8 @@ describe('relationship Orbit verification helper', () => {
|
|||
});
|
||||
|
||||
it('extracts the run id from human scan output', () => {
|
||||
assert.equal(extractRunId(`KLO scan completed\nStatus: done\nRun: scan-orbit-1\nConnection: orbit\n`), 'scan-orbit-1');
|
||||
assert.equal(extractRunId('KLO scan completed without a run line\n'), null);
|
||||
assert.equal(extractRunId(`KTX scan completed\nStatus: done\nRun: scan-orbit-1\nConnection: orbit\n`), 'scan-orbit-1');
|
||||
assert.equal(extractRunId('KTX scan completed without a run line\n'), null);
|
||||
});
|
||||
|
||||
it('formats successful Orbit verification evidence from the JSON report', () => {
|
||||
|
|
@ -126,16 +126,16 @@ describe('relationship Orbit verification helper', () => {
|
|||
date: '2026-05-07',
|
||||
connectionId: 'orbit',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
scanCommand: 'pnpm run klo -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
|
||||
reportCommand: 'pnpm run klo -- dev scan report --json --project-dir /tmp/orbit-project scan-orbit-1',
|
||||
scanCommand: 'pnpm run ktx -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
|
||||
reportCommand: 'pnpm run ktx -- dev scan report --json --project-dir /tmp/orbit-project scan-orbit-1',
|
||||
scanExitCode: 0,
|
||||
reportExitCode: 0,
|
||||
scanStdout: 'KLO scan completed\nRun: scan-orbit-1\n',
|
||||
scanStdout: 'KTX scan completed\nRun: scan-orbit-1\n',
|
||||
scanStderr: '',
|
||||
report: JSON.parse(successReportJson()),
|
||||
});
|
||||
|
||||
assert.match(markdown, /# KLO Relationship Discovery Orbit Verification/);
|
||||
assert.match(markdown, /# KTX Relationship Discovery Orbit Verification/);
|
||||
assert.match(markdown, /Outcome/);
|
||||
assert.match(markdown, /Exit code: 0/);
|
||||
assert.match(markdown, /Accepted: 14/);
|
||||
|
|
@ -152,7 +152,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
date: '2026-05-07',
|
||||
connectionId: 'orbit',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
scanCommand: 'pnpm run klo -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
|
||||
scanCommand: 'pnpm run ktx -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
|
||||
scanExitCode: 1,
|
||||
blocker: 'Connection "orbit" was not found',
|
||||
scanStdout: '',
|
||||
|
|
@ -177,13 +177,13 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async (path, content) => {
|
||||
writes.push({ path, content });
|
||||
},
|
||||
runWorkspaceKlo: async (argv, options) => {
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
if (argv[2] === 'report') {
|
||||
options.stdout.write(successReportJson());
|
||||
return 0;
|
||||
}
|
||||
options.stdout.write('KLO scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
|
|
@ -209,7 +209,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async (path, content) => {
|
||||
writes.push({ path, content });
|
||||
},
|
||||
runWorkspaceKlo: async (_argv, options) => {
|
||||
runWorkspaceKtx: async (_argv, options) => {
|
||||
options.stderr.write('Connection "orbit" was not found\n');
|
||||
return 1;
|
||||
},
|
||||
|
|
@ -231,14 +231,14 @@ describe('relationship Orbit verification helper', () => {
|
|||
mkdir: async () => {},
|
||||
writeFile: async () => {},
|
||||
execFile: async () => ({ stdout: '', stderr: '' }),
|
||||
runWorkspaceKlo: async (_argv, options) => {
|
||||
runWorkspaceKtx: async (_argv, options) => {
|
||||
sawExecFile = typeof options.execFile === 'function';
|
||||
options.stderr.write('ENOENT: no such file or directory, open \'/tmp/orbit-project/klo.yaml\'\n');
|
||||
options.stderr.write('ENOENT: no such file or directory, open \'/tmp/orbit-project/ktx.yaml\'\n');
|
||||
return 1;
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(sawExecFile, true);
|
||||
assert.equal(result.blocker, "ENOENT: no such file or directory, open '/tmp/orbit-project/klo.yaml'");
|
||||
assert.equal(result.blocker, "ENOENT: no such file or directory, open '/tmp/orbit-project/ktx.yaml'");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -218,9 +218,9 @@ async function main() {
|
|||
return;
|
||||
}
|
||||
|
||||
process.stdout.write(`KLO release mode: ${report.releaseMode}\n`);
|
||||
process.stdout.write(`KLO source revision: ${report.sourceRevision ?? 'local'}\n`);
|
||||
process.stdout.write(`KLO packages: ${report.packageNames.join(', ')}\n`);
|
||||
process.stdout.write(`KTX release mode: ${report.releaseMode}\n`);
|
||||
process.stdout.write(`KTX source revision: ${report.sourceRevision ?? 'local'}\n`);
|
||||
process.stdout.write(`KTX packages: ${report.packageNames.join(', ')}\n`);
|
||||
process.stdout.write(`Published package smoke: ${report.publishedPackageSmokeGate.status}\n`);
|
||||
process.stdout.write(`Published package smoke script: ${report.publishedPackageSmokeGate.script}\n`);
|
||||
process.stdout.write(`Published package smoke reason: ${report.publishedPackageSmokeGate.reason}\n`);
|
||||
|
|
|
|||
|
|
@ -18,24 +18,24 @@ async function writeReleaseMetadataInputs(root, options = {}) {
|
|||
name: packageInfo.name,
|
||||
version: '0.0.0-private',
|
||||
private:
|
||||
packageInfo.name === '@klo/context'
|
||||
packageInfo.name === '@ktx/context'
|
||||
? (options.contextPrivate ?? true)
|
||||
: packageInfo.name === '@klo/cli'
|
||||
: packageInfo.name === '@ktx/cli'
|
||||
? (options.cliPrivate ?? true)
|
||||
: true,
|
||||
});
|
||||
}
|
||||
|
||||
await mkdir(join(root, 'python', 'klo-sl'), { recursive: true });
|
||||
await mkdir(join(root, 'python', 'klo-daemon'), { recursive: true });
|
||||
await mkdir(join(root, 'python', 'ktx-sl'), { recursive: true });
|
||||
await mkdir(join(root, 'python', 'ktx-daemon'), { recursive: true });
|
||||
|
||||
await writeFile(
|
||||
join(root, 'python', 'klo-sl', 'pyproject.toml'),
|
||||
['[project]', 'name = "klo-sl"', 'version = "0.1.0"', ''].join('\n'),
|
||||
join(root, 'python', 'ktx-sl', 'pyproject.toml'),
|
||||
['[project]', 'name = "ktx-sl"', 'version = "0.1.0"', ''].join('\n'),
|
||||
);
|
||||
await writeFile(
|
||||
join(root, 'python', 'klo-daemon', 'pyproject.toml'),
|
||||
['[project]', 'name = "klo-daemon"', 'version = "0.1.0"', ''].join('\n'),
|
||||
join(root, 'python', 'ktx-daemon', 'pyproject.toml'),
|
||||
['[project]', 'name = "ktx-daemon"', 'version = "0.1.0"', ''].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -48,10 +48,10 @@ async function writeUploadableArtifactFixtures(layout) {
|
|||
layout.npmTarballs[packageInfo.name],
|
||||
`${packageInfo.name}-tarball`,
|
||||
]),
|
||||
[join(layout.pythonDir, 'klo_sl-0.1.0-py3-none-any.whl'), 'klo-sl-wheel'],
|
||||
[join(layout.pythonDir, 'klo_sl-0.1.0.tar.gz'), 'klo-sl-sdist'],
|
||||
[join(layout.pythonDir, 'klo_daemon-0.1.0-py3-none-any.whl'), 'klo-daemon-wheel'],
|
||||
[join(layout.pythonDir, 'klo_daemon-0.1.0.tar.gz'), 'klo-daemon-sdist'],
|
||||
[join(layout.pythonDir, 'ktx_sl-0.1.0-py3-none-any.whl'), 'ktx-sl-wheel'],
|
||||
[join(layout.pythonDir, 'ktx_sl-0.1.0.tar.gz'), 'ktx-sl-sdist'],
|
||||
[join(layout.pythonDir, 'ktx_daemon-0.1.0-py3-none-any.whl'), 'ktx-daemon-wheel'],
|
||||
[join(layout.pythonDir, 'ktx_daemon-0.1.0.tar.gz'), 'ktx-daemon-sdist'],
|
||||
]);
|
||||
|
||||
for (const [path, contents] of fileContents) {
|
||||
|
|
@ -74,7 +74,7 @@ function releasePolicy(overrides = {}) {
|
|||
python: {
|
||||
publish: false,
|
||||
repository: null,
|
||||
packages: ['klo-sl', 'klo-daemon'],
|
||||
packages: ['ktx-sl', 'ktx-daemon'],
|
||||
...pythonOverrides,
|
||||
},
|
||||
publishedPackageSmoke: {
|
||||
|
|
@ -109,8 +109,8 @@ async function writeReadyFixture(root, options = {}) {
|
|||
}
|
||||
|
||||
describe('release readiness policy', () => {
|
||||
it('reads the checked release policy path from the KLO root', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-policy-test-'));
|
||||
it('reads the checked release policy path from the KTX root', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-policy-test-'));
|
||||
try {
|
||||
const policy = releasePolicy();
|
||||
await writePolicy(root, policy);
|
||||
|
|
@ -123,7 +123,7 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('accepts the current ci-artifact-only policy, package metadata, and artifact manifest', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-ready-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-ready-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root);
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ describe('release readiness policy', () => {
|
|||
sourceRevision: 'abc123',
|
||||
npmPublishEnabled: false,
|
||||
pythonPublishEnabled: false,
|
||||
packageNames: [...NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name), 'klo-sl', 'klo-daemon'],
|
||||
packageNames: [...NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name), 'ktx-sl', 'ktx-daemon'],
|
||||
publishedPackageSmokeGate: {
|
||||
status: 'not_required',
|
||||
script: 'pnpm run release:published-smoke',
|
||||
|
|
@ -159,12 +159,12 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('reports policy-controlled published package smoke config when present', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-config-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-smoke-config-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
publishedPackageSmoke: {
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
version: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -178,7 +178,7 @@ describe('release readiness policy', () => {
|
|||
script: 'pnpm run release:published-smoke',
|
||||
reason: 'Published package smoke remains pending until release-policy.json enables npm registry publishing.',
|
||||
configSource: 'release-policy',
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
version: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
});
|
||||
|
|
@ -188,13 +188,13 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('reports required published package smoke when release mode requires it', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-required-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-smoke-required-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
releaseMode: 'published-package-smoke-required',
|
||||
publishedPackageSmoke: {
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
version: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -210,13 +210,13 @@ describe('release readiness policy', () => {
|
|||
sourceRevision: 'abc123',
|
||||
npmPublishEnabled: false,
|
||||
pythonPublishEnabled: false,
|
||||
packageNames: [...NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name), 'klo-sl', 'klo-daemon'],
|
||||
packageNames: [...NPM_ARTIFACT_PACKAGES.map((packageInfo) => packageInfo.name), 'ktx-sl', 'ktx-daemon'],
|
||||
publishedPackageSmokeGate: {
|
||||
status: 'required',
|
||||
script: 'pnpm run release:published-smoke',
|
||||
reason: 'Run the published package smoke before accepting the hybrid-search release.',
|
||||
configSource: 'release-policy',
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
version: '2026.5.8',
|
||||
registry: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
|
@ -228,7 +228,7 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects required published smoke mode without a package name', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-required-missing-config-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-smoke-required-missing-config-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
|
|
@ -247,13 +247,13 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects required published smoke mode while publishing decisions remain', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-required-blocked-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-smoke-required-blocked-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
releaseMode: 'published-package-smoke-required',
|
||||
publishedPackageSmoke: {
|
||||
packageName: '@klo/cli-public',
|
||||
packageName: '@ktx/cli-public',
|
||||
version: 'latest',
|
||||
registry: null,
|
||||
},
|
||||
|
|
@ -270,7 +270,7 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects unsupported release modes', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-unsupported-mode-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-unsupported-mode-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
|
|
@ -288,7 +288,7 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects publish-enabled npm policy while releaseMode is ci-artifact-only', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-npm-publish-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-npm-publish-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
|
|
@ -306,7 +306,7 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects publish-enabled Python policy while releaseMode is ci-artifact-only', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-python-publish-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-python-publish-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
|
|
@ -324,12 +324,12 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects unsafe release-policy published package smoke config', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-smoke-invalid-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-smoke-invalid-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, {
|
||||
policy: releasePolicy({
|
||||
publishedPackageSmoke: {
|
||||
packageName: '@klo/cli public',
|
||||
packageName: '@ktx/cli public',
|
||||
version: 'latest',
|
||||
registry: null,
|
||||
},
|
||||
|
|
@ -346,13 +346,13 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects a public npm package while releaseMode is ci-artifact-only', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-public-npm-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-public-npm-test-'));
|
||||
try {
|
||||
await writeReadyFixture(root, { contextPrivate: false });
|
||||
|
||||
await assert.rejects(
|
||||
() => releaseReadinessReport(root),
|
||||
/ci-artifact-only policy npm package @klo\/context must remain private/,
|
||||
/ci-artifact-only policy npm package @ktx\/context must remain private/,
|
||||
);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
|
|
@ -360,7 +360,7 @@ describe('release readiness policy', () => {
|
|||
});
|
||||
|
||||
it('rejects stale artifacts before reporting release readiness', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'klo-release-stale-artifact-test-'));
|
||||
const root = await mkdtemp(join(tmpdir(), 'ktx-release-stale-artifact-test-'));
|
||||
try {
|
||||
const layout = await writeReadyFixture(root);
|
||||
await writeFile(layout.cliTarball, 'changed-cli-tarball');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { access as fsAccess, readdir as fsReaddir, stat as fsStat } from 'node:f
|
|||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
function kloRootDir() {
|
||||
function ktxRootDir() {
|
||||
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
}
|
||||
|
||||
|
|
@ -131,9 +131,9 @@ function runInherited(command, args, options) {
|
|||
});
|
||||
}
|
||||
|
||||
export async function runWorkspaceKlo(argv, options = {}) {
|
||||
export async function runWorkspaceKtx(argv, options = {}) {
|
||||
const cliArgv = argv[0] === '--' ? argv.slice(1) : argv;
|
||||
const rootDir = options.rootDir ?? kloRootDir();
|
||||
const rootDir = options.rootDir ?? ktxRootDir();
|
||||
const stdout = options.stdout ?? process.stdout;
|
||||
const stderr = options.stderr ?? process.stderr;
|
||||
const access = options.access ?? fsAccess;
|
||||
|
|
@ -155,13 +155,13 @@ export async function runWorkspaceKlo(argv, options = {}) {
|
|||
if (needsBuild) {
|
||||
stderr.write(
|
||||
binExists
|
||||
? 'KLO CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'
|
||||
: 'KLO CLI build output is missing. Building it now with `pnpm run build`...\n',
|
||||
? 'KTX CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'
|
||||
: 'KTX CLI build output is missing. Building it now with `pnpm run build`...\n',
|
||||
);
|
||||
const buildExitCode = await runCommand('pnpm', ['run', 'build'], { cwd: rootDir, env: commandEnv });
|
||||
if (buildExitCode !== 0) {
|
||||
stderr.write(
|
||||
'\nKLO CLI build failed. Run `pnpm run setup:dev` from the KLO directory, then retry this command.\n',
|
||||
'\nKTX CLI build failed. Run `pnpm run setup:dev` from the KTX directory, then retry this command.\n',
|
||||
);
|
||||
return buildExitCode;
|
||||
}
|
||||
|
|
@ -171,5 +171,5 @@ export async function runWorkspaceKlo(argv, options = {}) {
|
|||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
process.exitCode = await runWorkspaceKlo(process.argv.slice(2));
|
||||
process.exitCode = await runWorkspaceKtx(process.argv.slice(2));
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { runWorkspaceKlo } from './run-klo.mjs';
|
||||
import { runWorkspaceKtx } from './run-ktx.mjs';
|
||||
|
||||
function freshBuildFs() {
|
||||
return {
|
||||
|
|
@ -20,19 +20,19 @@ function freshBuildFs() {
|
|||
};
|
||||
}
|
||||
|
||||
test('runWorkspaceKlo runs the built CLI when it already exists', async () => {
|
||||
test('runWorkspaceKtx runs the built CLI when it already exists', async () => {
|
||||
const calls = [];
|
||||
const logs = [];
|
||||
const fs = freshBuildFs();
|
||||
|
||||
const exitCode = await runWorkspaceKlo(['--version'], {
|
||||
rootDir: '/workspace/klo',
|
||||
const exitCode = await runWorkspaceKtx(['--version'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: fs.stat,
|
||||
readdir: fs.readdir,
|
||||
execFile: async (command, args, options) => {
|
||||
calls.push({ command, args, cwd: options.cwd });
|
||||
return { stdout: '@klo/cli 0.0.0-private\n', stderr: '' };
|
||||
return { stdout: '@ktx/cli 0.0.0-private\n', stderr: '' };
|
||||
},
|
||||
stdout: { write: (chunk) => logs.push(['stdout', chunk]) },
|
||||
stderr: { write: (chunk) => logs.push(['stderr', chunk]) },
|
||||
|
|
@ -42,26 +42,26 @@ test('runWorkspaceKlo runs the built CLI when it already exists', async () => {
|
|||
assert.deepEqual(calls, [
|
||||
{
|
||||
command: process.execPath,
|
||||
args: ['/workspace/klo/packages/cli/dist/bin.js', '--version'],
|
||||
cwd: '/workspace/klo',
|
||||
args: ['/workspace/ktx/packages/cli/dist/bin.js', '--version'],
|
||||
cwd: '/workspace/ktx',
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(logs, [['stdout', '@klo/cli 0.0.0-private\n']]);
|
||||
assert.deepEqual(logs, [['stdout', '@ktx/cli 0.0.0-private\n']]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKlo forwards a caller-provided environment to buffered commands', async () => {
|
||||
test('runWorkspaceKtx forwards a caller-provided environment to buffered commands', async () => {
|
||||
const calls = [];
|
||||
const fs = freshBuildFs();
|
||||
|
||||
const exitCode = await runWorkspaceKlo(['--version'], {
|
||||
rootDir: '/workspace/klo',
|
||||
const exitCode = await runWorkspaceKtx(['--version'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: fs.stat,
|
||||
readdir: fs.readdir,
|
||||
env: { PATH: '/bin', GIT_CEILING_DIRECTORIES: '/workspace/klo/examples' },
|
||||
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: '@klo/cli 0.0.0-private\n', stderr: '' };
|
||||
return { stdout: '@ktx/cli 0.0.0-private\n', stderr: '' };
|
||||
},
|
||||
stdout: { write: () => undefined },
|
||||
stderr: { write: () => undefined },
|
||||
|
|
@ -71,25 +71,25 @@ test('runWorkspaceKlo forwards a caller-provided environment to buffered command
|
|||
assert.deepEqual(calls, [
|
||||
{
|
||||
command: process.execPath,
|
||||
args: ['/workspace/klo/packages/cli/dist/bin.js', '--version'],
|
||||
cwd: '/workspace/klo',
|
||||
env: { PATH: '/bin', GIT_CEILING_DIRECTORIES: '/workspace/klo/examples' },
|
||||
args: ['/workspace/ktx/packages/cli/dist/bin.js', '--version'],
|
||||
cwd: '/workspace/ktx',
|
||||
env: { PATH: '/bin', GIT_CEILING_DIRECTORIES: '/workspace/ktx/examples' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKlo drops a leading npm argument separator', async () => {
|
||||
test('runWorkspaceKtx drops a leading npm argument separator', async () => {
|
||||
const calls = [];
|
||||
const fs = freshBuildFs();
|
||||
|
||||
const exitCode = await runWorkspaceKlo(['--', 'connection', 'test', 'warehouse', '--help'], {
|
||||
rootDir: '/workspace/klo',
|
||||
const exitCode = await runWorkspaceKtx(['--', 'connection', 'test', 'warehouse', '--help'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: fs.stat,
|
||||
readdir: fs.readdir,
|
||||
execFile: async (command, args, options) => {
|
||||
calls.push({ command, args, cwd: options.cwd });
|
||||
return { stdout: 'Usage: klo connection test\n', stderr: '' };
|
||||
return { stdout: 'Usage: ktx connection test\n', stderr: '' };
|
||||
},
|
||||
stdout: { write: () => undefined },
|
||||
stderr: { write: () => undefined },
|
||||
|
|
@ -99,18 +99,18 @@ test('runWorkspaceKlo drops a leading npm argument separator', async () => {
|
|||
assert.deepEqual(calls, [
|
||||
{
|
||||
command: process.execPath,
|
||||
args: ['/workspace/klo/packages/cli/dist/bin.js', 'connection', 'test', 'warehouse', '--help'],
|
||||
cwd: '/workspace/klo',
|
||||
args: ['/workspace/ktx/packages/cli/dist/bin.js', 'connection', 'test', 'warehouse', '--help'],
|
||||
cwd: '/workspace/ktx',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKlo skips stale-build checks for shell completion when dist exists', async () => {
|
||||
test('runWorkspaceKtx skips stale-build checks for shell completion when dist exists', async () => {
|
||||
const calls = [];
|
||||
let statCalls = 0;
|
||||
|
||||
const exitCode = await runWorkspaceKlo(['dev', '__complete', '--shell', 'zsh', '--position', '2', '--', 'klo', ''], {
|
||||
rootDir: '/workspace/klo',
|
||||
const exitCode = await runWorkspaceKtx(['dev', '__complete', '--shell', 'zsh', '--position', '2', '--', 'ktx', ''], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: async (path) => {
|
||||
statCalls += 1;
|
||||
|
|
@ -136,7 +136,7 @@ test('runWorkspaceKlo skips stale-build checks for shell completion when dist ex
|
|||
{
|
||||
command: process.execPath,
|
||||
args: [
|
||||
'/workspace/klo/packages/cli/dist/bin.js',
|
||||
'/workspace/ktx/packages/cli/dist/bin.js',
|
||||
'dev',
|
||||
'__complete',
|
||||
'--shell',
|
||||
|
|
@ -144,21 +144,21 @@ test('runWorkspaceKlo skips stale-build checks for shell completion when dist ex
|
|||
'--position',
|
||||
'2',
|
||||
'--',
|
||||
'klo',
|
||||
'ktx',
|
||||
'',
|
||||
],
|
||||
cwd: '/workspace/klo',
|
||||
cwd: '/workspace/ktx',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKlo builds the workspace CLI before running it when dist is missing', async () => {
|
||||
test('runWorkspaceKtx builds the workspace CLI before running it when dist is missing', async () => {
|
||||
const calls = [];
|
||||
const logs = [];
|
||||
let binExists = false;
|
||||
|
||||
const exitCode = await runWorkspaceKlo(['setup', 'demo', '--mode', 'replay', '--no-input', '--viz'], {
|
||||
rootDir: '/workspace/klo',
|
||||
const exitCode = await runWorkspaceKtx(['setup', 'demo', '--mode', 'replay', '--no-input', '--viz'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => {
|
||||
if (!binExists) {
|
||||
throw Object.assign(new Error('missing'), { code: 'ENOENT' });
|
||||
|
|
@ -183,24 +183,24 @@ test('runWorkspaceKlo builds the workspace CLI before running it when dist is mi
|
|||
['pnpm', ['run', 'build']],
|
||||
[
|
||||
process.execPath,
|
||||
['/workspace/klo/packages/cli/dist/bin.js', 'setup', 'demo', '--mode', 'replay', '--no-input', '--viz'],
|
||||
['/workspace/ktx/packages/cli/dist/bin.js', 'setup', 'demo', '--mode', 'replay', '--no-input', '--viz'],
|
||||
],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(logs, [
|
||||
['stderr', 'KLO CLI build output is missing. Building it now with `pnpm run build`...\n'],
|
||||
['stderr', 'KTX CLI build output is missing. Building it now with `pnpm run build`...\n'],
|
||||
['stdout', 'build ok\n'],
|
||||
['stdout', 'Replay complete\n'],
|
||||
]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKlo rebuilds before running when workspace sources are newer than dist', async () => {
|
||||
test('runWorkspaceKtx rebuilds before running when workspace sources are newer than dist', async () => {
|
||||
const calls = [];
|
||||
const logs = [];
|
||||
let sourceMtimeMs = 3000;
|
||||
|
||||
const exitCode = await runWorkspaceKlo(['dev', 'scan', 'orbit', '--enrich'], {
|
||||
rootDir: '/workspace/klo',
|
||||
const exitCode = await runWorkspaceKtx(['dev', 'scan', 'orbit', '--enrich'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: async (path) => ({
|
||||
mtimeMs: path.endsWith('/packages/cli/dist/bin.js') ? 2000 : sourceMtimeMs,
|
||||
|
|
@ -232,11 +232,11 @@ test('runWorkspaceKlo rebuilds before running when workspace sources are newer t
|
|||
calls.map((call) => [call.command, call.args]),
|
||||
[
|
||||
['pnpm', ['run', 'build']],
|
||||
[process.execPath, ['/workspace/klo/packages/cli/dist/bin.js', 'dev', 'scan', 'orbit', '--enrich']],
|
||||
[process.execPath, ['/workspace/ktx/packages/cli/dist/bin.js', 'dev', 'scan', 'orbit', '--enrich']],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(logs, [
|
||||
['stderr', 'KLO CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'],
|
||||
['stderr', 'KTX CLI build output is stale. Rebuilding it now with `pnpm run build`...\n'],
|
||||
['stdout', 'build ok\n'],
|
||||
['stdout', 'scan ok\n'],
|
||||
]);
|
||||
|
|
@ -7,7 +7,7 @@ import { promisify } from 'node:util';
|
|||
|
||||
const execFileAsync = promisify(execFileCallback);
|
||||
|
||||
function kloRootDir() {
|
||||
function ktxRootDir() {
|
||||
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
}
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ function failureText(error) {
|
|||
}
|
||||
|
||||
export async function runSetupDev(options = {}) {
|
||||
const rootDir = options.rootDir ?? kloRootDir();
|
||||
const rootDir = options.rootDir ?? ktxRootDir();
|
||||
const execFile = options.execFile ?? execFileAsync;
|
||||
const log = options.log ?? ((line) => process.stdout.write(`${line}\n`));
|
||||
const phases = [
|
||||
|
|
@ -45,7 +45,7 @@ export async function runSetupDev(options = {}) {
|
|||
name: 'doctor setup',
|
||||
command: process.execPath,
|
||||
args: ['packages/cli/dist/bin.js', 'dev', 'doctor', 'setup', '--no-input'],
|
||||
retry: 'pnpm run klo -- dev doctor setup --no-input',
|
||||
retry: 'pnpm run ktx -- dev doctor setup --no-input',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ export async function runSetupDev(options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
log('Workspace CLI: pnpm run klo -- --help');
|
||||
log('Workspace CLI: pnpm run ktx -- --help');
|
||||
log('Optional global dev link: pnpm run link:dev');
|
||||
return { ok: true };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ test('runSetupDev runs phased setup without global linking', async () => {
|
|||
const logs = [];
|
||||
|
||||
const result = await runSetupDev({
|
||||
rootDir: '/workspace/klo',
|
||||
rootDir: '/workspace/ktx',
|
||||
execFile: async (command, args, options) => {
|
||||
calls.push({ command, args, cwd: options.cwd });
|
||||
return { stdout: `${command} ${args.join(' ')}`, stderr: '' };
|
||||
|
|
@ -34,7 +34,7 @@ test('runSetupDev stops at the failed phase and prints a retry command', async (
|
|||
const logs = [];
|
||||
|
||||
const result = await runSetupDev({
|
||||
rootDir: '/workspace/klo',
|
||||
rootDir: '/workspace/ktx',
|
||||
execFile: async (command, args) => {
|
||||
calls.push({ command, args });
|
||||
if (args.includes('native:rebuild')) {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ function assertIncludesAll(text, values) {
|
|||
}
|
||||
}
|
||||
|
||||
describe('standalone KLO CI workflow', () => {
|
||||
describe('standalone KTX CI workflow', () => {
|
||||
it('runs the package checks from a filtered repository root', async () => {
|
||||
const workflow = await readText('.github/workflows/ci.yml');
|
||||
|
||||
assert.match(workflow, /^name: KLO CI/m);
|
||||
assert.match(workflow, /^name: KTX CI/m);
|
||||
assertIncludesAll(workflow, [
|
||||
'permissions:',
|
||||
'contents: read',
|
||||
|
|
@ -37,9 +37,9 @@ describe('standalone KLO CI workflow', () => {
|
|||
]);
|
||||
|
||||
assert.doesNotMatch(workflow, /sparse-checkout/);
|
||||
assert.doesNotMatch(workflow, /cd klo/);
|
||||
assert.doesNotMatch(workflow, /klo\/pnpm-lock\.yaml/);
|
||||
assert.doesNotMatch(workflow, /klo\/uv\.lock/);
|
||||
assert.doesNotMatch(workflow, /cd ktx/);
|
||||
assert.doesNotMatch(workflow, /ktx\/pnpm-lock\.yaml/);
|
||||
assert.doesNotMatch(workflow, /ktx\/uv\.lock/);
|
||||
});
|
||||
|
||||
it('uploads verified artifacts from root-relative paths', async () => {
|
||||
|
|
@ -47,7 +47,7 @@ describe('standalone KLO CI workflow', () => {
|
|||
|
||||
assertIncludesAll(workflow, [
|
||||
'actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f',
|
||||
'name: klo-package-artifacts-${{ github.sha }}',
|
||||
'name: ktx-package-artifacts-${{ github.sha }}',
|
||||
'dist/artifacts/manifest.json',
|
||||
'dist/artifacts/npm/*.tgz',
|
||||
'dist/artifacts/python/*.whl',
|
||||
|
|
@ -56,7 +56,7 @@ describe('standalone KLO CI workflow', () => {
|
|||
'retention-days: 7',
|
||||
]);
|
||||
|
||||
assert.doesNotMatch(workflow, /klo\/dist\/artifacts/);
|
||||
assert.doesNotMatch(workflow, /ktx\/dist\/artifacts/);
|
||||
});
|
||||
|
||||
it('syncs injected workspace packages after package builds', async () => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { readFileSync } from 'node:fs';
|
|||
const [backend, filePath] = process.argv.slice(2);
|
||||
|
||||
function usage() {
|
||||
process.stderr.write('Usage: node klo/scripts/validate-llm-debug-jsonl.mjs anthropic|vertex /path/to/debug.jsonl\n');
|
||||
process.stderr.write('Usage: node ktx/scripts/validate-llm-debug-jsonl.mjs anthropic|vertex /path/to/debug.jsonl\n');
|
||||
}
|
||||
|
||||
function fail(message) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ function runValidator(args) {
|
|||
}
|
||||
|
||||
function writeDebugJsonl(records) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'klo-llm-debug-validator-'));
|
||||
const dir = mkdtempSync(join(tmpdir(), 'ktx-llm-debug-validator-'));
|
||||
const filePath = join(dir, 'debug.jsonl');
|
||||
writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join('\n')}\n`, 'utf8');
|
||||
return filePath;
|
||||
|
|
@ -52,7 +52,7 @@ test('prints usage and exits 2 when required arguments are missing', () => {
|
|||
const result = runValidator([]);
|
||||
|
||||
assert.equal(result.status, 2);
|
||||
assert.match(result.stderr, /Usage: node klo\/scripts\/validate-llm-debug-jsonl\.mjs anthropic\|vertex/);
|
||||
assert.match(result.stderr, /Usage: node ktx\/scripts\/validate-llm-debug-jsonl\.mjs anthropic\|vertex/);
|
||||
});
|
||||
|
||||
test('accepts sanitized debug JSONL with message, message-part, and tool cache markers', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue