@swarmvaultai/engine 0.6.5 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/chunk-HRRPWXRZ.js +1335 -0
- package/dist/index.js +425 -173
- package/dist/registry-NBLIJHZT.js +12 -0
- package/package.json +7 -8
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
firstSentences,
|
|
10
10
|
getProviderForTask,
|
|
11
11
|
initWorkspace,
|
|
12
|
+
isPathWithin,
|
|
12
13
|
listFilesRecursive,
|
|
13
14
|
loadVaultConfig,
|
|
14
15
|
normalizeWhitespace,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
uniqueBy,
|
|
22
23
|
writeFileIfChanged,
|
|
23
24
|
writeJsonFile
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HRRPWXRZ.js";
|
|
25
26
|
|
|
26
27
|
// src/agents.ts
|
|
27
28
|
import crypto from "crypto";
|
|
@@ -3869,8 +3870,31 @@ function interpreterFromShebang(content) {
|
|
|
3869
3870
|
}
|
|
3870
3871
|
return basename(parts[0] ?? "");
|
|
3871
3872
|
}
|
|
3872
|
-
function
|
|
3873
|
-
|
|
3873
|
+
function languageFromInterpreter(interpreter) {
|
|
3874
|
+
switch (interpreter) {
|
|
3875
|
+
case "sh":
|
|
3876
|
+
case "bash":
|
|
3877
|
+
case "zsh":
|
|
3878
|
+
case "dash":
|
|
3879
|
+
case "ksh":
|
|
3880
|
+
case "ash":
|
|
3881
|
+
return "bash";
|
|
3882
|
+
case "node":
|
|
3883
|
+
case "nodejs":
|
|
3884
|
+
return "javascript";
|
|
3885
|
+
case "python":
|
|
3886
|
+
case "python2":
|
|
3887
|
+
case "python3":
|
|
3888
|
+
return "python";
|
|
3889
|
+
case "ruby":
|
|
3890
|
+
return "ruby";
|
|
3891
|
+
case "php":
|
|
3892
|
+
return "php";
|
|
3893
|
+
case "lua":
|
|
3894
|
+
return "lua";
|
|
3895
|
+
default:
|
|
3896
|
+
return void 0;
|
|
3897
|
+
}
|
|
3874
3898
|
}
|
|
3875
3899
|
function formatDiagnosticCategory(category) {
|
|
3876
3900
|
switch (category) {
|
|
@@ -4472,8 +4496,11 @@ function inferCodeLanguage(filePath, mimeType = "", options = {}) {
|
|
|
4472
4496
|
if ([".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"].includes(extension)) {
|
|
4473
4497
|
return "cpp";
|
|
4474
4498
|
}
|
|
4475
|
-
if (!extension && options.executable
|
|
4476
|
-
|
|
4499
|
+
if (!extension && options.executable) {
|
|
4500
|
+
const fromShebang = languageFromInterpreter(interpreterFromShebang(options.content));
|
|
4501
|
+
if (fromShebang) {
|
|
4502
|
+
return fromShebang;
|
|
4503
|
+
}
|
|
4477
4504
|
}
|
|
4478
4505
|
return void 0;
|
|
4479
4506
|
}
|
|
@@ -6583,7 +6610,7 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
|
|
|
6583
6610
|
if (mimeType === "text/csv" || mimeType === "text/tab-separated-values" || filePath.toLowerCase().endsWith(".csv") || filePath.toLowerCase().endsWith(".tsv")) {
|
|
6584
6611
|
return "csv";
|
|
6585
6612
|
}
|
|
6586
|
-
if (mimeType.startsWith("text/")) {
|
|
6613
|
+
if (mimeType.startsWith("text/") || isStructuredTextMime(mimeType) || isKnownTextPath(filePath)) {
|
|
6587
6614
|
return "text";
|
|
6588
6615
|
}
|
|
6589
6616
|
if (mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || filePath.toLowerCase().endsWith(".xlsx")) {
|
|
@@ -6597,6 +6624,26 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
|
|
|
6597
6624
|
}
|
|
6598
6625
|
return "binary";
|
|
6599
6626
|
}
|
|
6627
|
+
function isStructuredTextMime(mimeType) {
|
|
6628
|
+
switch (mimeType) {
|
|
6629
|
+
case "application/json":
|
|
6630
|
+
case "application/json5":
|
|
6631
|
+
case "application/ld+json":
|
|
6632
|
+
case "application/manifest+json":
|
|
6633
|
+
case "application/xml":
|
|
6634
|
+
case "application/toml":
|
|
6635
|
+
case "application/yaml":
|
|
6636
|
+
case "application/x-yaml":
|
|
6637
|
+
case "application/javascript":
|
|
6638
|
+
case "application/ecmascript":
|
|
6639
|
+
case "application/typescript":
|
|
6640
|
+
case "application/x-sh":
|
|
6641
|
+
case "application/x-shellscript":
|
|
6642
|
+
return true;
|
|
6643
|
+
default:
|
|
6644
|
+
return false;
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6600
6647
|
async function localCodeDetectionOptions(absolutePath, payloadBytes) {
|
|
6601
6648
|
if (path12.extname(absolutePath)) {
|
|
6602
6649
|
return {};
|
|
@@ -6645,7 +6692,129 @@ function guessMimeType(target) {
|
|
|
6645
6692
|
if (isRstFilePath(target)) {
|
|
6646
6693
|
return "text/x-rst";
|
|
6647
6694
|
}
|
|
6648
|
-
|
|
6695
|
+
const extension = path12.extname(target).toLowerCase();
|
|
6696
|
+
if (extension === ".ts" || extension === ".tsx" || extension === ".mts" || extension === ".cts") {
|
|
6697
|
+
return "text/typescript";
|
|
6698
|
+
}
|
|
6699
|
+
const looked = mime.lookup(target);
|
|
6700
|
+
if (looked) {
|
|
6701
|
+
return looked;
|
|
6702
|
+
}
|
|
6703
|
+
if (isKnownTextPath(target)) {
|
|
6704
|
+
return "text/plain";
|
|
6705
|
+
}
|
|
6706
|
+
return "application/octet-stream";
|
|
6707
|
+
}
|
|
6708
|
+
var KNOWN_TEXT_DOTFILE_NAMES = /* @__PURE__ */ new Set([
|
|
6709
|
+
".gitignore",
|
|
6710
|
+
".gitattributes",
|
|
6711
|
+
".gitkeep",
|
|
6712
|
+
".gitmodules",
|
|
6713
|
+
".editorconfig",
|
|
6714
|
+
".npmrc",
|
|
6715
|
+
".yarnrc",
|
|
6716
|
+
".prettierignore",
|
|
6717
|
+
".prettierrc",
|
|
6718
|
+
".dockerignore",
|
|
6719
|
+
".eslintignore",
|
|
6720
|
+
".eslintrc",
|
|
6721
|
+
".nvmrc",
|
|
6722
|
+
".node-version",
|
|
6723
|
+
".python-version",
|
|
6724
|
+
".ruby-version",
|
|
6725
|
+
".tool-versions"
|
|
6726
|
+
]);
|
|
6727
|
+
var KNOWN_TEXT_BASENAMES = /* @__PURE__ */ new Set([
|
|
6728
|
+
"readme",
|
|
6729
|
+
"license",
|
|
6730
|
+
"licence",
|
|
6731
|
+
"copying",
|
|
6732
|
+
"unlicense",
|
|
6733
|
+
"notice",
|
|
6734
|
+
"authors",
|
|
6735
|
+
"contributors",
|
|
6736
|
+
"patents",
|
|
6737
|
+
"maintainers",
|
|
6738
|
+
"owners",
|
|
6739
|
+
"codeowners",
|
|
6740
|
+
"changelog",
|
|
6741
|
+
"changes",
|
|
6742
|
+
"history",
|
|
6743
|
+
"news",
|
|
6744
|
+
"todo",
|
|
6745
|
+
"install",
|
|
6746
|
+
"dockerfile",
|
|
6747
|
+
"containerfile",
|
|
6748
|
+
"makefile",
|
|
6749
|
+
"gnumakefile",
|
|
6750
|
+
"rakefile",
|
|
6751
|
+
"gemfile",
|
|
6752
|
+
"procfile",
|
|
6753
|
+
"jenkinsfile",
|
|
6754
|
+
"vagrantfile",
|
|
6755
|
+
"brewfile",
|
|
6756
|
+
"go.mod",
|
|
6757
|
+
"go.sum",
|
|
6758
|
+
"go.work",
|
|
6759
|
+
"go.work.sum",
|
|
6760
|
+
"cargo.lock",
|
|
6761
|
+
"pipfile",
|
|
6762
|
+
"pipfile.lock",
|
|
6763
|
+
"poetry.lock",
|
|
6764
|
+
"uv.lock",
|
|
6765
|
+
"py.typed",
|
|
6766
|
+
"package-lock.json",
|
|
6767
|
+
"yarn.lock",
|
|
6768
|
+
"pnpm-lock.yaml",
|
|
6769
|
+
"composer.lock",
|
|
6770
|
+
"requirements.txt"
|
|
6771
|
+
]);
|
|
6772
|
+
var KNOWN_TEXT_BASENAME_PREFIXES = ["license", "licence", "copying", "unlicense", "readme", "changelog", "dockerfile", "containerfile"];
|
|
6773
|
+
var KNOWN_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
6774
|
+
".toml",
|
|
6775
|
+
".lock",
|
|
6776
|
+
".tmpl",
|
|
6777
|
+
".template",
|
|
6778
|
+
".mustache",
|
|
6779
|
+
".hbs",
|
|
6780
|
+
".handlebars",
|
|
6781
|
+
".ejs",
|
|
6782
|
+
".njk",
|
|
6783
|
+
".liquid",
|
|
6784
|
+
".vim",
|
|
6785
|
+
".typed",
|
|
6786
|
+
".env",
|
|
6787
|
+
".properties",
|
|
6788
|
+
".ini",
|
|
6789
|
+
".cfg",
|
|
6790
|
+
".conf",
|
|
6791
|
+
".config",
|
|
6792
|
+
".bazel",
|
|
6793
|
+
".bzl",
|
|
6794
|
+
".bat",
|
|
6795
|
+
".cmd"
|
|
6796
|
+
]);
|
|
6797
|
+
function isKnownTextPath(target) {
|
|
6798
|
+
const basename = path12.basename(target).toLowerCase();
|
|
6799
|
+
if (KNOWN_TEXT_DOTFILE_NAMES.has(basename)) {
|
|
6800
|
+
return true;
|
|
6801
|
+
}
|
|
6802
|
+
if (basename === ".env" || basename.startsWith(".env.")) {
|
|
6803
|
+
return true;
|
|
6804
|
+
}
|
|
6805
|
+
if (KNOWN_TEXT_BASENAMES.has(basename)) {
|
|
6806
|
+
return true;
|
|
6807
|
+
}
|
|
6808
|
+
for (const prefix of KNOWN_TEXT_BASENAME_PREFIXES) {
|
|
6809
|
+
if (basename === prefix || basename.startsWith(`${prefix}-`) || basename.startsWith(`${prefix}.`)) {
|
|
6810
|
+
return true;
|
|
6811
|
+
}
|
|
6812
|
+
}
|
|
6813
|
+
const extension = path12.extname(target).toLowerCase();
|
|
6814
|
+
if (extension && KNOWN_TEXT_EXTENSIONS.has(extension)) {
|
|
6815
|
+
return true;
|
|
6816
|
+
}
|
|
6817
|
+
return false;
|
|
6649
6818
|
}
|
|
6650
6819
|
function sourceGroupIdFor(prepared) {
|
|
6651
6820
|
const originKey = prepared.originType === "url" ? prepared.url ?? prepared.title : prepared.originalPath ?? prepared.title;
|
|
@@ -8564,7 +8733,7 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
8564
8733
|
const sourceRefs = [];
|
|
8565
8734
|
for (const ref of refs) {
|
|
8566
8735
|
const resolved = path12.resolve(path12.dirname(absolutePath), ref);
|
|
8567
|
-
if (!
|
|
8736
|
+
if (!isPathWithin(inputDir, resolved) || !await fileExists(resolved)) {
|
|
8568
8737
|
continue;
|
|
8569
8738
|
}
|
|
8570
8739
|
sourceRefs.push({
|
|
@@ -13592,7 +13761,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
13592
13761
|
if (!providerConfig) {
|
|
13593
13762
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
13594
13763
|
}
|
|
13595
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
13764
|
+
const { createProvider: createProvider2 } = await import("./registry-NBLIJHZT.js");
|
|
13596
13765
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
13597
13766
|
}
|
|
13598
13767
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -17631,9 +17800,16 @@ async function listPages(rootDir) {
|
|
|
17631
17800
|
return graph?.pages ?? [];
|
|
17632
17801
|
}
|
|
17633
17802
|
async function readPage(rootDir, relativePath) {
|
|
17803
|
+
if (!relativePath) {
|
|
17804
|
+
return null;
|
|
17805
|
+
}
|
|
17634
17806
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17635
17807
|
const absolutePath = path23.resolve(paths.wikiDir, relativePath);
|
|
17636
|
-
if (!
|
|
17808
|
+
if (!isPathWithin(paths.wikiDir, absolutePath)) {
|
|
17809
|
+
return null;
|
|
17810
|
+
}
|
|
17811
|
+
const stats = await fs19.stat(absolutePath).catch(() => null);
|
|
17812
|
+
if (!stats?.isFile()) {
|
|
17637
17813
|
return null;
|
|
17638
17814
|
}
|
|
17639
17815
|
const raw = await fs19.readFile(absolutePath, "utf8");
|
|
@@ -17662,6 +17838,28 @@ async function getWorkspaceInfo(rootDir) {
|
|
|
17662
17838
|
pageCount: pages.length
|
|
17663
17839
|
};
|
|
17664
17840
|
}
|
|
17841
|
+
function extractClaimSectionLines(content) {
|
|
17842
|
+
const lines = content.split("\n");
|
|
17843
|
+
let inClaims = false;
|
|
17844
|
+
let found = false;
|
|
17845
|
+
const claimLines = [];
|
|
17846
|
+
for (const line of lines) {
|
|
17847
|
+
const trimmed = line.trimEnd();
|
|
17848
|
+
if (trimmed === "## Claims") {
|
|
17849
|
+
inClaims = true;
|
|
17850
|
+
found = true;
|
|
17851
|
+
continue;
|
|
17852
|
+
}
|
|
17853
|
+
if (inClaims) {
|
|
17854
|
+
if (/^#{1,2}\s/.test(trimmed)) {
|
|
17855
|
+
inClaims = false;
|
|
17856
|
+
continue;
|
|
17857
|
+
}
|
|
17858
|
+
claimLines.push(line);
|
|
17859
|
+
}
|
|
17860
|
+
}
|
|
17861
|
+
return found ? claimLines : null;
|
|
17862
|
+
}
|
|
17665
17863
|
function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sourceProjects) {
|
|
17666
17864
|
const manifestMap = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
17667
17865
|
const pageMap2 = new Map(graph.pages.map((page) => [page.id, page]));
|
|
@@ -17707,8 +17905,9 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
17707
17905
|
const absolutePath = path23.join(paths.wikiDir, page.path);
|
|
17708
17906
|
if (await fileExists(absolutePath)) {
|
|
17709
17907
|
const content = await fs19.readFile(absolutePath, "utf8");
|
|
17710
|
-
|
|
17711
|
-
|
|
17908
|
+
const claimLines = extractClaimSectionLines(content);
|
|
17909
|
+
if (claimLines !== null) {
|
|
17910
|
+
const uncited = claimLines.filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
17712
17911
|
if (uncited.length) {
|
|
17713
17912
|
findings.push({
|
|
17714
17913
|
severity: "warning",
|
|
@@ -17823,7 +18022,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
17823
18022
|
}
|
|
17824
18023
|
|
|
17825
18024
|
// src/mcp.ts
|
|
17826
|
-
var SERVER_VERSION = "0.6.
|
|
18025
|
+
var SERVER_VERSION = "0.6.6";
|
|
17827
18026
|
async function createMcpServer(rootDir) {
|
|
17828
18027
|
const server = new McpServer({
|
|
17829
18028
|
name: "swarmvault",
|
|
@@ -18158,7 +18357,7 @@ async function createMcpServer(rootDir) {
|
|
|
18158
18357
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
18159
18358
|
const relativePath = decodeURIComponent(encodedPath);
|
|
18160
18359
|
const absolutePath = path24.resolve(paths.sessionsDir, relativePath);
|
|
18161
|
-
if (!
|
|
18360
|
+
if (!isPathWithin(paths.sessionsDir, absolutePath) || !await fileExists(absolutePath)) {
|
|
18162
18361
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
18163
18362
|
}
|
|
18164
18363
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs20.readFile(absolutePath, "utf8"));
|
|
@@ -18497,6 +18696,15 @@ var DOCS_HINT_SEGMENTS = /* @__PURE__ */ new Set([
|
|
|
18497
18696
|
function uniqueStrings4(values) {
|
|
18498
18697
|
return uniqueBy(values.filter(Boolean), (value) => value);
|
|
18499
18698
|
}
|
|
18699
|
+
function sourceOutputSchemaHash(schemas, projectIds) {
|
|
18700
|
+
if (!projectIds.length) {
|
|
18701
|
+
return schemas.effective.global.hash;
|
|
18702
|
+
}
|
|
18703
|
+
return composeVaultSchema(
|
|
18704
|
+
schemas.root,
|
|
18705
|
+
uniqueStrings4([...projectIds].sort((left, right) => left.localeCompare(right))).map((projectId) => schemas.projects[projectId]).filter((schema) => Boolean(schema?.hash))
|
|
18706
|
+
).hash;
|
|
18707
|
+
}
|
|
18500
18708
|
function normalizeManagedStatus(value) {
|
|
18501
18709
|
return value === "missing" || value === "error" ? value : "ready";
|
|
18502
18710
|
}
|
|
@@ -19080,6 +19288,7 @@ async function writeSourceBriefForScope(rootDir, source) {
|
|
|
19080
19288
|
return null;
|
|
19081
19289
|
}
|
|
19082
19290
|
const graph = await readJsonFile(paths.graphPath);
|
|
19291
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19083
19292
|
const relatedPages = graph ? scopedSourcePages(graph, source.sourceIds) : [];
|
|
19084
19293
|
const relatedPageIds = relatedPages.slice(0, 12).map((page) => page.id);
|
|
19085
19294
|
const relatedNodeIds = graph ? scopedNodeIds(graph, source.sourceIds).slice(0, 20) : [];
|
|
@@ -19090,7 +19299,7 @@ async function writeSourceBriefForScope(rootDir, source) {
|
|
|
19090
19299
|
question: `Brief ${source.title}`,
|
|
19091
19300
|
answer: markdown,
|
|
19092
19301
|
citations: source.sourceIds,
|
|
19093
|
-
schemaHash:
|
|
19302
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19094
19303
|
outputFormat: "report",
|
|
19095
19304
|
relatedPageIds,
|
|
19096
19305
|
relatedNodeIds,
|
|
@@ -19325,6 +19534,7 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
|
|
|
19325
19534
|
throw new Error(`Could not generate a source review for ${scope.id}.`);
|
|
19326
19535
|
}
|
|
19327
19536
|
const graph = await readJsonFile(paths.graphPath);
|
|
19537
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19328
19538
|
const scopeManifests = manifestsForScope(graph, scope);
|
|
19329
19539
|
const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
19330
19540
|
const relatedPageIds = relatedPages.slice(0, 16).map((page) => page.id);
|
|
@@ -19336,7 +19546,7 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
|
|
|
19336
19546
|
question: `Review ${scope.title}`,
|
|
19337
19547
|
answer: markdown,
|
|
19338
19548
|
citations: scope.sourceIds,
|
|
19339
|
-
schemaHash:
|
|
19549
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19340
19550
|
outputFormat: "report",
|
|
19341
19551
|
relatedPageIds,
|
|
19342
19552
|
relatedNodeIds,
|
|
@@ -19570,6 +19780,7 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
|
|
|
19570
19780
|
throw new Error(`Could not generate a source guide for ${scope.id}.`);
|
|
19571
19781
|
}
|
|
19572
19782
|
const graph = await readJsonFile(paths.graphPath);
|
|
19783
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19573
19784
|
const scopeManifests = manifestsForScope(graph, scope);
|
|
19574
19785
|
const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
19575
19786
|
const contradictions = findContradictionsForScope(scope, await readGraphReport(rootDir));
|
|
@@ -19583,7 +19794,7 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
|
|
|
19583
19794
|
question: `Guide ${scope.title}`,
|
|
19584
19795
|
answer: markdown,
|
|
19585
19796
|
citations: scope.sourceIds,
|
|
19586
|
-
schemaHash:
|
|
19797
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19587
19798
|
outputFormat: "report",
|
|
19588
19799
|
relatedPageIds,
|
|
19589
19800
|
relatedNodeIds,
|
|
@@ -19672,6 +19883,7 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
|
|
|
19672
19883
|
await compileVault(rootDir, {});
|
|
19673
19884
|
graph = await readJsonFile(paths.graphPath);
|
|
19674
19885
|
}
|
|
19886
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19675
19887
|
const scopeManifests = manifestsForScope(graph, scope);
|
|
19676
19888
|
const sourcePages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
19677
19889
|
const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
|
|
@@ -19737,7 +19949,7 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
|
|
|
19737
19949
|
question: `Guided Session ${scope.title}`,
|
|
19738
19950
|
answer: sessionMarkdown,
|
|
19739
19951
|
citations: scope.sourceIds,
|
|
19740
|
-
schemaHash:
|
|
19952
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19741
19953
|
outputFormat: "report",
|
|
19742
19954
|
relatedPageIds,
|
|
19743
19955
|
relatedNodeIds,
|
|
@@ -20782,10 +20994,21 @@ async function getWatchStatus(rootDir) {
|
|
|
20782
20994
|
|
|
20783
20995
|
// src/viewer.ts
|
|
20784
20996
|
var execFileAsync = promisify(execFile);
|
|
20997
|
+
async function isReadableFile(absolutePath) {
|
|
20998
|
+
try {
|
|
20999
|
+
const stats = await fs23.stat(absolutePath);
|
|
21000
|
+
return stats.isFile();
|
|
21001
|
+
} catch {
|
|
21002
|
+
return false;
|
|
21003
|
+
}
|
|
21004
|
+
}
|
|
20785
21005
|
async function readViewerPage(rootDir, relativePath) {
|
|
21006
|
+
if (!relativePath) {
|
|
21007
|
+
return null;
|
|
21008
|
+
}
|
|
20786
21009
|
const { paths } = await loadVaultConfig(rootDir);
|
|
20787
21010
|
const absolutePath = path28.resolve(paths.wikiDir, relativePath);
|
|
20788
|
-
if (!
|
|
21011
|
+
if (!isPathWithin(paths.wikiDir, absolutePath) || !await isReadableFile(absolutePath)) {
|
|
20789
21012
|
return null;
|
|
20790
21013
|
}
|
|
20791
21014
|
const raw = await fs23.readFile(absolutePath, "utf8");
|
|
@@ -20799,9 +21022,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
20799
21022
|
};
|
|
20800
21023
|
}
|
|
20801
21024
|
async function readViewerAsset(rootDir, relativePath) {
|
|
21025
|
+
if (!relativePath) {
|
|
21026
|
+
return null;
|
|
21027
|
+
}
|
|
20802
21028
|
const { paths } = await loadVaultConfig(rootDir);
|
|
20803
21029
|
const absolutePath = path28.resolve(paths.wikiDir, relativePath);
|
|
20804
|
-
if (!
|
|
21030
|
+
if (!isPathWithin(paths.wikiDir, absolutePath) || !await isReadableFile(absolutePath)) {
|
|
20805
21031
|
return null;
|
|
20806
21032
|
}
|
|
20807
21033
|
return {
|
|
@@ -20843,178 +21069,204 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
20843
21069
|
await ensureViewerDist(paths.viewerDistDir);
|
|
20844
21070
|
const server = http.createServer(async (request, response) => {
|
|
20845
21071
|
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? `localhost:${effectivePort}`}`);
|
|
20846
|
-
|
|
20847
|
-
if (
|
|
20848
|
-
|
|
20849
|
-
|
|
21072
|
+
try {
|
|
21073
|
+
if (url.pathname === "/api/graph") {
|
|
21074
|
+
if (!await fileExists(paths.graphPath)) {
|
|
21075
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21076
|
+
response.end(JSON.stringify({ error: "Graph artifact not found. Run `swarmvault compile` first." }));
|
|
21077
|
+
return;
|
|
21078
|
+
}
|
|
21079
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21080
|
+
if (!graph) {
|
|
21081
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21082
|
+
response.end(JSON.stringify({ error: "Graph artifact not found. Run `swarmvault compile` first." }));
|
|
21083
|
+
return;
|
|
21084
|
+
}
|
|
21085
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
21086
|
+
const report = await readJsonFile(reportPath) ?? null;
|
|
21087
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21088
|
+
response.end(JSON.stringify(buildViewerGraphArtifact(graph, { report, full: options.full ?? false })));
|
|
20850
21089
|
return;
|
|
20851
21090
|
}
|
|
20852
|
-
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
21091
|
+
if (url.pathname === "/api/graph/query") {
|
|
21092
|
+
const question = url.searchParams.get("q") ?? "";
|
|
21093
|
+
const traversal = url.searchParams.get("traversal");
|
|
21094
|
+
const budget = Number.parseInt(url.searchParams.get("budget") ?? "12", 10);
|
|
21095
|
+
const result = await queryGraphVault(rootDir, question, {
|
|
21096
|
+
traversal: traversal === "dfs" ? "dfs" : "bfs",
|
|
21097
|
+
budget: Number.isFinite(budget) ? budget : 12
|
|
21098
|
+
});
|
|
21099
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21100
|
+
response.end(JSON.stringify(result));
|
|
20856
21101
|
return;
|
|
20857
21102
|
}
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
if (url.pathname === "/api/graph/query") {
|
|
20865
|
-
const question = url.searchParams.get("q") ?? "";
|
|
20866
|
-
const traversal = url.searchParams.get("traversal");
|
|
20867
|
-
const budget = Number.parseInt(url.searchParams.get("budget") ?? "12", 10);
|
|
20868
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20869
|
-
response.end(
|
|
20870
|
-
JSON.stringify(
|
|
20871
|
-
await queryGraphVault(rootDir, question, {
|
|
20872
|
-
traversal: traversal === "dfs" ? "dfs" : "bfs",
|
|
20873
|
-
budget: Number.isFinite(budget) ? budget : 12
|
|
20874
|
-
})
|
|
20875
|
-
)
|
|
20876
|
-
);
|
|
20877
|
-
return;
|
|
20878
|
-
}
|
|
20879
|
-
if (url.pathname === "/api/graph/path") {
|
|
20880
|
-
const from = url.searchParams.get("from") ?? "";
|
|
20881
|
-
const to = url.searchParams.get("to") ?? "";
|
|
20882
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20883
|
-
response.end(JSON.stringify(await pathGraphVault(rootDir, from, to)));
|
|
20884
|
-
return;
|
|
20885
|
-
}
|
|
20886
|
-
if (url.pathname === "/api/graph/explain") {
|
|
20887
|
-
const target2 = url.searchParams.get("target") ?? "";
|
|
20888
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20889
|
-
response.end(JSON.stringify(await explainGraphVault(rootDir, target2)));
|
|
20890
|
-
return;
|
|
20891
|
-
}
|
|
20892
|
-
if (url.pathname === "/api/search") {
|
|
20893
|
-
if (!await fileExists(paths.searchDbPath)) {
|
|
20894
|
-
response.writeHead(404, { "content-type": "application/json" });
|
|
20895
|
-
response.end(JSON.stringify({ error: "Search index not found. Run `swarmvault compile` first." }));
|
|
21103
|
+
if (url.pathname === "/api/graph/path") {
|
|
21104
|
+
const from = url.searchParams.get("from") ?? "";
|
|
21105
|
+
const to = url.searchParams.get("to") ?? "";
|
|
21106
|
+
const result = await pathGraphVault(rootDir, from, to);
|
|
21107
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21108
|
+
response.end(JSON.stringify(result));
|
|
20896
21109
|
return;
|
|
20897
21110
|
}
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
|
|
20909
|
-
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20914
|
-
response.end(JSON.stringify(results));
|
|
20915
|
-
return;
|
|
20916
|
-
}
|
|
20917
|
-
if (url.pathname === "/api/graph-report") {
|
|
20918
|
-
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
20919
|
-
if (!await fileExists(reportPath)) {
|
|
20920
|
-
response.writeHead(404, { "content-type": "application/json" });
|
|
20921
|
-
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
21111
|
+
if (url.pathname === "/api/graph/explain") {
|
|
21112
|
+
const target2 = url.searchParams.get("target") ?? "";
|
|
21113
|
+
if (!target2) {
|
|
21114
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21115
|
+
response.end(JSON.stringify({ error: "Missing explain target." }));
|
|
21116
|
+
return;
|
|
21117
|
+
}
|
|
21118
|
+
try {
|
|
21119
|
+
const result = await explainGraphVault(rootDir, target2);
|
|
21120
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21121
|
+
response.end(JSON.stringify(result));
|
|
21122
|
+
} catch (error) {
|
|
21123
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21124
|
+
response.end(JSON.stringify({ error: error instanceof Error ? error.message : `Could not resolve graph target: ${target2}` }));
|
|
21125
|
+
}
|
|
20922
21126
|
return;
|
|
20923
21127
|
}
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
21128
|
+
if (url.pathname === "/api/search") {
|
|
21129
|
+
if (!await fileExists(paths.searchDbPath)) {
|
|
21130
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21131
|
+
response.end(JSON.stringify({ error: "Search index not found. Run `swarmvault compile` first." }));
|
|
21132
|
+
return;
|
|
21133
|
+
}
|
|
21134
|
+
const query = url.searchParams.get("q") ?? "";
|
|
21135
|
+
const limit = Number.parseInt(url.searchParams.get("limit") ?? "10", 10);
|
|
21136
|
+
const kind = url.searchParams.get("kind") ?? "all";
|
|
21137
|
+
const status = url.searchParams.get("status") ?? "all";
|
|
21138
|
+
const project = url.searchParams.get("project") ?? "all";
|
|
21139
|
+
const sourceType = url.searchParams.get("sourceType") ?? "all";
|
|
21140
|
+
const sourceClass = url.searchParams.get("sourceClass") ?? "all";
|
|
21141
|
+
const results = searchPages(paths.searchDbPath, query, {
|
|
21142
|
+
limit: Number.isFinite(limit) ? limit : 10,
|
|
21143
|
+
kind,
|
|
21144
|
+
status,
|
|
21145
|
+
project,
|
|
21146
|
+
sourceType,
|
|
21147
|
+
sourceClass
|
|
21148
|
+
});
|
|
21149
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21150
|
+
response.end(JSON.stringify(results));
|
|
20939
21151
|
return;
|
|
20940
21152
|
}
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
response.writeHead(
|
|
20950
|
-
response.end(
|
|
21153
|
+
if (url.pathname === "/api/graph-report") {
|
|
21154
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
21155
|
+
if (!await fileExists(reportPath)) {
|
|
21156
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21157
|
+
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
21158
|
+
return;
|
|
21159
|
+
}
|
|
21160
|
+
const body = await fs23.readFile(reportPath, "utf8");
|
|
21161
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21162
|
+
response.end(body);
|
|
20951
21163
|
return;
|
|
20952
21164
|
}
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
if (url.pathname === "/api/reviews" && request.method === "GET") {
|
|
20958
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20959
|
-
response.end(JSON.stringify(await listApprovals(rootDir)));
|
|
20960
|
-
return;
|
|
20961
|
-
}
|
|
20962
|
-
if (url.pathname === "/api/review" && request.method === "GET") {
|
|
20963
|
-
const approvalId = url.searchParams.get("id") ?? "";
|
|
20964
|
-
if (!approvalId) {
|
|
20965
|
-
response.writeHead(400, { "content-type": "application/json" });
|
|
20966
|
-
response.end(JSON.stringify({ error: "Missing approval id." }));
|
|
21165
|
+
if (url.pathname === "/api/watch-status") {
|
|
21166
|
+
const watchStatus = await getWatchStatus(rootDir);
|
|
21167
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21168
|
+
response.end(JSON.stringify(watchStatus));
|
|
20967
21169
|
return;
|
|
20968
21170
|
}
|
|
20969
|
-
|
|
20970
|
-
|
|
20971
|
-
|
|
20972
|
-
|
|
20973
|
-
|
|
20974
|
-
|
|
20975
|
-
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
response.writeHead(400, { "content-type": "application/json" });
|
|
20980
|
-
response.end(JSON.stringify({ error: "Missing approval id or invalid review action." }));
|
|
21171
|
+
if (url.pathname === "/api/page") {
|
|
21172
|
+
const relativePath2 = url.searchParams.get("path") ?? "";
|
|
21173
|
+
const page = await readViewerPage(rootDir, relativePath2);
|
|
21174
|
+
if (!page) {
|
|
21175
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21176
|
+
response.end(JSON.stringify({ error: `Page not found: ${relativePath2}` }));
|
|
21177
|
+
return;
|
|
21178
|
+
}
|
|
21179
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21180
|
+
response.end(JSON.stringify(page));
|
|
20981
21181
|
return;
|
|
20982
21182
|
}
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20993
|
-
if (url.pathname === "/api/candidate" && request.method === "POST") {
|
|
20994
|
-
const body = await readJsonBody(request);
|
|
20995
|
-
const target2 = typeof body.target === "string" ? body.target : "";
|
|
20996
|
-
const action = url.searchParams.get("action") ?? "";
|
|
20997
|
-
if (!target2 || action !== "promote" && action !== "archive") {
|
|
20998
|
-
response.writeHead(400, { "content-type": "application/json" });
|
|
20999
|
-
response.end(JSON.stringify({ error: "Missing candidate target or invalid candidate action." }));
|
|
21183
|
+
if (url.pathname === "/api/asset") {
|
|
21184
|
+
const relativePath2 = url.searchParams.get("path") ?? "";
|
|
21185
|
+
const asset = await readViewerAsset(rootDir, relativePath2);
|
|
21186
|
+
if (!asset) {
|
|
21187
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21188
|
+
response.end(JSON.stringify({ error: `Asset not found: ${relativePath2}` }));
|
|
21189
|
+
return;
|
|
21190
|
+
}
|
|
21191
|
+
response.writeHead(200, { "content-type": asset.mimeType });
|
|
21192
|
+
response.end(asset.buffer);
|
|
21000
21193
|
return;
|
|
21001
21194
|
}
|
|
21002
|
-
|
|
21003
|
-
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21195
|
+
if (url.pathname === "/api/reviews" && request.method === "GET") {
|
|
21196
|
+
const approvals = await listApprovals(rootDir);
|
|
21197
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21198
|
+
response.end(JSON.stringify(approvals));
|
|
21199
|
+
return;
|
|
21200
|
+
}
|
|
21201
|
+
if (url.pathname === "/api/review" && request.method === "GET") {
|
|
21202
|
+
const approvalId = url.searchParams.get("id") ?? "";
|
|
21203
|
+
if (!approvalId) {
|
|
21204
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21205
|
+
response.end(JSON.stringify({ error: "Missing approval id." }));
|
|
21206
|
+
return;
|
|
21207
|
+
}
|
|
21208
|
+
const approval = await readApproval(rootDir, approvalId);
|
|
21209
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21210
|
+
response.end(JSON.stringify(approval));
|
|
21211
|
+
return;
|
|
21212
|
+
}
|
|
21213
|
+
if (url.pathname === "/api/review" && request.method === "POST") {
|
|
21214
|
+
const body = await readJsonBody(request);
|
|
21215
|
+
const approvalId = typeof body.approvalId === "string" ? body.approvalId : "";
|
|
21216
|
+
const targets = Array.isArray(body.targets) ? body.targets.filter((item) => typeof item === "string") : [];
|
|
21217
|
+
const action = url.searchParams.get("action") ?? "";
|
|
21218
|
+
if (!approvalId || action !== "accept" && action !== "reject") {
|
|
21219
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21220
|
+
response.end(JSON.stringify({ error: "Missing approval id or invalid review action." }));
|
|
21221
|
+
return;
|
|
21222
|
+
}
|
|
21223
|
+
const result = action === "accept" ? await acceptApproval(rootDir, approvalId, targets) : await rejectApproval(rootDir, approvalId, targets);
|
|
21224
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21225
|
+
response.end(JSON.stringify(result));
|
|
21226
|
+
return;
|
|
21227
|
+
}
|
|
21228
|
+
if (url.pathname === "/api/candidates" && request.method === "GET") {
|
|
21229
|
+
const candidates = await listCandidates(rootDir);
|
|
21230
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21231
|
+
response.end(JSON.stringify(candidates));
|
|
21232
|
+
return;
|
|
21233
|
+
}
|
|
21234
|
+
if (url.pathname === "/api/candidate" && request.method === "POST") {
|
|
21235
|
+
const body = await readJsonBody(request);
|
|
21236
|
+
const target2 = typeof body.target === "string" ? body.target : "";
|
|
21237
|
+
const action = url.searchParams.get("action") ?? "";
|
|
21238
|
+
if (!target2 || action !== "promote" && action !== "archive") {
|
|
21239
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21240
|
+
response.end(JSON.stringify({ error: "Missing candidate target or invalid candidate action." }));
|
|
21241
|
+
return;
|
|
21242
|
+
}
|
|
21243
|
+
const result = action === "promote" ? await promoteCandidate(rootDir, target2) : await archiveCandidate(rootDir, target2);
|
|
21244
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21245
|
+
response.end(JSON.stringify(result));
|
|
21246
|
+
return;
|
|
21247
|
+
}
|
|
21248
|
+
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
21249
|
+
const target = path28.join(paths.viewerDistDir, relativePath);
|
|
21250
|
+
const fallback = path28.join(paths.viewerDistDir, "index.html");
|
|
21251
|
+
const filePath = await fileExists(target) ? target : fallback;
|
|
21252
|
+
if (!await fileExists(filePath)) {
|
|
21253
|
+
response.writeHead(503, { "content-type": "text/plain" });
|
|
21254
|
+
response.end("Viewer build not found. Run `pnpm build` first.");
|
|
21255
|
+
return;
|
|
21256
|
+
}
|
|
21257
|
+
const staticBody = await fs23.readFile(filePath);
|
|
21258
|
+
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
21259
|
+
response.end(staticBody);
|
|
21260
|
+
} catch (error) {
|
|
21261
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21262
|
+
console.error(`[viewer] ${request.method ?? "GET"} ${url.pathname} failed: ${message}`);
|
|
21263
|
+
if (!response.headersSent) {
|
|
21264
|
+
response.writeHead(500, { "content-type": "application/json" });
|
|
21265
|
+
response.end(JSON.stringify({ error: message }));
|
|
21266
|
+
} else {
|
|
21267
|
+
response.end();
|
|
21268
|
+
}
|
|
21015
21269
|
}
|
|
21016
|
-
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
21017
|
-
response.end(await fs23.readFile(filePath));
|
|
21018
21270
|
});
|
|
21019
21271
|
await new Promise((resolve) => {
|
|
21020
21272
|
server.listen(effectivePort, resolve);
|