chore: standardize daemon naming on "KTX daemon"

Replace inconsistent names ("KTX Python daemon", "KTX local embeddings
daemon", "KTX managed daemon", "Python daemon") with the single name
"KTX daemon" in CLI output, errors, command descriptions, test
assertions, smoke scripts, docs, AGENTS.md, issue templates, and
codecov flags. The daemon is a portable compute server with endpoints
for SQL analysis, semantic layer, LookML, database introspection, and
embeddings; the previous labels misrepresented it as embeddings-only or
exposed implementation details ("Python", "managed").

The "KTX Python runtime" concept (installed interpreter + packages) is
deliberately left as-is — it is a separate concept from the daemon
process.
This commit is contained in:
Andrey Avtomonov 2026-05-20 11:10:27 +02:00
parent 2c9a58bb56
commit a0d3ddbbc2
30 changed files with 62 additions and 62 deletions

View file

@ -44,7 +44,7 @@ body:
- Connector (SQL Server) - Connector (SQL Server)
- Connector (SQLite) - Connector (SQLite)
- Python semantic layer - Python semantic layer
- Python daemon - KTX daemon
- Docs - Docs
- Other - Other
validations: validations:

View file

@ -24,7 +24,7 @@ body:
- Connectors - Connectors
- Context engine - Context engine
- Python semantic layer - Python semantic layer
- Python daemon - KTX daemon
- Docs - Docs
- Other - Other
validations: validations:

View file

@ -69,7 +69,7 @@ KTX is a pnpm + uv workspace.
- LLM package: `packages/llm` - LLM package: `packages/llm`
- Database connectors: `packages/connector-*` - Database connectors: `packages/connector-*`
- Python semantic layer: `python/ktx-sl` - Python semantic layer: `python/ktx-sl`
- Python daemon: `python/ktx-daemon` - KTX daemon: `python/ktx-daemon`
- Examples and fixtures: `examples/` - Examples and fixtures: `examples/`
- Workspace scripts: `scripts/` - Workspace scripts: `scripts/`
- Local agent skills and internal planning docs are private overlays. Do not - Local agent skills and internal planning docs are private overlays. Do not
@ -134,7 +134,7 @@ shared contracts or package exports are affected.
test file test file
- TypeScript dead-code tooling/config changes: `pnpm run dead-code` - TypeScript dead-code tooling/config changes: `pnpm run dead-code`
- Python semantic layer: `uv run pytest python/ktx-sl/tests -q` - Python semantic layer: `uv run pytest python/ktx-sl/tests -q`
- Python daemon: `uv run pytest python/ktx-daemon/tests -q` - KTX daemon: `uv run pytest python/ktx-daemon/tests -q`
- Python files: also run `uv run pre-commit run --files [FILES]` when - Python files: also run `uv run pre-commit run --files [FILES]` when
pre-commit is configured pre-commit is configured

View file

@ -97,7 +97,7 @@ component_management:
paths: paths:
- python/ktx-sl/semantic_layer/** - python/ktx-sl/semantic_layer/**
- component_id: py_daemon - component_id: py_daemon
name: Python daemon name: KTX daemon
paths: paths:
- python/ktx-daemon/src/ktx_daemon/** - python/ktx-daemon/src/ktx_daemon/**

View file

@ -146,7 +146,7 @@ Success requires (canonical shape - supersedes the example in the docs):
Do **not** run `--deep` ingest in this flow - that requires LLM time and is out of scope. Do **not** run `--deep` ingest in this flow - that requires LLM time and is out of scope.
### Optional: directly probe the embeddings daemon ### Optional: directly probe the KTX daemon
If the user asks for stronger verification that `sentence-transformers` is actually serving (not just that setup said "ok"), do all of: If the user asks for stronger verification that `sentence-transformers` is actually serving (not just that setup said "ok"), do all of:
@ -155,7 +155,7 @@ If the user asks for stronger verification that `sentence-transformers` is actua
3. `curl -sS http://127.0.0.1:<port>/health` → expect HTTP 200 with `{"status":"healthy",…}`. 3. `curl -sS http://127.0.0.1:<port>/health` → expect HTTP 200 with `{"status":"healthy",…}`.
4. `curl -sS -X POST http://127.0.0.1:<port>/embeddings/compute -H 'content-type: application/json' -d '{"text":"hello"}'` → expect `{"embedding": [...384 floats...]}`. 4. `curl -sS -X POST http://127.0.0.1:<port>/embeddings/compute -H 'content-type: application/json' -d '{"text":"hello"}'` → expect `{"embedding": [...384 floats...]}`.
Discover the port from setup's log line `Started KTX local embeddings daemon: http://127.0.0.1:<port>` or from the daemon's OpenAPI at `GET /openapi.json`. Note: the routes are `/health` and `/embeddings/compute` - not `/healthz` or `/embeddings`. Discover the port from setup's log line `Started KTX daemon: http://127.0.0.1:<port>` or from the daemon's OpenAPI at `GET /openapi.json`. Note: the routes are `/health` and `/embeddings/compute` - not `/healthz` or `/embeddings`.
## Phase 6 - Final report ## Phase 6 - Final report
@ -167,7 +167,7 @@ KTX SETUP COMPLETE
Project: <path> Project: <path>
LLM: <backend> / <model> LLM: <backend> / <model>
Embeddings: <backend> / <model> Embeddings: <backend> / <model>
Runtime: managed Python ✓ (if sentence-transformers daemon was started) Runtime: managed Python ✓ (if the KTX daemon was started)
Connections: Connections:
- <name> (<driver>) status=ok schemas=[…] tables=<N> - <name> (<driver>) status=ok schemas=[…] tables=<N>

View file

@ -44,8 +44,8 @@ directory. Use it from any directory to generate editor or agent schema files.
| Subcommand | Description | | Subcommand | Description |
|-----------|-------------| |-----------|-------------|
| `install` | Install the bundled Python runtime wheel into the managed runtime | | `install` | Install the bundled Python runtime wheel into the managed runtime |
| `start` | Start the KTX-managed Python HTTP daemon | | `start` | Start the KTX daemon |
| `stop` | Stop the KTX-managed Python HTTP daemon | | `stop` | Stop the KTX daemon |
| `status` | Show managed Python runtime status and readiness checks | | `status` | Show managed Python runtime status and readiness checks |
## `admin runtime` Options ## `admin runtime` Options

View file

@ -25,7 +25,7 @@ documentation, connector coverage, and examples.
| Context engine | `packages/context`, including project config, ingest orchestration, and semantic search | | Context engine | `packages/context`, including project config, ingest orchestration, and semantic search |
| Connectors | `packages/connector-*`, plus connector-specific tests and integration docs | | Connectors | `packages/connector-*`, plus connector-specific tests and integration docs |
| Python semantic layer | `python/ktx-sl` for planning and SQL generation | | Python semantic layer | `python/ktx-sl` for planning and SQL generation |
| Python daemon | `python/ktx-daemon` for the portable runtime API | | KTX daemon | `python/ktx-daemon` for the portable runtime API |
| Documentation | `docs-site/content/docs` for public docs and `docs-site/tests` for docs behavior | | Documentation | `docs-site/content/docs` for public docs and `docs-site/tests` for docs behavior |
## Development setup ## Development setup

View file

@ -27,7 +27,7 @@ warehouse credential.
`postgres-historic/` is a manual Docker-backed smoke for Postgres `postgres-historic/` is a manual Docker-backed smoke for Postgres
query-history ingest via `pg_stat_statements`. It verifies setup, staged query-history ingest via `pg_stat_statements`. It verifies setup, staged
query-history artifacts, managed daemon batch SQL analysis, bounded pattern query-history artifacts, KTX daemon batch SQL analysis, bounded pattern
WorkUnit shards, and no-WorkUnit idempotency for unchanged bucketed table WorkUnit shards, and no-WorkUnit idempotency for unchanged bucketed table
inputs and pattern shards. inputs and pattern shards.

View file

@ -14,7 +14,7 @@ generated local project.
The managed Python runtime smoke requires `uv` on `PATH`, isolates The managed Python runtime smoke requires `uv` on `PATH`, isolates
`KTX_RUNTIME_ROOT`, verifies `ktx admin runtime status`, runs `ktx sl query --yes` to `KTX_RUNTIME_ROOT`, verifies `ktx admin runtime status`, runs `ktx sl query --yes` to
install the core runtime from the bundled wheel, checks `ktx admin runtime status`, install the core runtime from the bundled wheel, checks `ktx admin runtime status`,
starts and reuses the managed daemon, and stops it. starts and reuses the KTX daemon, and stops it.
The artifact manifest contains the public `@kaelio/ktx` npm tarball and the The artifact manifest contains the public `@kaelio/ktx` npm tarball and the
bundled `kaelio-ktx` runtime wheel. The smoke does not install standalone bundled `kaelio-ktx` runtime wheel. The smoke does not install standalone

View file

@ -38,7 +38,7 @@ export function registerRuntimeCommands(program: Command, context: KtxCliCommand
runtime runtime
.command('start') .command('start')
.description('Start the KTX-managed Python HTTP daemon') .description('Start the KTX daemon')
.addOption(createRuntimeFeatureOption()) .addOption(createRuntimeFeatureOption())
.option('--force', 'Restart even when a matching daemon is already running', false) .option('--force', 'Restart even when a matching daemon is already running', false)
.action(async (options: { feature: RuntimeFeature; force?: boolean }, command: CommandWithGlobalOptions) => { .action(async (options: { feature: RuntimeFeature; force?: boolean }, command: CommandWithGlobalOptions) => {
@ -53,7 +53,7 @@ export function registerRuntimeCommands(program: Command, context: KtxCliCommand
runtime runtime
.command('stop') .command('stop')
.description('Stop the KTX-managed Python HTTP daemon') .description('Stop the KTX daemon')
.option('--all', 'Stop all KTX daemon processes recorded or discoverable on this machine', false) .option('--all', 'Stop all KTX daemon processes recorded or discoverable on this machine', false)
.action(async (options: { all?: boolean }, command: CommandWithGlobalOptions) => { .action(async (options: { all?: boolean }, command: CommandWithGlobalOptions) => {
await runRuntimeArgs(context, { await runRuntimeArgs(context, {

View file

@ -706,7 +706,7 @@ function failedStepDetail(result: KtxPublicIngestTargetResult): string | null {
const INTERNAL_FAILURE_LINE_RE = const INTERNAL_FAILURE_LINE_RE =
/^(Report|Run|Job|Status|Adapter|Connection|Sync|Mode|Dry run|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/; /^(Report|Run|Job|Status|Adapter|Connection|Sync|Mode|Dry run|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
const ACTIONABLE_FAILURE_LINE_RE = const ACTIONABLE_FAILURE_LINE_RE =
/^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX managed daemon|Error:|Failed\b|Could not\b|Cannot\b)/; /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/;
function trimErrorPrefix(line: string): string { function trimErrorPrefix(line: string): string {
return line.replace(/^Error:\s*/, ''); return line.replace(/^Error:\s*/, '');

