kibi-cli 2.2.0 → 2.4.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/dist/cli-json-command.d.ts.map +1 -1
- package/dist/cli-json-command.js +2 -2
- package/dist/cli-operation-loader.d.ts.map +1 -1
- package/dist/cli-operation-loader.js +3 -0
- package/dist/cli-operation-metadata.d.ts +5 -1
- package/dist/cli-operation-metadata.d.ts.map +1 -1
- package/dist/cli-operation-metadata.js +6 -1
- package/dist/cli-register-maintenance.d.ts.map +1 -1
- package/dist/cli-register-maintenance.js +7 -0
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +4 -0
- package/dist/commands/check-diff.d.ts +5 -0
- package/dist/commands/check-diff.d.ts.map +1 -0
- package/dist/commands/check-diff.js +62 -0
- package/dist/commands/check-generated.d.ts.map +1 -1
- package/dist/commands/check-generated.js +41 -18
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +210 -73
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +63 -29
- package/dist/commands/github-init.d.ts.map +1 -1
- package/dist/commands/github-init.js +9 -2
- package/dist/commands/merge-driver.d.ts +31 -0
- package/dist/commands/merge-driver.d.ts.map +1 -0
- package/dist/commands/merge-driver.js +268 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +3 -1
- package/dist/engine.d.ts +15 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +252 -52
- package/dist/extractors/symbols-coordinator.d.ts +7 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +75 -39
- package/dist/operations/bootstrap/candidates.js +1 -1
- package/dist/operations/bootstrap/discovery.js +1 -1
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +21 -5
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +3 -0
- package/dist/operations/bootstrap/guidance.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance.js +7 -1
- package/dist/operations/bootstrap/intent-claims.d.ts +17 -0
- package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -0
- package/dist/operations/bootstrap/intent-claims.js +95 -0
- package/dist/operations/bootstrap/presentation.d.ts +2 -0
- package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
- package/dist/operations/bootstrap/presentation.js +60 -1
- package/dist/operations/bootstrap/types.d.ts +28 -0
- package/dist/operations/bootstrap/types.d.ts.map +1 -1
- package/dist/operations/bootstrap/types.js +16 -0
- package/dist/operations/modeling/predicate-inference-1.js +1 -1
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +175 -53
- package/dist/operations/mutation/symbol-refresh.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-refresh.js +4 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +5 -7
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +5 -9
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +1 -0
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analysis-receipt.js +7 -1
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +26 -4
- package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
- package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -1
- package/dist/performance-trace.d.ts +17 -0
- package/dist/performance-trace.d.ts.map +1 -0
- package/dist/performance-trace.js +134 -0
- package/dist/plugins/approved-source-analyzers.d.ts +13 -0
- package/dist/plugins/approved-source-analyzers.d.ts.map +1 -0
- package/dist/plugins/approved-source-analyzers.generated.d.ts +71 -0
- package/dist/plugins/approved-source-analyzers.generated.d.ts.map +1 -0
- package/dist/plugins/approved-source-analyzers.generated.js +77 -0
- package/dist/plugins/approved-source-analyzers.js +3 -0
- package/dist/plugins/maintenance-source-analysis.d.ts +14 -0
- package/dist/plugins/maintenance-source-analysis.d.ts.map +1 -0
- package/dist/plugins/maintenance-source-analysis.js +191 -0
- package/dist/plugins/registry.d.ts +2 -1
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +6 -1
- package/dist/plugins/source-analysis-service.d.ts +36 -1
- package/dist/plugins/source-analysis-service.d.ts.map +1 -1
- package/dist/plugins/source-analysis-service.js +184 -27
- package/dist/plugins/source-change-analysis.d.ts +15 -0
- package/dist/plugins/source-change-analysis.d.ts.map +1 -0
- package/dist/plugins/source-change-analysis.js +54 -0
- package/dist/plugins/source-classification.d.ts +41 -0
- package/dist/plugins/source-classification.d.ts.map +1 -0
- package/dist/plugins/source-classification.js +490 -0
- package/dist/prolog/swipl-resolver.d.ts +88 -0
- package/dist/prolog/swipl-resolver.d.ts.map +1 -0
- package/dist/prolog/swipl-resolver.js +446 -0
- package/dist/prolog.d.ts +15 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +67 -6
- package/dist/public/diagnostic-usage.d.ts +14 -0
- package/dist/public/diagnostic-usage.d.ts.map +1 -1
- package/dist/public/diagnostic-usage.js +55 -1
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +56 -5
- package/dist/public/impact/diagnostics.d.ts.map +1 -1
- package/dist/public/impact/diagnostics.js +3 -1
- package/dist/public/impact/manifest.d.ts.map +1 -1
- package/dist/public/impact/manifest.js +15 -1
- package/dist/public/impact/source-changes.d.ts.map +1 -1
- package/dist/public/impact/source-changes.js +35 -27
- package/dist/public/impact/types.d.ts +2 -0
- package/dist/public/impact/types.d.ts.map +1 -1
- package/dist/public/operations/catalog.d.ts +117 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/catalog.js +2 -0
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +309 -8
- package/dist/public/operations/impact-review-preparation.d.ts +14 -0
- package/dist/public/operations/impact-review-preparation.d.ts.map +1 -0
- package/dist/public/operations/impact-review-preparation.js +379 -0
- package/dist/public/operations/index.d.ts +2 -0
- package/dist/public/operations/index.d.ts.map +1 -1
- package/dist/public/operations/index.js +2 -0
- package/dist/public/operations/node-ports.d.ts.map +1 -1
- package/dist/public/operations/node-ports.js +1 -35
- package/dist/public/operations/proof-receipt-projection.d.ts +5 -0
- package/dist/public/operations/proof-receipt-projection.d.ts.map +1 -0
- package/dist/public/operations/proof-receipt-projection.js +36 -0
- package/dist/public/operations/specs/bootstrap.d.ts +73 -1
- package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
- package/dist/public/operations/specs/bootstrap.js +66 -1
- package/dist/public/operations/specs/impact-review-preparation.d.ts +47 -0
- package/dist/public/operations/specs/impact-review-preparation.d.ts.map +1 -0
- package/dist/public/operations/specs/impact-review-preparation.js +43 -0
- package/dist/public/operations/symbol-repair-plan.d.ts.map +1 -1
- package/dist/public/operations/symbol-repair-plan.js +3 -1
- package/dist/public/operations/types.d.ts +1 -1
- package/dist/public/operations/types.d.ts.map +1 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +47 -11
- package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +6 -1
- package/dist/public/skills/kibi-usage/SKILL.md +14 -0
- package/dist/public/skills/kibi-usage/resources/operation-access.md +5 -0
- package/dist/public/telemetry-acceptance.d.ts +8 -0
- package/dist/public/telemetry-acceptance.d.ts.map +1 -1
- package/dist/public/telemetry-acceptance.js +10 -0
- package/dist/traceability/analysis-gate.d.ts +39 -0
- package/dist/traceability/analysis-gate.d.ts.map +1 -0
- package/dist/traceability/analysis-gate.js +51 -0
- package/dist/traceability/git-change-snapshot.d.ts +27 -0
- package/dist/traceability/git-change-snapshot.d.ts.map +1 -0
- package/dist/traceability/git-change-snapshot.js +411 -0
- package/dist/traceability/git-staged.d.ts +5 -1
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +16 -11
- package/dist/traceability/impact-evaluator.d.ts +54 -0
- package/dist/traceability/impact-evaluator.d.ts.map +1 -0
- package/dist/traceability/impact-evaluator.js +270 -0
- package/dist/traceability/impact-git-tree.d.ts +12 -0
- package/dist/traceability/impact-git-tree.d.ts.map +1 -0
- package/dist/traceability/impact-git-tree.js +46 -0
- package/dist/traceability/impact-knowledge.d.ts +16 -0
- package/dist/traceability/impact-knowledge.d.ts.map +1 -0
- package/dist/traceability/impact-knowledge.js +165 -0
- package/dist/traceability/impact-policy.d.ts +15 -0
- package/dist/traceability/impact-policy.d.ts.map +1 -0
- package/dist/traceability/impact-policy.js +48 -0
- package/dist/traceability/impact-policy.v1.schema.json +48 -0
- package/dist/traceability/impact-pr-diff.d.ts +5 -0
- package/dist/traceability/impact-pr-diff.d.ts.map +1 -0
- package/dist/traceability/impact-pr-diff.js +83 -0
- package/dist/traceability/impact-record-validation.d.ts +9 -0
- package/dist/traceability/impact-record-validation.d.ts.map +1 -0
- package/dist/traceability/impact-record-validation.js +113 -0
- package/dist/traceability/impact-review.d.ts +148 -0
- package/dist/traceability/impact-review.d.ts.map +1 -0
- package/dist/traceability/impact-review.js +494 -0
- package/dist/traceability/impact-review.schema.json +365 -0
- package/dist/traceability/impact-side-evidence.d.ts +13 -0
- package/dist/traceability/impact-side-evidence.d.ts.map +1 -0
- package/dist/traceability/impact-side-evidence.js +240 -0
- package/dist/traceability/reviewed-coordinate-refresh.d.ts +10 -0
- package/dist/traceability/reviewed-coordinate-refresh.d.ts.map +1 -0
- package/dist/traceability/reviewed-coordinate-refresh.js +49 -0
- package/dist/traceability/snapshot-knowledge.d.ts +4 -0
- package/dist/traceability/snapshot-knowledge.d.ts.map +1 -0
- package/dist/traceability/snapshot-knowledge.js +98 -0
- package/dist/traceability/staged-file-coverage.d.ts +23 -0
- package/dist/traceability/staged-file-coverage.d.ts.map +1 -1
- package/dist/traceability/staged-impact-contract.d.ts.map +1 -1
- package/dist/traceability/staged-impact-contract.js +2 -16
- package/dist/traceability/staged-symbols-manifest.d.ts +4 -0
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +17 -6
- package/dist/traceability/symbol-extract.d.ts +3 -5
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +49 -34
- package/dist/traceability/temp-kb.d.ts +1 -1
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +49 -13
- package/dist/traceability/trusted-pr-event.d.ts +13 -0
- package/dist/traceability/trusted-pr-event.d.ts.map +1 -0
- package/dist/traceability/trusted-pr-event.js +105 -0
- package/dist/utils/canonical-path.d.ts +3 -0
- package/dist/utils/canonical-path.d.ts.map +1 -0
- package/dist/utils/canonical-path.js +33 -0
- package/package.json +14 -5
- package/src/public/diagnostic-usage.ts +59 -1
- package/src/public/impact/analyzer.ts +88 -5
- package/src/public/impact/diagnostics.ts +7 -5
- package/src/public/impact/manifest.ts +14 -1
- package/src/public/impact/source-changes.ts +39 -34
- package/src/public/impact/types.ts +5 -0
- package/src/public/operations/catalog.ts +2 -0
- package/src/public/operations/contracts.ts +385 -9
- package/src/public/operations/impact-review-preparation.ts +548 -0
- package/src/public/operations/index.ts +2 -0
- package/src/public/operations/node-ports.ts +1 -39
- package/src/public/operations/proof-receipt-projection.ts +39 -0
- package/src/public/operations/specs/bootstrap.ts +78 -4
- package/src/public/operations/specs/impact-review-preparation.ts +46 -0
- package/src/public/operations/symbol-repair-plan.ts +3 -1
- package/src/public/operations/types.ts +1 -0
- package/src/public/skills/kibi-bootstrap/SKILL.md +47 -11
- package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +6 -1
- package/src/public/skills/kibi-usage/SKILL.md +14 -0
- package/src/public/skills/kibi-usage/resources/operation-access.md +5 -0
- package/src/public/telemetry-acceptance.ts +9 -0
- package/src/traceability/impact-policy.v1.schema.json +48 -0
- package/src/traceability/impact-review.schema.json +365 -0
- package/templates/github/kibi-badge.yml +0 -4
- package/templates/github/kibi-report.yml +0 -4
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
* Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the SWI-Prolog executable Kibi runs, in this order:
|
|
12
|
+
*
|
|
13
|
+
* 1. `KIBI_SWIPL=<absolute path>` (an explicit operator choice always wins).
|
|
14
|
+
* 2. The bundled platform package (`kibi-swipl-<platform>`), after its build
|
|
15
|
+
* manifest and binary checksum verify. `KIBI_SWIPL=system` skips this step.
|
|
16
|
+
* 3. `swipl` on `PATH`, subject to the SWI-Prolog 9.0 minimum.
|
|
17
|
+
* 4. Otherwise an error naming the platform, the platform package that would
|
|
18
|
+
* have covered it, and the OS install command.
|
|
19
|
+
*/
|
|
20
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
21
|
+
import { constants, accessSync, existsSync, readFileSync, statSync, } from "node:fs";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { BUNDLED_BINARY_PATH, MANIFEST_FILE, PLATFORM_PACKAGES, locatePlatformPackage, platformKey, sha256File, validateBuildManifest, } from "kibi-swipl";
|
|
24
|
+
export const MINIMUM_SWIPL_MAJOR = 9;
|
|
25
|
+
/** Structured failure; `message` is the complete operator-facing guidance. */
|
|
26
|
+
export class SwiplResolutionError extends Error {
|
|
27
|
+
code;
|
|
28
|
+
/** Detected host, e.g. "linux-x64 (glibc)". */
|
|
29
|
+
platform;
|
|
30
|
+
/** Platform package that would have covered this host, when one exists. */
|
|
31
|
+
packageName;
|
|
32
|
+
constructor(code, message, platform, packageName) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "SwiplResolutionError";
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.platform = platform;
|
|
37
|
+
this.packageName = packageName;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function detectGlibc() {
|
|
41
|
+
if (process.platform !== "linux")
|
|
42
|
+
return undefined;
|
|
43
|
+
try {
|
|
44
|
+
const report = process.report?.getReport?.();
|
|
45
|
+
if (report !== undefined) {
|
|
46
|
+
return typeof report.header?.glibcVersionRuntime === "string";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Fall back to the musl loader probe below.
|
|
51
|
+
}
|
|
52
|
+
return !["/lib/ld-musl-x86_64.so.1", "/lib/ld-musl-aarch64.so.1"].some((loader) => existsSync(loader));
|
|
53
|
+
}
|
|
54
|
+
function defaultRunVersion(bin) {
|
|
55
|
+
return execFileSync(bin, ["--version"], {
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
timeout: 10_000,
|
|
58
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function defaultDeps() {
|
|
62
|
+
return {
|
|
63
|
+
platform: process.platform,
|
|
64
|
+
arch: process.arch,
|
|
65
|
+
glibc: detectGlibc(),
|
|
66
|
+
locatePackage: (name) => locatePlatformPackage(name),
|
|
67
|
+
runVersion: defaultRunVersion,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Parse `SWI-Prolog version 10.0.2 for x86_64-linux` into its version. */
|
|
71
|
+
export function parseSwiplVersion(output) {
|
|
72
|
+
const match = /version\s+(\d+)\.(\d+)(?:\.(\d+))?/i.exec(output);
|
|
73
|
+
if (match === null)
|
|
74
|
+
return undefined;
|
|
75
|
+
const [, major, minor, patch] = match;
|
|
76
|
+
return {
|
|
77
|
+
version: `${major}.${minor}${patch === undefined ? "" : `.${patch}`}`,
|
|
78
|
+
major: Number.parseInt(major ?? "0", 10),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function installCommand(platform) {
|
|
82
|
+
if (platform === "linux") {
|
|
83
|
+
return "sudo apt-add-repository ppa:swi-prolog/stable && sudo apt-get update && sudo apt-get install swi-prolog";
|
|
84
|
+
}
|
|
85
|
+
if (platform === "darwin")
|
|
86
|
+
return "brew install swi-prolog";
|
|
87
|
+
return "download the installer from https://www.swi-prolog.org/download/stable and add swipl to PATH";
|
|
88
|
+
}
|
|
89
|
+
function hostLabel(deps) {
|
|
90
|
+
const libc = deps.platform === "linux" && deps.glibc !== undefined
|
|
91
|
+
? ` (${deps.glibc ? "glibc" : "musl"})`
|
|
92
|
+
: "";
|
|
93
|
+
return `${deps.platform}-${deps.arch}${libc}`;
|
|
94
|
+
}
|
|
95
|
+
function isExecutableFile(candidate) {
|
|
96
|
+
try {
|
|
97
|
+
if (!statSync(candidate).isFile())
|
|
98
|
+
return false;
|
|
99
|
+
accessSync(candidate, constants.X_OK);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function inspectBundle(deps) {
|
|
107
|
+
const key = platformKey({
|
|
108
|
+
platform: deps.platform,
|
|
109
|
+
arch: deps.arch,
|
|
110
|
+
glibc: deps.glibc,
|
|
111
|
+
});
|
|
112
|
+
if (key === undefined)
|
|
113
|
+
return { kind: "unsupported" };
|
|
114
|
+
const packageName = PLATFORM_PACKAGES[key].package;
|
|
115
|
+
const packageDir = deps.locatePackage(packageName);
|
|
116
|
+
if (packageDir === undefined)
|
|
117
|
+
return { kind: "missing", key, packageName };
|
|
118
|
+
const manifestPath = path.join(packageDir, MANIFEST_FILE);
|
|
119
|
+
const binary = path.join(packageDir, BUNDLED_BINARY_PATH);
|
|
120
|
+
if (!existsSync(manifestPath)) {
|
|
121
|
+
// A workspace checkout links the package without the pipeline payload.
|
|
122
|
+
// A binary without a manifest is a damaged install, not a placeholder.
|
|
123
|
+
if (!existsSync(binary))
|
|
124
|
+
return { kind: "empty", key, packageName };
|
|
125
|
+
return {
|
|
126
|
+
kind: "corrupt",
|
|
127
|
+
key,
|
|
128
|
+
packageName,
|
|
129
|
+
reason: `${MANIFEST_FILE} is missing next to ${BUNDLED_BINARY_PATH}`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const corrupt = (reason) => ({
|
|
133
|
+
kind: "corrupt",
|
|
134
|
+
key,
|
|
135
|
+
packageName,
|
|
136
|
+
reason,
|
|
137
|
+
});
|
|
138
|
+
let manifest;
|
|
139
|
+
let packageJson;
|
|
140
|
+
try {
|
|
141
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
return corrupt(`${MANIFEST_FILE} is not valid JSON (${error instanceof Error ? error.message : String(error)})`);
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
packageJson = JSON.parse(readFileSync(path.join(packageDir, "package.json"), "utf8"));
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return corrupt(`package.json is unreadable (${error instanceof Error ? error.message : String(error)})`);
|
|
151
|
+
}
|
|
152
|
+
const packageVersion = packageJson.kibi?.swiplVersion;
|
|
153
|
+
const checked = validateBuildManifest(manifest, {
|
|
154
|
+
target: key,
|
|
155
|
+
swiplVersion: typeof packageVersion === "string" ? packageVersion : undefined,
|
|
156
|
+
});
|
|
157
|
+
if (!checked.ok)
|
|
158
|
+
return corrupt(checked.reason);
|
|
159
|
+
if (!isExecutableFile(binary)) {
|
|
160
|
+
return corrupt(`${BUNDLED_BINARY_PATH} is missing or not executable`);
|
|
161
|
+
}
|
|
162
|
+
let actual;
|
|
163
|
+
try {
|
|
164
|
+
actual = sha256File(binary);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
return corrupt(`${BUNDLED_BINARY_PATH} is unreadable (${error instanceof Error ? error.message : String(error)})`);
|
|
168
|
+
}
|
|
169
|
+
if (actual !== checked.binarySha256) {
|
|
170
|
+
return corrupt(`${BUNDLED_BINARY_PATH} SHA-256 ${actual} does not match the manifest (${checked.binarySha256})`);
|
|
171
|
+
}
|
|
172
|
+
const home = path.join(packageDir, checked.home);
|
|
173
|
+
let homeIsDirectory = false;
|
|
174
|
+
try {
|
|
175
|
+
homeIsDirectory = statSync(home).isDirectory();
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// reported below
|
|
179
|
+
}
|
|
180
|
+
if (!homeIsDirectory)
|
|
181
|
+
return corrupt(`${checked.home} is missing`);
|
|
182
|
+
return {
|
|
183
|
+
kind: "ok",
|
|
184
|
+
key,
|
|
185
|
+
packageName,
|
|
186
|
+
resolved: {
|
|
187
|
+
bin: binary,
|
|
188
|
+
home,
|
|
189
|
+
version: checked.swiplVersion,
|
|
190
|
+
source: "bundled",
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function probeVersion(bin, deps) {
|
|
195
|
+
let output;
|
|
196
|
+
try {
|
|
197
|
+
output = deps.runVersion(bin);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
return {
|
|
201
|
+
kind: "unusable",
|
|
202
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const parsed = parseSwiplVersion(output);
|
|
206
|
+
if (parsed === undefined) {
|
|
207
|
+
return {
|
|
208
|
+
kind: "unusable",
|
|
209
|
+
detail: `unrecognized --version output ${JSON.stringify(output.trim().slice(0, 80))}`,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (parsed.major < MINIMUM_SWIPL_MAJOR) {
|
|
213
|
+
return { kind: "too-old", version: parsed.version };
|
|
214
|
+
}
|
|
215
|
+
return { kind: "ok", version: parsed.version };
|
|
216
|
+
}
|
|
217
|
+
function findOnPath(env, deps) {
|
|
218
|
+
for (const directory of (env.PATH ?? "").split(path.delimiter)) {
|
|
219
|
+
if (directory === "" || !path.isAbsolute(directory))
|
|
220
|
+
continue;
|
|
221
|
+
const candidate = path.join(directory, "swipl");
|
|
222
|
+
if (!isExecutableFile(candidate))
|
|
223
|
+
continue;
|
|
224
|
+
const probe = probeVersion(candidate, deps);
|
|
225
|
+
if (probe.kind === "ok") {
|
|
226
|
+
return {
|
|
227
|
+
kind: "ok",
|
|
228
|
+
resolved: { bin: candidate, version: probe.version, source: "path" },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return probe.kind === "too-old"
|
|
232
|
+
? { kind: "too-old", bin: candidate, version: probe.version }
|
|
233
|
+
: { kind: "unusable", bin: candidate, detail: probe.detail };
|
|
234
|
+
}
|
|
235
|
+
return { kind: "absent" };
|
|
236
|
+
}
|
|
237
|
+
function resolveFromEnv(override, deps) {
|
|
238
|
+
const platform = hostLabel(deps);
|
|
239
|
+
const key = platformKey({
|
|
240
|
+
platform: deps.platform,
|
|
241
|
+
arch: deps.arch,
|
|
242
|
+
glibc: deps.glibc,
|
|
243
|
+
});
|
|
244
|
+
const packageName = key === undefined ? undefined : PLATFORM_PACKAGES[key].package;
|
|
245
|
+
const invalid = (detail) => new SwiplResolutionError("swipl_env_invalid", `KIBI_SWIPL=${override} ${detail}. Set KIBI_SWIPL to the absolute path of a SWI-Prolog ${MINIMUM_SWIPL_MAJOR}.0+ executable, to "system" to use swipl from PATH, or unset it to use the bundled runtime.`, platform, packageName);
|
|
246
|
+
if (!path.isAbsolute(override))
|
|
247
|
+
return fail(invalid("is not an absolute path"));
|
|
248
|
+
if (!isExecutableFile(override)) {
|
|
249
|
+
return fail(invalid("is not an executable file"));
|
|
250
|
+
}
|
|
251
|
+
const probe = probeVersion(override, deps);
|
|
252
|
+
if (probe.kind === "unusable") {
|
|
253
|
+
return fail(invalid(`could not report its version (${probe.detail})`));
|
|
254
|
+
}
|
|
255
|
+
if (probe.kind === "too-old") {
|
|
256
|
+
return fail(invalid(`is SWI-Prolog ${probe.version}, but ${MINIMUM_SWIPL_MAJOR}.0 or newer is required`));
|
|
257
|
+
}
|
|
258
|
+
return { bin: override, version: probe.version, source: "env" };
|
|
259
|
+
}
|
|
260
|
+
function fail(error) {
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
function notFoundError(deps, bundle, lookup) {
|
|
264
|
+
const platform = hostLabel(deps);
|
|
265
|
+
const command = installCommand(deps.platform);
|
|
266
|
+
const lines = [
|
|
267
|
+
`Kibi could not find a usable SWI-Prolog (${MINIMUM_SWIPL_MAJOR}.0 or newer is required).`,
|
|
268
|
+
`Detected platform: ${platform}.`,
|
|
269
|
+
];
|
|
270
|
+
let packageName;
|
|
271
|
+
if (bundle === "skipped") {
|
|
272
|
+
lines.push("Bundled SWI-Prolog: skipped because KIBI_SWIPL=system.");
|
|
273
|
+
const key = platformKey({
|
|
274
|
+
platform: deps.platform,
|
|
275
|
+
arch: deps.arch,
|
|
276
|
+
glibc: deps.glibc,
|
|
277
|
+
});
|
|
278
|
+
if (key !== undefined)
|
|
279
|
+
packageName = PLATFORM_PACKAGES[key].package;
|
|
280
|
+
}
|
|
281
|
+
else if (bundle.kind === "unsupported") {
|
|
282
|
+
lines.push(`Bundled SWI-Prolog: no bundled build exists for ${platform} (supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}).`);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
packageName = bundle.packageName;
|
|
286
|
+
if (bundle.kind === "empty") {
|
|
287
|
+
lines.push(`Bundled SWI-Prolog: package ${bundle.packageName} is installed but carries no runtime (a source checkout that was not populated from a release build).`);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
lines.push(`Bundled SWI-Prolog: package ${bundle.packageName} would cover this platform but is not installed. It is skipped by installs that omit optional dependencies (npm --omit=optional, --no-optional) and by pnpm supportedArchitectures settings that exclude this platform.`);
|
|
291
|
+
lines.push(` Add it with: npm install --save-dev ${bundle.packageName}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (bundle === "skipped" && packageName !== undefined) {
|
|
295
|
+
lines.push(` To use it, unset KIBI_SWIPL and install ${packageName}.`);
|
|
296
|
+
}
|
|
297
|
+
switch (lookup.kind) {
|
|
298
|
+
case "absent":
|
|
299
|
+
lines.push("System SWI-Prolog: swipl was not found on PATH.");
|
|
300
|
+
break;
|
|
301
|
+
case "too-old":
|
|
302
|
+
lines.push(`System SWI-Prolog: swipl on PATH (${lookup.bin}) is version ${lookup.version}, older than ${MINIMUM_SWIPL_MAJOR}.0.`);
|
|
303
|
+
break;
|
|
304
|
+
case "unusable":
|
|
305
|
+
lines.push(`System SWI-Prolog: swipl on PATH (${lookup.bin}) could not report its version (${lookup.detail}).`);
|
|
306
|
+
break;
|
|
307
|
+
case "ok":
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
lines.push(` Install it with: ${command}`);
|
|
311
|
+
lines.push(`Or set KIBI_SWIPL to the absolute path of a SWI-Prolog ${MINIMUM_SWIPL_MAJOR}.0+ executable.`);
|
|
312
|
+
return new SwiplResolutionError("swipl_not_found", lines.join("\n"), platform, packageName);
|
|
313
|
+
}
|
|
314
|
+
/** Uncached resolution with injectable host facts; the test seam. */
|
|
315
|
+
export function resolveSwiplWith(env, deps) {
|
|
316
|
+
const override = env.KIBI_SWIPL?.trim() ?? "";
|
|
317
|
+
if (override !== "" && override !== "system") {
|
|
318
|
+
return resolveFromEnv(override, deps);
|
|
319
|
+
}
|
|
320
|
+
let bundle = "skipped";
|
|
321
|
+
if (override === "") {
|
|
322
|
+
bundle = inspectBundle(deps);
|
|
323
|
+
if (bundle.kind === "ok")
|
|
324
|
+
return bundle.resolved;
|
|
325
|
+
if (bundle.kind === "corrupt") {
|
|
326
|
+
throw new SwiplResolutionError("swipl_bundle_corrupt", [
|
|
327
|
+
`The bundled SWI-Prolog package ${bundle.packageName} is damaged: ${bundle.reason}.`,
|
|
328
|
+
`Detected platform: ${hostLabel(deps)}.`,
|
|
329
|
+
`Reinstall it (for example: npm install --save-dev ${bundle.packageName}), or set KIBI_SWIPL=system to use swipl from PATH instead.`,
|
|
330
|
+
].join("\n"), hostLabel(deps), bundle.packageName);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const lookup = findOnPath(env, deps);
|
|
334
|
+
if (lookup.kind === "ok")
|
|
335
|
+
return lookup.resolved;
|
|
336
|
+
throw notFoundError(deps, bundle, lookup);
|
|
337
|
+
}
|
|
338
|
+
const cache = new Map();
|
|
339
|
+
/**
|
|
340
|
+
* Resolve the SWI-Prolog executable for this process. The result is cached for
|
|
341
|
+
* the lifetime of the process, keyed by the inputs that select it.
|
|
342
|
+
*/
|
|
343
|
+
export function resolveSwipl(env = process.env) {
|
|
344
|
+
const key = `${env.KIBI_SWIPL ?? ""}\0${env.PATH ?? ""}`;
|
|
345
|
+
const cached = cache.get(key);
|
|
346
|
+
if (cached !== undefined)
|
|
347
|
+
return cached;
|
|
348
|
+
const resolved = resolveSwiplWith(env, defaultDeps());
|
|
349
|
+
cache.set(key, resolved);
|
|
350
|
+
return resolved;
|
|
351
|
+
}
|
|
352
|
+
/** Test seam: forget every cached resolution. */
|
|
353
|
+
export function resetSwiplResolverCache() {
|
|
354
|
+
cache.clear();
|
|
355
|
+
}
|
|
356
|
+
/** Stable identity of a resolved runtime, compared across engine processes. */
|
|
357
|
+
export function swiplIdentity(resolved) {
|
|
358
|
+
return `${resolved.bin}@${resolved.version}`;
|
|
359
|
+
}
|
|
360
|
+
/** Environment additions a child needs to run the resolved SWI-Prolog. */
|
|
361
|
+
export function swiplChildEnv(resolved) {
|
|
362
|
+
return resolved.home === undefined ? {} : { SWI_HOME_DIR: resolved.home };
|
|
363
|
+
}
|
|
364
|
+
/** Report the bundled package's state without resolving a runtime. */
|
|
365
|
+
export function inspectSwiplBundle(deps = defaultDeps()) {
|
|
366
|
+
const bundle = inspectBundle(deps);
|
|
367
|
+
switch (bundle.kind) {
|
|
368
|
+
case "unsupported":
|
|
369
|
+
return { state: "unsupported" };
|
|
370
|
+
case "missing":
|
|
371
|
+
case "empty":
|
|
372
|
+
return { state: bundle.kind, packageName: bundle.packageName };
|
|
373
|
+
case "corrupt":
|
|
374
|
+
return {
|
|
375
|
+
state: "corrupt",
|
|
376
|
+
packageName: bundle.packageName,
|
|
377
|
+
reason: bundle.reason,
|
|
378
|
+
};
|
|
379
|
+
case "ok":
|
|
380
|
+
return { state: "installed", packageName: bundle.packageName };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/** Libraries Kibi loads at runtime; a SWI-Prolog lacking one cannot serve it. */
|
|
384
|
+
export const REQUIRED_SWIPL_LIBRARIES = [
|
|
385
|
+
"semweb/rdf_db",
|
|
386
|
+
"semweb/rdf_persistency",
|
|
387
|
+
"semweb/sparql_client",
|
|
388
|
+
"pcre",
|
|
389
|
+
"crypto",
|
|
390
|
+
"sha",
|
|
391
|
+
"http/json",
|
|
392
|
+
"http/json_convert",
|
|
393
|
+
"chr",
|
|
394
|
+
"clpfd",
|
|
395
|
+
"thread",
|
|
396
|
+
"persistency",
|
|
397
|
+
"filesex",
|
|
398
|
+
"readutil",
|
|
399
|
+
"date",
|
|
400
|
+
"aggregate",
|
|
401
|
+
"solution_sequences",
|
|
402
|
+
];
|
|
403
|
+
const MISSING_LIBRARY_MARKER = "__KIBI_MISSING_LIBRARY__:";
|
|
404
|
+
function defaultGoalRunner(bin, args, env) {
|
|
405
|
+
const result = spawnSync(bin, [...args], {
|
|
406
|
+
encoding: "utf8",
|
|
407
|
+
timeout: 60_000,
|
|
408
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
409
|
+
env,
|
|
410
|
+
});
|
|
411
|
+
return {
|
|
412
|
+
status: result.status,
|
|
413
|
+
stdout: result.stdout ?? "",
|
|
414
|
+
stderr: result.stderr ?? "",
|
|
415
|
+
...(result.error === undefined ? {} : { error: result.error }),
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Load every required library in one throwaway SWI-Prolog process and report
|
|
420
|
+
* the ones that fail. A probe that cannot run is reported as such, never as a
|
|
421
|
+
* pass.
|
|
422
|
+
*/
|
|
423
|
+
export function probeRequiredLibraries(resolved, run = defaultGoalRunner, env = process.env) {
|
|
424
|
+
const goal = `forall(member(L, [${REQUIRED_SWIPL_LIBRARIES.join(", ")}]), (catch(use_module(library(L)), _, fail) -> true ; format("${MISSING_LIBRARY_MARKER}~w~n", [L])))`;
|
|
425
|
+
const result = run(resolved.bin, ["-q", "-g", goal, "-t", "halt"], {
|
|
426
|
+
...env,
|
|
427
|
+
...swiplChildEnv(resolved),
|
|
428
|
+
});
|
|
429
|
+
if (result.error !== undefined) {
|
|
430
|
+
return { kind: "failed", detail: result.error.message };
|
|
431
|
+
}
|
|
432
|
+
if (result.status !== 0) {
|
|
433
|
+
const detail = result.stderr.trim().split("\n")[0] ?? "";
|
|
434
|
+
return {
|
|
435
|
+
kind: "failed",
|
|
436
|
+
detail: `exit status ${result.status ?? "none"}${detail === "" ? "" : `: ${detail}`}`,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
const libraries = result.stdout
|
|
440
|
+
.split("\n")
|
|
441
|
+
.filter((line) => line.startsWith(MISSING_LIBRARY_MARKER))
|
|
442
|
+
.map((line) => line.slice(MISSING_LIBRARY_MARKER.length).trim());
|
|
443
|
+
return libraries.length === 0
|
|
444
|
+
? { kind: "ok" }
|
|
445
|
+
: { kind: "missing", libraries };
|
|
446
|
+
}
|
package/dist/prolog.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { type PrologErrorRecord } from "./prolog/error-terms.js";
|
|
2
|
+
import { type ResolvedSwipl } from "./prolog/swipl-resolver.js";
|
|
2
3
|
export declare function resolveKbPlPath(): string;
|
|
3
4
|
export interface PrologOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Explicit SWI-Prolog executable. When omitted, the executable comes from
|
|
7
|
+
* the resolver (KIBI_SWIPL, the bundled platform package, then PATH).
|
|
8
|
+
*/
|
|
4
9
|
swiplPath?: string;
|
|
10
|
+
/** Resolver seam for tests; defaults to the per-process SWI-Prolog resolver. */
|
|
11
|
+
swiplResolver?: () => ResolvedSwipl;
|
|
5
12
|
timeout?: number;
|
|
6
13
|
/**
|
|
7
14
|
* Force one-shot SWI execution. Production callers are Node-only and keep
|
|
@@ -37,7 +44,8 @@ export declare function registerProcessExitOnce(current: (() => void) | null, ha
|
|
|
37
44
|
export declare function bindProcessExitHandler(current: (() => void) | null, terminate: () => void | Promise<void>, on?: (event: "exit", listener: () => void) => void): () => void;
|
|
38
45
|
export declare class PrologProcess {
|
|
39
46
|
private process;
|
|
40
|
-
private
|
|
47
|
+
private explicitSwiplPath;
|
|
48
|
+
private readonly swiplResolver;
|
|
41
49
|
private timeout;
|
|
42
50
|
private outputBuffer;
|
|
43
51
|
private outputBufferBytes;
|
|
@@ -71,6 +79,12 @@ export declare class PrologProcess {
|
|
|
71
79
|
* restart it (and re-establish its session state) before querying again.
|
|
72
80
|
*/
|
|
73
81
|
needsRestart(): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The executable and SWI home to launch. An explicit `swiplPath` is used
|
|
84
|
+
* verbatim; otherwise the per-process resolver decides, so a missing or
|
|
85
|
+
* damaged runtime fails with its complete remediation text.
|
|
86
|
+
*/
|
|
87
|
+
private launchTarget;
|
|
74
88
|
start(): Promise<void>;
|
|
75
89
|
private waitForReady;
|
|
76
90
|
query(goal: string | string[]): Promise<QueryResult>;
|
package/dist/prolog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"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"}
|
package/dist/prolog.js
CHANGED
|
@@ -19,9 +19,12 @@ import { spawn } from "node:child_process";
|
|
|
19
19
|
import { existsSync } from "node:fs";
|
|
20
20
|
import { createRequire } from "node:module";
|
|
21
21
|
import path from "node:path";
|
|
22
|
+
import { performance } from "node:perf_hooks";
|
|
22
23
|
import { fileURLToPath } from "node:url";
|
|
23
24
|
import { getKbPlPathOverride, isPrologDebugEnabled } from "./env.js";
|
|
25
|
+
import { writePerformanceTraceEvent } from "./performance-trace.js";
|
|
24
26
|
import { extractPrologErrorRecord, } from "./prolog/error-terms.js";
|
|
27
|
+
import { resolveSwipl, swiplChildEnv, } from "./prolog/swipl-resolver.js";
|
|
25
28
|
const importMetaDir = path.dirname(fileURLToPath(import.meta.url));
|
|
26
29
|
const DEFAULT_PROLOG_OUTPUT_MAX_BUFFER_BYTES = 8 * 1024 * 1024;
|
|
27
30
|
const PROLOG_OUTPUT_OVERFLOW_ERROR = "Query exceeded bounded Prolog output capacity (ENOBUFS); narrow the operation or reduce stored entity size";
|
|
@@ -110,7 +113,8 @@ export function bindProcessExitHandler(current, terminate, on = (event, listener
|
|
|
110
113
|
}
|
|
111
114
|
export class PrologProcess {
|
|
112
115
|
process = null;
|
|
113
|
-
|
|
116
|
+
explicitSwiplPath;
|
|
117
|
+
swiplResolver;
|
|
114
118
|
timeout;
|
|
115
119
|
outputBuffer = "";
|
|
116
120
|
outputBufferBytes = 0;
|
|
@@ -133,7 +137,8 @@ export class PrologProcess {
|
|
|
133
137
|
interactiveStarted = false;
|
|
134
138
|
terminationReason = null;
|
|
135
139
|
constructor(options = {}) {
|
|
136
|
-
this.
|
|
140
|
+
this.explicitSwiplPath = options.swiplPath || undefined;
|
|
141
|
+
this.swiplResolver = options.swiplResolver ?? (() => resolveSwipl());
|
|
137
142
|
this.timeout = options.timeout || 30000;
|
|
138
143
|
this.maxOutputBytes =
|
|
139
144
|
options.maxOutputBytes !== undefined &&
|
|
@@ -164,15 +169,31 @@ export class PrologProcess {
|
|
|
164
169
|
needsRestart() {
|
|
165
170
|
return this.interactiveStarted && !this.isProcessUsable();
|
|
166
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* The executable and SWI home to launch. An explicit `swiplPath` is used
|
|
174
|
+
* verbatim; otherwise the per-process resolver decides, so a missing or
|
|
175
|
+
* damaged runtime fails with its complete remediation text.
|
|
176
|
+
*/
|
|
177
|
+
launchTarget() {
|
|
178
|
+
const explicit = this.explicitSwiplPath;
|
|
179
|
+
if (explicit !== undefined)
|
|
180
|
+
return { bin: explicit, env: {} };
|
|
181
|
+
const resolved = this.swiplResolver();
|
|
182
|
+
return { bin: resolved.bin, env: swiplChildEnv(resolved) };
|
|
183
|
+
}
|
|
167
184
|
async start() {
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
const explicit = this.explicitSwiplPath;
|
|
186
|
+
if (explicit !== undefined &&
|
|
187
|
+
!existsSync(explicit) &&
|
|
188
|
+
explicit !== "swipl") {
|
|
189
|
+
throw new Error(`SWI-Prolog not found at ${explicit}. Please install SWI-Prolog or check your PATH.`);
|
|
170
190
|
}
|
|
191
|
+
const launch = this.launchTarget();
|
|
171
192
|
const kbPath = resolveKbPlPath();
|
|
172
193
|
this.interactiveStarted = true;
|
|
173
194
|
this.terminationReason = null;
|
|
174
195
|
this.invalidateCache();
|
|
175
|
-
this.process = spawn(
|
|
196
|
+
this.process = spawn(launch.bin, [
|
|
176
197
|
"-g",
|
|
177
198
|
`use_module('${kbPath}'), use_module(library(semweb/rdf_db)), set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])`,
|
|
178
199
|
"--quiet",
|
|
@@ -180,6 +201,7 @@ export class PrologProcess {
|
|
|
180
201
|
detached: process.platform !== "win32",
|
|
181
202
|
env: {
|
|
182
203
|
...process.env,
|
|
204
|
+
...launch.env,
|
|
183
205
|
KIBI_RUNTIME_NAME: process.versions.bun !== undefined ? "bun" : "node",
|
|
184
206
|
KIBI_RUNTIME_VERSION: process.versions.bun ?? process.versions.node ?? "unknown",
|
|
185
207
|
KIBI_PACKAGE_VERSIONS: process.env.KIBI_PACKAGE_VERSIONS ?? KIBI_PACKAGE_VERSIONS,
|
|
@@ -243,12 +265,23 @@ export class PrologProcess {
|
|
|
243
265
|
}
|
|
244
266
|
// implements REQ-core-prolog-process-management
|
|
245
267
|
async query(goal) {
|
|
268
|
+
// A lost interactive session must fail before any cached answer is served.
|
|
269
|
+
if (!this.useOneShotMode && this.needsRestart()) {
|
|
270
|
+
throw this.lostProcessError();
|
|
271
|
+
}
|
|
246
272
|
const isSingleGoal = typeof goal === "string";
|
|
247
273
|
const goalKey = isSingleGoal ? goal : null;
|
|
248
274
|
const cacheable = goalKey !== null && this.isCacheableGoal(goalKey);
|
|
249
275
|
if (cacheable) {
|
|
276
|
+
const cacheLookupStartedAt = process.env.KIBI_PERF_TIMINGS === "1" ? performance.now() : null;
|
|
250
277
|
const cachedResult = this.cache.get(goalKey);
|
|
251
278
|
if (cachedResult) {
|
|
279
|
+
if (cacheLookupStartedAt !== null) {
|
|
280
|
+
writePerformanceTraceEvent({
|
|
281
|
+
kind: "prolog-process-cache-hit",
|
|
282
|
+
durationMs: performance.now() - cacheLookupStartedAt,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
252
285
|
return cachedResult;
|
|
253
286
|
}
|
|
254
287
|
}
|
|
@@ -416,13 +449,23 @@ export class PrologProcess {
|
|
|
416
449
|
releaseQuery = resolve;
|
|
417
450
|
});
|
|
418
451
|
await previousQuery;
|
|
452
|
+
const tracingEnabled = process.env.KIBI_PERF_TIMINGS === "1";
|
|
453
|
+
let roundTripStartedAt = null;
|
|
419
454
|
try {
|
|
420
455
|
if (!this.isProcessUsable()) {
|
|
421
456
|
throw this.lostProcessError();
|
|
422
457
|
}
|
|
458
|
+
if (tracingEnabled)
|
|
459
|
+
roundTripStartedAt = performance.now();
|
|
423
460
|
return await runInteractiveQuery();
|
|
424
461
|
}
|
|
425
462
|
finally {
|
|
463
|
+
if (roundTripStartedAt !== null) {
|
|
464
|
+
writePerformanceTraceEvent({
|
|
465
|
+
kind: "prolog-round-trip",
|
|
466
|
+
durationMs: performance.now() - roundTripStartedAt,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
426
469
|
releaseQuery();
|
|
427
470
|
}
|
|
428
471
|
}
|
|
@@ -534,10 +577,12 @@ export class PrologProcess {
|
|
|
534
577
|
const packageVersions = process.env.KIBI_PACKAGE_VERSIONS ?? KIBI_PACKAGE_VERSIONS;
|
|
535
578
|
let child;
|
|
536
579
|
try {
|
|
537
|
-
|
|
580
|
+
const launch = this.launchTarget();
|
|
581
|
+
child = spawn(launch.bin, ["-q", "-g", prologGoal, "-t", "halt"], {
|
|
538
582
|
detached: process.platform !== "win32",
|
|
539
583
|
env: {
|
|
540
584
|
...process.env,
|
|
585
|
+
...launch.env,
|
|
541
586
|
KIBI_GOAL: combinedGoal,
|
|
542
587
|
KIBI_RUNTIME_NAME: runtimeName,
|
|
543
588
|
KIBI_RUNTIME_VERSION: runtimeVersion,
|
|
@@ -820,6 +865,20 @@ export class PrologProcess {
|
|
|
820
865
|
// Diagnostic markers intentionally contain words such as `lock` and the
|
|
821
866
|
// audit path. Remove them before classifying the actual Prolog error so a
|
|
822
867
|
// contradiction at the check stage is not mistaken for an audit lock.
|
|
868
|
+
// Preserve only bounded identifiers from the formal existence error. Its
|
|
869
|
+
// context can contain authored dictionaries or query data and is never shown.
|
|
870
|
+
const existence = errorText.match(/^__KIBI_ERROR__:error\(existence_error\((key|procedure),([^,()\r\n]+)(?:,|\),)/m);
|
|
871
|
+
const identifier = existence?.[2]?.trim().replace(/^'([^']+)'$/, "$1");
|
|
872
|
+
if (existence?.[1] === "key" && identifier !== undefined) {
|
|
873
|
+
if (/^[A-Za-z][A-Za-z0-9_-]{0,95}$/.test(identifier)) {
|
|
874
|
+
return `Predicate or file not found (missing dictionary key: ${identifier})`;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (existence?.[1] === "procedure" && identifier !== undefined) {
|
|
878
|
+
if (/^(?:[A-Za-z][A-Za-z0-9_]{0,95}:)?[A-Za-z][A-Za-z0-9_]{0,95}\/[0-9]{1,3}$/.test(identifier)) {
|
|
879
|
+
return `Predicate or file not found (missing procedure: ${identifier})`;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
823
882
|
const cleanError = errorText.replace(/^__KIBI_(?:STAGE|RUNTIME|ERROR)__:[^\r\n]*\r?\n?/gm, "");
|
|
824
883
|
if (cleanError.includes("stale_snapshot")) {
|
|
825
884
|
return "KB snapshot is stale; reattach or refresh the runtime before retrying (stale_snapshot)";
|
|
@@ -861,6 +920,8 @@ export class PrologProcess {
|
|
|
861
920
|
cleanError.includes("Unknown procedure")) {
|
|
862
921
|
return "Predicate or file not found";
|
|
863
922
|
}
|
|
923
|
+
if (existence !== null)
|
|
924
|
+
return "Predicate or file not found";
|
|
864
925
|
if (cleanError.includes("permission_error")) {
|
|
865
926
|
return "Access denied or KB locked";
|
|
866
927
|
}
|
|
@@ -13,5 +13,19 @@ export interface DiagnosticUsageInput {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function diagnosticMutationFingerprint(args: Readonly<Record<string, unknown>>): string;
|
|
15
15
|
export declare function deriveDiagnosticUsageFields(tool: string, args: Readonly<Record<string, unknown>>, telemetry: Readonly<Record<string, unknown>> | null, result: unknown): Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the operator opted into usage telemetry through the environment.
|
|
18
|
+
*
|
|
19
|
+
* `KIBI_DIAGNOSTIC_MODE` is the one opt-in shared with the MCP server, so a
|
|
20
|
+
* single setting covers both peer surfaces. `KIBI_CLI_DIAGNOSTIC_MODE` is the
|
|
21
|
+
* older CLI-only spelling and keeps working.
|
|
22
|
+
*/
|
|
23
|
+
export declare function diagnosticEnvOptIn(env?: NodeJS.ProcessEnv): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The host that ran this CLI call. Hosts that launch Kibi set `KIBI_HOST`;
|
|
26
|
+
* Claude Code marks every shell it runs with `CLAUDECODE=1`, which is the
|
|
27
|
+
* only ambient signal trusted here. Anything else stays `unknown`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function diagnosticCliHost(env?: NodeJS.ProcessEnv): string;
|
|
16
30
|
export declare function appendCliDiagnosticUsage(input: DiagnosticUsageInput): void;
|
|
17
31
|
//# sourceMappingURL=diagnostic-usage.d.ts.map
|