kibi-cli 2.2.0 → 2.3.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-operation-loader.d.ts.map +1 -1
- package/dist/cli-operation-loader.js +3 -0
- package/dist/cli-operation-metadata.d.ts +4 -0
- package/dist/cli-operation-metadata.d.ts.map +1 -1
- package/dist/cli-operation-metadata.js +5 -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/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/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/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/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 +44 -0
- 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/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/types.d.ts +1 -1
- package/dist/public/operations/types.d.ts.map +1 -1
- package/dist/public/skills/kibi-usage/resources/operation-access.md +1 -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/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/impact-review-preparation.ts +46 -0
- package/src/public/operations/types.ts +1 -0
- package/src/public/skills/kibi-usage/resources/operation-access.md +1 -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
package/dist/engine.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ export type { EngineAttachmentIdentity, EngineCommandV1, EngineRequest, } from "
|
|
|
6
6
|
export declare const ENGINE_PROTOCOL_VERSION = 1;
|
|
7
7
|
export declare const ENGINE_IDLE_TIMEOUT_MS: number;
|
|
8
8
|
export declare const ENGINE_PACKAGE_VERSIONS: string;
|
|
9
|
+
/**
|
|
10
|
+
* Identity of the SWI-Prolog this process would launch (`<bin>@<version>`).
|
|
11
|
+
* The daemon and every client compare it; a daemon running a different
|
|
12
|
+
* resolved Prolog is replaced, never reused.
|
|
13
|
+
*/
|
|
14
|
+
export declare function engineSwiplIdentity(): string;
|
|
9
15
|
export declare function requestEngineSignalShutdown(shutdown: () => unknown): () => void;
|
|
10
16
|
export declare function readJournalGeneration(branchPath: string): string;
|
|
11
17
|
export declare function readEngineAttachmentIdentity(branchPath: string): EngineAttachmentIdentity | null;
|
|
@@ -83,6 +89,14 @@ export declare class EngineClient {
|
|
|
83
89
|
invalidateCache(): void;
|
|
84
90
|
isRunning(): boolean;
|
|
85
91
|
private readAttachedIdentity;
|
|
92
|
+
private daemonRuntimeMatches;
|
|
93
|
+
/**
|
|
94
|
+
* A live daemon keeps the SWI-Prolog it was started with. When this client
|
|
95
|
+
* resolves a different executable or version (KIBI_SWIPL changed, the
|
|
96
|
+
* bundled package was upgraded, a pre-handshake daemon), the daemon is
|
|
97
|
+
* stopped and replaced; it is never reused.
|
|
98
|
+
*/
|
|
99
|
+
private reconcileRuntime;
|
|
86
100
|
private reconcileAttachment;
|
|
87
101
|
private shutdownConnectedDaemon;
|
|
88
102
|
getPid(): number;
|
|
@@ -112,7 +126,7 @@ export declare class EngineSession {
|
|
|
112
126
|
*/
|
|
113
127
|
ensureLive(): Promise<boolean>;
|
|
114
128
|
}
|
|
115
|
-
export declare function runEngineDaemon(
|
|
129
|
+
export declare function runEngineDaemon(requestedOptions: {
|
|
116
130
|
readonly workspaceRoot: string;
|
|
117
131
|
readonly branch: string;
|
|
118
132
|
readonly socketPath: string;
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -6,21 +6,34 @@
|
|
|
6
6
|
* the local socket; Prolog itself never crosses the process boundary.
|
|
7
7
|
*/
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
|
-
import { chmodSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
9
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
10
|
+
import { chmodSync, closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
11
11
|
import * as net from "node:net";
|
|
12
12
|
import * as os from "node:os";
|
|
13
13
|
import * as path from "node:path";
|
|
14
|
+
import { performance } from "node:perf_hooks";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { writePerformanceTraceEvent } from "./performance-trace.js";
|
|
15
17
|
import { PrologProcess, resolveKbPlPath } from "./prolog.js";
|
|
16
18
|
import { parseEntityFromList, parseListOfLists } from "./prolog/codec.js";
|
|
17
19
|
import { retryAttachAfterBreakingStaleLock } from "./prolog/store-lock.js";
|
|
20
|
+
import { resolveSwipl, swiplIdentity } from "./prolog/swipl-resolver.js";
|
|
18
21
|
import { queryEntityChunks } from "./public/operations/discovery-entities.js";
|
|
19
22
|
import { isValidBranchName } from "./utils/branch-resolver.js";
|
|
20
23
|
import { branchStorePath, ensureBranchStoreManifest, } from "./utils/branch-store-locator.js";
|
|
24
|
+
import { canonicalFilesystemPath } from "./utils/canonical-path.js";
|
|
21
25
|
export const ENGINE_PROTOCOL_VERSION = 1;
|
|
22
26
|
export const ENGINE_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
23
27
|
export const ENGINE_PACKAGE_VERSIONS = process.env.KIBI_PACKAGE_VERSIONS ?? "unknown";
|
|
28
|
+
/**
|
|
29
|
+
* Identity of the SWI-Prolog this process would launch (`<bin>@<version>`).
|
|
30
|
+
* The daemon and every client compare it; a daemon running a different
|
|
31
|
+
* resolved Prolog is replaced, never reused.
|
|
32
|
+
*/
|
|
33
|
+
// implements REQ-prolog-daemon-runtime-identity
|
|
34
|
+
export function engineSwiplIdentity() {
|
|
35
|
+
return swiplIdentity(resolveSwipl());
|
|
36
|
+
}
|
|
24
37
|
const ENGINE_QUERY_CACHE_MAX_ENTRIES = 128;
|
|
25
38
|
const ENGINE_QUERY_CACHE_MAX_RESULT_BYTES = 8 * 1024 * 1024;
|
|
26
39
|
const ENGINE_FRESHNESS_CACHE_MS = 100;
|
|
@@ -259,17 +272,75 @@ export function runtimeDirectory() {
|
|
|
259
272
|
const configured = process.env.KIBI_RUNTIME_DIR ??
|
|
260
273
|
process.env.XDG_RUNTIME_DIR ??
|
|
261
274
|
path.join(os.tmpdir(), "kibi-runtime");
|
|
262
|
-
|
|
275
|
+
// Windows named pipes have no Unix socket pathname budget. Preserve its
|
|
276
|
+
// filesystem location policy for the associated pid files.
|
|
277
|
+
if (process.platform === "win32") {
|
|
278
|
+
for (const candidate of [
|
|
279
|
+
configured,
|
|
280
|
+
path.join(os.tmpdir(), "kibi-runtime"),
|
|
281
|
+
]) {
|
|
282
|
+
try {
|
|
283
|
+
mkdirSync(candidate, { recursive: true, mode: 0o700 });
|
|
284
|
+
const probe = path.join(candidate, `.kibi-write-${process.pid}`);
|
|
285
|
+
writeFileSync(probe, "", { mode: 0o600 });
|
|
286
|
+
unlinkSync(probe);
|
|
287
|
+
return candidate;
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Try the existing system-temp fallback.
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
throw new Error("Unable to create a writable Kibi engine runtime directory");
|
|
294
|
+
}
|
|
295
|
+
const euid = process.geteuid?.();
|
|
296
|
+
if (euid === undefined)
|
|
297
|
+
throw new Error("Kibi engine runtime euid unavailable");
|
|
298
|
+
let configuredIdentity;
|
|
299
|
+
try {
|
|
300
|
+
configuredIdentity = canonicalFilesystemPath(configured);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// An inaccessible or dangling configured path remains a distinct identity;
|
|
304
|
+
// it is never accepted as the socket directory below.
|
|
305
|
+
configuredIdentity = path.resolve(configured);
|
|
306
|
+
}
|
|
307
|
+
const configuredKey = createHash("sha256")
|
|
308
|
+
.update(configuredIdentity)
|
|
309
|
+
.digest("hex")
|
|
310
|
+
.slice(0, 32);
|
|
311
|
+
const candidates = [
|
|
312
|
+
configured,
|
|
313
|
+
path.join("/tmp", `kb-${euid}-${configuredKey}`),
|
|
314
|
+
];
|
|
315
|
+
const maximumBytes = process.platform === "linux" ? 107 : 103;
|
|
316
|
+
const socketName = `kibi-${"0".repeat(32)}.sock`;
|
|
263
317
|
for (const candidate of candidates) {
|
|
264
318
|
try {
|
|
319
|
+
try {
|
|
320
|
+
if (lstatSync(candidate).isSymbolicLink())
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
if (error.code !== "ENOENT")
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
265
327
|
mkdirSync(candidate, { recursive: true, mode: 0o700 });
|
|
328
|
+
const directory = lstatSync(candidate);
|
|
329
|
+
if (!directory.isDirectory() ||
|
|
330
|
+
directory.uid !== euid ||
|
|
331
|
+
(directory.mode & 0o077) !== 0)
|
|
332
|
+
continue;
|
|
333
|
+
const canonical = canonicalFilesystemPath(candidate);
|
|
334
|
+
if (Buffer.byteLength(path.join(canonical, socketName), "utf8") >
|
|
335
|
+
maximumBytes)
|
|
336
|
+
continue;
|
|
266
337
|
// XDG_RUNTIME_DIR can exist but be mounted read-only (for example in a
|
|
267
338
|
// constrained container). Probe the directory before using it for the
|
|
268
339
|
// socket, start lock, and pid files so auto-start fails over cleanly.
|
|
269
|
-
const probe = path.join(
|
|
270
|
-
writeFileSync(probe, "", { mode: 0o600 });
|
|
340
|
+
const probe = path.join(canonical, `.kibi-write-${randomUUID()}`);
|
|
341
|
+
writeFileSync(probe, "", { mode: 0o600, flag: "wx" });
|
|
271
342
|
unlinkSync(probe);
|
|
272
|
-
return
|
|
343
|
+
return canonical;
|
|
273
344
|
}
|
|
274
345
|
catch {
|
|
275
346
|
// Try the private system-temp fallback below.
|
|
@@ -327,18 +398,11 @@ export function fsyncJournaledBranchStore(branchPath) {
|
|
|
327
398
|
}
|
|
328
399
|
// implements REQ-core-journaled-engine-persistence
|
|
329
400
|
export function engineSocketPath(workspaceRoot, branch) {
|
|
330
|
-
const canonicalRoot = (
|
|
331
|
-
try {
|
|
332
|
-
return realpathSync(workspaceRoot);
|
|
333
|
-
}
|
|
334
|
-
catch {
|
|
335
|
-
return path.resolve(workspaceRoot);
|
|
336
|
-
}
|
|
337
|
-
})();
|
|
401
|
+
const canonicalRoot = canonicalFilesystemPath(workspaceRoot);
|
|
338
402
|
const key = createHash("sha256")
|
|
339
403
|
.update(`${canonicalRoot}\0${branch}`)
|
|
340
|
-
// 128 bits
|
|
341
|
-
//
|
|
404
|
+
// Retain 128 bits of workspace/branch identity; runtimeDirectory budgets
|
|
405
|
+
// the complete pathname rather than shortening this identity.
|
|
342
406
|
.digest("hex")
|
|
343
407
|
.slice(0, 32);
|
|
344
408
|
if (process.platform === "win32") {
|
|
@@ -675,22 +739,36 @@ export class EngineClient {
|
|
|
675
739
|
if (!isValidBranchName(options.branch)) {
|
|
676
740
|
throw new Error(`Invalid Kibi engine branch name: ${options.branch}`);
|
|
677
741
|
}
|
|
678
|
-
|
|
679
|
-
try {
|
|
680
|
-
this.workspaceRoot = realpathSync(resolvedWorkspaceRoot);
|
|
681
|
-
}
|
|
682
|
-
catch {
|
|
683
|
-
this.workspaceRoot = resolvedWorkspaceRoot;
|
|
684
|
-
}
|
|
742
|
+
this.workspaceRoot = canonicalFilesystemPath(options.workspaceRoot);
|
|
685
743
|
this.branch = options.branch;
|
|
686
744
|
this.timeout = options.timeout ?? 120_000;
|
|
687
745
|
this.allowPublicationLock = options.allowPublicationLock ?? false;
|
|
688
746
|
}
|
|
689
747
|
async start(allowSpawn = true) {
|
|
690
748
|
const wasConnected = this.socket !== null && !this.socket.destroyed;
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
749
|
+
for (let attempt = 0;; attempt += 1) {
|
|
750
|
+
await this.connect(allowSpawn);
|
|
751
|
+
if (wasConnected || this.socket === null)
|
|
752
|
+
return;
|
|
753
|
+
try {
|
|
754
|
+
await this.reconcileRuntime();
|
|
755
|
+
await this.reconcileAttachment();
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
// A listener can begin draining after the kernel accepts a connection
|
|
760
|
+
// but before the read-only runtime and attachment handshakes finish.
|
|
761
|
+
// Retry those handshakes once; request() never retries an application
|
|
762
|
+
// RPC.
|
|
763
|
+
if (attempt > 0 ||
|
|
764
|
+
!allowSpawn ||
|
|
765
|
+
!(error instanceof Error) ||
|
|
766
|
+
error.message !== "Kibi engine connection closed" ||
|
|
767
|
+
(this.socket !== null && !this.socket.destroyed))
|
|
768
|
+
throw error;
|
|
769
|
+
this.socket?.destroy();
|
|
770
|
+
this.socket = null;
|
|
771
|
+
}
|
|
694
772
|
}
|
|
695
773
|
}
|
|
696
774
|
async connect(allowSpawn = true) {
|
|
@@ -729,6 +807,22 @@ export class EngineClient {
|
|
|
729
807
|
}
|
|
730
808
|
}
|
|
731
809
|
if (this.socket === null) {
|
|
810
|
+
// A draining daemon removes its listener before saving, but retains
|
|
811
|
+
// its PID until the branch lock is released. Do not start a second
|
|
812
|
+
// writer while that process is still completing shutdown.
|
|
813
|
+
const existingPid = this.getPid();
|
|
814
|
+
if (existingPid > 0) {
|
|
815
|
+
let alive = false;
|
|
816
|
+
try {
|
|
817
|
+
process.kill(existingPid, 0);
|
|
818
|
+
alive = true;
|
|
819
|
+
}
|
|
820
|
+
catch { }
|
|
821
|
+
if (alive) {
|
|
822
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
732
826
|
lock = tryAcquireStartLock(lockPath);
|
|
733
827
|
if (lock === null) {
|
|
734
828
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
@@ -780,7 +874,10 @@ export class EngineClient {
|
|
|
780
874
|
this.attachSocket(this.socket);
|
|
781
875
|
}
|
|
782
876
|
attachSocket(socket) {
|
|
877
|
+
this.inputBuffer = Buffer.alloc(0);
|
|
783
878
|
socket.on("data", (chunk) => {
|
|
879
|
+
if (this.socket !== socket)
|
|
880
|
+
return;
|
|
784
881
|
try {
|
|
785
882
|
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
786
883
|
this.inputBuffer = parseFrames(Buffer.concat([this.inputBuffer, bytes]), (value) => this.handleResponse(value));
|
|
@@ -790,6 +887,8 @@ export class EngineClient {
|
|
|
790
887
|
}
|
|
791
888
|
});
|
|
792
889
|
socket.on("close", () => {
|
|
890
|
+
if (this.socket !== socket)
|
|
891
|
+
return;
|
|
793
892
|
this.socket = null;
|
|
794
893
|
const error = new Error("Kibi engine connection closed");
|
|
795
894
|
for (const pending of this.pending.values()) {
|
|
@@ -798,6 +897,8 @@ export class EngineClient {
|
|
|
798
897
|
this.pending.clear();
|
|
799
898
|
});
|
|
800
899
|
socket.on("error", (error) => {
|
|
900
|
+
if (this.socket !== socket)
|
|
901
|
+
return;
|
|
801
902
|
for (const pending of this.pending.values()) {
|
|
802
903
|
this.settlePending(pending, () => pending.reject(error));
|
|
803
904
|
}
|
|
@@ -859,6 +960,7 @@ export class EngineClient {
|
|
|
859
960
|
id,
|
|
860
961
|
protocolVersion: ENGINE_PROTOCOL_VERSION,
|
|
861
962
|
packageVersions: ENGINE_PACKAGE_VERSIONS,
|
|
963
|
+
prologIdentity: engineSwiplIdentity(),
|
|
862
964
|
workspaceRoot: this.workspaceRoot,
|
|
863
965
|
branch: this.branch,
|
|
864
966
|
}), ignoreWriteFailure);
|
|
@@ -968,6 +1070,7 @@ export class EngineClient {
|
|
|
968
1070
|
cancelOf: requestId,
|
|
969
1071
|
protocolVersion: ENGINE_PROTOCOL_VERSION,
|
|
970
1072
|
packageVersions: ENGINE_PACKAGE_VERSIONS,
|
|
1073
|
+
prologIdentity: engineSwiplIdentity(),
|
|
971
1074
|
workspaceRoot: this.workspaceRoot,
|
|
972
1075
|
branch: this.branch,
|
|
973
1076
|
}), ignoreWriteFailure);
|
|
@@ -1010,6 +1113,36 @@ export class EngineClient {
|
|
|
1010
1113
|
return null;
|
|
1011
1114
|
return parseEngineAttachmentIdentity(status.bindings.JsonString);
|
|
1012
1115
|
}
|
|
1116
|
+
async daemonRuntimeMatches() {
|
|
1117
|
+
const reply = await this.request({
|
|
1118
|
+
method: "handshake",
|
|
1119
|
+
});
|
|
1120
|
+
return reply?.prologIdentity === engineSwiplIdentity();
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* A live daemon keeps the SWI-Prolog it was started with. When this client
|
|
1124
|
+
* resolves a different executable or version (KIBI_SWIPL changed, the
|
|
1125
|
+
* bundled package was upgraded, a pre-handshake daemon), the daemon is
|
|
1126
|
+
* stopped and replaced; it is never reused.
|
|
1127
|
+
*/
|
|
1128
|
+
// implements REQ-prolog-daemon-runtime-identity
|
|
1129
|
+
async reconcileRuntime() {
|
|
1130
|
+
if (this.replacingStaleDaemon || this.socket === null)
|
|
1131
|
+
return;
|
|
1132
|
+
if (await this.daemonRuntimeMatches())
|
|
1133
|
+
return;
|
|
1134
|
+
this.replacingStaleDaemon = true;
|
|
1135
|
+
try {
|
|
1136
|
+
await this.shutdownConnectedDaemon();
|
|
1137
|
+
await this.connect(true);
|
|
1138
|
+
if (!(await this.daemonRuntimeMatches())) {
|
|
1139
|
+
throw new Error(`Kibi engine is running a different SWI-Prolog than this client resolved (${engineSwiplIdentity()}); run 'kibi engine stop' and retry`);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
finally {
|
|
1143
|
+
this.replacingStaleDaemon = false;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1013
1146
|
async reconcileAttachment() {
|
|
1014
1147
|
if (this.replacingStaleDaemon || this.socket === null)
|
|
1015
1148
|
return;
|
|
@@ -1034,26 +1167,40 @@ export class EngineClient {
|
|
|
1034
1167
|
}
|
|
1035
1168
|
async shutdownConnectedDaemon() {
|
|
1036
1169
|
const socketPath = engineSocketPath(this.workspaceRoot, this.branch);
|
|
1170
|
+
let requestError;
|
|
1037
1171
|
if (!this.isRunning()) {
|
|
1038
1172
|
if (existsSync(socketPath)) {
|
|
1039
1173
|
throw new Error(`Kibi engine socket remains present but is not reachable: ${socketPath}`);
|
|
1040
1174
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1175
|
+
const existingPid = this.getPid();
|
|
1176
|
+
if (existingPid <= 0)
|
|
1177
|
+
return;
|
|
1178
|
+
try {
|
|
1179
|
+
process.kill(existingPid, 0);
|
|
1180
|
+
}
|
|
1181
|
+
catch {
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
// An already-draining daemon can have no listener while its durability
|
|
1185
|
+
// save still owns the store. A stop caller must wait for that handoff.
|
|
1046
1186
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1187
|
+
else {
|
|
1188
|
+
try {
|
|
1189
|
+
await this.request({ method: "stop" });
|
|
1190
|
+
}
|
|
1191
|
+
catch (error) {
|
|
1192
|
+
requestError = error;
|
|
1193
|
+
}
|
|
1194
|
+
this.socket?.destroy();
|
|
1195
|
+
this.socket = null;
|
|
1049
1196
|
}
|
|
1050
|
-
this.
|
|
1051
|
-
this.socket = null;
|
|
1197
|
+
const pidPath = enginePidPath(this.workspaceRoot, this.branch);
|
|
1052
1198
|
const deadline = Date.now() + 5_000;
|
|
1053
|
-
while (existsSync(socketPath)
|
|
1199
|
+
while ((existsSync(socketPath) || existsSync(pidPath)) &&
|
|
1200
|
+
Date.now() < deadline) {
|
|
1054
1201
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
1055
1202
|
}
|
|
1056
|
-
if (existsSync(socketPath)) {
|
|
1203
|
+
if (existsSync(socketPath) || existsSync(pidPath)) {
|
|
1057
1204
|
const detail = requestError instanceof Error ? `: ${requestError.message}` : "";
|
|
1058
1205
|
throw new Error(`Kibi engine did not stop within 5 seconds${detail}`);
|
|
1059
1206
|
}
|
|
@@ -1079,11 +1226,8 @@ export class EngineClient {
|
|
|
1079
1226
|
throw new Error("Unable to start Kibi engine for stop request");
|
|
1080
1227
|
}
|
|
1081
1228
|
}
|
|
1082
|
-
if (!this.isRunning()) {
|
|
1083
|
-
|
|
1084
|
-
throw new Error(`Kibi engine socket remains present but is not reachable: ${socketPath}`);
|
|
1085
|
-
}
|
|
1086
|
-
return;
|
|
1229
|
+
if (!this.isRunning() && existsSync(socketPath)) {
|
|
1230
|
+
throw new Error(`Kibi engine socket remains present but is not reachable: ${socketPath}`);
|
|
1087
1231
|
}
|
|
1088
1232
|
await this.shutdownConnectedDaemon();
|
|
1089
1233
|
}
|
|
@@ -1301,7 +1445,9 @@ function recoverInterruptedGeneration(branchPath) {
|
|
|
1301
1445
|
}
|
|
1302
1446
|
}
|
|
1303
1447
|
function isEngineLifecycleRequest(request) {
|
|
1304
|
-
if (request.method === "stop" ||
|
|
1448
|
+
if (request.method === "stop" ||
|
|
1449
|
+
request.method === "cancel" ||
|
|
1450
|
+
request.method === "handshake")
|
|
1305
1451
|
return true;
|
|
1306
1452
|
const kind = request.command?.kind;
|
|
1307
1453
|
return (request.method === "command" &&
|
|
@@ -1386,11 +1532,15 @@ export class EngineSession {
|
|
|
1386
1532
|
return true;
|
|
1387
1533
|
}
|
|
1388
1534
|
}
|
|
1389
|
-
// implements REQ-core-journaled-engine-
|
|
1390
|
-
export async function runEngineDaemon(
|
|
1391
|
-
if (!isValidBranchName(
|
|
1392
|
-
throw new Error(`Invalid Kibi engine branch name: ${
|
|
1535
|
+
// implements REQ-core-journaled-engine-persistence
|
|
1536
|
+
export async function runEngineDaemon(requestedOptions) {
|
|
1537
|
+
if (!isValidBranchName(requestedOptions.branch)) {
|
|
1538
|
+
throw new Error(`Invalid Kibi engine branch name: ${requestedOptions.branch}`);
|
|
1393
1539
|
}
|
|
1540
|
+
const options = {
|
|
1541
|
+
...requestedOptions,
|
|
1542
|
+
workspaceRoot: canonicalFilesystemPath(requestedOptions.workspaceRoot),
|
|
1543
|
+
};
|
|
1394
1544
|
const branchPath = ensureBranchStoreManifest(options.workspaceRoot, options.branch);
|
|
1395
1545
|
await ensureJournaledBranchStoreAsync(branchPath);
|
|
1396
1546
|
const prolog = new PrologProcess({
|
|
@@ -1403,6 +1553,7 @@ export async function runEngineDaemon(options) {
|
|
|
1403
1553
|
const session = new EngineSession(prolog, branchPath);
|
|
1404
1554
|
await session.boot();
|
|
1405
1555
|
let attachedIdentity = readEngineAttachmentIdentity(branchPath);
|
|
1556
|
+
const serverPrologIdentity = engineSwiplIdentity();
|
|
1406
1557
|
mkdirSync(path.dirname(options.socketPath), { recursive: true, mode: 0o700 });
|
|
1407
1558
|
if (existsSync(options.socketPath)) {
|
|
1408
1559
|
const isLivePeerSignal = (error) => {
|
|
@@ -1473,6 +1624,10 @@ export async function runEngineDaemon(options) {
|
|
|
1473
1624
|
let freshnessCache = null;
|
|
1474
1625
|
const clients = new Set();
|
|
1475
1626
|
let shuttingDown = false;
|
|
1627
|
+
let finishShutdown;
|
|
1628
|
+
const shutdownFinished = new Promise((resolve) => {
|
|
1629
|
+
finishShutdown = resolve;
|
|
1630
|
+
});
|
|
1476
1631
|
const exactBranchStatus = (result) => {
|
|
1477
1632
|
if (!result.success || typeof result.bindings.JsonString !== "string") {
|
|
1478
1633
|
return result;
|
|
@@ -1520,7 +1675,7 @@ export async function runEngineDaemon(options) {
|
|
|
1520
1675
|
}, engineWorkspaceWatchdogMs());
|
|
1521
1676
|
workspaceWatchdog.unref();
|
|
1522
1677
|
const scheduleIdleExit = () => {
|
|
1523
|
-
if (activeClients > 0)
|
|
1678
|
+
if (shuttingDown || activeClients > 0)
|
|
1524
1679
|
return;
|
|
1525
1680
|
if (!idleCompactionQueued) {
|
|
1526
1681
|
idleCompactionQueued = true;
|
|
@@ -1552,11 +1707,19 @@ export async function runEngineDaemon(options) {
|
|
|
1552
1707
|
request.packageVersions !== expectedPackages) {
|
|
1553
1708
|
throw new Error(`Kibi engine package-version mismatch: client=${request.packageVersions ?? "missing"}, server=${expectedPackages}`);
|
|
1554
1709
|
}
|
|
1555
|
-
if (
|
|
1556
|
-
|
|
1710
|
+
if (canonicalFilesystemPath(request.workspaceRoot ?? "") !==
|
|
1711
|
+
options.workspaceRoot ||
|
|
1557
1712
|
request.branch !== options.branch) {
|
|
1558
1713
|
throw new Error("Kibi engine workspace identity mismatch");
|
|
1559
1714
|
}
|
|
1715
|
+
// A client that resolved a different Prolog must not be served by this
|
|
1716
|
+
// daemon. Handshake reports the identity and stop must still work so the
|
|
1717
|
+
// client can replace the daemon.
|
|
1718
|
+
if (request.method !== "handshake" &&
|
|
1719
|
+
request.method !== "stop" &&
|
|
1720
|
+
request.prologIdentity !== serverPrologIdentity) {
|
|
1721
|
+
throw new Error(`Kibi engine SWI-Prolog mismatch: client=${request.prologIdentity ?? "missing"}, server=${serverPrologIdentity}`);
|
|
1722
|
+
}
|
|
1560
1723
|
if (!isEngineLifecycleRequest(request) && (await session.ensureLive())) {
|
|
1561
1724
|
// A recycled session re-read the store; nothing cached from the lost
|
|
1562
1725
|
// session may be served, and the attachment identity may have moved.
|
|
@@ -1747,8 +1910,15 @@ export async function runEngineDaemon(options) {
|
|
|
1747
1910
|
}
|
|
1748
1911
|
}
|
|
1749
1912
|
case "kbStatus": {
|
|
1913
|
+
const freshnessCacheLookupStartedAt = process.env.KIBI_PERF_TIMINGS === "1" ? performance.now() : null;
|
|
1750
1914
|
if (freshnessCache !== null &&
|
|
1751
1915
|
Date.now() - freshnessCache.capturedAt <= ENGINE_FRESHNESS_CACHE_MS) {
|
|
1916
|
+
if (freshnessCacheLookupStartedAt !== null) {
|
|
1917
|
+
writePerformanceTraceEvent({
|
|
1918
|
+
kind: "engine-freshness-cache-hit",
|
|
1919
|
+
durationMs: performance.now() - freshnessCacheLookupStartedAt,
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1752
1922
|
return freshnessCache.result;
|
|
1753
1923
|
}
|
|
1754
1924
|
const result = exactBranchStatus(await prolog.query("status:kb_status_json(JsonString)"));
|
|
@@ -1768,9 +1938,17 @@ export async function runEngineDaemon(options) {
|
|
|
1768
1938
|
freshnessCache = null;
|
|
1769
1939
|
}
|
|
1770
1940
|
if (cacheableEngineGoal(request.goal)) {
|
|
1941
|
+
const queryCacheLookupStartedAt = process.env.KIBI_PERF_TIMINGS === "1" ? performance.now() : null;
|
|
1771
1942
|
const cached = queryCache.get(request.goal);
|
|
1772
|
-
if (cached !== undefined)
|
|
1943
|
+
if (cached !== undefined) {
|
|
1944
|
+
if (queryCacheLookupStartedAt !== null) {
|
|
1945
|
+
writePerformanceTraceEvent({
|
|
1946
|
+
kind: "engine-query-cache-hit",
|
|
1947
|
+
durationMs: performance.now() - queryCacheLookupStartedAt,
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1773
1950
|
return cached;
|
|
1951
|
+
}
|
|
1774
1952
|
}
|
|
1775
1953
|
const result = await prolog.query(request.goal);
|
|
1776
1954
|
session.recordModuleLoad(request.goal, result);
|
|
@@ -1810,6 +1988,8 @@ export async function runEngineDaemon(options) {
|
|
|
1810
1988
|
if (typeof request.targetDirectory !== "string")
|
|
1811
1989
|
throw new Error("export.targetDirectory is required");
|
|
1812
1990
|
return prolog.query(`kb_storage_export('${quoteProlog(request.targetDirectory)}')`);
|
|
1991
|
+
case "handshake":
|
|
1992
|
+
return { prologIdentity: serverPrologIdentity };
|
|
1813
1993
|
case "stop":
|
|
1814
1994
|
setImmediate(() => void shutdown());
|
|
1815
1995
|
return { stopped: true };
|
|
@@ -1836,6 +2016,10 @@ export async function runEngineDaemon(options) {
|
|
|
1836
2016
|
if (idleTimer)
|
|
1837
2017
|
clearTimeout(idleTimer);
|
|
1838
2018
|
clearInterval(workspaceWatchdog);
|
|
2019
|
+
// Stop accepting clients before the first asynchronous durability step.
|
|
2020
|
+
// Keep the PID published until Prolog releases the branch-store lock.
|
|
2021
|
+
server.close();
|
|
2022
|
+
await queue.catch(() => undefined);
|
|
1839
2023
|
const saved = session.needsRecycle()
|
|
1840
2024
|
? null
|
|
1841
2025
|
: await prolog.query("kb_save").catch(() => null);
|
|
@@ -1851,7 +2035,6 @@ export async function runEngineDaemon(options) {
|
|
|
1851
2035
|
await prolog.terminate().catch(() => undefined);
|
|
1852
2036
|
for (const client of clients)
|
|
1853
2037
|
client.destroy();
|
|
1854
|
-
server.close();
|
|
1855
2038
|
// A close callback can remain pending on older Node releases when a
|
|
1856
2039
|
// client disconnects during `server.close()`. The daemon has already
|
|
1857
2040
|
// removed its socket/pid and has no useful work left, so do not keep the
|
|
@@ -1872,6 +2055,7 @@ export async function runEngineDaemon(options) {
|
|
|
1872
2055
|
// gone, force the final event-loop exit rather than retaining a stale
|
|
1873
2056
|
// detached Node process on platform-specific server bookkeeping.
|
|
1874
2057
|
setTimeout(() => process.exit(0), 50).unref();
|
|
2058
|
+
finishShutdown();
|
|
1875
2059
|
};
|
|
1876
2060
|
// Detached engines must cross the same durability boundary when a service
|
|
1877
2061
|
// manager or a test harness terminates them as they do for an RPC stop.
|
|
@@ -1880,6 +2064,12 @@ export async function runEngineDaemon(options) {
|
|
|
1880
2064
|
process.once("SIGTERM", requestSignalShutdown);
|
|
1881
2065
|
process.once("SIGINT", requestSignalShutdown);
|
|
1882
2066
|
server.on("connection", (socket) => {
|
|
2067
|
+
// A peer may disappear while the daemon refuses or drains its socket.
|
|
2068
|
+
socket.on("error", () => undefined);
|
|
2069
|
+
if (shuttingDown) {
|
|
2070
|
+
socket.destroy();
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
1883
2073
|
clients.add(socket);
|
|
1884
2074
|
activeClients += 1;
|
|
1885
2075
|
// Request ids are per-connection. Cancel marks must not be daemon-global
|
|
@@ -1891,6 +2081,15 @@ export async function runEngineDaemon(options) {
|
|
|
1891
2081
|
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
1892
2082
|
buffer = parseFrames(Buffer.concat([buffer, bytes]), (value) => {
|
|
1893
2083
|
const request = value;
|
|
2084
|
+
if (shuttingDown) {
|
|
2085
|
+
writeSocketFrame(socket, {
|
|
2086
|
+
id: request.id,
|
|
2087
|
+
ok: false,
|
|
2088
|
+
error: "Kibi engine is shutting down; request was not executed",
|
|
2089
|
+
serverPackageVersions: ENGINE_PACKAGE_VERSIONS,
|
|
2090
|
+
});
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
1894
2093
|
if (request.method === "cancel" &&
|
|
1895
2094
|
typeof request.cancelOf === "number") {
|
|
1896
2095
|
cancelledRequests.add(request.cancelOf);
|
|
@@ -1959,4 +2158,5 @@ export async function runEngineDaemon(options) {
|
|
|
1959
2158
|
await new Promise((resolve) => {
|
|
1960
2159
|
server.once("close", resolve);
|
|
1961
2160
|
});
|
|
2161
|
+
await shutdownFinished;
|
|
1962
2162
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CapabilityRegistry } from "../plugins/registry.js";
|
|
2
|
+
import type { SourceAnalysisService } from "../plugins/source-analysis-service.js";
|
|
2
3
|
import { type ManifestSymbolEntry, enrichSymbolCoordinatesWithTsMorph } from "./symbols-ts.js";
|
|
3
4
|
export type { ManifestSymbolEntry };
|
|
4
5
|
export type SourceAnalysisMode = "parser" | "fallback";
|
|
@@ -36,7 +37,13 @@ export interface AnalyzeSourceTextOptions {
|
|
|
36
37
|
registry?: CapabilityRegistry;
|
|
37
38
|
}
|
|
38
39
|
interface EnrichSymbolCoordinatesDeps {
|
|
40
|
+
sourceAnalysisService: SourceAnalysisService;
|
|
39
41
|
enrichTsCoordinates: typeof enrichSymbolCoordinatesWithTsMorph;
|
|
42
|
+
/** Coordinate-only sync may locate explicit declarations in decorated Python.
|
|
43
|
+
* Completeness-sensitive callers must also verify the captured policy/review. */
|
|
44
|
+
allowPythonDecoratorCoordinates?: boolean;
|
|
45
|
+
/** Additional snapshot/policy authorization for completeness-sensitive gates. */
|
|
46
|
+
verifyPythonDecoratorCoordinates?: (logicalPath: string, analysis: Awaited<ReturnType<SourceAnalysisService["analyzeTextV2"]>>) => Promise<void>;
|
|
40
47
|
}
|
|
41
48
|
export declare function analyzeSourceText(entries: ManifestSymbolEntry[], workspaceRoot: string, deps?: Partial<EnrichSymbolCoordinatesDeps>): Promise<ManifestSymbolEntry[]>;
|
|
42
49
|
export declare function analyzeSourceText(filePath: string, content: string, options?: AnalyzeSourceTextOptions): SourceAnalysisResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../src/extractors/symbols-coordinator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../src/extractors/symbols-coordinator.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AASnF,OAAO,EACL,KAAK,mBAAmB,EAExB,kCAAkC,EACnC,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEvD,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,OAAO,GACP,QAAQ,GACR,UAAU,GACV,UAAU,GACV,WAAW,GACX,MAAM,GACN,MAAM,GACN,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACxC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAAC;CACtE;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACrC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,UAAU,2BAA2B;IACnC,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,mBAAmB,EAAE,OAAO,kCAAkC,CAAC;IAC/D;qFACiF;IACjF,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAC1C,iFAAiF;IACjF,gCAAgC,CAAC,EAAE,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC,CAAC,KAClE,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAMD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,EAAE,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,GAC1C,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAClC,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,wBAAwB,GACjC,oBAAoB,CAAC;AA8DxB;;;GAGG;AAEH,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAO/B;AA+CD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,mBAAmB,EAAE,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,GAC1C,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA6FhC"}
|