kibi-mcp 0.15.3 → 0.16.1
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/diagnostics.js +1 -1
- package/dist/server/docs.js +9 -7
- package/dist/server/tools.js +17 -0
- package/dist/tools/autopilot-discovery.js +1 -1
- package/dist/tools/autopilot-generate.js +39 -17
- package/dist/tools/model-requirement.js +10 -0
- package/dist/tools/sparql.js +96 -0
- package/dist/tools/suggest-predicates.js +557 -0
- package/dist/tools/upsert.js +210 -75
- package/dist/tools/validate-upsert.js +37 -0
- package/dist/tools-config.js +121 -11
- package/package.json +3 -3
package/dist/diagnostics.js
CHANGED
|
@@ -37,7 +37,7 @@ let diagnosticUsageLogPath = null;
|
|
|
37
37
|
export function initializeDiagnosticMode(enabled = DIAGNOSTIC_MODE_ENABLED) {
|
|
38
38
|
diagnosticUsageLogPath = null;
|
|
39
39
|
if (!enabled) {
|
|
40
|
-
|
|
40
|
+
process.env.KIBI_MCP_DIAGNOSTIC_MODE = "0";
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
const workspaceRoot = resolveWorkspaceRoot();
|
package/dist/server/docs.js
CHANGED
|
@@ -38,6 +38,7 @@ function renderToolsDoc() {
|
|
|
38
38
|
lines.push("");
|
|
39
39
|
lines.push("Modeling note: Kibi has eight core entity types grouped into common authoring (req, scenario, test, fact) and supporting/system (adr, flag, event, symbol).");
|
|
40
40
|
lines.push("Only strict domain facts (`fact_kind: subject` + `property_value`) participate in contradiction inference; use `flag` for runtime/config gates and `fact_kind: observation` or `meta` for bug/workaround notes.");
|
|
41
|
+
lines.push("Predicate flow: before writing ontology prose, call `kb_suggest_predicates`; apply a suggested `fact_kind: predicate` via `requires_predicate`, or record the returned `review:ontology-gap` observation when no predicate fits.");
|
|
41
42
|
return lines.join("\n");
|
|
42
43
|
}
|
|
43
44
|
export const PROMPTS = [
|
|
@@ -135,8 +136,9 @@ export const PROMPTS = [
|
|
|
135
136
|
"3. **Create-before-link**: Create endpoint entities with `kb_upsert` before linking them.",
|
|
136
137
|
"4. **Validate intent**: If creating links, call `kb_query` for both endpoint IDs first to ensure they exist.",
|
|
137
138
|
"5. **Model requirements as facts**: For new/updated reqs, create/reuse fact entities first, then express req semantics with `constrains` + `requires_property` (automated via `kb_model_requirement`).",
|
|
138
|
-
"
|
|
139
|
-
"
|
|
139
|
+
"6. **Suggest predicates before prose**: For ontology-lane requirements, spell out the prose claim and call `kb_suggest_predicates` before writing `fact_kind: observation`. Apply the selected `fact_kind: predicate` applyPlan, then attach the returned `relationshipPlan` as `requires_predicate` while preserving existing req metadata; use the returned `review:ontology-gap` observation when no predicate fits.",
|
|
140
|
+
"7. **Mutate**: Call `kb_upsert` for create/update, or `kb_delete` for explicit removals.",
|
|
141
|
+
"8. **Targeted checks**: Run `kb_check` after meaningful mutations; specify only the rules you need.",
|
|
140
142
|
"",
|
|
141
143
|
"If a tool returns empty results, do not assume failure. Re-check filters (type, id, tags, sourceFile, limit, or offset).",
|
|
142
144
|
].join("\n"),
|
|
@@ -203,16 +205,16 @@ function registerDocResources() {
|
|
|
203
205
|
"4. Reuse the same constrained fact ID across related requirements; vary property facts only when semantics differ",
|
|
204
206
|
'5. `kb_check` with `{ "rules": ["required-fields","no-dangling-refs"] }` for targeted validation',
|
|
205
207
|
"",
|
|
208
|
+
"## Model requirements as ontology predicates",
|
|
209
|
+
'1. Spell out the requirement prose and call `kb_suggest_predicates` with `{ "text": "...", "requirementId": "REQ-..." }`',
|
|
210
|
+
"2. If candidates are returned, apply the top or user-selected `structuredContent.applyPlan` to create `fact_kind: predicate`, then attach `structuredContent.relationshipPlan` with `requires_predicate` while preserving existing req metadata",
|
|
211
|
+
"3. If no candidate fits, apply or review the returned `review:ontology-gap` observation instead of silently writing prose",
|
|
212
|
+
"",
|
|
206
213
|
"Note: Kibi has eight core entity types. Create or reuse `fact` entities first, then create `req` entities and link with `constrains` and `requires_property` (create-before-link).",
|
|
207
214
|
"Only strict domain facts are contradiction-safe. Use `flag` for runtime/config gates; use `fact` with `fact_kind: observation` or `meta` for bug/workaround notes.",
|
|
208
215
|
"",
|
|
209
216
|
"## Find missing coverage",
|
|
210
217
|
'1. `kb_find_gaps` with `{ "type": "req", "missingRelationships": ["specified_by", "verified_by"] }` to find under-linked requirements',
|
|
211
|
-
"",
|
|
212
|
-
"## Find missing coverage",
|
|
213
|
-
"",
|
|
214
|
-
"## Find missing coverage",
|
|
215
|
-
'1. `kb_find_gaps` with `{ "type": "req", "missingRelationships": ["specified_by", "verified_by"] }` to find under-linked requirements',
|
|
216
218
|
'2. `kb_coverage` with `{ "by": "req", "includePassing": false }` to review evaluated coverage rows',
|
|
217
219
|
'3. `kb_graph` with `{ "seedIds": ["REQ-001"], "direction": "both", "depth": 2 }` to inspect neighboring entities',
|
|
218
220
|
"",
|
package/dist/server/tools.js
CHANGED
|
@@ -30,8 +30,11 @@ import { handleKbModelRequirement, } from "../tools/model-requirement.js";
|
|
|
30
30
|
import { handleKbQuery } from "../tools/query.js";
|
|
31
31
|
import { handleKbSearch } from "../tools/search.js";
|
|
32
32
|
import { handleKbSkillsList, handleKbSkillsLoad, handleKbSkillsRead, } from "../tools/skills.js";
|
|
33
|
+
import { handleSparql } from "../tools/sparql.js";
|
|
33
34
|
import { handleKbStatus } from "../tools/status.js";
|
|
35
|
+
import { handleKbSuggestPredicates, } from "../tools/suggest-predicates.js";
|
|
34
36
|
import { handleKbUpsert } from "../tools/upsert.js";
|
|
37
|
+
import { handleKbValidateUpsert } from "../tools/validate-upsert.js";
|
|
35
38
|
const DEFAULT_TOOL_TIMEOUT_MS = 90_000;
|
|
36
39
|
const TOOL_TIMEOUT_ENV = "KIBI_MCP_TOOL_TIMEOUT_MS";
|
|
37
40
|
const defaultToolsServerDeps = {
|
|
@@ -78,6 +81,7 @@ const DEFAULT_TOOLS_RUNTIME = {
|
|
|
78
81
|
handleKbDelete,
|
|
79
82
|
handleKbFindGaps,
|
|
80
83
|
handleKbGraph,
|
|
84
|
+
handleSparql,
|
|
81
85
|
handleKbQuery,
|
|
82
86
|
handleKbSearch,
|
|
83
87
|
handleKbStatus,
|
|
@@ -85,7 +89,9 @@ const DEFAULT_TOOLS_RUNTIME = {
|
|
|
85
89
|
handleKbSkillsLoad,
|
|
86
90
|
handleKbSkillsRead,
|
|
87
91
|
handleKbUpsert,
|
|
92
|
+
handleKbValidateUpsert,
|
|
88
93
|
handleKbModelRequirement,
|
|
94
|
+
handleKbSuggestPredicates,
|
|
89
95
|
handleKbAutopilotGenerate,
|
|
90
96
|
};
|
|
91
97
|
// implements REQ-008
|
|
@@ -391,10 +397,17 @@ runtime = DEFAULT_TOOLS_RUNTIME) {
|
|
|
391
397
|
const prolog = await runtime.ensureProlog();
|
|
392
398
|
return runtime.handleKbGraph(prolog, args);
|
|
393
399
|
}, runtime);
|
|
400
|
+
addTool(server, "kb_sparql_remote", toolDef("kb_sparql_remote").description, toolDef("kb_sparql_remote").inputSchema, async (args) => {
|
|
401
|
+
const prolog = await runtime.ensureProlog();
|
|
402
|
+
return runtime.handleSparql(prolog, args);
|
|
403
|
+
}, runtime);
|
|
394
404
|
addTool(server, "kb_upsert", toolDef("kb_upsert").description, toolDef("kb_upsert").inputSchema, async (args) => {
|
|
395
405
|
const prolog = await runtime.ensureProlog();
|
|
396
406
|
return runtime.handleKbUpsert(prolog, args);
|
|
397
407
|
}, runtime);
|
|
408
|
+
addTool(server, "kb_validate_upsert", toolDef("kb_validate_upsert").description, toolDef("kb_validate_upsert").inputSchema, async (args) => {
|
|
409
|
+
return runtime.handleKbValidateUpsert(args);
|
|
410
|
+
}, runtime);
|
|
398
411
|
addTool(server, "kb_delete", toolDef("kb_delete").description, toolDef("kb_delete").inputSchema, async (args) => {
|
|
399
412
|
const prolog = await runtime.ensureProlog();
|
|
400
413
|
return runtime.handleKbDelete(prolog, args);
|
|
@@ -407,6 +420,10 @@ runtime = DEFAULT_TOOLS_RUNTIME) {
|
|
|
407
420
|
const prolog = await runtime.ensureProlog();
|
|
408
421
|
return runtime.handleKbModelRequirement(prolog, args);
|
|
409
422
|
}, runtime);
|
|
423
|
+
addTool(server, "kb_suggest_predicates", toolDef("kb_suggest_predicates").description, toolDef("kb_suggest_predicates").inputSchema, async (args) => {
|
|
424
|
+
const prolog = await runtime.ensureProlog();
|
|
425
|
+
return runtime.handleKbSuggestPredicates(prolog, args);
|
|
426
|
+
}, runtime);
|
|
410
427
|
addTool(server, "kb_autopilot_generate", toolDef("kb_autopilot_generate").description, toolDef("kb_autopilot_generate").inputSchema, async (args) => {
|
|
411
428
|
const prolog = await runtime.ensureProlog();
|
|
412
429
|
return runtime.handleKbAutopilotGenerate(prolog, args);
|
|
@@ -847,7 +847,7 @@ export async function classifyActivationState(workspaceRoot, prolog) {
|
|
|
847
847
|
}
|
|
848
848
|
}
|
|
849
849
|
// Recursively collect markdown files under `dir`, excluding known ignore dirs.
|
|
850
|
-
function collectMarkdownFiles(dir, workspaceRoot, vendoredRoots) {
|
|
850
|
+
export function collectMarkdownFiles(dir, workspaceRoot, vendoredRoots) {
|
|
851
851
|
const results = [];
|
|
852
852
|
if (!fs.existsSync(dir))
|
|
853
853
|
return results;
|
|
@@ -6,6 +6,25 @@ import { buildGenericMarkdownCandidates, buildNormativeRequirementCandidates, bu
|
|
|
6
6
|
import { discoverProviderEvidence, resolveActivationPolicy, } from "./autopilot-discovery.js";
|
|
7
7
|
import { loadEntities } from "./entity-query.js";
|
|
8
8
|
import { getWorkspaceMigrationWarning } from "./model-requirement.js";
|
|
9
|
+
const defaultAutopilotGenerateDeps = {
|
|
10
|
+
buildGenericMarkdownCandidates,
|
|
11
|
+
buildNormativeRequirementCandidates,
|
|
12
|
+
buildProviderEvidenceCandidates,
|
|
13
|
+
buildSymbolManifestCandidates,
|
|
14
|
+
buildTypedMarkdownCandidates,
|
|
15
|
+
collectSourceOnlyAuthoringSignals,
|
|
16
|
+
discoverProviderEvidence,
|
|
17
|
+
getWorkspaceMigrationWarning,
|
|
18
|
+
loadEntities,
|
|
19
|
+
resolveActivationPolicy,
|
|
20
|
+
};
|
|
21
|
+
let autopilotGenerateDeps = defaultAutopilotGenerateDeps;
|
|
22
|
+
export function _setAutopilotGenerateDepsForTests(deps) {
|
|
23
|
+
autopilotGenerateDeps = { ...defaultAutopilotGenerateDeps, ...deps };
|
|
24
|
+
}
|
|
25
|
+
export function _resetAutopilotGenerateDepsForTests() {
|
|
26
|
+
autopilotGenerateDeps = defaultAutopilotGenerateDeps;
|
|
27
|
+
}
|
|
9
28
|
function clamp(value, min, max) {
|
|
10
29
|
return Math.max(min, Math.min(max, value));
|
|
11
30
|
}
|
|
@@ -377,7 +396,7 @@ _prolog, args) {
|
|
|
377
396
|
// Gather existing entity ids to suppress duplicates
|
|
378
397
|
let existingIds = new Set();
|
|
379
398
|
try {
|
|
380
|
-
const entities = await loadEntities(prolog, {});
|
|
399
|
+
const entities = await autopilotGenerateDeps.loadEntities(prolog, {});
|
|
381
400
|
for (const e of entities) {
|
|
382
401
|
const id = String(e.id ?? "");
|
|
383
402
|
if (id)
|
|
@@ -389,10 +408,10 @@ _prolog, args) {
|
|
|
389
408
|
existingIds = new Set();
|
|
390
409
|
}
|
|
391
410
|
const workspaceRoot = resolveWorkspaceRoot();
|
|
392
|
-
const activation = await resolveActivationPolicy(workspaceRoot, prolog);
|
|
411
|
+
const activation = await autopilotGenerateDeps.resolveActivationPolicy(workspaceRoot, prolog);
|
|
393
412
|
const activationState = activation.activationState;
|
|
394
|
-
const activationDiscovery = discoverProviderEvidence(workspaceRoot, activation);
|
|
395
|
-
const migrationWarning = await getWorkspaceMigrationWarning(workspaceRoot);
|
|
413
|
+
const activationDiscovery = autopilotGenerateDeps.discoverProviderEvidence(workspaceRoot, activation);
|
|
414
|
+
const migrationWarning = await autopilotGenerateDeps.getWorkspaceMigrationWarning(workspaceRoot);
|
|
396
415
|
const declaredContext = normalizeBootstrapContext(bootstrapContext);
|
|
397
416
|
const discoveredCandidatePaths = activationDiscovery.evidence.reduce((acc, item) => {
|
|
398
417
|
const relativePath = item.relativePath;
|
|
@@ -414,7 +433,7 @@ _prolog, args) {
|
|
|
414
433
|
markdownFiles: [],
|
|
415
434
|
evidence: candidateDiscovery.evidence.filter((item) => item.kind !== "generic_markdown"),
|
|
416
435
|
};
|
|
417
|
-
let sourceOnlySignals = collectSourceOnlyAuthoringSignals(guidanceDiscovery, {
|
|
436
|
+
let sourceOnlySignals = autopilotGenerateDeps.collectSourceOnlyAuthoringSignals(guidanceDiscovery, {
|
|
418
437
|
ids: existingIds,
|
|
419
438
|
workspaceRoot,
|
|
420
439
|
}, normalizedMinConfidence);
|
|
@@ -435,28 +454,31 @@ _prolog, args) {
|
|
|
435
454
|
return `${entityType}::${String(title).trim().toLowerCase().replace(/\s+/g, " ")}`;
|
|
436
455
|
}
|
|
437
456
|
if (activation.allowCandidateGeneration) {
|
|
438
|
-
typedMarkdownCandidates =
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
457
|
+
typedMarkdownCandidates =
|
|
458
|
+
autopilotGenerateDeps.buildTypedMarkdownCandidates(candidateDiscovery, {
|
|
459
|
+
ids: existingIds,
|
|
460
|
+
workspaceRoot,
|
|
461
|
+
});
|
|
462
|
+
manifestCandidates = autopilotGenerateDeps.buildSymbolManifestCandidates(candidateDiscovery, {
|
|
443
463
|
ids: existingIds,
|
|
444
464
|
workspaceRoot,
|
|
445
465
|
});
|
|
446
466
|
if (includeGenericMarkdown) {
|
|
447
|
-
genericCandidates = buildGenericMarkdownCandidates(candidateDiscovery, {
|
|
467
|
+
genericCandidates = autopilotGenerateDeps.buildGenericMarkdownCandidates(candidateDiscovery, {
|
|
448
468
|
ids: existingIds,
|
|
449
469
|
workspaceRoot,
|
|
450
470
|
}, normalizedMinConfidence);
|
|
451
|
-
normativeRequirementCandidates =
|
|
471
|
+
normativeRequirementCandidates =
|
|
472
|
+
autopilotGenerateDeps.buildNormativeRequirementCandidates(candidateDiscovery, {
|
|
473
|
+
ids: existingIds,
|
|
474
|
+
workspaceRoot,
|
|
475
|
+
}, normalizedMinConfidence);
|
|
476
|
+
}
|
|
477
|
+
providerEvidenceCandidates =
|
|
478
|
+
autopilotGenerateDeps.buildProviderEvidenceCandidates(candidateDiscovery, {
|
|
452
479
|
ids: existingIds,
|
|
453
480
|
workspaceRoot,
|
|
454
481
|
}, normalizedMinConfidence);
|
|
455
|
-
}
|
|
456
|
-
providerEvidenceCandidates = buildProviderEvidenceCandidates(candidateDiscovery, {
|
|
457
|
-
ids: existingIds,
|
|
458
|
-
workspaceRoot,
|
|
459
|
-
}, normalizedMinConfidence);
|
|
460
482
|
allCandidates = [
|
|
461
483
|
...typedMarkdownCandidates,
|
|
462
484
|
...manifestCandidates,
|
|
@@ -308,6 +308,15 @@ export async function handleKbModelRequirement(_prolog, args) {
|
|
|
308
308
|
});
|
|
309
309
|
const applyPlan = strictWriteSetToApplyPlan(writeSet);
|
|
310
310
|
const migrationWarning = await getWorkspaceMigrationWarning();
|
|
311
|
+
const warnings = writeSet.isStrict
|
|
312
|
+
? []
|
|
313
|
+
: [
|
|
314
|
+
{
|
|
315
|
+
kind: "low_confidence_observation_downgrade",
|
|
316
|
+
message: `Claim confidence ${writeSet.confidence.toFixed(2)} is below the strict threshold 0.70, so Kibi emitted an observation fact instead of strict subject/property facts.`,
|
|
317
|
+
nextAction: "If this is normative, provide subjectKey, propertyKey, operator, and value explicitly, then apply the returned strict write-set sequentially.",
|
|
318
|
+
},
|
|
319
|
+
];
|
|
311
320
|
const strictSummary = writeSet.isStrict
|
|
312
321
|
? `Modeled strict requirement into ${applyPlan.length} sequential applyPlan step(s).`
|
|
313
322
|
: "Modeled a non-blocking observation review artifact; deterministic claim extraction stayed below the strict threshold.";
|
|
@@ -322,6 +331,7 @@ export async function handleKbModelRequirement(_prolog, args) {
|
|
|
322
331
|
confidence: writeSet.confidence,
|
|
323
332
|
extractionMode: extracted.extractionMode,
|
|
324
333
|
extractionWarnings: extracted.extractionWarnings,
|
|
334
|
+
warnings,
|
|
325
335
|
migrationWarning,
|
|
326
336
|
};
|
|
327
337
|
return {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { runJsonModuleQuery, toPrologAtom } from "./core-module.js";
|
|
2
|
+
// implements REQ-002, REQ-013
|
|
3
|
+
export async function handleSparql(prolog, args) {
|
|
4
|
+
validateSparqlArgs(args);
|
|
5
|
+
try {
|
|
6
|
+
const payload = await runJsonModuleQuery(prolog, "sparql_client.pl", `kibi_sparql_client:remote_sparql_select_json(${toPrologAtom(args.endpoint)}, ${toPrologAtom(args.query)}, ${toSparqlOptions(args)}, JsonString)`, "SPARQL remote query");
|
|
7
|
+
const rows = payload?.rows ?? [];
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: "text",
|
|
12
|
+
text: `Remote SPARQL query returned ${rows.length} row${rows.length === 1 ? "" : "s"}.`,
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
...(payload !== undefined ? { structuredContent: payload } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20
|
+
throw new Error(`SPARQL remote query failed: ${message}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function validateSparqlArgs(args) {
|
|
24
|
+
if (!args.endpoint || args.endpoint.trim().length === 0) {
|
|
25
|
+
throw new Error("SPARQL endpoint is required");
|
|
26
|
+
}
|
|
27
|
+
if (!args.query || args.query.trim().length === 0) {
|
|
28
|
+
throw new Error("SPARQL query is required");
|
|
29
|
+
}
|
|
30
|
+
if (!isRemoteHttpEndpoint(args.endpoint)) {
|
|
31
|
+
throw new Error("SPARQL endpoint must be an http:// or https:// URL");
|
|
32
|
+
}
|
|
33
|
+
if (!isSelectQuery(args.query)) {
|
|
34
|
+
throw new Error("SPARQL query must be a SELECT query");
|
|
35
|
+
}
|
|
36
|
+
if (!isPublicRemoteEndpoint(args.endpoint)) {
|
|
37
|
+
throw new Error("SPARQL endpoint must target a public remote host");
|
|
38
|
+
}
|
|
39
|
+
if (args.timeoutMs !== undefined &&
|
|
40
|
+
(!Number.isFinite(args.timeoutMs) || args.timeoutMs <= 0)) {
|
|
41
|
+
throw new Error("SPARQL timeoutMs must be a positive number when provided");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isRemoteHttpEndpoint(endpoint) {
|
|
45
|
+
try {
|
|
46
|
+
const url = new URL(endpoint);
|
|
47
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isSelectQuery(query) {
|
|
54
|
+
return /^\s*select\b/i.test(query);
|
|
55
|
+
}
|
|
56
|
+
function isPublicRemoteEndpoint(endpoint) {
|
|
57
|
+
const url = new URL(endpoint);
|
|
58
|
+
const host = normalizeHostname(url.hostname);
|
|
59
|
+
return !isLocalOrPrivateHost(host);
|
|
60
|
+
}
|
|
61
|
+
function normalizeHostname(hostname) {
|
|
62
|
+
return hostname
|
|
63
|
+
.toLowerCase()
|
|
64
|
+
.replace(/^\[/, "")
|
|
65
|
+
.replace(/\]$/, "")
|
|
66
|
+
.replace(/\.$/, "");
|
|
67
|
+
}
|
|
68
|
+
function isLocalOrPrivateHost(host) {
|
|
69
|
+
if (host === "localhost" ||
|
|
70
|
+
host.endsWith(".localhost") ||
|
|
71
|
+
host === "0.0.0.0" ||
|
|
72
|
+
host === "::1" ||
|
|
73
|
+
(host.includes(":") &&
|
|
74
|
+
(host.startsWith("fe80:") ||
|
|
75
|
+
host.startsWith("fc") ||
|
|
76
|
+
host.startsWith("fd")))) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
const octets = host.split(".").map((part) => Number(part));
|
|
80
|
+
if (octets.length !== 4 || octets.some((octet) => !Number.isInteger(octet))) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const [first = -1, second = -1] = octets;
|
|
84
|
+
return (first === 10 ||
|
|
85
|
+
first === 127 ||
|
|
86
|
+
(first === 169 && second === 254) ||
|
|
87
|
+
(first === 172 && second >= 16 && second <= 31) ||
|
|
88
|
+
(first === 192 && second === 168));
|
|
89
|
+
}
|
|
90
|
+
function toSparqlOptions(args) {
|
|
91
|
+
if (args.timeoutMs === undefined) {
|
|
92
|
+
return "[]";
|
|
93
|
+
}
|
|
94
|
+
const timeoutSeconds = Math.max(1, Math.ceil(args.timeoutMs / 1000));
|
|
95
|
+
return `[timeout(${timeoutSeconds})]`;
|
|
96
|
+
}
|