@ngockhoale/ukit 3.1.4 → 3.1.6
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 3.1.6 - 2026-09-26
|
|
6
|
+
|
|
7
|
+
- **Decision endpoint URL normalization**: a `UKIT_DECISION_BASE_URL` that already ends in `/v1` (OpenAI convention) no longer produces `…/v1/v1/chat/completions`; the client strips a trailing `/v1` before joining. Applied to `src/decision/client.js` and mirrored in `unic-decision.mjs`.
|
|
8
|
+
|
|
9
|
+
## 3.1.5 - 2026-09-26
|
|
10
|
+
|
|
11
|
+
- **Install/update warns when the decision lane has no API key.** After a project refresh, `ukit install` (and `ukit update`'s refresh) prints a bold-red warning with setup instructions — `ukit decision --base-url <url> --key <key>`, `~/.ukit/storage/gatewayDecision.json`, or `UKIT_DECISION_API_KEY` — whenever no dedicated decision key and no shared UNIC token are resolvable. When a key (or the shared UNIC token fallback) is found, a one-line confirmation is printed instead.
|
|
12
|
+
|
|
5
13
|
## 3.1.4 - 2026-09-26
|
|
6
14
|
|
|
7
15
|
- **`ukit decision` — user-facing decision-lane gateway setup.** Resolution order (per field): env `UKIT_DECISION_BASE_URL`/`UKIT_DECISION_API_KEY`/`UKIT_DECISION_SCHEME` → `~/.ukit/storage/gatewayDecision.json` (new per-lane file; `gateway<Lane>.json` pattern) → `~/.ukit/storage/config.json` `decisionGateway` → `openai.unicjsc.com` + shared UNIC token. File field names mirror the env vars; `scheme` selects `Authorization: Bearer` vs `x-api-key`. Both files are seeded by `ukit install` with the full key set (`mergeStrategy: skip` — `ukit update` never overwrites user values). Commands: `--status`, `--base-url/--key/--scheme`, `--set-unic`, `--clear`.
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import { buildPathConfig } from '../../core/paths.js';
|
|
|
2
2
|
import { runInstallPipeline } from '../../core/runInstallPipeline.js';
|
|
3
3
|
import { formatRepairReport } from '../../core/repairBrokenHooks.js';
|
|
4
4
|
import { formatGatewayResilienceReport } from '../../core/gatewayResilienceEnv.js';
|
|
5
|
+
import { resolveDecisionApiKey, resolveDecisionBaseUrl } from '../../core/gatewayProbe.js';
|
|
5
6
|
import { buildCodeIndex, buildDerivedIndexArtifacts } from '../../index/buildIndex.js';
|
|
6
7
|
import { installIndexRefreshHooks } from '../../index/gitHooks.js';
|
|
7
8
|
import fs from 'node:fs/promises';
|
|
@@ -339,6 +340,29 @@ export async function runInstall({ packageRoot, projectRoot, packageVersion, arg
|
|
|
339
340
|
console.log(`[UKit] ${line}`);
|
|
340
341
|
}
|
|
341
342
|
|
|
343
|
+
// Decision-lane credential check: warn (bold red) when no dedicated key is
|
|
344
|
+
// configured. Resolution: env UKIT_DECISION_API_KEY →
|
|
345
|
+
// ~/.ukit/storage/gatewayDecision.json → config.json decisionGateway →
|
|
346
|
+
// shared UNIC token. Tell the user exactly how to set it.
|
|
347
|
+
try {
|
|
348
|
+
const decisionKey = await resolveDecisionApiKey({ projectRoot });
|
|
349
|
+
const decisionBase = resolveDecisionBaseUrl({});
|
|
350
|
+
const R = '[1;31m';
|
|
351
|
+
const X = '[0m';
|
|
352
|
+
if (!decisionKey?.value) {
|
|
353
|
+
console.warn(`${R}[UKit] WARNING — decision lane (unic-decision) has NO API key configured.${X}`);
|
|
354
|
+
console.warn(`${R}[UKit] The typed-decision features will report 'unavailable' and fall back to deterministic rules.${X}`);
|
|
355
|
+
console.warn(`${R}[UKit] Set it with: ukit decision --base-url https://openai.unicjsc.com --key <YOUR_KEY>${X}`);
|
|
356
|
+
console.warn(`${R}[UKit] Or edit ~/.ukit/storage/gatewayDecision.json -> UKIT_DECISION_API_KEY, or export UKIT_DECISION_API_KEY=<key>.${X}`);
|
|
357
|
+
} else if (decisionKey.source === 'shared-unic-token') {
|
|
358
|
+
console.log(`[UKit] Decision lane: using shared UNIC token at ${decisionBase.baseUrl} (no dedicated key needed).`);
|
|
359
|
+
} else {
|
|
360
|
+
console.log(`[UKit] Decision lane: key configured (${decisionKey.source}) at ${decisionBase.baseUrl}.`);
|
|
361
|
+
}
|
|
362
|
+
} catch {
|
|
363
|
+
// advisory only — never fail install on the check
|
|
364
|
+
}
|
|
365
|
+
|
|
342
366
|
const docsLabels = [
|
|
343
367
|
'docs/PROJECT.md',
|
|
344
368
|
'docs/MEMORY.md',
|
package/src/decision/client.js
CHANGED
|
@@ -56,6 +56,15 @@ const DEFAULT_CHECKPOINTS = {
|
|
|
56
56
|
const FALLBACK_CHECKPOINT = 'unic-decision';
|
|
57
57
|
|
|
58
58
|
const DEFAULT_TIMEOUT_MS = 5000;
|
|
59
|
+
|
|
60
|
+
// Join `<baseUrl>/v1/chat/completions` tolerantly: users reasonably paste a
|
|
61
|
+
// base that already ends in /v1 (OpenAI convention). Strip a trailing /v1
|
|
62
|
+
// before appending so `.../v1` and `.../` yield the same endpoint.
|
|
63
|
+
function decisionCompletionsUrl(baseUrl) {
|
|
64
|
+
const trimmed = String(baseUrl).replace(/\/+$/, '').replace(/\/v1$/i, '');
|
|
65
|
+
return `${trimmed}/v1/chat/completions`;
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
const DEFAULT_MAX_RETRIES = 1;
|
|
60
69
|
const DEFAULT_BREAKER = { failureThreshold: 3, cooldownMs: 30_000 };
|
|
61
70
|
|
|
@@ -218,7 +227,7 @@ export function createDecisionClient({
|
|
|
218
227
|
if (!base?.baseUrl) return null;
|
|
219
228
|
const key = await resolveDecisionApiKey({ projectRoot, homeDir, env });
|
|
220
229
|
return {
|
|
221
|
-
url: `${
|
|
230
|
+
url: `${decisionCompletionsUrl(base.baseUrl)}`,
|
|
222
231
|
headers: buildAuthHeaders(key),
|
|
223
232
|
};
|
|
224
233
|
}
|
|
@@ -486,6 +486,15 @@ const DEFAULT_CHECKPOINTS = {
|
|
|
486
486
|
|
|
487
487
|
const DEFAULT_TIMEOUT_MS = 5000;
|
|
488
488
|
const DEFAULT_MAX_RETRIES = 1;
|
|
489
|
+
|
|
490
|
+
// Join `<baseUrl>/v1/chat/completions` tolerantly: users reasonably paste a
|
|
491
|
+
// base that already ends in /v1 (OpenAI convention). Strip a trailing /v1
|
|
492
|
+
// before appending so `.../v1` and `.../` yield the same endpoint.
|
|
493
|
+
function decisionCompletionsUrl(baseUrl) {
|
|
494
|
+
const trimmed = String(baseUrl).replace(/\/+$/, '').replace(/\/v1$/i, '');
|
|
495
|
+
return `${trimmed}/v1/chat/completions`;
|
|
496
|
+
}
|
|
497
|
+
|
|
489
498
|
const DEFAULT_STATE_LIMITS = { multilingual: 1024, english: 512 };
|
|
490
499
|
|
|
491
500
|
function decisionPlaneConfig(config) {
|
|
@@ -671,7 +680,7 @@ export async function requestBatch(batch, {
|
|
|
671
680
|
}
|
|
672
681
|
const key = resolveDecisionApiKey({ projectRoot, homeDir, env });
|
|
673
682
|
const endpoint = {
|
|
674
|
-
url:
|
|
683
|
+
url: decisionCompletionsUrl(base.baseUrl),
|
|
675
684
|
headers: buildAuthHeaders(key),
|
|
676
685
|
};
|
|
677
686
|
|
|
@@ -801,7 +810,7 @@ export async function runHealthProbe({
|
|
|
801
810
|
try {
|
|
802
811
|
const res = await postWithDeadline(
|
|
803
812
|
transport,
|
|
804
|
-
|
|
813
|
+
decisionCompletionsUrl(base.baseUrl),
|
|
805
814
|
buildAuthHeaders(key),
|
|
806
815
|
body,
|
|
807
816
|
timeoutMs,
|