View file

@ -1334,7 +1334,7 @@ describe('runKtxIngest', () => {
); );
}); });
it('passes managed daemon options to adapters and pull-config options when no explicit daemon URL is set', async () => { it('passes KTX daemon options to adapters and pull-config options when no explicit daemon URL is set', async () => {
const projectDir = join(tempDir, 'managed-daemon-ingest-project'); const projectDir = join(tempDir, 'managed-daemon-ingest-project');
await initKtxProject({ projectDir }); await initKtxProject({ projectDir });
await writeWarehouseConfig(projectDir); await writeWarehouseConfig(projectDir);

View file

@ -117,7 +117,7 @@ describe('managedLocalEmbeddingProjectConfig', () => {
}); });
describe('managedLocalEmbeddingHealthConfig', () => { describe('managedLocalEmbeddingHealthConfig', () => {
it('uses the active managed daemon URL for the immediate health check', () => { it('uses the active KTX daemon URL for the immediate health check', () => {
expect( expect(
managedLocalEmbeddingHealthConfig({ managedLocalEmbeddingHealthConfig({
baseUrl: 'http://127.0.0.1:61234', baseUrl: 'http://127.0.0.1:61234',
@ -134,7 +134,7 @@ describe('managedLocalEmbeddingHealthConfig', () => {
}); });
describe('ensureManagedLocalEmbeddingsDaemon', () => { describe('ensureManagedLocalEmbeddingsDaemon', () => {
it('ensures the local-embeddings feature and starts the managed daemon', async () => { it('ensures the local-embeddings feature and starts the KTX daemon', async () => {
const io = makeIo(); const io = makeIo();
const ensureRuntime = vi.fn(async () => runtime()); const ensureRuntime = vi.fn(async () => runtime());
const startDaemon = vi.fn(async () => daemonResult('started')); const startDaemon = vi.fn(async () => daemonResult('started'));
@ -169,7 +169,7 @@ describe('ensureManagedLocalEmbeddingsDaemon', () => {
features: ['local-embeddings'], features: ['local-embeddings'],
force: false, force: false,
}); });
expect(io.stderr()).toContain('Started KTX local embeddings daemon: http://127.0.0.1:61234'); expect(io.stderr()).toContain('Started KTX daemon: http://127.0.0.1:61234');
}); });
it('reuses an already running daemon without reporting a new start', async () => { it('reuses an already running daemon without reporting a new start', async () => {
@ -184,6 +184,6 @@ describe('ensureManagedLocalEmbeddingsDaemon', () => {
startDaemon: vi.fn(async () => daemonResult('reused')), startDaemon: vi.fn(async () => daemonResult('reused')),
}); });
expect(io.stderr()).toContain('Using KTX local embeddings daemon: http://127.0.0.1:61234'); expect(io.stderr()).toContain('Using KTX daemon: http://127.0.0.1:61234');
}); });
}); });

View file

@ -89,7 +89,7 @@ export async function ensureManagedLocalEmbeddingsDaemon(
}); });
const verb = daemon.status === 'started' ? 'Started' : 'Using'; const verb = daemon.status === 'started' ? 'Started' : 'Using';
options.io.stderr.write(`${verb} KTX local embeddings daemon: ${daemon.baseUrl}\n`); options.io.stderr.write(`${verb} KTX daemon: ${daemon.baseUrl}\n`);
return { return {
baseUrl: daemon.baseUrl, baseUrl: daemon.baseUrl,

View file

@ -125,7 +125,7 @@ function daemonOptionsBase(root: string) {
} as const; } as const;
} }
describe('managed Python daemon lifecycle', () => { describe('KTX daemon lifecycle', () => {
let tempDir: string; let tempDir: string;
beforeEach(async () => { beforeEach(async () => {

View file

@ -325,7 +325,7 @@ async function waitForHealth(input: {
return; return;
} }
lastDetail = finalHealth.detail; lastDetail = finalHealth.detail;
throw new Error(`KTX Python daemon failed to start: ${lastDetail}. stderr: ${input.state.stderrLog}`); throw new Error(`KTX daemon failed to start: ${lastDetail}. stderr: ${input.state.stderrLog}`);
} }
async function removeState(layout: ManagedPythonDaemonLayout): Promise<void> { async function removeState(layout: ManagedPythonDaemonLayout): Promise<void> {
@ -705,7 +705,7 @@ export async function startManagedPythonDaemon(
); );
child.unref(); child.unref();
if (!child.pid) { if (!child.pid) {
throw new Error(`KTX Python daemon did not report a pid. stderr: ${layout.daemonStderrPath}`); throw new Error(`KTX daemon did not report a pid. stderr: ${layout.daemonStderrPath}`);
} }
const state: ManagedPythonDaemonState = { const state: ManagedPythonDaemonState = {
schemaVersion: 1, schemaVersion: 1,

View file

@ -57,7 +57,7 @@ describe('createManagedPythonDaemonBaseUrlResolver', () => {
features: ['core'], features: ['core'],
force: false, force: false,
}); });
expect(testIo.stderr()).toContain('Started KTX Python daemon: http://127.0.0.1:61234'); expect(testIo.stderr()).toContain('Started KTX daemon: http://127.0.0.1:61234');
}); });
it('reports daemon reuse without reinstalling after the first resolved URL', async () => { it('reports daemon reuse without reinstalling after the first resolved URL', async () => {
@ -86,7 +86,7 @@ describe('createManagedPythonDaemonBaseUrlResolver', () => {
expect(ensureRuntime).toHaveBeenCalledTimes(1); expect(ensureRuntime).toHaveBeenCalledTimes(1);
expect(startDaemon).toHaveBeenCalledTimes(1); expect(startDaemon).toHaveBeenCalledTimes(1);
expect(testIo.stderr()).toContain('Using existing KTX Python daemon: http://127.0.0.1:61234'); expect(testIo.stderr()).toContain('Using existing KTX daemon: http://127.0.0.1:61234');
}); });
}); });
@ -104,8 +104,8 @@ describe('createManagedDaemonHttpJsonRunner', () => {
}); });
}); });
describe('managed daemon ingest ports', () => { describe('KTX daemon ingest ports', () => {
it('creates a Looker table parser backed by the managed daemon runner', async () => { it('creates a Looker table parser backed by the KTX daemon runner', async () => {
const requestJson = vi.fn(async () => ({ const requestJson = vi.fn(async () => ({
results: { results: {
'model.explore': { 'model.explore': {
@ -135,7 +135,7 @@ describe('managed daemon ingest ports', () => {
}); });
}); });
it('creates a SQL analysis port backed by the managed daemon runner', async () => { it('creates a SQL analysis port backed by the KTX daemon runner', async () => {
const requestJson = vi.fn(async () => ({ const requestJson = vi.fn(async () => ({
fingerprint: 'select-orders', fingerprint: 'select-orders',
normalized_sql: 'SELECT * FROM public.orders WHERE id = ?', normalized_sql: 'SELECT * FROM public.orders WHERE id = ?',
@ -157,7 +157,7 @@ describe('managed daemon ingest ports', () => {
}); });
}); });
it('routes SQL batch analysis through the managed daemon runner', async () => { it('routes SQL batch analysis through the KTX daemon runner', async () => {
const requestJson = vi.fn(async () => ({ const requestJson = vi.fn(async () => ({
results: { results: {
orders: { orders: {

View file

@ -70,7 +70,7 @@ function normalizedBaseUrl(baseUrl: string): string {
function parseJsonObject(raw: string, path: string): Record<string, unknown> { function parseJsonObject(raw: string, path: string): Record<string, unknown> {
const parsed = JSON.parse(raw) as unknown; const parsed = JSON.parse(raw) as unknown;
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`KTX managed daemon HTTP ${path} returned non-object JSON`); throw new Error(`KTX daemon HTTP ${path} returned non-object JSON`);
} }
return parsed as Record<string, unknown>; return parsed as Record<string, unknown>;
} }
@ -101,7 +101,7 @@ export async function postManagedDaemonJson(
const text = Buffer.concat(chunks).toString('utf8'); const text = Buffer.concat(chunks).toString('utf8');
const statusCode = response.statusCode ?? 0; const statusCode = response.statusCode ?? 0;
if (statusCode < 200 || statusCode >= 300) { if (statusCode < 200 || statusCode >= 300) {
reject(new Error(`KTX managed daemon HTTP ${path} failed with ${statusCode}: ${text}`)); reject(new Error(`KTX daemon HTTP ${path} failed with ${statusCode}: ${text}`));
return; return;
} }
try { try {
@ -142,7 +142,7 @@ export function createManagedPythonDaemonBaseUrlResolver(
force: false, force: false,
}); });
const verb = daemon.status === 'started' ? 'Started' : 'Using existing'; const verb = daemon.status === 'started' ? 'Started' : 'Using existing';
options.io.stderr.write(`${verb} KTX Python daemon: ${daemon.baseUrl}\n`); options.io.stderr.write(`${verb} KTX daemon: ${daemon.baseUrl}\n`);
cachedBaseUrl = daemon.baseUrl; cachedBaseUrl = daemon.baseUrl;
return cachedBaseUrl; return cachedBaseUrl;
}; };

View file

@ -679,7 +679,7 @@ function createCapturedPublicIngestIo(): CapturedPublicIngestIo {
const INTERNAL_STATUS_LINE_RE = const INTERNAL_STATUS_LINE_RE =
/^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/; /^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/;
const ACTIONABLE_FAILURE_LINE_RE = const ACTIONABLE_FAILURE_LINE_RE =
/^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX managed daemon|Error:|Failed\b|Could not\b|Cannot\b)/; /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/;
const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed KTX command\.?$/; const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed KTX command\.?$/;
function trimErrorPrefix(line: string): string { function trimErrorPrefix(line: string): string {

View file

@ -94,7 +94,7 @@ export function resolveProjectRuntimeRequirements(
requirements.push({ requirements.push({
feature: 'core', feature: 'core',
reason: 'database-introspection', reason: 'database-introspection',
detail: 'Database introspection fallback uses the Python daemon.', detail: 'Database introspection fallback uses the KTX daemon.',
}); });
} }

View file

@ -109,7 +109,7 @@ describe('runKtxRuntime', () => {
expect(io.stderr()).toBe(''); expect(io.stderr()).toBe('');
}); });
it('starts the managed Python daemon and prints the base URL', async () => { it('starts the KTX daemon and prints the base URL', async () => {
const io = makeIo(); const io = makeIo();
const deps: KtxRuntimeDeps = { const deps: KtxRuntimeDeps = {
startDaemon: vi.fn(async (): Promise<ManagedPythonDaemonStartResult> => ({ startDaemon: vi.fn(async (): Promise<ManagedPythonDaemonStartResult> => ({
@ -160,14 +160,14 @@ describe('runKtxRuntime', () => {
features: ['local-embeddings'], features: ['local-embeddings'],
force: true, force: true,
}); });
expect(io.stdout()).toContain('Started KTX Python daemon'); expect(io.stdout()).toContain('Started KTX daemon');
expect(io.stdout()).toContain('url: http://127.0.0.1:61234'); expect(io.stdout()).toContain('url: http://127.0.0.1:61234');
expect(io.stdout()).toContain('pid: 4242'); expect(io.stdout()).toContain('pid: 4242');
expect(io.stdout()).toContain('features: core, local-embeddings'); expect(io.stdout()).toContain('features: core, local-embeddings');
expect(io.stdout()).toContain('stderr: /work/proj/.ktx/runtime/daemon.stderr.log'); expect(io.stdout()).toContain('stderr: /work/proj/.ktx/runtime/daemon.stderr.log');
}); });
it('stops the managed Python daemon', async () => { it('stops the KTX daemon', async () => {
const io = makeIo(); const io = makeIo();
const deps: KtxRuntimeDeps = { const deps: KtxRuntimeDeps = {
stopDaemon: vi.fn(async (): Promise<ManagedPythonDaemonStopResult> => ({ stopDaemon: vi.fn(async (): Promise<ManagedPythonDaemonStopResult> => ({
@ -208,11 +208,11 @@ describe('runKtxRuntime', () => {
).resolves.toBe(0); ).resolves.toBe(0);
expect(deps.stopDaemon).toHaveBeenCalledWith({ cliVersion: '0.2.0', projectDir: '/work/proj' }); expect(deps.stopDaemon).toHaveBeenCalledWith({ cliVersion: '0.2.0', projectDir: '/work/proj' });
expect(io.stdout()).toContain('Stopped KTX Python daemon'); expect(io.stdout()).toContain('Stopped KTX daemon');
expect(io.stdout()).toContain('pid: 4242'); expect(io.stdout()).toContain('pid: 4242');
}); });
it('stops all discovered Python daemons and reports the summary', async () => { it('stops all discovered KTX daemons and reports the summary', async () => {
const io = makeIo(); const io = makeIo();
const deps: KtxRuntimeDeps = { const deps: KtxRuntimeDeps = {
stopAllDaemons: vi.fn(async (): Promise<ManagedPythonDaemonStopAllResult> => ({ stopAllDaemons: vi.fn(async (): Promise<ManagedPythonDaemonStopAllResult> => ({
@ -231,7 +231,7 @@ describe('runKtxRuntime', () => {
).resolves.toBe(0); ).resolves.toBe(0);
expect(deps.stopAllDaemons).toHaveBeenCalledWith({ cliVersion: '0.2.0', projectDir: '/work/proj' }); expect(deps.stopAllDaemons).toHaveBeenCalledWith({ cliVersion: '0.2.0', projectDir: '/work/proj' });
expect(io.stdout()).toContain('Stopped 2 KTX Python daemons'); expect(io.stdout()).toContain('Stopped 2 KTX daemons');
expect(io.stdout()).toContain('pid: 4242 source: state url: http://127.0.0.1:61234'); expect(io.stdout()).toContain('pid: 4242 source: state url: http://127.0.0.1:61234');
expect(io.stdout()).toContain('pid: 5252 source: process url: http://127.0.0.1:8765'); expect(io.stdout()).toContain('pid: 5252 source: process url: http://127.0.0.1:8765');
}); });
@ -259,7 +259,7 @@ describe('runKtxRuntime', () => {
runKtxRuntime({ command: 'stop', cliVersion: '0.2.0', projectDir: '/work/proj', all: true }, io.io, deps), runKtxRuntime({ command: 'stop', cliVersion: '0.2.0', projectDir: '/work/proj', all: true }, io.io, deps),
).resolves.toBe(1); ).resolves.toBe(1);
expect(io.stderr()).toContain('Stopped 0 KTX Python daemons; failed 1'); expect(io.stderr()).toContain('Stopped 0 KTX daemons; failed 1');
expect(io.stderr()).toContain('pid: 4242 source: state url: http://127.0.0.1:61234'); expect(io.stderr()).toContain('pid: 4242 source: state url: http://127.0.0.1:61234');
expect(io.stderr()).toContain('process scan: ps failed'); expect(io.stderr()).toContain('process scan: ps failed');
}); });

View file

@ -56,7 +56,7 @@ function writeInstallResult(io: KtxCliIo, result: ManagedPythonRuntimeInstallRes
function writeDaemonStart(io: KtxCliIo, result: ManagedPythonDaemonStartResult): void { function writeDaemonStart(io: KtxCliIo, result: ManagedPythonDaemonStartResult): void {
const verb = result.status === 'reused' ? 'Using existing' : 'Started'; const verb = result.status === 'reused' ? 'Using existing' : 'Started';
io.stdout.write(`${verb} KTX Python daemon\n`); io.stdout.write(`${verb} KTX daemon\n`);
io.stdout.write(`url: ${result.baseUrl}\n`); io.stdout.write(`url: ${result.baseUrl}\n`);
io.stdout.write(`pid: ${result.state.pid}\n`); io.stdout.write(`pid: ${result.state.pid}\n`);
io.stdout.write(`version: ${result.state.version}\n`); io.stdout.write(`version: ${result.state.version}\n`);
@ -68,10 +68,10 @@ function writeDaemonStart(io: KtxCliIo, result: ManagedPythonDaemonStartResult):
function writeDaemonStop(io: KtxCliIo, result: ManagedPythonDaemonStopResult): void { function writeDaemonStop(io: KtxCliIo, result: ManagedPythonDaemonStopResult): void {
if (result.status === 'already-stopped') { if (result.status === 'already-stopped') {
io.stdout.write('KTX Python daemon already stopped\n'); io.stdout.write('KTX daemon already stopped\n');
return; return;
} }
io.stdout.write('Stopped KTX Python daemon\n'); io.stdout.write('Stopped KTX daemon\n');
io.stdout.write(`pid: ${result.state?.pid ?? 'unknown'}\n`); io.stdout.write(`pid: ${result.state?.pid ?? 'unknown'}\n`);
io.stdout.write(`state: ${result.layout.daemonStatePath}\n`); io.stdout.write(`state: ${result.layout.daemonStatePath}\n`);
} }
@ -94,11 +94,11 @@ function writeDaemonStopAll(io: KtxCliIo, result: ManagedPythonDaemonStopAllResu
result.failed.length === 0 && result.failed.length === 0 &&
result.scanErrors.length === 0 result.scanErrors.length === 0
) { ) {
io.stdout.write('No KTX Python daemons found\n'); io.stdout.write('No KTX daemons found\n');
return 0; return 0;
} }
if (failed === 0) { if (failed === 0) {
io.stdout.write(`Stopped ${result.stopped.length} KTX Python daemons\n`); io.stdout.write(`Stopped ${result.stopped.length} KTX daemons\n`);
if (result.stale.length > 0) { if (result.stale.length > 0) {
io.stdout.write(`Cleaned ${result.stale.length} stale daemon states\n`); io.stdout.write(`Cleaned ${result.stale.length} stale daemon states\n`);
} }
@ -111,7 +111,7 @@ function writeDaemonStopAll(io: KtxCliIo, result: ManagedPythonDaemonStopAllResu
return 0; return 0;
} }
io.stderr.write( io.stderr.write(
`Stopped ${result.stopped.length} KTX Python daemons; failed ${result.failed.length}${ `Stopped ${result.stopped.length} KTX daemons; failed ${result.failed.length}${
result.stale.length > 0 ? `; cleaned stale ${result.stale.length}` : '' result.stale.length > 0 ? `; cleaned stale ${result.stale.length}` : ''
}\n`, }\n`,
); );

View file

@ -376,7 +376,7 @@ describe('runKtxScan', () => {
expect(io.stdout()).not.toContain('/~'); expect(io.stdout()).not.toContain('/~');
}); });
it('passes managed daemon options to local ingest adapters when no explicit daemon URL is set', async () => { it('passes KTX daemon options to local ingest adapters when no explicit daemon URL is set', async () => {
await initKtxProject({ projectDir: tempDir }); await initKtxProject({ projectDir: tempDir });
const createLocalIngestAdapters = vi.fn(() => []); const createLocalIngestAdapters = vi.fn(() => []);
const runLocalScan = vi.fn( const runLocalScan = vi.fn(

View file

@ -361,7 +361,7 @@ describe('setup embeddings step', () => {
); );
expect(result.status).toBe('failed'); expect(result.status).toBe('failed');
expect(io.stderr()).toContain('Recent local embeddings daemon stderr:'); expect(io.stderr()).toContain('Recent KTX daemon stderr:');
expect(io.stderr()).toContain('daemon traceback line 6'); expect(io.stderr()).toContain('daemon traceback line 6');
expect(io.stderr()).toContain('daemon traceback line 45'); expect(io.stderr()).toContain('daemon traceback line 45');
expect(io.stderr()).not.toContain('daemon traceback line 5'); expect(io.stderr()).not.toContain('daemon traceback line 5');
@ -391,7 +391,7 @@ describe('setup embeddings step', () => {
); );
expect(result.status).toBe('failed'); expect(result.status).toBe('failed');
expect(io.stderr()).not.toContain('Recent local embeddings daemon stderr:'); expect(io.stderr()).not.toContain('Recent KTX daemon stderr:');
}); });
it('uses fixed OpenAI defaults and only asks for credentials when OpenAI is selected', async () => { it('uses fixed OpenAI defaults and only asks for credentials when OpenAI is selected', async () => {

View file

@ -312,7 +312,7 @@ function localEmbeddingSetupMessage(message: string, stderrTail: string[] = []):
'The first run may download Python packages and the all-MiniLM-L6-v2 model.', 'The first run may download Python packages and the all-MiniLM-L6-v2 model.',
]; ];
if (stderrTail.length > 0) { if (stderrTail.length > 0) {
lines.push('Recent local embeddings daemon stderr:', ...stderrTail); lines.push('Recent KTX daemon stderr:', ...stderrTail);
} }
return lines.join('\n'); return lines.join('\n');
} }

View file

@ -97,7 +97,7 @@ describe('runKtxSetupRuntimeStep', () => {
expect(io.stderr()).toContain('ktx admin runtime install --yes'); expect(io.stderr()).toContain('ktx admin runtime install --yes');
}); });
it('starts the managed local embeddings daemon for configured sentence-transformers embeddings', async () => { it('starts the KTX daemon for configured sentence-transformers embeddings', async () => {
const io = makeIo(); const io = makeIo();
const ensureLocalEmbeddings = vi.fn(async () => ({ const ensureLocalEmbeddings = vi.fn(async () => ({
baseUrl: 'http://127.0.0.1:61234', baseUrl: 'http://127.0.0.1:61234',

View file

@ -346,7 +346,7 @@ export async function runLocalEmbeddingsRuntimeSmoke(options = {}) {
900_000, 900_000,
); );
validateEmbeddingResponse(embeddingResponse, 384); validateEmbeddingResponse(embeddingResponse, 384);
process.stdout.write('KTX local embeddings daemon computed a 384-dimensional embedding\n'); process.stdout.write('KTX daemon computed a 384-dimensional embedding\n');
const setup = await run(commands[5].command, commands[5].args, { const setup = await run(commands[5].command, commands[5].args, {
cwd: installDir, cwd: installDir,
@ -369,7 +369,7 @@ export async function runLocalEmbeddingsRuntimeSmoke(options = {}) {
}); });
requireSuccess(commands[6].label, stop); requireSuccess(commands[6].label, stop);
daemonStarted = false; daemonStarted = false;
requireOutput(commands[6].label, stop, /Stopped KTX Python daemon/); requireOutput(commands[6].label, stop, /Stopped KTX daemon/);
process.stdout.write('KTX local embeddings runtime smoke verified\n'); process.stdout.write('KTX local embeddings runtime smoke verified\n');
} finally { } finally {

View file

@ -129,13 +129,13 @@ describe('localEmbeddingsSmokeCommands', () => {
describe('parseDaemonBaseUrl', () => { describe('parseDaemonBaseUrl', () => {
it('extracts the daemon URL from runtime start output', () => { it('extracts the daemon URL from runtime start output', () => {
assert.equal( assert.equal(
parseDaemonBaseUrl('Started KTX Python daemon\nurl: http://127.0.0.1:61234\nfeatures: local-embeddings\n'), parseDaemonBaseUrl('Started KTX daemon\nurl: http://127.0.0.1:61234\nfeatures: local-embeddings\n'),
'http://127.0.0.1:61234', 'http://127.0.0.1:61234',
); );
}); });
it('rejects output without a daemon URL', () => { it('rejects output without a daemon URL', () => {
assert.throws(() => parseDaemonBaseUrl('Started KTX Python daemon\n'), /Daemon URL was not printed/); assert.throws(() => parseDaemonBaseUrl('Started KTX daemon\n'), /Daemon URL was not printed/);
}); });
}); });

View file

@ -804,19 +804,19 @@ try {
const runtimeStart = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'start']); const runtimeStart = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'start']);
requireSuccess('ktx admin runtime start', runtimeStart); requireSuccess('ktx admin runtime start', runtimeStart);
daemonStarted = true; daemonStarted = true;
requireOutput('ktx admin runtime start', runtimeStart, /Started KTX Python daemon/); requireOutput('ktx admin runtime start', runtimeStart, /Started KTX daemon/);
requireOutput('ktx admin runtime start', runtimeStart, /url: http:\\/\\/127\\.0\\.0\\.1:\\d+/); requireOutput('ktx admin runtime start', runtimeStart, /url: http:\\/\\/127\\.0\\.0\\.1:\\d+/);
requireOutput('ktx admin runtime start', runtimeStart, /features: core/); requireOutput('ktx admin runtime start', runtimeStart, /features: core/);
const runtimeStartReuse = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'start']); const runtimeStartReuse = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'start']);
requireSuccess('ktx admin runtime start reuse', runtimeStartReuse); requireSuccess('ktx admin runtime start reuse', runtimeStartReuse);
requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /Using existing KTX Python daemon/); requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /Using existing KTX daemon/);
requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /features: core/); requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /features: core/);
const runtimeStop = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'stop']); const runtimeStop = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'stop']);
requireSuccess('ktx admin runtime stop', runtimeStop); requireSuccess('ktx admin runtime stop', runtimeStop);
daemonStarted = false; daemonStarted = false;
requireOutput('ktx admin runtime stop', runtimeStop, /Stopped KTX Python daemon/); requireOutput('ktx admin runtime stop', runtimeStop, /Stopped KTX daemon/);
process.stdout.write('ktx admin runtime daemon lifecycle verified\\n'); process.stdout.write('ktx admin runtime daemon lifecycle verified\\n');
const structuralScan = await run('pnpm', ['exec', 'ktx', 'ingest', 'warehouse', const structuralScan = await run('pnpm', ['exec', 'ktx', 'ingest', 'warehouse',

View file

@ -502,7 +502,7 @@ describe('verification snippets', () => {
assert.match(source, /status: ready/); assert.match(source, /status: ready/);
assert.match(source, /ktx admin runtime start/); assert.match(source, /ktx admin runtime start/);
assert.match(source, /ktx admin runtime start reuse/); assert.match(source, /ktx admin runtime start reuse/);
assert.match(source, /Using existing KTX Python daemon/); assert.match(source, /Using existing KTX daemon/);
assert.match(source, /ktx admin runtime stop/); assert.match(source, /ktx admin runtime stop/);
assert.doesNotMatch(source, /ktx admin runtime prune/); assert.doesNotMatch(source, /ktx admin runtime prune/);
assert.doesNotMatch(source, /staleRuntimeDir/); assert.doesNotMatch(source, /staleRuntimeDir/);