@kaelio/ktx 0.8.0 → 0.10.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.8.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +17 -2
- package/dist/cli-program.d.ts +3 -0
- package/dist/cli-program.js +42 -2
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.js +94 -3
- package/dist/commands/setup-commands.js +3 -4
- package/dist/connection-recovery.d.ts +34 -0
- package/dist/connection-recovery.js +82 -0
- package/dist/connection.js +26 -2
- package/dist/connectors/bigquery/connector.d.ts +2 -5
- package/dist/connectors/bigquery/connector.js +2 -2
- package/dist/connectors/clickhouse/connector.d.ts +2 -5
- package/dist/connectors/clickhouse/connector.js +2 -2
- package/dist/connectors/mysql/connector.d.ts +7 -6
- package/dist/connectors/mysql/connector.js +25 -5
- package/dist/connectors/mysql/dialect.d.ts +1 -1
- package/dist/connectors/mysql/dialect.js +12 -2
- package/dist/connectors/postgres/connector.d.ts +2 -5
- package/dist/connectors/postgres/connector.js +2 -2
- package/dist/connectors/snowflake/connector.d.ts +2 -5
- package/dist/connectors/snowflake/connector.js +2 -2
- package/dist/connectors/sqlite/connector.d.ts +2 -5
- package/dist/connectors/sqlite/connector.js +2 -2
- package/dist/connectors/sqlserver/connector.d.ts +2 -5
- package/dist/connectors/sqlserver/connector.js +2 -2
- package/dist/context/connections/drivers.d.ts +0 -1
- package/dist/context/connections/drivers.js +0 -7
- package/dist/context/connections/query-executor.d.ts +2 -1
- package/dist/context/core/abort.d.ts +9 -0
- package/dist/context/core/abort.js +36 -0
- package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
- package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
- package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
- package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
- package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
- package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
- package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
- package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
- package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
- package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
- package/dist/context/ingest/final-gate-repair.d.ts +1 -0
- package/dist/context/ingest/final-gate-repair.js +1 -0
- package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
- package/dist/context/ingest/ingest-bundle.runner.js +127 -53
- package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
- package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
- package/dist/context/ingest/local-adapters.js +21 -4
- package/dist/context/ingest/local-bundle-runtime.js +13 -5
- package/dist/context/ingest/local-ingest.d.ts +1 -0
- package/dist/context/ingest/local-ingest.js +13 -3
- package/dist/context/ingest/memory-flow/events.js +1 -1
- package/dist/context/ingest/memory-flow/schema.js +8 -3
- package/dist/context/ingest/memory-flow/types.d.ts +7 -3
- package/dist/context/ingest/ports.d.ts +3 -5
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
- package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
- package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
- package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
- package/dist/context/ingest/types.d.ts +1 -0
- package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
- package/dist/context/llm/ai-sdk-runtime.js +152 -16
- package/dist/context/llm/claude-code-runtime.d.ts +6 -4
- package/dist/context/llm/claude-code-runtime.js +127 -48
- package/dist/context/llm/codex-exec-events.d.ts +20 -0
- package/dist/context/llm/codex-exec-events.js +155 -0
- package/dist/context/llm/codex-isolation.d.ts +3 -0
- package/dist/context/llm/codex-isolation.js +5 -0
- package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
- package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
- package/dist/context/llm/codex-models.d.ts +2 -0
- package/dist/context/llm/codex-models.js +17 -0
- package/dist/context/llm/codex-runtime-config.d.ts +16 -0
- package/dist/context/llm/codex-runtime-config.js +19 -0
- package/dist/context/llm/codex-runtime.d.ts +37 -0
- package/dist/context/llm/codex-runtime.js +347 -0
- package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
- package/dist/context/llm/codex-sdk-runner.js +63 -0
- package/dist/context/llm/local-config.d.ts +16 -4
- package/dist/context/llm/local-config.js +18 -2
- package/dist/context/llm/rate-limit-governor.d.ts +103 -0
- package/dist/context/llm/rate-limit-governor.js +285 -0
- package/dist/context/llm/runtime-port.d.ts +3 -6
- package/dist/context/mcp/context-tools.js +43 -13
- package/dist/context/project/config.d.ts +14 -0
- package/dist/context/project/config.js +37 -2
- package/dist/context/scan/types.d.ts +15 -2
- package/dist/context/scan/types.js +12 -0
- package/dist/context/sl/description-normalization.js +4 -14
- package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
- package/dist/context/sql-analysis/ports.d.ts +12 -2
- package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +63 -32
- package/dist/demo-metrics.d.ts +0 -2
- package/dist/demo-metrics.js +1 -11
- package/dist/ingest.d.ts +1 -0
- package/dist/ingest.js +32 -3
- package/dist/io/buffered-command-io.d.ts +11 -0
- package/dist/io/buffered-command-io.js +28 -0
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/llm/types.d.ts +1 -1
- package/dist/local-adapters.d.ts +10 -2
- package/dist/local-adapters.js +19 -3
- package/dist/memory-flow-hud.js +8 -16
- package/dist/next-steps.js +1 -2
- package/dist/progress-port-adapter.d.ts +6 -0
- package/dist/progress-port-adapter.js +18 -0
- package/dist/public-ingest.d.ts +20 -1
- package/dist/public-ingest.js +228 -42
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +21 -3
- package/dist/setup-context.d.ts +2 -0
- package/dist/setup-context.js +133 -27
- package/dist/setup-databases.d.ts +18 -1
- package/dist/setup-databases.js +378 -249
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +11 -15
- package/dist/setup-models.js +140 -276
- package/dist/setup-prompts.js +3 -2
- package/dist/setup-ready-menu.d.ts +16 -2
- package/dist/setup-ready-menu.js +37 -5
- package/dist/setup-sources.js +115 -35
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +23 -11
- package/dist/sl.d.ts +2 -2
- package/dist/sl.js +20 -4
- package/dist/sql.js +18 -2
- package/dist/star-prompt/cache.d.ts +16 -0
- package/dist/star-prompt/cache.js +45 -0
- package/dist/star-prompt/star-count.d.ts +7 -0
- package/dist/star-prompt/star-count.js +66 -0
- package/dist/star-prompt/star-line.d.ts +12 -0
- package/dist/star-prompt/star-line.js +26 -0
- package/dist/status-project.d.ts +11 -0
- package/dist/status-project.js +50 -1
- package/dist/telemetry/command-hook.d.ts +1 -0
- package/dist/telemetry/command-hook.js +3 -1
- package/dist/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +35 -6
- package/dist/telemetry/events.js +25 -2
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/identity.d.ts +0 -1
- package/dist/telemetry/identity.js +6 -6
- package/dist/telemetry/index.d.ts +15 -2
- package/dist/telemetry/index.js +15 -3
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -0
- package/dist/telemetry/scrubber.d.ts +10 -0
- package/dist/telemetry/scrubber.js +20 -0
- package/dist/update-check/cache.d.ts +21 -0
- package/dist/update-check/cache.js +38 -0
- package/dist/update-check/channel.d.ts +15 -0
- package/dist/update-check/channel.js +30 -0
- package/dist/update-check/registry.d.ts +1 -0
- package/dist/update-check/registry.js +45 -0
- package/dist/update-check/update-check.d.ts +43 -0
- package/dist/update-check/update-check.js +116 -0
- package/package.json +12 -4
- package/dist/context/connections/local-query-executor.d.ts +0 -6
- package/dist/context/connections/local-query-executor.js +0 -39
- package/dist/context/connections/postgres-query-executor.d.ts +0 -25
- package/dist/context/connections/postgres-query-executor.js +0 -53
- package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
- package/dist/context/connections/sqlite-query-executor.js +0 -74
package/dist/setup-demo-tour.js
CHANGED
package/dist/setup-embeddings.js
CHANGED
|
@@ -138,8 +138,8 @@ async function chooseCredentialRef(backend, args, io, deps) {
|
|
|
138
138
|
const choice = await prompts.select({
|
|
139
139
|
message: `How should KTX find your ${embeddingBackendDisplayName(backend)} embedding API key?`,
|
|
140
140
|
options: [
|
|
141
|
-
{ value: 'env', label: `Use ${defaultEnv} from the environment` },
|
|
142
141
|
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
|
|
142
|
+
{ value: 'env', label: `Use ${defaultEnv} from the environment` },
|
|
143
143
|
{ value: 'back', label: 'Back' },
|
|
144
144
|
],
|
|
145
145
|
});
|
package/dist/setup-models.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type KtxProjectLlmConfig } from './context/project/config.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type KtxLlmConfig } from './llm/types.js';
|
|
3
3
|
import { type KtxLlmHealthCheckResult } from './llm/model-health.js';
|
|
4
4
|
import { type KtxCliSpinner } from './clack.js';
|
|
5
5
|
import type { KtxCliIo } from './cli-runtime.js';
|
|
@@ -10,7 +10,6 @@ export interface KtxSetupModelArgs {
|
|
|
10
10
|
llmBackend?: KtxSetupLlmBackend;
|
|
11
11
|
anthropicApiKeyEnv?: string;
|
|
12
12
|
anthropicApiKeyFile?: string;
|
|
13
|
-
llmModel?: string;
|
|
14
13
|
vertexProject?: string;
|
|
15
14
|
vertexLocation?: string;
|
|
16
15
|
forcePrompt?: boolean;
|
|
@@ -33,13 +32,7 @@ export type KtxSetupModelResult = {
|
|
|
33
32
|
status: 'failed';
|
|
34
33
|
projectDir: string;
|
|
35
34
|
};
|
|
36
|
-
|
|
37
|
-
export interface AnthropicModelChoice {
|
|
38
|
-
id: string;
|
|
39
|
-
label: string;
|
|
40
|
-
recommended: boolean;
|
|
41
|
-
}
|
|
42
|
-
export type KtxSetupLlmBackend = 'anthropic' | 'vertex' | 'claude-code';
|
|
35
|
+
export type KtxSetupLlmBackend = 'anthropic' | 'vertex' | 'claude-code' | 'codex';
|
|
43
36
|
/** @internal */
|
|
44
37
|
export interface KtxSetupModelPromptAdapter {
|
|
45
38
|
select(options: {
|
|
@@ -62,9 +55,7 @@ export interface KtxSetupModelPromptAdapter {
|
|
|
62
55
|
}
|
|
63
56
|
export interface KtxSetupModelDeps {
|
|
64
57
|
env?: NodeJS.ProcessEnv;
|
|
65
|
-
fetch?: typeof fetch;
|
|
66
58
|
prompts?: KtxSetupModelPromptAdapter;
|
|
67
|
-
listModels?: (apiKey: string) => Promise<AnthropicModelChoice[]>;
|
|
68
59
|
healthCheck?: (config: KtxLlmConfig) => Promise<KtxLlmHealthCheckResult>;
|
|
69
60
|
claudeCodeAuthProbe?: (input: {
|
|
70
61
|
projectDir: string;
|
|
@@ -76,18 +67,23 @@ export interface KtxSetupModelDeps {
|
|
|
76
67
|
ok: false;
|
|
77
68
|
message: string;
|
|
78
69
|
}>;
|
|
70
|
+
codexAuthProbe?: (input: {
|
|
71
|
+
projectDir: string;
|
|
72
|
+
model: string;
|
|
73
|
+
}) => Promise<{
|
|
74
|
+
ok: true;
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
message: string;
|
|
78
|
+
}>;
|
|
79
79
|
readGcloudProject?: () => Promise<string | undefined>;
|
|
80
80
|
listGcloudProjects?: () => Promise<GcloudProjectChoice[]>;
|
|
81
81
|
spinner?: () => KtxCliSpinner;
|
|
82
82
|
}
|
|
83
|
-
/** @internal */
|
|
84
|
-
export declare const BUNDLED_ANTHROPIC_MODELS: AnthropicModelChoice[];
|
|
85
83
|
interface GcloudProjectChoice {
|
|
86
84
|
projectId: string;
|
|
87
85
|
name?: string;
|
|
88
86
|
}
|
|
89
|
-
/** @internal */
|
|
90
|
-
export declare function fetchAnthropicModels(apiKey: string, fetchFn?: typeof fetch): Promise<AnthropicModelChoice[]>;
|
|
91
87
|
export declare function isKtxSetupLlmConfigReady(config: KtxProjectLlmConfig): boolean;
|
|
92
88
|
export declare function runKtxSetupAnthropicModelStep(args: KtxSetupModelArgs, io: KtxCliIo, deps?: KtxSetupModelDeps): Promise<KtxSetupModelResult>;
|
|
93
89
|
export {};
|
package/dist/setup-models.js
CHANGED
|
@@ -3,10 +3,14 @@ import { writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { resolveLocalKtxLlmConfig } from './context/llm/local-config.js';
|
|
5
5
|
import { runClaudeCodeAuthProbe } from './context/llm/claude-code-runtime.js';
|
|
6
|
+
import { formatCodexIsolationWarning } from './context/llm/codex-isolation.js';
|
|
7
|
+
import { runCodexAuthProbe } from './context/llm/codex-runtime.js';
|
|
8
|
+
import { DEFAULT_CODEX_MODEL } from './context/llm/codex-models.js';
|
|
6
9
|
import { resolveKtxConfigReference } from './context/core/config-reference.js';
|
|
7
10
|
import { serializeKtxProjectConfig } from './context/project/config.js';
|
|
8
11
|
import { loadKtxProject } from './context/project/project.js';
|
|
9
12
|
import { markKtxSetupStateStepComplete } from './context/project/setup-config.js';
|
|
13
|
+
import { KTX_MODEL_ROLES } from './llm/types.js';
|
|
10
14
|
import { runKtxLlmHealthCheck } from './llm/model-health.js';
|
|
11
15
|
import { formatClaudeCodePromptCachingWarning, ignoredClaudeCodePromptCachingFields, } from './claude-code-prompt-caching.js';
|
|
12
16
|
import { createClackSpinner } from './clack.js';
|
|
@@ -17,57 +21,46 @@ const ESC = String.fromCharCode(0x1b);
|
|
|
17
21
|
function yellow(text) {
|
|
18
22
|
return `${ESC}[33m${text}${ESC}[39m`;
|
|
19
23
|
}
|
|
20
|
-
/** @internal */
|
|
21
|
-
export const BUNDLED_ANTHROPIC_MODELS = [
|
|
22
|
-
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', recommended: true },
|
|
23
|
-
{ id: 'claude-opus-4-6', label: 'Claude Opus 4.6', recommended: false },
|
|
24
|
-
{ id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5', recommended: false },
|
|
25
|
-
];
|
|
26
|
-
const VERTEX_ANTHROPIC_MODELS = [
|
|
27
|
-
{ id: 'claude-opus-4-7', label: 'Claude Opus 4.7', recommended: false },
|
|
28
|
-
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', recommended: false },
|
|
29
|
-
{ id: 'claude-opus-4-6', label: 'Claude Opus 4.6', recommended: false },
|
|
30
|
-
{ id: 'claude-opus-4-5', label: 'Claude Opus 4.5', recommended: false },
|
|
31
|
-
{ id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5', recommended: false },
|
|
32
|
-
{ id: 'claude-sonnet-4-5', label: 'Claude Sonnet 4.5', recommended: false },
|
|
33
|
-
{ id: 'claude-opus-4-1', label: 'Claude Opus 4.1', recommended: false },
|
|
34
|
-
];
|
|
35
|
-
const CLAUDE_CODE_MODELS = [
|
|
36
|
-
{ id: 'sonnet', label: 'Claude Sonnet', recommended: true },
|
|
37
|
-
{ id: 'opus', label: 'Claude Opus', recommended: false },
|
|
38
|
-
{ id: 'haiku', label: 'Claude Haiku', recommended: false },
|
|
39
|
-
];
|
|
40
|
-
const HIDDEN_ANTHROPIC_MODEL_PATTERNS = [
|
|
41
|
-
/^claude-sonnet-4$/i,
|
|
42
|
-
/^claude-opus-4$/i,
|
|
43
|
-
/^Claude Sonnet 4$/i,
|
|
44
|
-
/^Claude Opus 4$/i,
|
|
45
|
-
];
|
|
46
24
|
const ANTHROPIC_CREDENTIAL_PROMPT_CONTEXT = 'KTX uses the key to verify Anthropic model access now and to run ingest agents that turn schemas, SQL, ' +
|
|
47
25
|
'BI metadata, and docs into semantic-layer sources and wiki context. ktx.yaml stores an env: or file: ' +
|
|
48
26
|
'reference, not the raw key.';
|
|
49
|
-
const ANTHROPIC_MODEL_PROMPT_CONTEXT = 'KTX uses this as the default model for ingest agents that turn schemas, SQL, BI metadata, and docs ' +
|
|
50
|
-
'into semantic-layer sources and wiki context.';
|
|
51
27
|
const VERTEX_PROJECT_PROMPT_CONTEXT = 'KTX stores the selected Google Cloud project ID in ktx.yaml and uses Application Default Credentials for ' +
|
|
52
28
|
'access. Project visibility depends on the signed-in Google account and organization permissions.';
|
|
53
29
|
const DEFAULT_VERTEX_LOCATION = 'us-east5';
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
30
|
+
const ANTHROPIC_PRESET = {
|
|
31
|
+
default: 'claude-sonnet-4-6',
|
|
32
|
+
triage: 'claude-haiku-4-5',
|
|
33
|
+
candidateExtraction: 'claude-sonnet-4-6',
|
|
34
|
+
curator: 'claude-opus-4-7',
|
|
35
|
+
reconcile: 'claude-opus-4-7',
|
|
36
|
+
repair: 'claude-haiku-4-5',
|
|
37
|
+
};
|
|
38
|
+
const CLAUDE_CODE_PRESET = {
|
|
39
|
+
default: 'sonnet',
|
|
40
|
+
triage: 'haiku',
|
|
41
|
+
candidateExtraction: 'sonnet',
|
|
42
|
+
curator: 'opus',
|
|
43
|
+
reconcile: 'opus',
|
|
44
|
+
repair: 'haiku',
|
|
45
|
+
};
|
|
46
|
+
const CODEX_PRESET = {
|
|
47
|
+
default: DEFAULT_CODEX_MODEL,
|
|
48
|
+
triage: DEFAULT_CODEX_MODEL,
|
|
49
|
+
candidateExtraction: DEFAULT_CODEX_MODEL,
|
|
50
|
+
curator: DEFAULT_CODEX_MODEL,
|
|
51
|
+
reconcile: DEFAULT_CODEX_MODEL,
|
|
52
|
+
repair: DEFAULT_CODEX_MODEL,
|
|
53
|
+
};
|
|
54
|
+
const MODEL_PRESETS = {
|
|
55
|
+
anthropic: ANTHROPIC_PRESET,
|
|
56
|
+
vertex: ANTHROPIC_PRESET,
|
|
57
|
+
'claude-code': CLAUDE_CODE_PRESET,
|
|
58
|
+
codex: CODEX_PRESET,
|
|
59
|
+
};
|
|
60
|
+
function presetForBackend(backend) {
|
|
61
|
+
return MODEL_PRESETS[backend];
|
|
70
62
|
}
|
|
63
|
+
const execFileAsync = promisify(execFile);
|
|
71
64
|
function createPromptAdapter() {
|
|
72
65
|
return createKtxSetupPromptAdapter({ selectCancelValue: 'back' });
|
|
73
66
|
}
|
|
@@ -106,35 +99,6 @@ async function defaultListGcloudProjects() {
|
|
|
106
99
|
})
|
|
107
100
|
.filter((project) => Boolean(project));
|
|
108
101
|
}
|
|
109
|
-
/** @internal */
|
|
110
|
-
export async function fetchAnthropicModels(apiKey, fetchFn = fetch) {
|
|
111
|
-
const response = await fetchFn('https://api.anthropic.com/v1/models?limit=1000', {
|
|
112
|
-
headers: {
|
|
113
|
-
'anthropic-version': '2023-06-01',
|
|
114
|
-
'x-api-key': apiKey,
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
if (!response.ok) {
|
|
118
|
-
if (response.status === 401 || response.status === 403) {
|
|
119
|
-
throw new AnthropicModelDiscoveryError(`Anthropic model discovery failed with HTTP ${response.status}`, 'authentication', response.status);
|
|
120
|
-
}
|
|
121
|
-
throw new AnthropicModelDiscoveryError(`Anthropic model discovery failed with HTTP ${response.status}`, 'http', response.status);
|
|
122
|
-
}
|
|
123
|
-
const body = (await response.json());
|
|
124
|
-
const models = (body.data ?? [])
|
|
125
|
-
.map((item) => ({
|
|
126
|
-
id: typeof item.id === 'string' ? item.id : '',
|
|
127
|
-
label: typeof item.display_name === 'string' ? item.display_name : typeof item.id === 'string' ? item.id : '',
|
|
128
|
-
recommended: false,
|
|
129
|
-
}))
|
|
130
|
-
.filter((item) => item.id.startsWith('claude-'))
|
|
131
|
-
.filter(isSelectableAnthropicModel);
|
|
132
|
-
if (models.length === 0) {
|
|
133
|
-
throw new AnthropicModelDiscoveryError('Anthropic model discovery returned no Claude models', 'empty-response');
|
|
134
|
-
}
|
|
135
|
-
const recommendedIndex = models.findIndex((item) => item.id.includes('sonnet'));
|
|
136
|
-
return models.map((item, index) => ({ ...item, recommended: index === Math.max(recommendedIndex, 0) }));
|
|
137
|
-
}
|
|
138
102
|
export function isKtxSetupLlmConfigReady(config) {
|
|
139
103
|
let resolved;
|
|
140
104
|
try {
|
|
@@ -149,16 +113,26 @@ export function isKtxSetupLlmConfigReady(config) {
|
|
|
149
113
|
if (resolved.backend === 'vertex') {
|
|
150
114
|
return typeof resolved.vertex?.location === 'string' && resolved.vertex.location.trim().length > 0;
|
|
151
115
|
}
|
|
152
|
-
return resolved.backend === 'anthropic' ||
|
|
116
|
+
return (resolved.backend === 'anthropic' ||
|
|
117
|
+
resolved.backend === 'gateway' ||
|
|
118
|
+
resolved.backend === 'claude-code' ||
|
|
119
|
+
resolved.backend === 'codex');
|
|
153
120
|
}
|
|
154
121
|
function hasUsableConfiguredLlm(config) {
|
|
155
122
|
return isKtxSetupLlmConfigReady(config.llm);
|
|
156
123
|
}
|
|
157
|
-
function buildProjectLlmConfig(existing, provider,
|
|
124
|
+
function buildProjectLlmConfig(existing, provider, models) {
|
|
158
125
|
if (provider.backend === 'claude-code') {
|
|
159
126
|
return {
|
|
160
127
|
provider: { backend: 'claude-code' },
|
|
161
|
-
models
|
|
128
|
+
models,
|
|
129
|
+
promptCaching: existing.promptCaching,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (provider.backend === 'codex') {
|
|
133
|
+
return {
|
|
134
|
+
provider: { backend: 'codex' },
|
|
135
|
+
models,
|
|
162
136
|
promptCaching: existing.promptCaching,
|
|
163
137
|
};
|
|
164
138
|
}
|
|
@@ -168,7 +142,7 @@ function buildProjectLlmConfig(existing, provider, model) {
|
|
|
168
142
|
backend: 'vertex',
|
|
169
143
|
vertex: provider.vertex,
|
|
170
144
|
},
|
|
171
|
-
models
|
|
145
|
+
models,
|
|
172
146
|
promptCaching: { ...(existing.promptCaching ?? {}), enabled: true, vertexFallbackTo5m: true },
|
|
173
147
|
};
|
|
174
148
|
}
|
|
@@ -177,7 +151,7 @@ function buildProjectLlmConfig(existing, provider, model) {
|
|
|
177
151
|
backend: 'anthropic',
|
|
178
152
|
anthropic: { api_key: provider.credentialRef },
|
|
179
153
|
},
|
|
180
|
-
models
|
|
154
|
+
models,
|
|
181
155
|
promptCaching: { ...(existing.promptCaching ?? {}), enabled: true },
|
|
182
156
|
};
|
|
183
157
|
}
|
|
@@ -276,8 +250,8 @@ async function chooseCredentialRef(args, io, deps) {
|
|
|
276
250
|
const choice = await prompts.select({
|
|
277
251
|
message: `How should KTX find your Anthropic API key?\n\n${ANTHROPIC_CREDENTIAL_PROMPT_CONTEXT}`,
|
|
278
252
|
options: [
|
|
279
|
-
{ value: 'env', label: 'Use ANTHROPIC_API_KEY from the environment' },
|
|
280
253
|
{ value: 'paste', label: 'Paste a key and save it as a local secret file' },
|
|
254
|
+
{ value: 'env', label: 'Use ANTHROPIC_API_KEY from the environment' },
|
|
281
255
|
{ value: 'back', label: 'Back' },
|
|
282
256
|
],
|
|
283
257
|
});
|
|
@@ -316,14 +290,11 @@ function requestedBackend(args) {
|
|
|
316
290
|
if (args.vertexProject || args.vertexLocation) {
|
|
317
291
|
return 'vertex';
|
|
318
292
|
}
|
|
319
|
-
if (args.anthropicApiKeyEnv || args.anthropicApiKeyFile
|
|
293
|
+
if (args.anthropicApiKeyEnv || args.anthropicApiKeyFile) {
|
|
320
294
|
return 'anthropic';
|
|
321
295
|
}
|
|
322
296
|
return undefined;
|
|
323
297
|
}
|
|
324
|
-
function requestedModel(args) {
|
|
325
|
-
return args.llmModel;
|
|
326
|
-
}
|
|
327
298
|
async function chooseBackend(args, io, deps) {
|
|
328
299
|
const explicit = requestedBackend(args);
|
|
329
300
|
if (explicit) {
|
|
@@ -340,6 +311,7 @@ async function chooseBackend(args, io, deps) {
|
|
|
340
311
|
message: 'Which LLM provider should KTX use?',
|
|
341
312
|
options: [
|
|
342
313
|
{ value: 'claude-code', label: 'Claude subscription (Pro/Max)' },
|
|
314
|
+
{ value: 'codex', label: 'Codex subscription' },
|
|
343
315
|
{ value: 'anthropic', label: 'Anthropic API key' },
|
|
344
316
|
{ value: 'vertex', label: 'Google Vertex AI for Anthropic Claude' },
|
|
345
317
|
{ value: 'back', label: 'Back' },
|
|
@@ -350,7 +322,7 @@ async function chooseBackend(args, io, deps) {
|
|
|
350
322
|
}
|
|
351
323
|
return {
|
|
352
324
|
status: 'ready',
|
|
353
|
-
backend: choice === 'vertex' || choice === 'claude-code' ? choice : 'anthropic',
|
|
325
|
+
backend: choice === 'vertex' || choice === 'claude-code' || choice === 'codex' ? choice : 'anthropic',
|
|
354
326
|
prompted: true,
|
|
355
327
|
};
|
|
356
328
|
}
|
|
@@ -541,141 +513,11 @@ async function chooseVertexConfig(args, io, deps) {
|
|
|
541
513
|
},
|
|
542
514
|
};
|
|
543
515
|
}
|
|
544
|
-
async function
|
|
545
|
-
const providedModel = requestedModel(args);
|
|
546
|
-
if (providedModel) {
|
|
547
|
-
return { status: 'ready', model: providedModel };
|
|
548
|
-
}
|
|
549
|
-
if (args.inputMode === 'disabled') {
|
|
550
|
-
io.stderr.write('Missing LLM model: pass --llm-model.\n');
|
|
551
|
-
return { status: 'missing-input' };
|
|
552
|
-
}
|
|
553
|
-
let models;
|
|
554
|
-
try {
|
|
555
|
-
models = deps.listModels
|
|
556
|
-
? await deps.listModels(credentialValue)
|
|
557
|
-
: await fetchAnthropicModels(credentialValue, deps.fetch);
|
|
558
|
-
}
|
|
559
|
-
catch (error) {
|
|
560
|
-
if (isAnthropicModelAuthenticationError(error)) {
|
|
561
|
-
const statusSuffix = error.status ? ` (HTTP ${error.status})` : '';
|
|
562
|
-
io.stderr.write(`Anthropic API key is invalid or unauthorized${statusSuffix}. Check the key and try again.\n`);
|
|
563
|
-
return { status: 'invalid-credential' };
|
|
564
|
-
}
|
|
565
|
-
io.stderr.write('Could not fetch live Anthropic models. Showing bundled defaults. Setup will still test the selected model before saving it.\n');
|
|
566
|
-
models = BUNDLED_ANTHROPIC_MODELS;
|
|
567
|
-
}
|
|
568
|
-
const selectableModels = models.filter(isSelectableAnthropicModel);
|
|
569
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
570
|
-
const modelOptions = [
|
|
571
|
-
...selectableModels.map((model) => ({
|
|
572
|
-
value: model.id,
|
|
573
|
-
label: model.label || model.id,
|
|
574
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
575
|
-
})),
|
|
576
|
-
{ value: 'manual', label: 'Enter a model ID manually' },
|
|
577
|
-
{ value: 'back', label: 'Back' },
|
|
578
|
-
];
|
|
579
|
-
const choice = await prompts.autocomplete({
|
|
580
|
-
message: `Which Anthropic model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
581
|
-
placeholder: 'Type to search models',
|
|
582
|
-
options: modelOptions,
|
|
583
|
-
});
|
|
584
|
-
if (choice === 'back') {
|
|
585
|
-
return { status: 'back' };
|
|
586
|
-
}
|
|
587
|
-
if (choice === 'manual') {
|
|
588
|
-
const manual = await prompts.text({
|
|
589
|
-
message: withTextInputNavigation('Anthropic model ID'),
|
|
590
|
-
placeholder: selectableModels.find((model) => model.recommended)?.id ?? selectableModels[0]?.id,
|
|
591
|
-
});
|
|
592
|
-
if (manual === undefined) {
|
|
593
|
-
return { status: 'back' };
|
|
594
|
-
}
|
|
595
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
596
|
-
}
|
|
597
|
-
return { status: 'ready', model: choice };
|
|
598
|
-
}
|
|
599
|
-
async function chooseVertexModel(args, io, deps) {
|
|
600
|
-
const providedModel = requestedModel(args);
|
|
601
|
-
if (providedModel) {
|
|
602
|
-
return { status: 'ready', model: providedModel };
|
|
603
|
-
}
|
|
604
|
-
if (args.inputMode === 'disabled') {
|
|
605
|
-
io.stderr.write('Missing LLM model: pass --llm-model.\n');
|
|
606
|
-
return { status: 'missing-input' };
|
|
607
|
-
}
|
|
608
|
-
const selectableModels = VERTEX_ANTHROPIC_MODELS.filter(isSelectableAnthropicModel);
|
|
609
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
610
|
-
const choice = await prompts.autocomplete({
|
|
611
|
-
message: `Which Anthropic model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
612
|
-
placeholder: 'Type to search models',
|
|
613
|
-
options: [
|
|
614
|
-
...selectableModels.map((model) => ({
|
|
615
|
-
value: model.id,
|
|
616
|
-
label: model.label || model.id,
|
|
617
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
618
|
-
})),
|
|
619
|
-
{ value: 'manual', label: 'Enter a model ID manually' },
|
|
620
|
-
{ value: 'back', label: 'Back' },
|
|
621
|
-
],
|
|
622
|
-
});
|
|
623
|
-
if (choice === 'back') {
|
|
624
|
-
return { status: 'back' };
|
|
625
|
-
}
|
|
626
|
-
if (choice === 'manual') {
|
|
627
|
-
const manual = await prompts.text({
|
|
628
|
-
message: withTextInputNavigation('Anthropic model ID'),
|
|
629
|
-
placeholder: selectableModels.find((model) => model.recommended)?.id ?? selectableModels[0]?.id,
|
|
630
|
-
});
|
|
631
|
-
if (manual === undefined) {
|
|
632
|
-
return { status: 'back' };
|
|
633
|
-
}
|
|
634
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
635
|
-
}
|
|
636
|
-
return { status: 'ready', model: choice };
|
|
637
|
-
}
|
|
638
|
-
async function chooseClaudeCodeModel(args, deps) {
|
|
639
|
-
const providedModel = requestedModel(args);
|
|
640
|
-
if (providedModel) {
|
|
641
|
-
return { status: 'ready', model: providedModel };
|
|
642
|
-
}
|
|
643
|
-
if (args.inputMode === 'disabled') {
|
|
644
|
-
return { status: 'ready', model: 'sonnet' };
|
|
645
|
-
}
|
|
646
|
-
const prompts = deps.prompts ?? createPromptAdapter();
|
|
647
|
-
const choice = await prompts.select({
|
|
648
|
-
message: `Which Claude Code model should KTX use?\n\n${ANTHROPIC_MODEL_PROMPT_CONTEXT}`,
|
|
649
|
-
options: [
|
|
650
|
-
...CLAUDE_CODE_MODELS.map((model) => ({
|
|
651
|
-
value: model.id,
|
|
652
|
-
label: model.label,
|
|
653
|
-
...(model.recommended ? { hint: 'recommended' } : {}),
|
|
654
|
-
})),
|
|
655
|
-
{ value: 'manual', label: 'Enter a Claude Code model ID manually' },
|
|
656
|
-
{ value: 'back', label: 'Back' },
|
|
657
|
-
],
|
|
658
|
-
});
|
|
659
|
-
if (choice === 'back') {
|
|
660
|
-
return { status: 'back' };
|
|
661
|
-
}
|
|
662
|
-
if (choice === 'manual') {
|
|
663
|
-
const manual = await prompts.text({
|
|
664
|
-
message: withTextInputNavigation('Claude Code model ID'),
|
|
665
|
-
placeholder: CLAUDE_CODE_MODELS.find((model) => model.recommended)?.id ?? CLAUDE_CODE_MODELS[0]?.id,
|
|
666
|
-
});
|
|
667
|
-
if (manual === undefined) {
|
|
668
|
-
return { status: 'back' };
|
|
669
|
-
}
|
|
670
|
-
return manual.trim() ? { status: 'ready', model: manual.trim() } : { status: 'missing-input' };
|
|
671
|
-
}
|
|
672
|
-
return { status: 'ready', model: choice };
|
|
673
|
-
}
|
|
674
|
-
async function persistLlmConfig(projectDir, provider, model) {
|
|
516
|
+
async function persistLlmConfig(projectDir, provider, models) {
|
|
675
517
|
const project = await loadKtxProject({ projectDir });
|
|
676
518
|
const config = {
|
|
677
519
|
...project.config,
|
|
678
|
-
llm: buildProjectLlmConfig(project.config.llm, provider,
|
|
520
|
+
llm: buildProjectLlmConfig(project.config.llm, provider, models),
|
|
679
521
|
scan: {
|
|
680
522
|
...project.config.scan,
|
|
681
523
|
enrichment: {
|
|
@@ -696,6 +538,48 @@ function buildInteractiveRetryArgs(args, backend) {
|
|
|
696
538
|
skipLlm: args.skipLlm,
|
|
697
539
|
};
|
|
698
540
|
}
|
|
541
|
+
function distinctPresetModels(preset) {
|
|
542
|
+
const models = [];
|
|
543
|
+
const seen = new Set();
|
|
544
|
+
for (const role of KTX_MODEL_ROLES) {
|
|
545
|
+
const model = preset[role];
|
|
546
|
+
if (!seen.has(model)) {
|
|
547
|
+
seen.add(model);
|
|
548
|
+
models.push(model);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return models;
|
|
552
|
+
}
|
|
553
|
+
function rolesUsingModel(preset, model) {
|
|
554
|
+
return KTX_MODEL_ROLES.filter((role) => preset[role] === model);
|
|
555
|
+
}
|
|
556
|
+
function formatPresetFallbackWarning(roles, unavailableModel, anchorModel) {
|
|
557
|
+
return `LLM model ${unavailableModel} is unavailable for ${roles.join(', ')}; using ${anchorModel} for those roles.`;
|
|
558
|
+
}
|
|
559
|
+
async function validatePresetModels(preset, validateModel, io) {
|
|
560
|
+
const anchorModel = preset.default;
|
|
561
|
+
const degraded = { ...preset };
|
|
562
|
+
const models = distinctPresetModels(preset);
|
|
563
|
+
const anchorResult = await validateModel(anchorModel);
|
|
564
|
+
if (!anchorResult.ok) {
|
|
565
|
+
return { status: 'failed', message: anchorResult.message };
|
|
566
|
+
}
|
|
567
|
+
for (const model of models) {
|
|
568
|
+
if (model === anchorModel) {
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
const result = await validateModel(model);
|
|
572
|
+
if (result.ok) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
const affectedRoles = rolesUsingModel(degraded, model);
|
|
576
|
+
for (const role of affectedRoles) {
|
|
577
|
+
degraded[role] = anchorModel;
|
|
578
|
+
}
|
|
579
|
+
io.stderr.write(`${formatPresetFallbackWarning(affectedRoles, model, anchorModel)}\n`);
|
|
580
|
+
}
|
|
581
|
+
return { status: 'ready', models: degraded };
|
|
582
|
+
}
|
|
699
583
|
export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
700
584
|
if (args.skipLlm) {
|
|
701
585
|
io.stdout.write('│ LLM setup skipped.\n');
|
|
@@ -707,7 +591,6 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
707
591
|
!args.llmBackend &&
|
|
708
592
|
!args.anthropicApiKeyEnv &&
|
|
709
593
|
!args.anthropicApiKeyFile &&
|
|
710
|
-
!args.llmModel &&
|
|
711
594
|
!args.vertexProject &&
|
|
712
595
|
!args.vertexLocation) {
|
|
713
596
|
io.stdout.write(`│ LLM ready: yes (${project.config.llm.models.default})\n`);
|
|
@@ -732,55 +615,50 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
732
615
|
if (vertex.status !== 'ready') {
|
|
733
616
|
return { status: vertex.status, projectDir: args.projectDir };
|
|
734
617
|
}
|
|
735
|
-
const
|
|
736
|
-
|
|
618
|
+
const preset = presetForBackend('vertex');
|
|
619
|
+
const validation = await validatePresetModels(preset, async (model) => runLlmHealthCheckWithProgress(buildVertexHealthConfig(vertex.values, model), 'Vertex AI', model, healthCheck, deps), io);
|
|
620
|
+
if (validation.status !== 'ready') {
|
|
621
|
+
io.stderr.write(`Vertex AI Anthropic model health check failed: ${formatVertexHealthFailure(validation.message, vertex.values)}\n`);
|
|
622
|
+
if (args.inputMode === 'disabled') {
|
|
623
|
+
return { status: 'failed', projectDir: args.projectDir };
|
|
624
|
+
}
|
|
625
|
+
io.stderr.write('Choose a different Vertex AI project or location, or Back.\n');
|
|
737
626
|
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
738
627
|
continue;
|
|
739
628
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
if (model.status !== 'ready') {
|
|
744
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
745
|
-
}
|
|
746
|
-
const health = await runLlmHealthCheckWithProgress(buildVertexHealthConfig(vertex.values, model.model), 'Vertex AI', model.model, healthCheck, deps);
|
|
747
|
-
if (health.ok) {
|
|
748
|
-
await persistLlmConfig(args.projectDir, { backend: 'vertex', vertex: vertex.refs }, model.model);
|
|
749
|
-
io.stdout.write(`│ LLM ready: yes (${model.model})\n`);
|
|
750
|
-
return { status: 'ready', projectDir: args.projectDir };
|
|
751
|
-
}
|
|
752
|
-
io.stderr.write(`Vertex AI Anthropic model health check failed: ${formatVertexHealthFailure(health.message, vertex.values)}\n`);
|
|
753
|
-
if (args.inputMode === 'disabled') {
|
|
754
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
755
|
-
}
|
|
756
|
-
io.stderr.write('Choose a different Vertex AI project, location, or model, or Back.\n');
|
|
757
|
-
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
758
|
-
continue;
|
|
629
|
+
await persistLlmConfig(args.projectDir, { backend: 'vertex', vertex: vertex.refs }, validation.models);
|
|
630
|
+
io.stdout.write(`│ LLM ready: yes (${validation.models.default})\n`);
|
|
631
|
+
return { status: 'ready', projectDir: args.projectDir };
|
|
759
632
|
}
|
|
760
633
|
if (backendChoice.backend === 'claude-code') {
|
|
761
|
-
const
|
|
762
|
-
if (model.status === 'back' && backendChoice.prompted) {
|
|
763
|
-
attemptArgs = buildInteractiveRetryArgs(args);
|
|
764
|
-
continue;
|
|
765
|
-
}
|
|
766
|
-
if (model.status === 'invalid-credential') {
|
|
767
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
768
|
-
}
|
|
769
|
-
if (model.status !== 'ready') {
|
|
770
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
771
|
-
}
|
|
634
|
+
const preset = presetForBackend('claude-code');
|
|
772
635
|
const probe = deps.claudeCodeAuthProbe ?? runClaudeCodeAuthProbe;
|
|
773
|
-
const
|
|
774
|
-
if (
|
|
775
|
-
io.stderr.write(`${
|
|
636
|
+
const validation = await validatePresetModels(preset, async (model) => probe({ projectDir: args.projectDir, model, env: deps.env ?? process.env }), io);
|
|
637
|
+
if (validation.status !== 'ready') {
|
|
638
|
+
io.stderr.write(`${validation.message}\n`);
|
|
776
639
|
return { status: 'failed', projectDir: args.projectDir };
|
|
777
640
|
}
|
|
778
|
-
const warning = formatClaudeCodePromptCachingWarning(ignoredClaudeCodePromptCachingFields(buildProjectLlmConfig(project.config.llm, { backend: 'claude-code' },
|
|
641
|
+
const warning = formatClaudeCodePromptCachingWarning(ignoredClaudeCodePromptCachingFields(buildProjectLlmConfig(project.config.llm, { backend: 'claude-code' }, validation.models)));
|
|
779
642
|
if (warning) {
|
|
780
643
|
io.stderr.write(`${warning}\n`);
|
|
781
644
|
}
|
|
782
|
-
await persistLlmConfig(args.projectDir, { backend: 'claude-code' },
|
|
783
|
-
io.stdout.write(`│ LLM ready: yes (${
|
|
645
|
+
await persistLlmConfig(args.projectDir, { backend: 'claude-code' }, validation.models);
|
|
646
|
+
io.stdout.write(`│ LLM ready: yes (${validation.models.default})\n`);
|
|
647
|
+
return { status: 'ready', projectDir: args.projectDir };
|
|
648
|
+
}
|
|
649
|
+
if (backendChoice.backend === 'codex') {
|
|
650
|
+
const preset = presetForBackend('codex');
|
|
651
|
+
const probe = deps.codexAuthProbe ?? runCodexAuthProbe;
|
|
652
|
+
const validation = await validatePresetModels(preset, async (model) => probe({ projectDir: args.projectDir, model }), io);
|
|
653
|
+
if (validation.status !== 'ready') {
|
|
654
|
+
io.stderr.write(`${validation.message}\n`);
|
|
655
|
+
return { status: 'failed', projectDir: args.projectDir };
|
|
656
|
+
}
|
|
657
|
+
// Prefix the clack gutter so the warning sits inside the setup frame
|
|
658
|
+
// instead of breaking out of it; kept on stderr for scripted runs.
|
|
659
|
+
io.stderr.write(`│ ${formatCodexIsolationWarning()}\n`);
|
|
660
|
+
await persistLlmConfig(args.projectDir, { backend: 'codex' }, validation.models);
|
|
661
|
+
io.stdout.write(`│ LLM ready: yes (codex, ${validation.models.default})\n`);
|
|
784
662
|
return { status: 'ready', projectDir: args.projectDir };
|
|
785
663
|
}
|
|
786
664
|
const credential = await chooseCredentialRef(backendArgs, io, deps);
|
|
@@ -791,8 +669,10 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
791
669
|
if (credential.status !== 'ready') {
|
|
792
670
|
return { status: credential.status, projectDir: args.projectDir };
|
|
793
671
|
}
|
|
794
|
-
const
|
|
795
|
-
|
|
672
|
+
const preset = presetForBackend('anthropic');
|
|
673
|
+
const validation = await validatePresetModels(preset, async (model) => runLlmHealthCheckWithProgress(buildAnthropicHealthConfig(credential.value, model), 'Anthropic API', model, healthCheck, deps), io);
|
|
674
|
+
if (validation.status !== 'ready') {
|
|
675
|
+
io.stderr.write(`Anthropic model health check failed: ${validation.message}\n`);
|
|
796
676
|
if (args.inputMode === 'disabled') {
|
|
797
677
|
return { status: 'failed', projectDir: args.projectDir };
|
|
798
678
|
}
|
|
@@ -800,24 +680,8 @@ export async function runKtxSetupAnthropicModelStep(args, io, deps = {}) {
|
|
|
800
680
|
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
801
681
|
continue;
|
|
802
682
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
if (model.status !== 'ready') {
|
|
808
|
-
return { status: model.status, projectDir: args.projectDir };
|
|
809
|
-
}
|
|
810
|
-
const health = await runLlmHealthCheckWithProgress(buildAnthropicHealthConfig(credential.value, model.model), 'Anthropic API', model.model, healthCheck, deps);
|
|
811
|
-
if (health.ok) {
|
|
812
|
-
await persistLlmConfig(args.projectDir, { backend: 'anthropic', credentialRef: credential.ref }, model.model);
|
|
813
|
-
io.stdout.write(`│ LLM ready: yes (${model.model})\n`);
|
|
814
|
-
return { status: 'ready', projectDir: args.projectDir };
|
|
815
|
-
}
|
|
816
|
-
io.stderr.write(`Anthropic model health check failed: ${health.message}\n`);
|
|
817
|
-
if (args.inputMode === 'disabled') {
|
|
818
|
-
return { status: 'failed', projectDir: args.projectDir };
|
|
819
|
-
}
|
|
820
|
-
io.stderr.write('Choose a different credential source or model, or Back.\n');
|
|
821
|
-
attemptArgs = buildInteractiveRetryArgs(args, backendChoice.backend);
|
|
683
|
+
await persistLlmConfig(args.projectDir, { backend: 'anthropic', credentialRef: credential.ref }, validation.models);
|
|
684
|
+
io.stdout.write(`│ LLM ready: yes (${validation.models.default})\n`);
|
|
685
|
+
return { status: 'ready', projectDir: args.projectDir };
|
|
822
686
|
}
|
|
823
687
|
}
|
package/dist/setup-prompts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { autocomplete, autocompleteMultiselect, cancel, confirm, intro, isCancel, log, multiselect, note,
|
|
1
|
+
import { autocomplete, autocompleteMultiselect, cancel, confirm, intro, isCancel, log, multiselect, note, select, text, } from '@clack/prompts';
|
|
2
2
|
import { withMenuOptionsSpacing, withTextInputNavigation } from './prompt-navigation.js';
|
|
3
|
+
import { revealPassword } from './reveal-password-prompt.js';
|
|
3
4
|
import { withSetupInterruptConfirmation } from './setup-interrupt.js';
|
|
4
5
|
const DEFAULT_SETUP_CANCEL_MESSAGE = 'Setup cancelled.';
|
|
5
6
|
export function createKtxSetupPromptAdapter(options) {
|
|
@@ -89,7 +90,7 @@ export function createKtxSetupPromptAdapter(options) {
|
|
|
89
90
|
return isCancel(value) ? undefined : String(value);
|
|
90
91
|
},
|
|
91
92
|
async password(promptOptions) {
|
|
92
|
-
const value = await withSetupInterruptConfirmation(() =>
|
|
93
|
+
const value = await withSetupInterruptConfirmation(() => revealPassword({ ...promptOptions, message: withTextInputNavigation(promptOptions.message) }));
|
|
93
94
|
return isCancel(value) ? undefined : String(value);
|
|
94
95
|
},
|
|
95
96
|
cancel(message) {
|