@kaelio/ktx 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/python/{kaelio_ktx-0.1.0-py3-none-any.whl → kaelio_ktx-0.2.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/admin-reindex.d.ts +15 -0
- package/dist/admin-reindex.js +168 -0
- package/dist/admin-reindex.test.js +116 -0
- package/dist/{dev.d.ts → admin.d.ts} +1 -1
- package/dist/{dev.js → admin.js} +14 -12
- package/dist/admin.test.d.ts +1 -0
- package/dist/{dev.test.js → admin.test.js} +36 -31
- package/dist/cli-program.js +7 -7
- package/dist/cli-program.test.js +1 -1
- package/dist/cli-runtime.d.ts +2 -0
- package/dist/commands/connection-commands.js +11 -10
- package/dist/commands/connection-selection.d.ts +11 -0
- package/dist/commands/connection-selection.js +9 -0
- package/dist/commands/ingest-commands.js +32 -26
- package/dist/commands/knowledge-commands.js +17 -28
- package/dist/commands/mcp-commands.js +17 -11
- package/dist/commands/setup-commands.js +14 -26
- package/dist/commands/sl-commands.js +27 -32
- package/dist/doctor.test.js +7 -8
- package/dist/example-smoke.test.js +3 -3
- package/dist/index.test.js +102 -70
- package/dist/ingest-depth.js +0 -1
- package/dist/ingest.test-utils.js +2 -2
- package/dist/ingest.test.js +4 -4
- package/dist/io/print-list.test.js +4 -4
- package/dist/knowledge.js +1 -1
- package/dist/managed-local-embeddings.d.ts +2 -0
- package/dist/managed-local-embeddings.js +2 -0
- package/dist/managed-local-embeddings.test.js +2 -0
- package/dist/managed-mcp-daemon.js +3 -2
- package/dist/managed-mcp-daemon.test.js +25 -0
- package/dist/managed-python-command.js +2 -2
- package/dist/managed-python-command.test.js +4 -3
- package/dist/managed-python-daemon.js +3 -2
- package/dist/managed-python-daemon.test.js +20 -0
- package/dist/managed-python-runtime.d.ts +5 -1
- package/dist/managed-python-runtime.js +50 -6
- package/dist/managed-python-runtime.test.js +53 -23
- package/dist/memory-flow-tui.test.js +2 -2
- package/dist/next-steps.d.ts +6 -6
- package/dist/next-steps.js +4 -4
- package/dist/next-steps.test.js +5 -5
- package/dist/print-command-tree.test.js +1 -1
- package/dist/proxy-env.d.ts +1 -0
- package/dist/proxy-env.js +23 -0
- package/dist/proxy-env.test.d.ts +1 -0
- package/dist/proxy-env.test.js +17 -0
- package/dist/public-ingest.js +3 -5
- package/dist/public-ingest.test.js +7 -3
- package/dist/runtime.test.js +2 -1
- package/dist/scan.test.js +2 -2
- package/dist/setup-agents.js +6 -4
- package/dist/setup-agents.test.js +35 -1
- package/dist/setup-embeddings.d.ts +1 -0
- package/dist/setup-embeddings.js +29 -7
- package/dist/setup-embeddings.test.js +49 -7
- package/dist/setup-models.d.ts +0 -1
- package/dist/setup-models.js +2 -3
- package/dist/setup-models.test.js +8 -10
- package/dist/setup-project.d.ts +9 -1
- package/dist/setup-project.js +52 -25
- package/dist/setup-project.test.js +8 -8
- package/dist/setup-runtime.test.js +4 -2
- package/dist/setup.d.ts +1 -2
- package/dist/setup.js +21 -5
- package/dist/setup.test.js +160 -43
- package/dist/sl.js +1 -1
- package/dist/sl.test.js +2 -1
- package/dist/standalone-smoke.test.js +8 -5
- package/dist/status-project.js +1 -10
- package/node_modules/@ktx/context/dist/index-sync/index.d.ts +2 -0
- package/node_modules/@ktx/context/dist/index-sync/index.js +1 -0
- package/node_modules/@ktx/context/dist/index-sync/reindex.d.ts +20 -0
- package/node_modules/@ktx/context/dist/index-sync/reindex.js +141 -0
- package/node_modules/@ktx/context/dist/index-sync/reindex.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/index-sync/reindex.test.js +139 -0
- package/node_modules/@ktx/context/dist/index-sync/types.d.ts +29 -0
- package/node_modules/@ktx/context/dist/index-sync/types.js +1 -0
- package/node_modules/@ktx/context/dist/index.d.ts +1 -0
- package/node_modules/@ktx/context/dist/index.js +1 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/local-ingest-acceptance.test.js +1 -1
- package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +8 -8
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +4 -1
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +3 -3
- package/node_modules/@ktx/context/dist/ingest/local-embedding-provider.integration.test.js +9 -10
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +2 -2
- package/node_modules/@ktx/context/dist/llm/local-config.js +2 -15
- package/node_modules/@ktx/context/dist/llm/local-config.test.js +3 -7
- package/node_modules/@ktx/context/dist/memory/local-memory.js +9 -3
- package/node_modules/@ktx/context/dist/project/config.d.ts +0 -5
- package/node_modules/@ktx/context/dist/project/config.js +5 -5
- package/node_modules/@ktx/context/dist/project/config.test.js +4 -7
- package/node_modules/@ktx/context/dist/scan/enrichment-state.test.js +4 -4
- package/node_modules/@ktx/context/dist/scan/index.d.ts +1 -1
- package/node_modules/@ktx/context/dist/scan/local-enrichment.d.ts +2 -6
- package/node_modules/@ktx/context/dist/scan/local-enrichment.js +31 -47
- package/node_modules/@ktx/context/dist/scan/local-enrichment.test.js +35 -18
- package/node_modules/@ktx/context/dist/scan/local-scan.test.js +2 -3
- package/node_modules/@ktx/context/dist/sl/ports.d.ts +3 -3
- package/node_modules/@ktx/context/dist/sl/sl-search.service.d.ts +3 -2
- package/node_modules/@ktx/context/dist/sl/sl-search.service.js +47 -45
- package/node_modules/@ktx/context/dist/sl/sl-search.service.test.js +61 -0
- package/node_modules/@ktx/context/dist/sl/sqlite-sl-sources-index.d.ts +4 -3
- package/node_modules/@ktx/context/dist/sl/sqlite-sl-sources-index.js +15 -5
- package/node_modules/@ktx/context/dist/sl/sqlite-sl-sources-index.test.js +24 -0
- package/node_modules/@ktx/context/dist/wiki/knowledge-wiki.service.d.ts +3 -2
- package/node_modules/@ktx/context/dist/wiki/knowledge-wiki.service.js +62 -51
- package/node_modules/@ktx/context/dist/wiki/knowledge-wiki.service.test.js +59 -3
- package/node_modules/@ktx/context/dist/wiki/ports.d.ts +3 -3
- package/node_modules/@ktx/context/dist/wiki/sqlite-knowledge-index.d.ts +33 -0
- package/node_modules/@ktx/context/dist/wiki/sqlite-knowledge-index.js +155 -2
- package/node_modules/@ktx/context/dist/wiki/sqlite-knowledge-index.test.js +26 -0
- package/node_modules/@ktx/context/package.json +5 -0
- package/node_modules/@ktx/llm/dist/embedding-provider.d.ts +0 -7
- package/node_modules/@ktx/llm/dist/embedding-provider.js +12 -138
- package/node_modules/@ktx/llm/dist/embedding-provider.test.js +10 -25
- package/node_modules/@ktx/llm/dist/types.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/{dev.test.d.ts → admin-reindex.test.d.ts} +0 -0
|
@@ -5,6 +5,7 @@ import { setTimeout as delay } from 'node:timers/promises';
|
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import { installManagedPythonRuntime, managedPythonDaemonLayout, runtimeFeatureSchema, } from './managed-python-runtime.js';
|
|
8
|
+
import { sanitizeChildProxyEnv } from './proxy-env.js';
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
const daemonStateSchema = z.object({
|
|
10
11
|
schemaVersion: z.literal(1),
|
|
@@ -483,10 +484,10 @@ export async function startManagedPythonDaemon(options) {
|
|
|
483
484
|
const child = spawnDaemon(installed.manifest.python.daemonExecutable, ['serve-http', '--host', '127.0.0.1', '--port', String(port)], {
|
|
484
485
|
detached: true,
|
|
485
486
|
stdio: ['ignore', stdout.fd, stderr.fd],
|
|
486
|
-
env: {
|
|
487
|
+
env: sanitizeChildProxyEnv({
|
|
487
488
|
...process.env,
|
|
488
489
|
KTX_DAEMON_VERSION: options.cliVersion,
|
|
489
|
-
},
|
|
490
|
+
}),
|
|
490
491
|
});
|
|
491
492
|
child.unref();
|
|
492
493
|
if (!child.pid) {
|
|
@@ -59,6 +59,7 @@ function installResult(root, features = ['core']) {
|
|
|
59
59
|
asset: {
|
|
60
60
|
manifest: manifest(root, features).asset,
|
|
61
61
|
wheelPath: join(root, 'assets', 'python', 'kaelio_ktx-0.2.0-py3-none-any.whl'),
|
|
62
|
+
requiresPython: { specifier: '>=3.13', minimumVersion: '3.13' },
|
|
62
63
|
},
|
|
63
64
|
manifest: manifest(root, features),
|
|
64
65
|
};
|
|
@@ -105,6 +106,7 @@ describe('managed Python daemon lifecycle', () => {
|
|
|
105
106
|
tempDir = await mkdtemp(join(tmpdir(), 'ktx-managed-daemon-'));
|
|
106
107
|
});
|
|
107
108
|
afterEach(async () => {
|
|
109
|
+
vi.unstubAllEnvs();
|
|
108
110
|
await rm(tempDir, { recursive: true, force: true });
|
|
109
111
|
});
|
|
110
112
|
it('reports stopped when no daemon state exists', async () => {
|
|
@@ -150,6 +152,24 @@ describe('managed Python daemon lifecycle', () => {
|
|
|
150
152
|
stderrLog: layout(tempDir).daemonStderrPath,
|
|
151
153
|
});
|
|
152
154
|
});
|
|
155
|
+
it('sanitizes IPv6 CIDR entries from child NO_PROXY env', async () => {
|
|
156
|
+
vi.stubEnv('NO_PROXY', 'localhost,fd07:b51a:cc66:f0::/64,127.0.0.0/8');
|
|
157
|
+
vi.stubEnv('no_proxy', '::1,fd00::/8,*.orb.local');
|
|
158
|
+
const spawnDaemon = makeSpawn(5555);
|
|
159
|
+
await startManagedPythonDaemon({
|
|
160
|
+
...daemonOptionsBase(tempDir),
|
|
161
|
+
features: ['local-embeddings'],
|
|
162
|
+
installRuntime: vi.fn(async () => installResult(tempDir, ['core', 'local-embeddings'])),
|
|
163
|
+
spawnDaemon,
|
|
164
|
+
fetch: makeFetch(),
|
|
165
|
+
allocatePort: vi.fn(async () => 61234),
|
|
166
|
+
now: () => new Date('2026-05-11T00:00:00.000Z'),
|
|
167
|
+
pollIntervalMs: 1,
|
|
168
|
+
});
|
|
169
|
+
const env = vi.mocked(spawnDaemon).mock.calls[0]?.[2].env;
|
|
170
|
+
expect(env?.NO_PROXY).toBe('localhost,127.0.0.0/8,::1,*.orb.local');
|
|
171
|
+
expect(env?.no_proxy).toBe(env?.NO_PROXY);
|
|
172
|
+
});
|
|
153
173
|
it('makes a final health probe before reporting startup failure', async () => {
|
|
154
174
|
const spawnDaemon = makeSpawn(5556);
|
|
155
175
|
const installRuntime = vi.fn(async () => installResult(tempDir));
|
|
@@ -75,6 +75,10 @@ export interface ManagedPythonDaemonLayout extends ManagedPythonRuntimeLayout {
|
|
|
75
75
|
export interface ManagedRuntimeAsset {
|
|
76
76
|
manifest: KtxRuntimeAssetManifest;
|
|
77
77
|
wheelPath: string;
|
|
78
|
+
requiresPython: {
|
|
79
|
+
specifier: string;
|
|
80
|
+
minimumVersion: string;
|
|
81
|
+
};
|
|
78
82
|
}
|
|
79
83
|
export type ManagedPythonRuntimeExec = (command: string, args: string[], options?: {
|
|
80
84
|
cwd?: string;
|
|
@@ -108,7 +112,7 @@ export interface ManagedPythonRuntimeDoctorCheck {
|
|
|
108
112
|
detail: string;
|
|
109
113
|
fix?: string;
|
|
110
114
|
}
|
|
111
|
-
export declare const MISSING_UV_RUNTIME_INSTALL_MESSAGE = "uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx
|
|
115
|
+
export declare const MISSING_UV_RUNTIME_INSTALL_MESSAGE = "uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx admin runtime install --yes";
|
|
112
116
|
export declare function managedPythonRuntimeLayout(options: ManagedPythonRuntimeLayoutOptions): ManagedPythonRuntimeLayout;
|
|
113
117
|
export declare function managedPythonDaemonLayout(options: ManagedPythonDaemonLayoutOptions): ManagedPythonDaemonLayout;
|
|
114
118
|
export declare function verifyRuntimeAsset(input: {
|
|
@@ -5,6 +5,7 @@ import { homedir } from 'node:os';
|
|
|
5
5
|
import { basename, join } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
|
+
import { strFromU8, unzipSync } from 'fflate';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
export const runtimeFeatureSchema = z.enum(['core', 'local-embeddings']);
|
|
@@ -31,7 +32,7 @@ const installedRuntimeManifestSchema = z.object({
|
|
|
31
32
|
}),
|
|
32
33
|
installLog: z.string().min(1),
|
|
33
34
|
});
|
|
34
|
-
export const MISSING_UV_RUNTIME_INSTALL_MESSAGE = 'uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx
|
|
35
|
+
export const MISSING_UV_RUNTIME_INSTALL_MESSAGE = 'uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx admin runtime install --yes';
|
|
35
36
|
function defaultAssetDir() {
|
|
36
37
|
return fileURLToPath(new URL('../assets/python/', import.meta.url));
|
|
37
38
|
}
|
|
@@ -100,6 +101,35 @@ async function readJsonFile(path) {
|
|
|
100
101
|
function isErrnoException(error, code) {
|
|
101
102
|
return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
|
|
102
103
|
}
|
|
104
|
+
function parseRequiresPythonFromWheel(input) {
|
|
105
|
+
let files;
|
|
106
|
+
try {
|
|
107
|
+
files = unzipSync(new Uint8Array(input.contents));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new Error(`Unable to read bundled Python runtime wheel metadata: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
}
|
|
112
|
+
const metadataEntry = Object.entries(files).find(([path]) => path.endsWith('.dist-info/METADATA'));
|
|
113
|
+
if (!metadataEntry) {
|
|
114
|
+
throw new Error(`Bundled Python runtime wheel metadata is missing: ${input.wheelPath}`);
|
|
115
|
+
}
|
|
116
|
+
const metadata = strFromU8(metadataEntry[1]);
|
|
117
|
+
const requiresPython = metadata
|
|
118
|
+
.split(/\r?\n/)
|
|
119
|
+
.map((line) => line.match(/^Requires-Python:\s*(.+)\s*$/i)?.[1]?.trim())
|
|
120
|
+
.find((value) => typeof value === 'string' && value.length > 0);
|
|
121
|
+
if (!requiresPython) {
|
|
122
|
+
throw new Error('Bundled Python runtime wheel metadata is missing Requires-Python');
|
|
123
|
+
}
|
|
124
|
+
const minimumMatch = requiresPython.match(/(?:^|[,\s])>=\s*([0-9]+)\.([0-9]+)(?:\.[0-9]+)?\b/);
|
|
125
|
+
if (!minimumMatch) {
|
|
126
|
+
throw new Error(`Unsupported bundled Python runtime Requires-Python: ${requiresPython}`);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
specifier: requiresPython,
|
|
130
|
+
minimumVersion: `${minimumMatch[1]}.${minimumMatch[2]}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
103
133
|
export async function verifyRuntimeAsset(input) {
|
|
104
134
|
const manifestPath = join(input.assetDir, 'manifest.json');
|
|
105
135
|
let manifestData;
|
|
@@ -124,7 +154,7 @@ export async function verifyRuntimeAsset(input) {
|
|
|
124
154
|
if (sha256 !== manifest.wheel.sha256 || wheel.byteLength !== manifest.wheel.bytes) {
|
|
125
155
|
throw new Error(`Bundled Python runtime wheel checksum mismatch: ${wheelPath}`);
|
|
126
156
|
}
|
|
127
|
-
return { manifest, wheelPath };
|
|
157
|
+
return { manifest, wheelPath, requiresPython: parseRequiresPythonFromWheel({ wheelPath, contents: wheel }) };
|
|
128
158
|
}
|
|
129
159
|
function normalizeFeatures(features) {
|
|
130
160
|
const requested = new Set(['core', ...features]);
|
|
@@ -155,6 +185,13 @@ function errorOutput(error) {
|
|
|
155
185
|
stderr: typeof value.stderr === 'string' ? value.stderr : '',
|
|
156
186
|
};
|
|
157
187
|
}
|
|
188
|
+
function installFailureMessage(input) {
|
|
189
|
+
const output = [input.stderr.trim(), input.stdout.trim()].filter((part) => part.length > 0).join('\n');
|
|
190
|
+
if (!output) {
|
|
191
|
+
return `Python runtime install failed. Install log: ${input.logPath}`;
|
|
192
|
+
}
|
|
193
|
+
return `Python runtime install failed.\n${output}\nInstall log: ${input.logPath}`;
|
|
194
|
+
}
|
|
158
195
|
async function runLogged(input) {
|
|
159
196
|
await appendFile(input.logPath, `$ ${input.command} ${input.args.join(' ')}\n`);
|
|
160
197
|
try {
|
|
@@ -175,7 +212,7 @@ async function runLogged(input) {
|
|
|
175
212
|
if (output.stderr) {
|
|
176
213
|
await appendFile(input.logPath, output.stderr.endsWith('\n') ? output.stderr : `${output.stderr}\n`);
|
|
177
214
|
}
|
|
178
|
-
throw new Error(
|
|
215
|
+
throw new Error(installFailureMessage({ logPath: input.logPath, stdout: output.stdout, stderr: output.stderr }));
|
|
179
216
|
}
|
|
180
217
|
}
|
|
181
218
|
function managedRuntimeUvEnv(baseEnv) {
|
|
@@ -214,7 +251,14 @@ export async function installManagedPythonRuntime(options) {
|
|
|
214
251
|
exec,
|
|
215
252
|
logPath: layout.installLogPath,
|
|
216
253
|
command: 'uv',
|
|
217
|
-
args: ['
|
|
254
|
+
args: ['python', 'install', asset.requiresPython.minimumVersion],
|
|
255
|
+
env: uvEnv,
|
|
256
|
+
});
|
|
257
|
+
await runLogged({
|
|
258
|
+
exec,
|
|
259
|
+
logPath: layout.installLogPath,
|
|
260
|
+
command: 'uv',
|
|
261
|
+
args: ['venv', '--python', asset.requiresPython.minimumVersion, layout.venvDir],
|
|
218
262
|
env: uvEnv,
|
|
219
263
|
});
|
|
220
264
|
const wheelSpec = features.includes('local-embeddings') ? `${asset.wheelPath}[local-embeddings]` : asset.wheelPath;
|
|
@@ -287,7 +331,7 @@ export async function doctorManagedPythonRuntime(options) {
|
|
|
287
331
|
id: 'uv',
|
|
288
332
|
label: 'uv',
|
|
289
333
|
detail: error instanceof Error ? error.message : String(error),
|
|
290
|
-
fix: 'Install uv, make sure it is on PATH, and run: ktx
|
|
334
|
+
fix: 'Install uv, make sure it is on PATH, and run: ktx admin runtime install --yes',
|
|
291
335
|
}));
|
|
292
336
|
}
|
|
293
337
|
try {
|
|
@@ -307,7 +351,7 @@ export async function doctorManagedPythonRuntime(options) {
|
|
|
307
351
|
id: 'runtime',
|
|
308
352
|
label: 'Managed Python runtime',
|
|
309
353
|
detail: status.detail,
|
|
310
|
-
...(status.kind === 'ready' ? {} : { fix: 'Run: ktx
|
|
354
|
+
...(status.kind === 'ready' ? {} : { fix: 'Run: ktx admin runtime install --yes' }),
|
|
311
355
|
}));
|
|
312
356
|
return checks;
|
|
313
357
|
}
|
|
@@ -2,12 +2,28 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
+
import { strToU8, zipSync } from 'fflate';
|
|
5
6
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
7
|
import { MISSING_UV_RUNTIME_INSTALL_MESSAGE, doctorManagedPythonRuntime, installManagedPythonRuntime, managedPythonDaemonLayout, managedPythonRuntimeLayout, readManagedPythonRuntimeStatus, verifyRuntimeAsset, } from './managed-python-runtime.js';
|
|
7
|
-
|
|
8
|
+
function runtimeWheelContents(input = {}) {
|
|
9
|
+
const label = input.label ?? 'runtime-wheel';
|
|
10
|
+
const requiresPython = input.requiresPython === null ? [] : [`Requires-Python: ${input.requiresPython ?? '>=3.13'}`];
|
|
11
|
+
return Buffer.from(zipSync({
|
|
12
|
+
'kaelio_ktx-0.1.0.dist-info/METADATA': strToU8([
|
|
13
|
+
'Metadata-Version: 2.4',
|
|
14
|
+
'Name: kaelio-ktx',
|
|
15
|
+
'Version: 0.1.0',
|
|
16
|
+
...requiresPython,
|
|
17
|
+
`Summary: ${label}`,
|
|
18
|
+
'',
|
|
19
|
+
].join('\n')),
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
async function writeAsset(root, options = {}) {
|
|
8
23
|
const assetDir = join(root, 'assets', 'python');
|
|
9
24
|
await mkdir(assetDir, { recursive: true });
|
|
10
25
|
const wheelPath = join(assetDir, 'kaelio_ktx-0.1.0-py3-none-any.whl');
|
|
26
|
+
const contents = options.contents ?? runtimeWheelContents(options);
|
|
11
27
|
await writeFile(wheelPath, contents);
|
|
12
28
|
await writeFile(join(assetDir, 'manifest.json'), `${JSON.stringify({
|
|
13
29
|
schemaVersion: 1,
|
|
@@ -17,7 +33,7 @@ async function writeAsset(root, contents = 'wheel-bytes') {
|
|
|
17
33
|
wheel: {
|
|
18
34
|
file: 'kaelio_ktx-0.1.0-py3-none-any.whl',
|
|
19
35
|
sha256: createHash('sha256').update(contents).digest('hex'),
|
|
20
|
-
bytes:
|
|
36
|
+
bytes: contents.byteLength,
|
|
21
37
|
},
|
|
22
38
|
}, null, 2)}\n`);
|
|
23
39
|
return { assetDir, wheelPath };
|
|
@@ -112,19 +128,20 @@ describe('verifyRuntimeAsset', () => {
|
|
|
112
128
|
await rm(tempDir, { recursive: true, force: true });
|
|
113
129
|
});
|
|
114
130
|
it('reads the manifest and verifies the wheel checksum', async () => {
|
|
115
|
-
const { assetDir, wheelPath } = await writeAsset(tempDir, 'valid-wheel');
|
|
131
|
+
const { assetDir, wheelPath } = await writeAsset(tempDir, { label: 'valid-wheel' });
|
|
116
132
|
const asset = await verifyRuntimeAsset({ assetDir });
|
|
117
133
|
expect(asset.manifest.distributionName).toBe('kaelio-ktx');
|
|
118
134
|
expect(asset.manifest.normalizedName).toBe('kaelio_ktx');
|
|
119
135
|
expect(asset.wheelPath).toBe(wheelPath);
|
|
136
|
+
expect(asset.requiresPython).toEqual({ specifier: '>=3.13', minimumVersion: '3.13' });
|
|
120
137
|
});
|
|
121
138
|
it('rejects a wheel whose checksum does not match the manifest', async () => {
|
|
122
|
-
const { assetDir, wheelPath } = await writeAsset(tempDir, 'original');
|
|
139
|
+
const { assetDir, wheelPath } = await writeAsset(tempDir, { label: 'original' });
|
|
123
140
|
await writeFile(wheelPath, 'tampered');
|
|
124
141
|
await expect(verifyRuntimeAsset({ assetDir })).rejects.toThrow(/Bundled Python runtime wheel checksum mismatch/);
|
|
125
142
|
});
|
|
126
143
|
it('rejects an unsafe wheel filename in the manifest', async () => {
|
|
127
|
-
const { assetDir } = await writeAsset(tempDir, 'valid-wheel');
|
|
144
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'valid-wheel' });
|
|
128
145
|
await writeFile(join(assetDir, 'manifest.json'), `${JSON.stringify({
|
|
129
146
|
schemaVersion: 1,
|
|
130
147
|
distributionName: 'kaelio-ktx',
|
|
@@ -142,6 +159,14 @@ describe('verifyRuntimeAsset', () => {
|
|
|
142
159
|
const assetDir = join(tempDir, 'packages', 'cli', 'assets', 'python');
|
|
143
160
|
await expect(verifyRuntimeAsset({ assetDir })).rejects.toThrow(/Missing bundled Python runtime manifest.*pnpm run artifacts:build/s);
|
|
144
161
|
});
|
|
162
|
+
it('rejects a bundled wheel without Requires-Python metadata', async () => {
|
|
163
|
+
const { assetDir } = await writeAsset(tempDir, { requiresPython: null });
|
|
164
|
+
await expect(verifyRuntimeAsset({ assetDir })).rejects.toThrow(/Bundled Python runtime wheel metadata is missing Requires-Python/);
|
|
165
|
+
});
|
|
166
|
+
it('rejects a bundled wheel without a supported minimum Python version', async () => {
|
|
167
|
+
const { assetDir } = await writeAsset(tempDir, { requiresPython: '<4' });
|
|
168
|
+
await expect(verifyRuntimeAsset({ assetDir })).rejects.toThrow(/Unsupported bundled Python runtime Requires-Python: <4/);
|
|
169
|
+
});
|
|
145
170
|
});
|
|
146
171
|
describe('installManagedPythonRuntime', () => {
|
|
147
172
|
let tempDir;
|
|
@@ -152,7 +177,7 @@ describe('installManagedPythonRuntime', () => {
|
|
|
152
177
|
await rm(tempDir, { recursive: true, force: true });
|
|
153
178
|
});
|
|
154
179
|
it('creates a venv, installs the core wheel, and writes a manifest', async () => {
|
|
155
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
180
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
156
181
|
const commands = [];
|
|
157
182
|
const exec = vi.fn(async (command, args) => {
|
|
158
183
|
commands.push({ command, args });
|
|
@@ -168,7 +193,8 @@ describe('installManagedPythonRuntime', () => {
|
|
|
168
193
|
expect(result.status).toBe('installed');
|
|
169
194
|
expect(commands).toEqual([
|
|
170
195
|
{ command: 'uv', args: ['--version'] },
|
|
171
|
-
{ command: 'uv', args: ['
|
|
196
|
+
{ command: 'uv', args: ['python', 'install', '3.13'] },
|
|
197
|
+
{ command: 'uv', args: ['venv', '--python', '3.13', result.layout.venvDir] },
|
|
172
198
|
{
|
|
173
199
|
command: 'uv',
|
|
174
200
|
args: ['pip', 'install', '--python', result.layout.pythonPath, result.asset.wheelPath],
|
|
@@ -181,7 +207,7 @@ describe('installManagedPythonRuntime', () => {
|
|
|
181
207
|
expect(manifest.python.daemonExecutable).toBe(result.layout.daemonPath);
|
|
182
208
|
});
|
|
183
209
|
it('disables repo uv config for managed runtime uv commands', async () => {
|
|
184
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
210
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
185
211
|
const commands = [];
|
|
186
212
|
const exec = vi.fn(async (command, args, options) => {
|
|
187
213
|
commands.push({ command, args, env: options?.env });
|
|
@@ -197,12 +223,13 @@ describe('installManagedPythonRuntime', () => {
|
|
|
197
223
|
});
|
|
198
224
|
expect(commands.map((call) => [call.command, call.args[0], call.env?.UV_NO_CONFIG, call.env?.PATH])).toEqual([
|
|
199
225
|
['uv', '--version', '1', '/opt/homebrew/bin'],
|
|
226
|
+
['uv', 'python', '1', '/opt/homebrew/bin'],
|
|
200
227
|
['uv', 'venv', '1', '/opt/homebrew/bin'],
|
|
201
228
|
['uv', 'pip', '1', '/opt/homebrew/bin'],
|
|
202
229
|
]);
|
|
203
230
|
});
|
|
204
231
|
it('installs the local-embeddings extra when requested', async () => {
|
|
205
|
-
const { assetDir } = await writeAsset(tempDir, 'embedding-wheel');
|
|
232
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'embedding-wheel' });
|
|
206
233
|
const commands = [];
|
|
207
234
|
const exec = vi.fn(async (command, args) => {
|
|
208
235
|
commands.push({ command, args });
|
|
@@ -223,7 +250,7 @@ describe('installManagedPythonRuntime', () => {
|
|
|
223
250
|
expect(manifest.features).toEqual(['core', 'local-embeddings']);
|
|
224
251
|
});
|
|
225
252
|
it('fails with the hard-prerequisite message when uv is missing', async () => {
|
|
226
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
253
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
227
254
|
const commands = [];
|
|
228
255
|
const exec = vi.fn(async (command, args) => {
|
|
229
256
|
commands.push({ command, args });
|
|
@@ -239,7 +266,7 @@ describe('installManagedPythonRuntime', () => {
|
|
|
239
266
|
expect(commands).toEqual([{ command: 'uv', args: ['--version'] }]);
|
|
240
267
|
});
|
|
241
268
|
it('reuses an existing compatible runtime when force is false', async () => {
|
|
242
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
269
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
243
270
|
const exec = vi.fn(async (command, args) => ({
|
|
244
271
|
stdout: command === 'uv' && args[0] === '--version' ? 'uv 0.9.5\n' : '',
|
|
245
272
|
stderr: '',
|
|
@@ -262,13 +289,16 @@ describe('installManagedPythonRuntime', () => {
|
|
|
262
289
|
exec,
|
|
263
290
|
});
|
|
264
291
|
expect(second.status).toBe('ready');
|
|
265
|
-
expect(exec).toHaveBeenCalledTimes(
|
|
292
|
+
expect(exec).toHaveBeenCalledTimes(4);
|
|
266
293
|
});
|
|
267
294
|
it('keeps failed install logs in the versioned runtime directory', async () => {
|
|
268
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
295
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
269
296
|
const exec = vi.fn(async (command, args) => {
|
|
270
297
|
if (command === 'uv' && args[0] === 'venv') {
|
|
271
|
-
throw Object.assign(new Error('uv venv failed'), {
|
|
298
|
+
throw Object.assign(new Error('uv venv failed'), {
|
|
299
|
+
stdout: 'creating\n',
|
|
300
|
+
stderr: '× No solution found\n╰─▶ current Python version (3.12.3) does not satisfy Python>=3.13\n',
|
|
301
|
+
});
|
|
272
302
|
}
|
|
273
303
|
return { stdout: command === 'uv' && args[0] === '--version' ? 'uv 0.9.5\n' : '', stderr: '' };
|
|
274
304
|
});
|
|
@@ -278,10 +308,10 @@ describe('installManagedPythonRuntime', () => {
|
|
|
278
308
|
assetDir,
|
|
279
309
|
features: ['core'],
|
|
280
310
|
exec,
|
|
281
|
-
})).rejects.toThrow(/Python
|
|
311
|
+
})).rejects.toThrow(/current Python version \(3\.12\.3\) does not satisfy Python>=3\.13/);
|
|
282
312
|
const log = await readFile(join(tempDir, 'runtime', '0.2.0', 'install.log'), 'utf8');
|
|
283
|
-
expect(log).toContain('$ uv venv');
|
|
284
|
-
expect(log).toContain('
|
|
313
|
+
expect(log).toContain('$ uv venv --python 3.13');
|
|
314
|
+
expect(log).toContain('current Python version (3.12.3) does not satisfy Python>=3.13');
|
|
285
315
|
});
|
|
286
316
|
});
|
|
287
317
|
describe('readManagedPythonRuntimeStatus', () => {
|
|
@@ -302,7 +332,7 @@ describe('readManagedPythonRuntimeStatus', () => {
|
|
|
302
332
|
expect(status.detail).toContain('No runtime manifest');
|
|
303
333
|
});
|
|
304
334
|
it('reports ready when manifest and executables exist', async () => {
|
|
305
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
335
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
306
336
|
const exec = vi.fn(async (command, args) => ({
|
|
307
337
|
stdout: command === 'uv' && args[0] === '--version' ? 'uv 0.9.5\n' : '',
|
|
308
338
|
stderr: '',
|
|
@@ -326,7 +356,7 @@ describe('readManagedPythonRuntimeStatus', () => {
|
|
|
326
356
|
expect(status.manifest?.features).toEqual(['core']);
|
|
327
357
|
});
|
|
328
358
|
it('reports broken when an executable is missing', async () => {
|
|
329
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
359
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
330
360
|
const exec = vi.fn(async (command, args) => ({
|
|
331
361
|
stdout: command === 'uv' && args[0] === '--version' ? 'uv 0.9.5\n' : '',
|
|
332
362
|
stderr: '',
|
|
@@ -356,7 +386,7 @@ describe('doctorManagedPythonRuntime', () => {
|
|
|
356
386
|
await rm(tempDir, { recursive: true, force: true });
|
|
357
387
|
});
|
|
358
388
|
it('checks uv, bundled assets, and installed runtime status', async () => {
|
|
359
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
389
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
360
390
|
const exec = vi.fn(async (command, args) => ({
|
|
361
391
|
stdout: command === 'uv' && args[0] === '--version' ? 'uv 0.9.5\n' : '',
|
|
362
392
|
stderr: '',
|
|
@@ -372,10 +402,10 @@ describe('doctorManagedPythonRuntime', () => {
|
|
|
372
402
|
['asset', 'pass'],
|
|
373
403
|
['runtime', 'fail'],
|
|
374
404
|
]);
|
|
375
|
-
expect(checks[2]?.fix).toBe('Run: ktx
|
|
405
|
+
expect(checks[2]?.fix).toBe('Run: ktx admin runtime install --yes');
|
|
376
406
|
});
|
|
377
407
|
it('reports uv as a hard prerequisite when uv is missing', async () => {
|
|
378
|
-
const { assetDir } = await writeAsset(tempDir, 'core-wheel');
|
|
408
|
+
const { assetDir } = await writeAsset(tempDir, { label: 'core-wheel' });
|
|
379
409
|
const exec = vi.fn(async () => {
|
|
380
410
|
throw new Error('spawn uv ENOENT');
|
|
381
411
|
});
|
|
@@ -390,7 +420,7 @@ describe('doctorManagedPythonRuntime', () => {
|
|
|
390
420
|
label: 'uv',
|
|
391
421
|
status: 'fail',
|
|
392
422
|
detail: MISSING_UV_RUNTIME_INSTALL_MESSAGE,
|
|
393
|
-
fix: 'Install uv, make sure it is on PATH, and run: ktx
|
|
423
|
+
fix: 'Install uv, make sure it is on PATH, and run: ktx admin runtime install --yes',
|
|
394
424
|
});
|
|
395
425
|
});
|
|
396
426
|
});
|
|
@@ -145,8 +145,8 @@ describe('MemoryFlowTuiApp', () => {
|
|
|
145
145
|
expect(frame).toContain('order lifecycle');
|
|
146
146
|
expect(frame).toContain('customer metrics');
|
|
147
147
|
expect(frame).toContain('KTX finished ingesting your data');
|
|
148
|
-
expect(frame).toContain('ktx sl
|
|
149
|
-
expect(frame).toContain('ktx wiki
|
|
148
|
+
expect(frame).toContain('ktx sl');
|
|
149
|
+
expect(frame).toContain('ktx wiki');
|
|
150
150
|
expect(frame).not.toContain('ktx serve --mcp stdio --user-id local');
|
|
151
151
|
expect(frame).not.toContain(['ktx', 'ask'].join(' '));
|
|
152
152
|
expect(frame).not.toContain(['ktx', 'mcp'].join(' '));
|
package/dist/next-steps.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const KTX_CONTEXT_BUILD_COMMANDS: readonly [{
|
|
2
|
-
readonly command: "ktx ingest
|
|
3
|
-
readonly description: "Build or refresh agent-ready context from configured connections";
|
|
2
|
+
readonly command: "ktx ingest";
|
|
3
|
+
readonly description: "Build or refresh agent-ready context from all configured connections";
|
|
4
4
|
}, {
|
|
5
5
|
readonly command: "ktx status";
|
|
6
6
|
readonly description: "Check setup and context readiness";
|
|
@@ -9,20 +9,20 @@ export declare const KTX_NEXT_STEP_DIRECT_COMMANDS: readonly [{
|
|
|
9
9
|
readonly command: "ktx status --json";
|
|
10
10
|
readonly description: "Verify project setup and context readiness";
|
|
11
11
|
}, {
|
|
12
|
-
readonly command: "ktx sl
|
|
12
|
+
readonly command: "ktx sl";
|
|
13
13
|
readonly description: "Inspect generated semantic-layer sources";
|
|
14
14
|
}, {
|
|
15
|
-
readonly command: "ktx wiki
|
|
15
|
+
readonly command: "ktx wiki";
|
|
16
16
|
readonly description: "Inspect generated wiki pages";
|
|
17
17
|
}];
|
|
18
18
|
export declare const KTX_NEXT_STEP_COMMANDS: readonly [{
|
|
19
19
|
readonly command: "ktx status --json";
|
|
20
20
|
readonly description: "Verify project setup and context readiness";
|
|
21
21
|
}, {
|
|
22
|
-
readonly command: "ktx sl
|
|
22
|
+
readonly command: "ktx sl";
|
|
23
23
|
readonly description: "Inspect generated semantic-layer sources";
|
|
24
24
|
}, {
|
|
25
|
-
readonly command: "ktx wiki
|
|
25
|
+
readonly command: "ktx wiki";
|
|
26
26
|
readonly description: "Inspect generated wiki pages";
|
|
27
27
|
}];
|
|
28
28
|
export declare const KTX_NEXT_STEP_COMMAND_WIDTH: number;
|
package/dist/next-steps.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const KTX_CONTEXT_BUILD_COMMANDS = [
|
|
2
2
|
{
|
|
3
|
-
command: 'ktx ingest
|
|
4
|
-
description: 'Build or refresh agent-ready context from configured connections',
|
|
3
|
+
command: 'ktx ingest',
|
|
4
|
+
description: 'Build or refresh agent-ready context from all configured connections',
|
|
5
5
|
},
|
|
6
6
|
{
|
|
7
7
|
command: 'ktx status',
|
|
@@ -14,11 +14,11 @@ export const KTX_NEXT_STEP_DIRECT_COMMANDS = [
|
|
|
14
14
|
description: 'Verify project setup and context readiness',
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
command: 'ktx sl
|
|
17
|
+
command: 'ktx sl',
|
|
18
18
|
description: 'Inspect generated semantic-layer sources',
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
|
-
command: 'ktx wiki
|
|
21
|
+
command: 'ktx wiki',
|
|
22
22
|
description: 'Inspect generated wiki pages',
|
|
23
23
|
},
|
|
24
24
|
];
|
package/dist/next-steps.test.js
CHANGED
|
@@ -4,8 +4,8 @@ describe('KTX demo next steps', () => {
|
|
|
4
4
|
it('uses supported context-build commands before agent usage', () => {
|
|
5
5
|
expect(KTX_CONTEXT_BUILD_COMMANDS).toEqual([
|
|
6
6
|
{
|
|
7
|
-
command: 'ktx ingest
|
|
8
|
-
description: 'Build or refresh agent-ready context from configured connections',
|
|
7
|
+
command: 'ktx ingest',
|
|
8
|
+
description: 'Build or refresh agent-ready context from all configured connections',
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
command: 'ktx status',
|
|
@@ -20,11 +20,11 @@ describe('KTX demo next steps', () => {
|
|
|
20
20
|
description: 'Verify project setup and context readiness',
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
command: 'ktx sl
|
|
23
|
+
command: 'ktx sl',
|
|
24
24
|
description: 'Inspect generated semantic-layer sources',
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
command: 'ktx wiki
|
|
27
|
+
command: 'ktx wiki',
|
|
28
28
|
description: 'Inspect generated wiki pages',
|
|
29
29
|
},
|
|
30
30
|
]);
|
|
@@ -54,7 +54,7 @@ describe('KTX demo next steps', () => {
|
|
|
54
54
|
}).join('\n');
|
|
55
55
|
expect(rendered).toContain('Build KTX context next.');
|
|
56
56
|
expect(rendered).toContain('Run ingest to build database schema context before context-source ingest.');
|
|
57
|
-
expect(rendered).toContain('ktx ingest
|
|
57
|
+
expect(rendered).toContain('ktx ingest');
|
|
58
58
|
expect(rendered).not.toContain('resume');
|
|
59
59
|
expect(rendered).not.toContain('scan');
|
|
60
60
|
expect(rendered).toContain('ktx status');
|
|
@@ -8,7 +8,7 @@ describe('renderKtxCommandTree', () => {
|
|
|
8
8
|
const topLevel = lines
|
|
9
9
|
.filter((line) => /^ {2}[├└]── \S/.test(line))
|
|
10
10
|
.map((line) => line.replace(/^ {2}[├└]── /, '').trim().split(' ')[0]);
|
|
11
|
-
for (const expected of ['setup', 'connection', 'ingest', 'sl', 'mcp', '
|
|
11
|
+
for (const expected of ['setup', 'connection', 'ingest', 'sl', 'mcp', 'admin']) {
|
|
12
12
|
expect(topLevel).toContain(expected);
|
|
13
13
|
}
|
|
14
14
|
expect(output).toContain('│ └── test [connectionId]');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sanitizeChildProxyEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const NO_PROXY_KEYS = ['NO_PROXY', 'no_proxy'];
|
|
2
|
+
function isIpv6CidrNoProxyEntry(entry) {
|
|
3
|
+
return entry.includes('/') && entry.includes(':');
|
|
4
|
+
}
|
|
5
|
+
function cleanedNoProxyValue(env) {
|
|
6
|
+
const entries = NO_PROXY_KEYS.flatMap((key) => (env[key] ?? '').split(','))
|
|
7
|
+
.map((entry) => entry.trim())
|
|
8
|
+
.filter((entry) => entry.length > 0 && !isIpv6CidrNoProxyEntry(entry));
|
|
9
|
+
if (!NO_PROXY_KEYS.some((key) => env[key] !== undefined)) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return [...new Set(entries)].join(',');
|
|
13
|
+
}
|
|
14
|
+
export function sanitizeChildProxyEnv(env) {
|
|
15
|
+
const sanitized = { ...env };
|
|
16
|
+
const noProxy = cleanedNoProxyValue(env);
|
|
17
|
+
if (noProxy === undefined) {
|
|
18
|
+
return sanitized;
|
|
19
|
+
}
|
|
20
|
+
sanitized.NO_PROXY = noProxy;
|
|
21
|
+
sanitized.no_proxy = noProxy;
|
|
22
|
+
return sanitized;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { sanitizeChildProxyEnv } from './proxy-env.js';
|
|
3
|
+
describe('sanitizeChildProxyEnv', () => {
|
|
4
|
+
it('drops IPv6 CIDR no-proxy entries and normalizes both env keys', () => {
|
|
5
|
+
const env = sanitizeChildProxyEnv({
|
|
6
|
+
NO_PROXY: 'localhost,127.0.0.1,127.0.0.0/8,fd07:b51a:cc66:f0::/64,*.orb.local',
|
|
7
|
+
no_proxy: '::1,0.250.250.0/24,fd00::/8,*.orb.internal',
|
|
8
|
+
});
|
|
9
|
+
expect(env.NO_PROXY).toBe('localhost,127.0.0.1,127.0.0.0/8,*.orb.local,::1,0.250.250.0/24,*.orb.internal');
|
|
10
|
+
expect(env.no_proxy).toBe(env.NO_PROXY);
|
|
11
|
+
});
|
|
12
|
+
it('preserves the input object and leaves missing proxy env unset', () => {
|
|
13
|
+
const input = { PATH: '/usr/bin' };
|
|
14
|
+
expect(sanitizeChildProxyEnv(input)).toEqual({ PATH: '/usr/bin' });
|
|
15
|
+
expect(input).toEqual({ PATH: '/usr/bin' });
|
|
16
|
+
});
|
|
17
|
+
});
|
package/dist/public-ingest.js
CHANGED
|
@@ -234,12 +234,10 @@ function targetForConnection(connectionId, connection, projectConfig, args, warn
|
|
|
234
234
|
throw new Error(`Connection "${connectionId}" uses unsupported public ingest driver "${driver || 'unknown'}"`);
|
|
235
235
|
}
|
|
236
236
|
export function buildPublicIngestPlan(project, args) {
|
|
237
|
-
|
|
238
|
-
throw new Error('Context build requires a connection id or all targets');
|
|
239
|
-
}
|
|
237
|
+
const allConnections = args.all || !args.targetConnectionId;
|
|
240
238
|
const entries = Object.entries(project.config.connections).sort(([a], [b]) => a.localeCompare(b));
|
|
241
|
-
const selected =
|
|
242
|
-
if (!
|
|
239
|
+
const selected = allConnections ? entries : entries.filter(([connectionId]) => connectionId === args.targetConnectionId);
|
|
240
|
+
if (!allConnections && selected.length === 0) {
|
|
243
241
|
throw new Error(`Connection "${args.targetConnectionId}" is not configured in ktx.yaml`);
|
|
244
242
|
}
|
|
245
243
|
if (selected.length === 0) {
|
|
@@ -109,9 +109,13 @@ describe('buildPublicIngestPlan', () => {
|
|
|
109
109
|
warnings: [],
|
|
110
110
|
});
|
|
111
111
|
});
|
|
112
|
-
it('
|
|
113
|
-
const project = projectWithConnections({
|
|
114
|
-
|
|
112
|
+
it('treats a bare invocation (no connection id, no --all) as all configured connections', () => {
|
|
113
|
+
const project = projectWithConnections({
|
|
114
|
+
warehouse: { driver: 'postgres' },
|
|
115
|
+
docs: { driver: 'notion' },
|
|
116
|
+
});
|
|
117
|
+
const plan = buildPublicIngestPlan(project, { projectDir: '/tmp/project', all: false });
|
|
118
|
+
expect(plan.targets.map((target) => target.connectionId).sort()).toEqual(['docs', 'warehouse']);
|
|
115
119
|
});
|
|
116
120
|
it('resolves database depth from flags, stored context, and defaults', () => {
|
|
117
121
|
const project = projectWithConnections({
|
package/dist/runtime.test.js
CHANGED
|
@@ -40,6 +40,7 @@ describe('runKtxRuntime', () => {
|
|
|
40
40
|
},
|
|
41
41
|
asset: {
|
|
42
42
|
wheelPath: '/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl',
|
|
43
|
+
requiresPython: { specifier: '>=3.13', minimumVersion: '3.13' },
|
|
43
44
|
manifest: {
|
|
44
45
|
schemaVersion: 1,
|
|
45
46
|
distributionName: 'kaelio-ktx',
|
|
@@ -245,7 +246,7 @@ describe('runKtxRuntime', () => {
|
|
|
245
246
|
label: 'Managed Python runtime',
|
|
246
247
|
status: 'fail',
|
|
247
248
|
detail: 'No runtime manifest at /runtime/0.2.0/manifest.json',
|
|
248
|
-
fix: 'Run: ktx
|
|
249
|
+
fix: 'Run: ktx admin runtime install --yes',
|
|
249
250
|
},
|
|
250
251
|
]),
|
|
251
252
|
};
|