@schift-io/knowledge-scope 0.1.0 → 0.2.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/README.md +112 -43
- package/dist/context-pack.js +13 -0
- package/dist/index.js +550 -101
- package/dist/main.js +520 -75
- package/dist/types/authorization.d.ts +15 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/context-pack/knowledge-scope-execution.d.ts +15 -0
- package/dist/types/context-pack/knowledge-scope-mount.d.ts +27 -0
- package/dist/types/context-pack/knowledge-scope.d.ts +87 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/local-documents/files.d.ts +32 -0
- package/dist/types/local-documents/index.d.ts +17 -0
- package/dist/types/local-documents/snapshot.d.ts +47 -0
- package/dist/types/local-query.d.ts +6 -0
- package/dist/types/local-quickstart.d.ts +15 -0
- package/dist/types/state-contract.d.ts +78 -0
- package/docs/PILOT.md +30 -17
- package/examples/local-documents/README.md +48 -0
- package/examples/local-documents/support-handbook.md +21 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4199,6 +4199,10 @@ var SearchProviderSchema = exports_external.object({
|
|
|
4199
4199
|
kind: exports_external.literal("schift_search"),
|
|
4200
4200
|
indexRef: KnowledgeScopeIdentifierSchema
|
|
4201
4201
|
}).strict();
|
|
4202
|
+
var LocalDocumentsProviderSchema = exports_external.object({
|
|
4203
|
+
kind: exports_external.literal("local_documents"),
|
|
4204
|
+
indexRef: KnowledgeScopeIdentifierSchema
|
|
4205
|
+
}).strict();
|
|
4202
4206
|
var WebProviderSchema = exports_external.object({
|
|
4203
4207
|
kind: exports_external.literal("web_search"),
|
|
4204
4208
|
provider: exports_external.enum(["customer", "schift"])
|
|
@@ -4207,6 +4211,7 @@ var QueryProviderSchema = exports_external.discriminatedUnion("kind", [
|
|
|
4207
4211
|
RecordsProviderSchema,
|
|
4208
4212
|
ConnectorProviderSchema,
|
|
4209
4213
|
SearchProviderSchema,
|
|
4214
|
+
LocalDocumentsProviderSchema,
|
|
4210
4215
|
WebProviderSchema
|
|
4211
4216
|
]).readonly();
|
|
4212
4217
|
var QueryCapabilitySchema = exports_external.object({
|
|
@@ -4315,6 +4320,7 @@ var CandidateProviderEvidenceSchema = exports_external.discriminatedUnion("kind"
|
|
|
4315
4320
|
auditPersisted: exports_external.boolean()
|
|
4316
4321
|
}).strict(),
|
|
4317
4322
|
SearchProviderSchema,
|
|
4323
|
+
LocalDocumentsProviderSchema,
|
|
4318
4324
|
WebProviderSchema
|
|
4319
4325
|
]).readonly();
|
|
4320
4326
|
var CandidateEnvelopeSchema = exports_external.object({
|
|
@@ -4416,6 +4422,7 @@ var bindingMatchesCapability = (binding, capability) => {
|
|
|
4416
4422
|
case "open_connector_action":
|
|
4417
4423
|
return binding.providerRef === capability.provider.actionId && binding.connectorRef === capability.provider.connectorRef;
|
|
4418
4424
|
case "schift_search":
|
|
4425
|
+
case "local_documents":
|
|
4419
4426
|
return binding.providerRef === capability.provider.indexRef && binding.connectorRef === undefined;
|
|
4420
4427
|
case "web_search":
|
|
4421
4428
|
return binding.providerRef === capability.provider.provider && binding.connectorRef === undefined;
|
|
@@ -4559,6 +4566,10 @@ var ProviderEvidenceSchema = exports_external.discriminatedUnion("kind", [
|
|
|
4559
4566
|
kind: exports_external.literal("schift_search"),
|
|
4560
4567
|
indexRef: KnowledgeScopeIdentifierSchema
|
|
4561
4568
|
}).strict(),
|
|
4569
|
+
exports_external.object({
|
|
4570
|
+
kind: exports_external.literal("local_documents"),
|
|
4571
|
+
indexRef: KnowledgeScopeIdentifierSchema
|
|
4572
|
+
}).strict(),
|
|
4562
4573
|
exports_external.object({
|
|
4563
4574
|
kind: exports_external.literal("web_search"),
|
|
4564
4575
|
provider: exports_external.enum(["customer", "schift"])
|
|
@@ -4688,6 +4699,8 @@ var providerEvidenceDenial = (capability, binding, candidate) => {
|
|
|
4688
4699
|
return evidence.auditPersisted ? undefined : "connector_audit_missing";
|
|
4689
4700
|
case "schift_search":
|
|
4690
4701
|
return evidence.kind === "schift_search" && evidence.indexRef === capability.provider.indexRef ? undefined : "provider_evidence_mismatch";
|
|
4702
|
+
case "local_documents":
|
|
4703
|
+
return evidence.kind === "local_documents" && evidence.indexRef === capability.provider.indexRef ? undefined : "provider_evidence_mismatch";
|
|
4691
4704
|
case "web_search":
|
|
4692
4705
|
return evidence.kind === "web_search" && evidence.provider === capability.provider.provider ? undefined : "provider_evidence_mismatch";
|
|
4693
4706
|
}
|
|
@@ -5595,21 +5608,170 @@ var createHttpProviderExecutionPort = (options) => ({
|
|
|
5595
5608
|
return parsed.data;
|
|
5596
5609
|
});
|
|
5597
5610
|
}
|
|
5611
|
+
case "local_documents":
|
|
5598
5612
|
case "web_search":
|
|
5599
5613
|
throw new HttpProviderAdapterError("invalid_configuration");
|
|
5600
5614
|
}
|
|
5601
5615
|
}
|
|
5602
5616
|
});
|
|
5603
5617
|
|
|
5618
|
+
// src/local-documents/files.ts
|
|
5619
|
+
import { constants } from "node:fs";
|
|
5620
|
+
import { lstat, open, opendir, realpath } from "node:fs/promises";
|
|
5621
|
+
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
5622
|
+
class LocalDocumentError extends Error {
|
|
5623
|
+
code;
|
|
5624
|
+
name = "LocalDocumentError";
|
|
5625
|
+
constructor(code) {
|
|
5626
|
+
super({
|
|
5627
|
+
local_source_invalid: "Select regular UTF-8 Markdown (.md) or text (.txt) files; symlinks, hardlinks and binary files are unsupported.",
|
|
5628
|
+
local_limit_exceeded: "Local documents exceed the supported file, byte, or chunk limits.",
|
|
5629
|
+
local_snapshot_invalid: "Local document snapshot is missing, unsafe, or corrupt; ingest the source again.",
|
|
5630
|
+
local_scope_invalid: "Local documents require the mounted tenant and document binding; filters and narrowed scopes are unsupported."
|
|
5631
|
+
}[code]);
|
|
5632
|
+
this.code = code;
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
var LOCAL_LIMITS = { files: 100, fileBytes: 1048576, totalBytes: 8388608, snapshotBytes: 16777216, chunks: 4000, entries: 2000, depth: 16, chunkCharacters: 2000 };
|
|
5636
|
+
var identityMatches = (left, right) => left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs && right.nlink === 1;
|
|
5637
|
+
var checkDirectories = async (directories) => {
|
|
5638
|
+
for (const directory of directories) {
|
|
5639
|
+
const current = await lstat(directory.path);
|
|
5640
|
+
if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== directory.dev || current.ino !== directory.ino || await realpath(directory.path) !== directory.path)
|
|
5641
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5642
|
+
}
|
|
5643
|
+
};
|
|
5644
|
+
var mapLocalFileError = (error, ownerOnly) => {
|
|
5645
|
+
if (error instanceof Error && "code" in error && typeof error.code === "string" && ["ENOENT", "ELOOP", "EACCES", "EPERM", "ENOTDIR", "EISDIR"].includes(error.code)) {
|
|
5646
|
+
throw new LocalDocumentError(ownerOnly ? "local_snapshot_invalid" : "local_source_invalid");
|
|
5647
|
+
}
|
|
5648
|
+
throw error;
|
|
5649
|
+
};
|
|
5650
|
+
var readBounded = async (source, maxBytes, ownerOnly = false) => {
|
|
5651
|
+
try {
|
|
5652
|
+
return await readFileContents(source, maxBytes, ownerOnly);
|
|
5653
|
+
} catch (error) {
|
|
5654
|
+
return mapLocalFileError(error, ownerOnly);
|
|
5655
|
+
}
|
|
5656
|
+
};
|
|
5657
|
+
var readFileContents = async (source, maxBytes, ownerOnly) => {
|
|
5658
|
+
if (!constants.O_NOFOLLOW)
|
|
5659
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5660
|
+
const path = typeof source === "string" ? source : source.path;
|
|
5661
|
+
if (typeof source !== "string") {
|
|
5662
|
+
await checkDirectories(source.ancestors);
|
|
5663
|
+
const currentPath = await realpath(path);
|
|
5664
|
+
const inside = relative(source.root, currentPath);
|
|
5665
|
+
if (currentPath !== path || inside === ".." || inside.startsWith(`..${sep}`))
|
|
5666
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5667
|
+
}
|
|
5668
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
5669
|
+
try {
|
|
5670
|
+
const before = await handle.stat();
|
|
5671
|
+
if (typeof source !== "string") {
|
|
5672
|
+
if (!identityMatches(source.identity, before))
|
|
5673
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5674
|
+
await checkDirectories(source.ancestors);
|
|
5675
|
+
}
|
|
5676
|
+
if (!before.isFile() || ownerOnly && ((before.mode & 63) !== 0 || process.getuid !== undefined && before.uid !== process.getuid()))
|
|
5677
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
5678
|
+
if (before.size > maxBytes)
|
|
5679
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
5680
|
+
const buffer = Buffer.alloc(maxBytes + 1);
|
|
5681
|
+
let length = 0;
|
|
5682
|
+
while (length < buffer.length) {
|
|
5683
|
+
const { bytesRead } = await handle.read(buffer, length, buffer.length - length, length);
|
|
5684
|
+
if (bytesRead === 0)
|
|
5685
|
+
break;
|
|
5686
|
+
length += bytesRead;
|
|
5687
|
+
}
|
|
5688
|
+
const after = await handle.stat();
|
|
5689
|
+
if (length > maxBytes)
|
|
5690
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
5691
|
+
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || typeof source !== "string" && !identityMatches(source.identity, after))
|
|
5692
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5693
|
+
if (typeof source !== "string")
|
|
5694
|
+
await checkDirectories(source.ancestors);
|
|
5695
|
+
const text = decodeUtf8Strict(buffer.subarray(0, length));
|
|
5696
|
+
if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/u.test(text))
|
|
5697
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5698
|
+
return text;
|
|
5699
|
+
} finally {
|
|
5700
|
+
await handle.close();
|
|
5701
|
+
}
|
|
5702
|
+
};
|
|
5703
|
+
var collectFiles = async (source) => {
|
|
5704
|
+
try {
|
|
5705
|
+
return await collectSourceFiles(source);
|
|
5706
|
+
} catch (error) {
|
|
5707
|
+
return mapLocalFileError(error, false);
|
|
5708
|
+
}
|
|
5709
|
+
};
|
|
5710
|
+
var collectSourceFiles = async (source) => {
|
|
5711
|
+
const selected = resolve(source);
|
|
5712
|
+
const selectedMetadata = await lstat(selected);
|
|
5713
|
+
if (selectedMetadata.isSymbolicLink())
|
|
5714
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5715
|
+
const root = await realpath(selected);
|
|
5716
|
+
const rootMetadata = await lstat(root);
|
|
5717
|
+
if (selectedMetadata.dev !== rootMetadata.dev || selectedMetadata.ino !== rootMetadata.ino)
|
|
5718
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5719
|
+
const boundary = rootMetadata.isDirectory() ? root : dirname(root);
|
|
5720
|
+
const parentMetadata = await lstat(dirname(boundary));
|
|
5721
|
+
const parents = [{ path: dirname(boundary), dev: parentMetadata.dev, ino: parentMetadata.ino }];
|
|
5722
|
+
const files = [];
|
|
5723
|
+
let entries = 1;
|
|
5724
|
+
const visit = async (path, depth, ancestors) => {
|
|
5725
|
+
await checkDirectories(ancestors);
|
|
5726
|
+
if (entries > LOCAL_LIMITS.entries || depth > LOCAL_LIMITS.depth)
|
|
5727
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
5728
|
+
const metadata = await lstat(path);
|
|
5729
|
+
if (metadata.isSymbolicLink())
|
|
5730
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5731
|
+
if (metadata.isDirectory()) {
|
|
5732
|
+
const directories = [...ancestors, { path, dev: metadata.dev, ino: metadata.ino }];
|
|
5733
|
+
await checkDirectories(directories);
|
|
5734
|
+
const directory = await opendir(path);
|
|
5735
|
+
for await (const child of directory) {
|
|
5736
|
+
entries += 1;
|
|
5737
|
+
if (entries > LOCAL_LIMITS.entries)
|
|
5738
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
5739
|
+
if (child.name.startsWith(".") || child.name === "node_modules")
|
|
5740
|
+
continue;
|
|
5741
|
+
await visit(join(path, child.name), depth + 1, directories);
|
|
5742
|
+
}
|
|
5743
|
+
await checkDirectories(directories);
|
|
5744
|
+
return;
|
|
5745
|
+
}
|
|
5746
|
+
if (!metadata.isFile())
|
|
5747
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5748
|
+
if (![".md", ".txt"].includes(extname(path).toLowerCase())) {
|
|
5749
|
+
if (depth === 0)
|
|
5750
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5751
|
+
return;
|
|
5752
|
+
}
|
|
5753
|
+
if (metadata.nlink !== 1 || await realpath(path) !== path)
|
|
5754
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5755
|
+
files.push({ path, root: boundary, identity: metadata, ancestors });
|
|
5756
|
+
if (files.length > LOCAL_LIMITS.files)
|
|
5757
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
5758
|
+
};
|
|
5759
|
+
const boundaryMetadata = await lstat(boundary);
|
|
5760
|
+
await visit(root, 0, rootMetadata.isDirectory() ? parents : [...parents, { path: boundary, dev: boundaryMetadata.dev, ino: boundaryMetadata.ino }]);
|
|
5761
|
+
if (files.length === 0)
|
|
5762
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
5763
|
+
return files.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
5764
|
+
};
|
|
5765
|
+
|
|
5604
5766
|
// src/application.ts
|
|
5605
5767
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
5606
5768
|
|
|
5607
5769
|
// src/authorization.ts
|
|
5608
5770
|
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
5609
|
-
import { constants } from "node:fs";
|
|
5610
|
-
import { chmod, link, lstat, mkdir, open, rm } from "node:fs/promises";
|
|
5771
|
+
import { constants as constants2 } from "node:fs";
|
|
5772
|
+
import { chmod, link, lstat as lstat2, mkdir, open as open2, rm } from "node:fs/promises";
|
|
5611
5773
|
import { homedir } from "node:os";
|
|
5612
|
-
import { join } from "node:path";
|
|
5774
|
+
import { join as join2 } from "node:path";
|
|
5613
5775
|
var AuthorizationSubjectSchema = CandidateEnvelopeSchema.unwrap().pick({
|
|
5614
5776
|
srn: true,
|
|
5615
5777
|
sourceId: true,
|
|
@@ -5652,18 +5814,18 @@ var authorizationSubjectFromCandidate = (candidate) => AuthorizationSubjectSchem
|
|
|
5652
5814
|
});
|
|
5653
5815
|
var hasCode = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
|
|
5654
5816
|
var assertOwnerOnly = async (path, directory) => {
|
|
5655
|
-
const metadata = await
|
|
5817
|
+
const metadata = await lstat2(path);
|
|
5656
5818
|
if (metadata.isSymbolicLink() || (metadata.mode & 63) !== 0 || (directory ? !metadata.isDirectory() : !metadata.isFile())) {
|
|
5657
5819
|
throw productError("state_permissions_invalid");
|
|
5658
5820
|
}
|
|
5659
5821
|
};
|
|
5660
5822
|
var readOwnerOnlyKey = async (path) => {
|
|
5661
|
-
if (typeof
|
|
5823
|
+
if (typeof constants2.O_NOFOLLOW !== "number" || constants2.O_NOFOLLOW === 0) {
|
|
5662
5824
|
throw productError("state_permissions_invalid");
|
|
5663
5825
|
}
|
|
5664
5826
|
let handle;
|
|
5665
5827
|
try {
|
|
5666
|
-
handle = await
|
|
5828
|
+
handle = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
|
|
5667
5829
|
} catch (error) {
|
|
5668
5830
|
if (hasCode(error, "ELOOP"))
|
|
5669
5831
|
throw productError("state_permissions_invalid");
|
|
@@ -5688,8 +5850,8 @@ class KnowledgeScopeAuthorization {
|
|
|
5688
5850
|
faults;
|
|
5689
5851
|
constructor(options = {}) {
|
|
5690
5852
|
const environment = options.environment ?? process.env;
|
|
5691
|
-
this.home = options.home ?? environment["SCHIFT_KS_HOME"] ??
|
|
5692
|
-
this.keyPath =
|
|
5853
|
+
this.home = options.home ?? environment["SCHIFT_KS_HOME"] ?? join2(homedir(), ".schift", "knowledge-scope");
|
|
5854
|
+
this.keyPath = join2(this.home, "authorization.key");
|
|
5693
5855
|
this.faults = options.faults;
|
|
5694
5856
|
}
|
|
5695
5857
|
async issue(subject) {
|
|
@@ -5727,8 +5889,8 @@ class KnowledgeScopeAuthorization {
|
|
|
5727
5889
|
return key;
|
|
5728
5890
|
}
|
|
5729
5891
|
async installKeyIfAbsent() {
|
|
5730
|
-
const temporaryPath =
|
|
5731
|
-
const handle = await
|
|
5892
|
+
const temporaryPath = join2(this.home, `.authorization-key-${process.pid}-${randomUUID()}.tmp`);
|
|
5893
|
+
const handle = await open2(temporaryPath, "wx", 384);
|
|
5732
5894
|
try {
|
|
5733
5895
|
await handle.writeFile(randomBytes(32));
|
|
5734
5896
|
await handle.sync();
|
|
@@ -5741,7 +5903,7 @@ class KnowledgeScopeAuthorization {
|
|
|
5741
5903
|
throw error;
|
|
5742
5904
|
return;
|
|
5743
5905
|
}
|
|
5744
|
-
const directory = await
|
|
5906
|
+
const directory = await open2(this.home, "r");
|
|
5745
5907
|
try {
|
|
5746
5908
|
await directory.sync();
|
|
5747
5909
|
} finally {
|
|
@@ -6108,6 +6270,8 @@ var providerReference = (capability) => {
|
|
|
6108
6270
|
return capability.provider.actionId;
|
|
6109
6271
|
case "schift_search":
|
|
6110
6272
|
return capability.provider.indexRef;
|
|
6273
|
+
case "local_documents":
|
|
6274
|
+
return capability.provider.indexRef;
|
|
6111
6275
|
case "web_search":
|
|
6112
6276
|
return capability.provider.provider;
|
|
6113
6277
|
}
|
|
@@ -6454,6 +6618,8 @@ var createKnowledgeScopeApi = (options) => {
|
|
|
6454
6618
|
return errorResponse(error.code, error.status);
|
|
6455
6619
|
if (error instanceof HttpProviderAdapterError)
|
|
6456
6620
|
return errorResponse(error.code, 502);
|
|
6621
|
+
if (error instanceof LocalDocumentError)
|
|
6622
|
+
return errorResponse(error.code, error.code === "local_snapshot_invalid" ? 503 : 400);
|
|
6457
6623
|
if (error instanceof KnowledgeScopeProductError)
|
|
6458
6624
|
return errorResponse(error.code, 400);
|
|
6459
6625
|
if (error instanceof SyntaxError || error instanceof URIError)
|
|
@@ -6513,11 +6679,11 @@ var serveKnowledgeScopeApi = async (options) => {
|
|
|
6513
6679
|
writeNodeResponse(outgoing, errorResponse(code, status));
|
|
6514
6680
|
});
|
|
6515
6681
|
});
|
|
6516
|
-
await new Promise((
|
|
6682
|
+
await new Promise((resolve2, reject) => {
|
|
6517
6683
|
server.once("error", reject);
|
|
6518
6684
|
server.listen(options.port ?? 8787, host, () => {
|
|
6519
6685
|
server.off("error", reject);
|
|
6520
|
-
|
|
6686
|
+
resolve2();
|
|
6521
6687
|
});
|
|
6522
6688
|
});
|
|
6523
6689
|
const address = server.address();
|
|
@@ -6594,8 +6760,8 @@ var createRemoteKnowledgeScopeApplication = (apiUrl, request = globalThis.fetch,
|
|
|
6594
6760
|
};
|
|
6595
6761
|
var isJsonObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6596
6762
|
// src/cli.ts
|
|
6597
|
-
import { mkdir as
|
|
6598
|
-
import { basename, resolve as
|
|
6763
|
+
import { mkdir as mkdir5, open as open4 } from "node:fs/promises";
|
|
6764
|
+
import { basename as basename2, resolve as resolve4 } from "node:path";
|
|
6599
6765
|
|
|
6600
6766
|
// src/cli-options.ts
|
|
6601
6767
|
class CliUsageError extends Error {
|
|
@@ -6612,12 +6778,13 @@ var specifications = {
|
|
|
6612
6778
|
lock: { count: 1, values: [] },
|
|
6613
6779
|
mount: { count: 1, values: ["--bindings", "--api-url"] },
|
|
6614
6780
|
inspect: { count: 1, values: ["--api-url"] },
|
|
6781
|
+
query: { count: 1, values: ["--query", "--api-url"] },
|
|
6615
6782
|
run: { count: 2, values: ["--input", "--api-url"] },
|
|
6616
6783
|
"run-batch": { count: 1, values: ["--input", "--api-url"] },
|
|
6617
6784
|
admit: { count: 1, values: ["--candidate", "--api-url"] },
|
|
6618
6785
|
unmount: { count: 1, values: ["--expected-revision", "--api-url"] },
|
|
6619
6786
|
serve: { count: 0, values: ["--host", "--port"] },
|
|
6620
|
-
quickstart: { count: 1, values: ["--index", "--tenant", "--query"] },
|
|
6787
|
+
quickstart: { count: 1, values: ["--source", "--index", "--tenant", "--query"] },
|
|
6621
6788
|
doctor: { count: 1, values: ["--query"], flags: ["--probe"] }
|
|
6622
6789
|
};
|
|
6623
6790
|
var parseCliOptions = (command, args) => {
|
|
@@ -6652,12 +6819,14 @@ var parseCliOptions = (command, args) => {
|
|
|
6652
6819
|
throw new CliUsageError("argument_invalid");
|
|
6653
6820
|
if (command === "doctor" && seen.has("--probe") !== seen.has("--query"))
|
|
6654
6821
|
throw new CliUsageError("argument_invalid");
|
|
6822
|
+
if (command === "quickstart" && seen.has("--source") && seen.has("--index"))
|
|
6823
|
+
throw new CliUsageError("argument_invalid");
|
|
6655
6824
|
return positional;
|
|
6656
6825
|
};
|
|
6657
6826
|
|
|
6658
6827
|
// src/quickstart.ts
|
|
6659
6828
|
import { mkdir as mkdir2, writeFile } from "node:fs/promises";
|
|
6660
|
-
import { join as
|
|
6829
|
+
import { join as join3, resolve as resolve2 } from "node:path";
|
|
6661
6830
|
|
|
6662
6831
|
// src/onboarding-config.ts
|
|
6663
6832
|
class OnboardingError extends Error {
|
|
@@ -6704,7 +6873,7 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6704
6873
|
});
|
|
6705
6874
|
if (!definition.success)
|
|
6706
6875
|
throw new OnboardingError("argument_invalid");
|
|
6707
|
-
const directory =
|
|
6876
|
+
const directory = resolve2(request.directory);
|
|
6708
6877
|
try {
|
|
6709
6878
|
await mkdir2(directory, { mode: 448 });
|
|
6710
6879
|
} catch (error) {
|
|
@@ -6712,20 +6881,20 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6712
6881
|
throw new OnboardingError("directory_exists");
|
|
6713
6882
|
throw error;
|
|
6714
6883
|
}
|
|
6715
|
-
const pack =
|
|
6716
|
-
const bindingsPath =
|
|
6717
|
-
const inputPath =
|
|
6884
|
+
const pack = join3(directory, "pack");
|
|
6885
|
+
const bindingsPath = join3(directory, "bindings.json");
|
|
6886
|
+
const inputPath = join3(directory, "input.json");
|
|
6718
6887
|
let installationId;
|
|
6719
6888
|
let artifactsComplete = false;
|
|
6720
6889
|
const recovery = () => installationId === undefined ? ["schift-ks", "mount", pack, "--bindings", bindingsPath] : ["schift-ks", "run", installationId, "search", "--input", inputPath];
|
|
6721
6890
|
try {
|
|
6722
|
-
await mkdir2(
|
|
6891
|
+
await mkdir2(join3(pack, "schemas"), { recursive: true, mode: 448 });
|
|
6723
6892
|
const bindings = { scopeAuthority: scopeAuthority.data, sourceBindings: [{ sourceId: "project-documents", sourceClass: "document", authority: "approved", permissionMode: "mirrored", providerRef: request.index, operationIds: ["search"] }] };
|
|
6724
6893
|
const input = { effectiveScope: { tenant: request.tenant }, input: { query: request.query } };
|
|
6725
6894
|
const files = {
|
|
6726
|
-
[
|
|
6727
|
-
[
|
|
6728
|
-
[
|
|
6895
|
+
[join3(pack, "scope.json")]: json(definition.data),
|
|
6896
|
+
[join3(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
|
|
6897
|
+
[join3(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
|
|
6729
6898
|
[bindingsPath]: json(bindings),
|
|
6730
6899
|
[inputPath]: input
|
|
6731
6900
|
};
|
|
@@ -6736,7 +6905,7 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6736
6905
|
artifactsComplete = true;
|
|
6737
6906
|
const mounted = KnowledgeScopeMountSchema.parse(await dependencies.embedded.mount(await dependencies.authoring.mountPayload(pack, bindingsPath)));
|
|
6738
6907
|
installationId = mounted.installationId;
|
|
6739
|
-
await writeFile(
|
|
6908
|
+
await writeFile(join3(directory, "installation.json"), `${JSON.stringify(mounted)}
|
|
6740
6909
|
`, { flag: "wx", mode: 384 });
|
|
6741
6910
|
const result = await dependencies.embedded.run({ installationId, operationId: "search", ...input });
|
|
6742
6911
|
return { directory, installationId, operationId: "search", result, recovery: recovery(), status: "completed" };
|
|
@@ -6746,6 +6915,266 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6746
6915
|
}
|
|
6747
6916
|
};
|
|
6748
6917
|
|
|
6918
|
+
// src/local-quickstart.ts
|
|
6919
|
+
import { mkdir as mkdir4, writeFile as writeFile2 } from "node:fs/promises";
|
|
6920
|
+
import { join as join5, resolve as resolve3 } from "node:path";
|
|
6921
|
+
|
|
6922
|
+
// src/local-documents/index.ts
|
|
6923
|
+
import { mkdir as mkdir3, lstat as lstat3, open as open3, link as link2, rm as rm2 } from "node:fs/promises";
|
|
6924
|
+
import { basename, join as join4 } from "node:path";
|
|
6925
|
+
import { fileURLToPath } from "node:url";
|
|
6926
|
+
import { homedir as homedir2 } from "node:os";
|
|
6927
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
6928
|
+
|
|
6929
|
+
// src/local-documents/snapshot.ts
|
|
6930
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
6931
|
+
import { pathToFileURL } from "node:url";
|
|
6932
|
+
var DocumentSchema = exports_external.object({ uri: exports_external.string().url(), text: exports_external.string().max(LOCAL_LIMITS.fileBytes), revision: exports_external.string().regex(/^sha256:[a-f0-9]{64}$/) }).strict().readonly();
|
|
6933
|
+
var SnapshotSchema = exports_external.object({ version: exports_external.literal(1), capturedAt: exports_external.string().datetime(), documents: exports_external.array(DocumentSchema).min(1).max(LOCAL_LIMITS.files).readonly() }).strict().readonly();
|
|
6934
|
+
var digest2 = (value) => createHash3("sha256").update(value).digest("hex");
|
|
6935
|
+
var snapshotId = (snapshot) => `local:${digest2(canonicalJson2(snapshot))}`;
|
|
6936
|
+
var buildSnapshot = async (source) => {
|
|
6937
|
+
const documents = [];
|
|
6938
|
+
let bytes = 0;
|
|
6939
|
+
for (const file of await collectFiles(source)) {
|
|
6940
|
+
const text = await readBounded(file, LOCAL_LIMITS.fileBytes);
|
|
6941
|
+
bytes += Buffer.byteLength(text);
|
|
6942
|
+
if (bytes > LOCAL_LIMITS.totalBytes)
|
|
6943
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6944
|
+
documents.push({ uri: pathToFileURL(file.path).href, text, revision: `sha256:${digest2(text)}` });
|
|
6945
|
+
}
|
|
6946
|
+
return { version: 1, capturedAt: new Date().toISOString(), documents };
|
|
6947
|
+
};
|
|
6948
|
+
var chunksFor = (snapshot) => {
|
|
6949
|
+
const chunks = [];
|
|
6950
|
+
let bytes = 0;
|
|
6951
|
+
for (const document of snapshot.documents) {
|
|
6952
|
+
const uri = new URL(document.uri);
|
|
6953
|
+
if (uri.protocol !== "file:" || uri.host !== "" || uri.search !== "" || uri.hash !== "" || document.revision !== `sha256:${digest2(document.text)}`)
|
|
6954
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6955
|
+
bytes += Buffer.byteLength(document.text);
|
|
6956
|
+
if (bytes > LOCAL_LIMITS.totalBytes)
|
|
6957
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6958
|
+
const lines = document.text.split(/\r?\n/u);
|
|
6959
|
+
let text = "";
|
|
6960
|
+
let start = 1;
|
|
6961
|
+
let end = 1;
|
|
6962
|
+
const flush = () => {
|
|
6963
|
+
if (text.trim().length > 0)
|
|
6964
|
+
chunks.push({ document, text, lineStart: start, lineEnd: end, id: `chunk:${digest2(`${document.uri}:${start}:${end}:${text}`)}` });
|
|
6965
|
+
text = "";
|
|
6966
|
+
if (chunks.length > LOCAL_LIMITS.chunks)
|
|
6967
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6968
|
+
};
|
|
6969
|
+
lines.forEach((line, offset) => {
|
|
6970
|
+
if (line.length > LOCAL_LIMITS.chunkCharacters)
|
|
6971
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6972
|
+
if (text.length + line.length + 1 > LOCAL_LIMITS.chunkCharacters)
|
|
6973
|
+
flush();
|
|
6974
|
+
if (text.length === 0)
|
|
6975
|
+
start = offset + 1;
|
|
6976
|
+
text += `${text.length === 0 ? "" : `
|
|
6977
|
+
`}${line}`;
|
|
6978
|
+
end = offset + 1;
|
|
6979
|
+
});
|
|
6980
|
+
flush();
|
|
6981
|
+
}
|
|
6982
|
+
return chunks;
|
|
6983
|
+
};
|
|
6984
|
+
var lexicalTerms = (text) => {
|
|
6985
|
+
const words = text.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
6986
|
+
return [...new Set(words.flatMap((word) => /[가-힣]/u.test(word) && word.length > 2 ? [word, ...Array.from({ length: word.length - 1 }, (_, index) => word.slice(index, index + 2))] : [word]))];
|
|
6987
|
+
};
|
|
6988
|
+
|
|
6989
|
+
// src/local-documents/index.ts
|
|
6990
|
+
var QuerySchema = exports_external.object({ query: exports_external.string().min(1).max(8192) }).strict();
|
|
6991
|
+
var assertPrivateDirectory = async (path) => {
|
|
6992
|
+
await mkdir3(path, { recursive: true, mode: 448 });
|
|
6993
|
+
const info = await lstat3(path);
|
|
6994
|
+
if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 63) !== 0 || process.getuid !== undefined && info.uid !== process.getuid())
|
|
6995
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6996
|
+
};
|
|
6997
|
+
|
|
6998
|
+
class LocalDocumentStore {
|
|
6999
|
+
home;
|
|
7000
|
+
constructor(options = {}) {
|
|
7001
|
+
this.home = options.home ?? (options.environment ?? process.env)["SCHIFT_KS_HOME"] ?? join4(homedir2(), ".schift", "knowledge-scope");
|
|
7002
|
+
}
|
|
7003
|
+
async directory() {
|
|
7004
|
+
try {
|
|
7005
|
+
await assertPrivateDirectory(this.home);
|
|
7006
|
+
const directory = join4(this.home, "local-documents");
|
|
7007
|
+
await assertPrivateDirectory(directory);
|
|
7008
|
+
return directory;
|
|
7009
|
+
} catch (error) {
|
|
7010
|
+
return mapLocalFileError(error, true);
|
|
7011
|
+
}
|
|
7012
|
+
}
|
|
7013
|
+
async ingest(sourcePath) {
|
|
7014
|
+
const snapshot = await buildSnapshot(sourcePath);
|
|
7015
|
+
const chunks = chunksFor(snapshot);
|
|
7016
|
+
if (chunks.length === 0)
|
|
7017
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
7018
|
+
const indexRef = snapshotId(snapshot);
|
|
7019
|
+
const directory = await this.directory();
|
|
7020
|
+
const temporary = join4(directory, `.ingest-${randomUUID3()}`);
|
|
7021
|
+
const serialized = canonicalJson2(snapshot);
|
|
7022
|
+
if (Buffer.byteLength(serialized) > LOCAL_LIMITS.snapshotBytes)
|
|
7023
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
7024
|
+
const handle = await open3(temporary, "wx", 384);
|
|
7025
|
+
try {
|
|
7026
|
+
await handle.writeFile(serialized, "utf8");
|
|
7027
|
+
await handle.sync();
|
|
7028
|
+
try {
|
|
7029
|
+
await link2(temporary, join4(directory, `${indexRef.slice(6)}.json`));
|
|
7030
|
+
} catch (error) {
|
|
7031
|
+
if (!(error instanceof Error && ("code" in error) && error.code === "EEXIST"))
|
|
7032
|
+
throw error;
|
|
7033
|
+
}
|
|
7034
|
+
} finally {
|
|
7035
|
+
await handle.close();
|
|
7036
|
+
await rm2(temporary, { force: true });
|
|
7037
|
+
}
|
|
7038
|
+
return { indexRef, documentCount: snapshot.documents.length, chunkCount: chunks.length };
|
|
7039
|
+
}
|
|
7040
|
+
async read(indexRef) {
|
|
7041
|
+
if (!/^local:[a-f0-9]{64}$/u.test(indexRef))
|
|
7042
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
7043
|
+
const directory = await this.directory();
|
|
7044
|
+
const text = await readBounded(join4(directory, `${indexRef.slice(6)}.json`), LOCAL_LIMITS.snapshotBytes, true);
|
|
7045
|
+
const parsed = SnapshotSchema.safeParse(parseJsonText(text, "local snapshot", { maxBytes: LOCAL_LIMITS.snapshotBytes }));
|
|
7046
|
+
if (!parsed.success || snapshotId(parsed.data) !== indexRef)
|
|
7047
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
7048
|
+
return parsed.data;
|
|
7049
|
+
}
|
|
7050
|
+
async execute(context) {
|
|
7051
|
+
const { capability, binding, request, mount } = context;
|
|
7052
|
+
if (mount.state !== "mounted" || !mount.sourceBindings.some((entry) => canonicalJson2(entry) === canonicalJson2(binding)) || capability.provider.kind !== "local_documents" || binding.sourceClass !== "document" || binding.providerRef !== capability.provider.indexRef || binding.connectorRef !== undefined || binding.permissionMode !== "static" || !binding.operationIds.includes(capability.operationId) || request.operationId !== capability.operationId || request.installationId !== mount.installationId || request.effectiveScope.tenant !== mount.scopeAuthority.tenant || request.effectiveScope.namespace !== undefined || request.effectiveScope.subject !== undefined || request.effectiveScope.session !== undefined || (capability.requiredProviderScopes?.length ?? 0) > 0 || Object.keys(request.filters ?? {}).length > 0)
|
|
7053
|
+
throw new LocalDocumentError("local_scope_invalid");
|
|
7054
|
+
const query = QuerySchema.safeParse(request.input);
|
|
7055
|
+
if (!query.success || !context.validateInput(request.input).valid)
|
|
7056
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
7057
|
+
const snapshot = await this.read(capability.provider.indexRef);
|
|
7058
|
+
const terms = lexicalTerms(query.data.query);
|
|
7059
|
+
const ranked = chunksFor(snapshot).map((chunk) => {
|
|
7060
|
+
const tokens = new Set(lexicalTerms(chunk.text));
|
|
7061
|
+
return { chunk, score: terms.filter((term) => tokens.has(term)).length };
|
|
7062
|
+
}).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.chunk.id.localeCompare(b.chunk.id));
|
|
7063
|
+
const results = [];
|
|
7064
|
+
let bytes = 0;
|
|
7065
|
+
for (const { chunk, score } of ranked.slice(0, Math.min(capability.limits?.maxRows ?? 8, 100))) {
|
|
7066
|
+
const sourcePath = fileURLToPath(chunk.document.uri);
|
|
7067
|
+
const documentId = `doc:${digest2(chunk.document.uri)}`;
|
|
7068
|
+
const payload = { text: chunk.text, sourceId: binding.sourceId, documentId, sourcePath, chunkId: chunk.id, lineStart: chunk.lineStart, lineEnd: chunk.lineEnd, score };
|
|
7069
|
+
if (!context.validateResult(payload).valid)
|
|
7070
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
7071
|
+
const result = ProviderResultSchema.parse({
|
|
7072
|
+
resultId: chunk.id,
|
|
7073
|
+
srn: `srn:local:${chunk.id.slice(6)}`,
|
|
7074
|
+
revision: chunk.document.revision,
|
|
7075
|
+
freshness: snapshot.capturedAt,
|
|
7076
|
+
payload,
|
|
7077
|
+
citation: { uri: `schift://local-documents/${capability.provider.indexRef}/${documentId}#L${chunk.lineStart}-L${chunk.lineEnd}`, label: `${basename(sourcePath)}:L${chunk.lineStart}-L${chunk.lineEnd}` },
|
|
7078
|
+
providerScopes: [],
|
|
7079
|
+
providerEvidence: { kind: "local_documents", indexRef: capability.provider.indexRef }
|
|
7080
|
+
});
|
|
7081
|
+
bytes += Buffer.byteLength(canonicalJson2(result));
|
|
7082
|
+
if (bytes > Math.min(capability.limits?.maxResultBytes ?? 1048576, 8388608))
|
|
7083
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
7084
|
+
results.push(result);
|
|
7085
|
+
}
|
|
7086
|
+
return results;
|
|
7087
|
+
}
|
|
7088
|
+
}
|
|
7089
|
+
|
|
7090
|
+
// src/local-quickstart.ts
|
|
7091
|
+
var localQuickstart = async (request, dependencies) => {
|
|
7092
|
+
const importer = dependencies.localDocuments;
|
|
7093
|
+
if (importer === undefined)
|
|
7094
|
+
throw new OnboardingError("local_import_unavailable");
|
|
7095
|
+
const scopeAuthority = ScopeAuthoritySchema.safeParse({ organizationId: "local-organization", tenant: request.tenant });
|
|
7096
|
+
if (!scopeAuthority.success || request.query.trim().length === 0 || request.query.length > 8192)
|
|
7097
|
+
throw new OnboardingError("argument_invalid");
|
|
7098
|
+
const directory = resolve3(request.directory);
|
|
7099
|
+
try {
|
|
7100
|
+
await mkdir4(directory, { mode: 448 });
|
|
7101
|
+
} catch (error) {
|
|
7102
|
+
if (error !== null && typeof error === "object" && "code" in error && error.code === "EEXIST")
|
|
7103
|
+
throw new OnboardingError("directory_exists", { nextAction: "Choose a new workspace directory; your existing files were not changed." });
|
|
7104
|
+
throw error;
|
|
7105
|
+
}
|
|
7106
|
+
const pack = join5(directory, "pack");
|
|
7107
|
+
const bindingsPath = join5(directory, "bindings.json");
|
|
7108
|
+
let installationId;
|
|
7109
|
+
let artifactsComplete = false;
|
|
7110
|
+
try {
|
|
7111
|
+
const imported = await importer.ingest(request.source);
|
|
7112
|
+
const definition = KnowledgeScopeDefinitionSchema.parse({
|
|
7113
|
+
packId: "local-project",
|
|
7114
|
+
version: "0.1.0",
|
|
7115
|
+
responsibility: "project-context",
|
|
7116
|
+
scope: { root: "tenant", descendants: ["namespace", "subject", "session"] },
|
|
7117
|
+
authority: { allowed: ["read", "draft"], forbidden: ["send", "approve", "mutate_source", "workflow"], precedence: ["primary", "operational", "approved", "observed", "derived"] },
|
|
7118
|
+
capabilities: [{ operationId: "search", provider: { kind: "local_documents", indexRef: imported.indexRef }, inputSchemaRef: "schemas/input.json", resultSchemaRef: "schemas/result.json", limits: { maxRows: 8, maxResultBytes: 131072 } }],
|
|
7119
|
+
contextPolicy: { mustConsider: [{ id: "project-evidence", minEvidence: 1, selector: { sourceClasses: ["document"] } }], mayConsider: [], mustNotUse: [{ id: "derived-context", selector: { authorities: ["derived"] } }] },
|
|
7120
|
+
evidence: { requireCitation: true, freshness: { defaultMaxAgeSeconds: 86400 }, coverageAssertions: ["project-evidence"] }
|
|
7121
|
+
});
|
|
7122
|
+
const bindings = { scopeAuthority: scopeAuthority.data, sourceBindings: [{ sourceId: "project-documents", sourceClass: "document", authority: "approved", permissionMode: "static", providerRef: imported.indexRef, operationIds: ["search"] }] };
|
|
7123
|
+
const input = { effectiveScope: { tenant: request.tenant }, input: { query: request.query } };
|
|
7124
|
+
await mkdir4(join5(pack, "schemas"), { recursive: true, mode: 448 });
|
|
7125
|
+
const files = {
|
|
7126
|
+
[join5(pack, "scope.json")]: parseJsonText(JSON.stringify(definition), "local definition"),
|
|
7127
|
+
[join5(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
|
|
7128
|
+
[join5(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
|
|
7129
|
+
[bindingsPath]: bindings,
|
|
7130
|
+
[join5(directory, "input.json")]: input
|
|
7131
|
+
};
|
|
7132
|
+
for (const [path, value] of Object.entries(files))
|
|
7133
|
+
await writeFile2(path, `${canonicalJson2(value)}
|
|
7134
|
+
`, { flag: "wx", mode: 384 });
|
|
7135
|
+
await dependencies.authoring.lock(pack);
|
|
7136
|
+
artifactsComplete = true;
|
|
7137
|
+
const mounted = KnowledgeScopeMountSchema.parse(await dependencies.embedded.mount(await dependencies.authoring.mountPayload(pack, bindingsPath)));
|
|
7138
|
+
installationId = mounted.installationId;
|
|
7139
|
+
await writeFile2(join5(directory, "installation.json"), `${JSON.stringify(mounted)}
|
|
7140
|
+
`, { flag: "wx", mode: 384 });
|
|
7141
|
+
const result = await dependencies.embedded.run({ installationId, operationId: "search", expectedRevision: mounted.revision, ...input });
|
|
7142
|
+
return {
|
|
7143
|
+
directory,
|
|
7144
|
+
installationId,
|
|
7145
|
+
tenant: request.tenant,
|
|
7146
|
+
operationId: "search",
|
|
7147
|
+
documentCount: imported.documentCount,
|
|
7148
|
+
chunkCount: imported.chunkCount,
|
|
7149
|
+
result,
|
|
7150
|
+
nextQuery: ["schift-ks", "query", installationId, "--query", "Your next question"],
|
|
7151
|
+
status: "completed",
|
|
7152
|
+
storage: "local_snapshot",
|
|
7153
|
+
note: "Documents stay on this computer. Search uses keywords, not semantic embeddings. Re-import into a new workspace to refresh the snapshot."
|
|
7154
|
+
};
|
|
7155
|
+
} catch (error) {
|
|
7156
|
+
const code = error instanceof LocalDocumentError || error instanceof OnboardingError || error instanceof KnowledgeScopeProductError || error instanceof KnowledgeScopeApiError ? error.code : "local_quickstart_failed";
|
|
7157
|
+
const recovery = installationId !== undefined ? ["schift-ks", "query", installationId, "--query", request.query] : artifactsComplete ? ["schift-ks", "mount", pack, "--bindings", bindingsPath] : ["schift-ks", "quickstart", "<new-workspace>", "--source", "<file-or-directory>", "--query", "<question>"];
|
|
7158
|
+
throw new OnboardingError(code, { directory, artifactsComplete, recovery, nextAction: artifactsComplete ? "Use the recovery command to retry; your source files were not changed." : "Check that your source contains readable .md or .txt files, then retry with a new workspace. Your source files were not changed.", ...installationId === undefined ? {} : { installationId } });
|
|
7159
|
+
}
|
|
7160
|
+
};
|
|
7161
|
+
|
|
7162
|
+
// src/local-query.ts
|
|
7163
|
+
var queryProject = async (request, application) => {
|
|
7164
|
+
if (request.query.trim().length === 0 || request.query.length > 8192)
|
|
7165
|
+
throw new OnboardingError("argument_invalid");
|
|
7166
|
+
const { mount } = exports_external.object({ mount: KnowledgeScopeMountSchema }).parse(await application.inspect(request.installationId));
|
|
7167
|
+
if (mount.installationId !== request.installationId)
|
|
7168
|
+
throw new OnboardingError("installation_mismatch");
|
|
7169
|
+
return application.run({
|
|
7170
|
+
installationId: mount.installationId,
|
|
7171
|
+
operationId: "search",
|
|
7172
|
+
effectiveScope: { tenant: mount.scopeAuthority.tenant },
|
|
7173
|
+
expectedRevision: mount.revision,
|
|
7174
|
+
input: { query: request.query }
|
|
7175
|
+
});
|
|
7176
|
+
};
|
|
7177
|
+
|
|
6749
7178
|
// src/doctor-config.ts
|
|
6750
7179
|
var ActionsSchema = exports_external.array(exports_external.object({ connectorRef: exports_external.string().min(1), connectorAlias: exports_external.string().min(1), actionId: exports_external.string().min(1) }).strict()).min(1);
|
|
6751
7180
|
var doctorConfiguration = (inspection, environment) => {
|
|
@@ -6754,6 +7183,10 @@ var doctorConfiguration = (inspection, environment) => {
|
|
|
6754
7183
|
for (const capability of definition.capabilities.filter((entry) => mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)))) {
|
|
6755
7184
|
let scopeEnvironment;
|
|
6756
7185
|
switch (capability.provider.kind) {
|
|
7186
|
+
case "local_documents":
|
|
7187
|
+
if ((capability.requiredProviderScopes?.length ?? 0) > 0)
|
|
7188
|
+
issues.push("local_documents_provider_scopes_unsupported");
|
|
7189
|
+
break;
|
|
6757
7190
|
case "schift_search":
|
|
6758
7191
|
issues.push(...searchConfiguration(environment));
|
|
6759
7192
|
if (environment["SCHIFT_KS_SEARCH_ORGANIZATION_ID"] !== mount.scopeAuthority.organizationId)
|
|
@@ -6804,8 +7237,8 @@ var doctor = async (request, application, environment) => {
|
|
|
6804
7237
|
throw new OnboardingError("argument_invalid");
|
|
6805
7238
|
const inspection = InspectionSchema.parse(await application.inspect(request.installationId));
|
|
6806
7239
|
const { definition, lock, mount } = inspection;
|
|
6807
|
-
const
|
|
6808
|
-
const integrity = lock.definitionDigest ===
|
|
7240
|
+
const digest3 = await digestKnowledgeScopeDefinition(definition);
|
|
7241
|
+
const integrity = lock.definitionDigest === digest3 && mount.definitionDigest === digest3 && mount.installationId === request.installationId;
|
|
6809
7242
|
const issues = doctorConfiguration(inspection, environment);
|
|
6810
7243
|
const configured = integrity && mount.state === "mounted" && issues.length === 0;
|
|
6811
7244
|
const report = { installationId: mount.installationId, integrity, state: mount.state, environment: [...new Set(issues)], configured };
|
|
@@ -6813,7 +7246,7 @@ var doctor = async (request, application, environment) => {
|
|
|
6813
7246
|
return { ...report, status: configured ? "configured" : "attention_required", evidenceVerified: false };
|
|
6814
7247
|
if (!configured)
|
|
6815
7248
|
throw new OnboardingError("configuration_invalid", { ...report });
|
|
6816
|
-
const capability = definition.capabilities.find((entry) => entry.provider.kind === "schift_search" && mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)));
|
|
7249
|
+
const capability = definition.capabilities.find((entry) => (entry.provider.kind === "schift_search" || entry.provider.kind === "local_documents") && mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)));
|
|
6817
7250
|
if (capability === undefined)
|
|
6818
7251
|
throw new OnboardingError("probe_operation_unavailable");
|
|
6819
7252
|
const result = await application.run({ installationId: mount.installationId, operationId: capability.operationId, effectiveScope: { tenant: mount.scopeAuthority.tenant }, expectedRevision: mount.revision, input: { query: request.query } });
|
|
@@ -6821,10 +7254,12 @@ var doctor = async (request, application, environment) => {
|
|
|
6821
7254
|
};
|
|
6822
7255
|
|
|
6823
7256
|
// src/cli.ts
|
|
6824
|
-
var COMMANDS = ["init", "validate", "lock", "mount", "inspect", "run", "run-batch", "admit", "unmount", "serve", "quickstart", "doctor"];
|
|
7257
|
+
var COMMANDS = ["init", "validate", "lock", "mount", "inspect", "run", "run-batch", "admit", "unmount", "serve", "quickstart", "query", "doctor"];
|
|
6825
7258
|
var HELP = {
|
|
6826
7259
|
bin: "schift-ks",
|
|
6827
7260
|
commands: COMMANDS,
|
|
7261
|
+
start: { example: "schift-ks quickstart ./my-project --source ./notes.md --query 'What is the refund policy?'", supported: [".md", ".txt", "directory"], accountRequired: false, behavior: "Imports a local snapshot; no upload or model calls." },
|
|
7262
|
+
followUp: "schift-ks query <installation-id> --query 'Your next question'",
|
|
6828
7263
|
serve: { apiTokenEnvironment: "SCHIFT_KS_API_TOKEN", loopbackOnly: true, tokenRequired: true }
|
|
6829
7264
|
};
|
|
6830
7265
|
var option = (args, name) => {
|
|
@@ -6865,7 +7300,7 @@ var candidateBatch = (value) => {
|
|
|
6865
7300
|
throw new CliUsageError("input_invalid");
|
|
6866
7301
|
};
|
|
6867
7302
|
var initialDefinition = (directory) => {
|
|
6868
|
-
const inferred =
|
|
7303
|
+
const inferred = basename2(resolve4(directory)).toLowerCase().replace(/[^a-z0-9._:-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6869
7304
|
const packId = inferred.length >= 2 ? inferred.slice(0, 128) : "example-scope";
|
|
6870
7305
|
return {
|
|
6871
7306
|
authority: {
|
|
@@ -6891,8 +7326,8 @@ var initialDefinition = (directory) => {
|
|
|
6891
7326
|
};
|
|
6892
7327
|
};
|
|
6893
7328
|
var initialize = async (directory) => {
|
|
6894
|
-
await
|
|
6895
|
-
const file = await
|
|
7329
|
+
await mkdir5(directory, { recursive: true, mode: 448 });
|
|
7330
|
+
const file = await open4(resolve4(directory, "scope.json"), "wx", 384);
|
|
6896
7331
|
try {
|
|
6897
7332
|
await file.writeFile(`${canonicalJson2(initialDefinition(directory))}
|
|
6898
7333
|
`, "utf8");
|
|
@@ -6913,8 +7348,14 @@ var execute = async (argv, dependencies) => {
|
|
|
6913
7348
|
const apiUrl = option(args, "--api-url");
|
|
6914
7349
|
const application = apiUrl === undefined ? dependencies.embedded : dependencies.remote(apiUrl);
|
|
6915
7350
|
switch (command) {
|
|
6916
|
-
case "quickstart":
|
|
7351
|
+
case "quickstart": {
|
|
7352
|
+
const source = option(args, "--source");
|
|
7353
|
+
if (source !== undefined)
|
|
7354
|
+
return localQuickstart({ directory: positional[0] ?? "", source, tenant: option(args, "--tenant") ?? "local-tenant", query: requiredOption(args, "--query") }, dependencies);
|
|
6917
7355
|
return quickstart({ directory: positional[0] ?? "", index: requiredOption(args, "--index"), tenant: requiredOption(args, "--tenant"), query: requiredOption(args, "--query") }, dependencies, dependencies.environment ?? {});
|
|
7356
|
+
}
|
|
7357
|
+
case "query":
|
|
7358
|
+
return queryProject({ installationId: positional[0] ?? "", query: requiredOption(args, "--query") }, application);
|
|
6918
7359
|
case "doctor":
|
|
6919
7360
|
return doctor({ installationId: positional[0] ?? "", ...args.includes("--probe") ? { query: requiredOption(args, "--query") } : {} }, application, dependencies.environment ?? {});
|
|
6920
7361
|
case "init":
|
|
@@ -6995,7 +7436,7 @@ var execute = async (argv, dependencies) => {
|
|
|
6995
7436
|
}
|
|
6996
7437
|
};
|
|
6997
7438
|
var errorCode = (error) => {
|
|
6998
|
-
if (error instanceof OnboardingError || error instanceof CliUsageError || error instanceof KnowledgeScopeApiError || error instanceof KnowledgeScopeProductError || error instanceof HttpProviderAdapterError) {
|
|
7439
|
+
if (error instanceof LocalDocumentError || error instanceof OnboardingError || error instanceof CliUsageError || error instanceof KnowledgeScopeApiError || error instanceof KnowledgeScopeProductError || error instanceof HttpProviderAdapterError) {
|
|
6999
7440
|
return error.code;
|
|
7000
7441
|
}
|
|
7001
7442
|
return "internal_error";
|
|
@@ -7010,16 +7451,16 @@ var runKnowledgeScopeCli = async (argv, dependencies, streams) => {
|
|
|
7010
7451
|
}
|
|
7011
7452
|
};
|
|
7012
7453
|
// src/main.ts
|
|
7013
|
-
import { constants as
|
|
7014
|
-
import { open as
|
|
7454
|
+
import { constants as constants5 } from "node:fs";
|
|
7455
|
+
import { open as open7 } from "node:fs/promises";
|
|
7015
7456
|
|
|
7016
7457
|
// src/portable.ts
|
|
7017
|
-
import { constants as
|
|
7018
|
-
import { lstat as
|
|
7019
|
-
import { dirname, join as
|
|
7020
|
-
import { randomUUID as
|
|
7458
|
+
import { constants as constants3 } from "node:fs";
|
|
7459
|
+
import { lstat as lstat4, mkdir as mkdir6, open as open5, readdir, rename, rm as rm3 } from "node:fs/promises";
|
|
7460
|
+
import { dirname as dirname2, join as join6, relative as relative2, resolve as resolve5, sep as sep2 } from "node:path";
|
|
7461
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
7021
7462
|
var normalizeRelativePath = (path) => {
|
|
7022
|
-
const normalized = path.split(
|
|
7463
|
+
const normalized = path.split(sep2).join("/");
|
|
7023
7464
|
if (normalized.length === 0 || normalized.startsWith("/") || normalized.includes("\\") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
7024
7465
|
throw productError("path_invalid");
|
|
7025
7466
|
}
|
|
@@ -7027,12 +7468,12 @@ var normalizeRelativePath = (path) => {
|
|
|
7027
7468
|
};
|
|
7028
7469
|
var hasCode2 = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
|
|
7029
7470
|
var readNoFollowText = async (path, maxBytes = DEFAULT_MAX_JSON_BYTES) => {
|
|
7030
|
-
if (typeof
|
|
7471
|
+
if (typeof constants3.O_NOFOLLOW !== "number" || constants3.O_NOFOLLOW === 0) {
|
|
7031
7472
|
throw productError("path_invalid");
|
|
7032
7473
|
}
|
|
7033
7474
|
let handle;
|
|
7034
7475
|
try {
|
|
7035
|
-
handle = await
|
|
7476
|
+
handle = await open5(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
7036
7477
|
} catch (error) {
|
|
7037
7478
|
if (hasCode2(error, "ELOOP"))
|
|
7038
7479
|
throw productError("path_invalid");
|
|
@@ -7062,18 +7503,18 @@ var readNoFollowText = async (path, maxBytes = DEFAULT_MAX_JSON_BYTES) => {
|
|
|
7062
7503
|
}
|
|
7063
7504
|
};
|
|
7064
7505
|
var walkJsonFiles = async (root, current = root) => {
|
|
7065
|
-
const metadata = await
|
|
7506
|
+
const metadata = await lstat4(current);
|
|
7066
7507
|
if (metadata.isSymbolicLink())
|
|
7067
7508
|
throw productError("path_invalid");
|
|
7068
7509
|
if (!metadata.isDirectory())
|
|
7069
7510
|
throw productError("path_invalid");
|
|
7070
7511
|
const paths = [];
|
|
7071
7512
|
for (const entry of await readdir(current, { withFileTypes: true })) {
|
|
7072
|
-
const absolute =
|
|
7073
|
-
const entryMetadata = await
|
|
7513
|
+
const absolute = join6(current, entry.name);
|
|
7514
|
+
const entryMetadata = await lstat4(absolute);
|
|
7074
7515
|
if (entryMetadata.isSymbolicLink())
|
|
7075
7516
|
throw productError("path_invalid");
|
|
7076
|
-
const path = normalizeRelativePath(
|
|
7517
|
+
const path = normalizeRelativePath(relative2(root, absolute));
|
|
7077
7518
|
if (entryMetadata.isDirectory()) {
|
|
7078
7519
|
if (path !== ".schift")
|
|
7079
7520
|
paths.push(...await walkJsonFiles(root, absolute));
|
|
@@ -7088,8 +7529,8 @@ var walkJsonFiles = async (root, current = root) => {
|
|
|
7088
7529
|
};
|
|
7089
7530
|
var readJsonFile = async (root, path) => {
|
|
7090
7531
|
const normalized = normalizeRelativePath(path);
|
|
7091
|
-
const absolute =
|
|
7092
|
-
const prefix = `${
|
|
7532
|
+
const absolute = resolve5(root, normalized);
|
|
7533
|
+
const prefix = `${resolve5(root)}${sep2}`;
|
|
7093
7534
|
if (!absolute.startsWith(prefix))
|
|
7094
7535
|
throw productError("path_invalid");
|
|
7095
7536
|
const text = await readNoFollowText(absolute).catch((error) => {
|
|
@@ -7108,7 +7549,7 @@ var declaredPaths = (definition) => [
|
|
|
7108
7549
|
]))
|
|
7109
7550
|
].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
7110
7551
|
var loadPortableScope = async (directory, options = {}) => {
|
|
7111
|
-
const root =
|
|
7552
|
+
const root = resolve5(directory);
|
|
7112
7553
|
const discovered = await walkJsonFiles(root);
|
|
7113
7554
|
if (!discovered.includes("scope.json"))
|
|
7114
7555
|
throw productError("portable_file_missing");
|
|
@@ -7128,7 +7569,7 @@ var loadPortableScope = async (directory, options = {}) => {
|
|
|
7128
7569
|
compileSchemaSubset(files[path]);
|
|
7129
7570
|
if (options.readLock === false)
|
|
7130
7571
|
return { directory: root, definition: parsedDefinition.data, files };
|
|
7131
|
-
const lockPath =
|
|
7572
|
+
const lockPath = join6(root, "scope.lock.json");
|
|
7132
7573
|
try {
|
|
7133
7574
|
const rawLock = parseJsonText(await readNoFollowText(lockPath), "scope.lock.json");
|
|
7134
7575
|
const parsedLock = KnowledgeScopeLockSchema.safeParse(rawLock);
|
|
@@ -7145,10 +7586,10 @@ var loadPortableScope = async (directory, options = {}) => {
|
|
|
7145
7586
|
var createPortableLock = async (directory) => {
|
|
7146
7587
|
const portable = await loadPortableScope(directory, { readLock: false });
|
|
7147
7588
|
const lock = await buildKnowledgeScopeLock(portable.definition, portable.files);
|
|
7148
|
-
const lockPath =
|
|
7149
|
-
const temporaryPath =
|
|
7150
|
-
await
|
|
7151
|
-
const handle = await
|
|
7589
|
+
const lockPath = join6(portable.directory, "scope.lock.json");
|
|
7590
|
+
const temporaryPath = join6(dirname2(lockPath), `.scope-lock-${randomUUID4()}.tmp`);
|
|
7591
|
+
await mkdir6(dirname2(lockPath), { recursive: true });
|
|
7592
|
+
const handle = await open5(temporaryPath, "wx", 420);
|
|
7152
7593
|
try {
|
|
7153
7594
|
await handle.writeFile(`${canonicalJson2(lock)}
|
|
7154
7595
|
`, "utf8");
|
|
@@ -7156,7 +7597,7 @@ var createPortableLock = async (directory) => {
|
|
|
7156
7597
|
await rename(temporaryPath, lockPath);
|
|
7157
7598
|
} finally {
|
|
7158
7599
|
await handle.close();
|
|
7159
|
-
await
|
|
7600
|
+
await rm3(temporaryPath, { force: true });
|
|
7160
7601
|
}
|
|
7161
7602
|
return lock;
|
|
7162
7603
|
};
|
|
@@ -7168,11 +7609,11 @@ var verifyPortableLock = async (directory) => {
|
|
|
7168
7609
|
};
|
|
7169
7610
|
|
|
7170
7611
|
// src/state-store.ts
|
|
7171
|
-
import { constants as
|
|
7172
|
-
import { chmod as chmod2, link as
|
|
7173
|
-
import { homedir as
|
|
7174
|
-
import { dirname as
|
|
7175
|
-
import { randomUUID as
|
|
7612
|
+
import { constants as constants4 } from "node:fs";
|
|
7613
|
+
import { chmod as chmod2, link as link3, lstat as lstat5, mkdir as mkdir7, open as open6, rename as rename2, rm as rm4 } from "node:fs/promises";
|
|
7614
|
+
import { homedir as homedir3 } from "node:os";
|
|
7615
|
+
import { dirname as dirname3, join as join7 } from "node:path";
|
|
7616
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
7176
7617
|
|
|
7177
7618
|
// src/state-contract.ts
|
|
7178
7619
|
var StateJsonValueSchema = exports_external.lazy(() => exports_external.union([
|
|
@@ -7238,21 +7679,21 @@ var hasCode3 = (value, code) => {
|
|
|
7238
7679
|
return value.code === code;
|
|
7239
7680
|
};
|
|
7240
7681
|
var assertOwnerOnly2 = async (path, expectedDirectory) => {
|
|
7241
|
-
const metadata = await
|
|
7682
|
+
const metadata = await lstat5(path);
|
|
7242
7683
|
if (metadata.isSymbolicLink() || (metadata.mode & 63) !== 0 || (expectedDirectory ? !metadata.isDirectory() : !metadata.isFile())) {
|
|
7243
7684
|
throw productError("state_permissions_invalid");
|
|
7244
7685
|
}
|
|
7245
7686
|
};
|
|
7246
7687
|
var noFollowReadFlags = () => {
|
|
7247
|
-
if (typeof
|
|
7688
|
+
if (typeof constants4.O_NOFOLLOW !== "number" || constants4.O_NOFOLLOW === 0) {
|
|
7248
7689
|
throw productError("state_permissions_invalid");
|
|
7249
7690
|
}
|
|
7250
|
-
return
|
|
7691
|
+
return constants4.O_RDONLY | constants4.O_NOFOLLOW;
|
|
7251
7692
|
};
|
|
7252
7693
|
var readOwnerOnlyFile = async (path, maxBytes) => {
|
|
7253
7694
|
let handle;
|
|
7254
7695
|
try {
|
|
7255
|
-
handle = await
|
|
7696
|
+
handle = await open6(path, noFollowReadFlags());
|
|
7256
7697
|
} catch (error) {
|
|
7257
7698
|
if (hasCode3(error, "ELOOP"))
|
|
7258
7699
|
throw productError("state_permissions_invalid");
|
|
@@ -7290,13 +7731,13 @@ class KnowledgeScopeStateStore {
|
|
|
7290
7731
|
faults;
|
|
7291
7732
|
constructor(options = {}) {
|
|
7292
7733
|
const environment = options.environment ?? process.env;
|
|
7293
|
-
this.home = options.home ?? environment["SCHIFT_KS_HOME"] ??
|
|
7294
|
-
this.statePath =
|
|
7295
|
-
this.lockPath =
|
|
7734
|
+
this.home = options.home ?? environment["SCHIFT_KS_HOME"] ?? join7(homedir3(), ".schift", "knowledge-scope");
|
|
7735
|
+
this.statePath = join7(this.home, "state.json");
|
|
7736
|
+
this.lockPath = join7(this.home, "state.lock");
|
|
7296
7737
|
this.faults = options.faults;
|
|
7297
7738
|
}
|
|
7298
7739
|
async initialize() {
|
|
7299
|
-
const created = await
|
|
7740
|
+
const created = await mkdir7(this.home, { recursive: true, mode: 448 });
|
|
7300
7741
|
if (created === undefined)
|
|
7301
7742
|
await assertOwnerOnly2(this.home, true);
|
|
7302
7743
|
else
|
|
@@ -7314,21 +7755,21 @@ class KnowledgeScopeStateStore {
|
|
|
7314
7755
|
}
|
|
7315
7756
|
}
|
|
7316
7757
|
async createInitialState() {
|
|
7317
|
-
const temporaryPath =
|
|
7318
|
-
const handle = await
|
|
7758
|
+
const temporaryPath = join7(dirname3(this.statePath), `.state-init-${process.pid}-${randomUUID5()}.tmp`);
|
|
7759
|
+
const handle = await open6(temporaryPath, "wx", 384);
|
|
7319
7760
|
try {
|
|
7320
7761
|
await handle.writeFile(serializeState(EMPTY_KNOWLEDGE_SCOPE_STATE), "utf8");
|
|
7321
7762
|
await handle.sync();
|
|
7322
7763
|
if (this.faults !== undefined)
|
|
7323
7764
|
await this.faults.beforeRename();
|
|
7324
7765
|
try {
|
|
7325
|
-
await
|
|
7766
|
+
await link3(temporaryPath, this.statePath);
|
|
7326
7767
|
} catch (error) {
|
|
7327
7768
|
if (!hasCode3(error, "EEXIST"))
|
|
7328
7769
|
throw error;
|
|
7329
7770
|
return;
|
|
7330
7771
|
}
|
|
7331
|
-
const directory = await
|
|
7772
|
+
const directory = await open6(this.home, "r");
|
|
7332
7773
|
try {
|
|
7333
7774
|
await directory.sync();
|
|
7334
7775
|
} finally {
|
|
@@ -7336,7 +7777,7 @@ class KnowledgeScopeStateStore {
|
|
|
7336
7777
|
}
|
|
7337
7778
|
} finally {
|
|
7338
7779
|
await handle.close();
|
|
7339
|
-
await
|
|
7780
|
+
await rm4(temporaryPath, { force: true });
|
|
7340
7781
|
}
|
|
7341
7782
|
}
|
|
7342
7783
|
async read() {
|
|
@@ -7368,13 +7809,13 @@ class KnowledgeScopeStateStore {
|
|
|
7368
7809
|
return mutation.value;
|
|
7369
7810
|
} finally {
|
|
7370
7811
|
await lockHandle.close();
|
|
7371
|
-
await
|
|
7812
|
+
await rm4(this.lockPath, { force: true });
|
|
7372
7813
|
}
|
|
7373
7814
|
}
|
|
7374
7815
|
async acquireLock() {
|
|
7375
7816
|
let handle;
|
|
7376
7817
|
try {
|
|
7377
|
-
handle = await
|
|
7818
|
+
handle = await open6(this.lockPath, "wx", 384);
|
|
7378
7819
|
} catch (error) {
|
|
7379
7820
|
if (!hasCode3(error, "EEXIST"))
|
|
7380
7821
|
throw error;
|
|
@@ -7390,28 +7831,28 @@ class KnowledgeScopeStateStore {
|
|
|
7390
7831
|
throw productError("lock_conflict");
|
|
7391
7832
|
}
|
|
7392
7833
|
try {
|
|
7393
|
-
const owner = { pid: process.pid, createdAt: Date.now(), nonce:
|
|
7834
|
+
const owner = { pid: process.pid, createdAt: Date.now(), nonce: randomUUID5() };
|
|
7394
7835
|
await handle.writeFile(`${canonicalJson2(owner)}
|
|
7395
7836
|
`, "utf8");
|
|
7396
7837
|
await handle.sync();
|
|
7397
7838
|
return handle;
|
|
7398
7839
|
} catch (error) {
|
|
7399
7840
|
await handle.close();
|
|
7400
|
-
await
|
|
7841
|
+
await rm4(this.lockPath, { force: true });
|
|
7401
7842
|
throw error;
|
|
7402
7843
|
}
|
|
7403
7844
|
}
|
|
7404
7845
|
async writeAtomically(state) {
|
|
7405
7846
|
const serialized = serializeState(state);
|
|
7406
|
-
const temporaryPath =
|
|
7407
|
-
const handle = await
|
|
7847
|
+
const temporaryPath = join7(dirname3(this.statePath), `.state-${process.pid}-${randomUUID5()}.tmp`);
|
|
7848
|
+
const handle = await open6(temporaryPath, "wx", 384);
|
|
7408
7849
|
try {
|
|
7409
7850
|
await handle.writeFile(serialized, "utf8");
|
|
7410
7851
|
await handle.sync();
|
|
7411
7852
|
if (this.faults !== undefined)
|
|
7412
7853
|
await this.faults.beforeRename();
|
|
7413
7854
|
await rename2(temporaryPath, this.statePath);
|
|
7414
|
-
const directory = await
|
|
7855
|
+
const directory = await open6(this.home, "r");
|
|
7415
7856
|
try {
|
|
7416
7857
|
await directory.sync();
|
|
7417
7858
|
} finally {
|
|
@@ -7420,7 +7861,7 @@ class KnowledgeScopeStateStore {
|
|
|
7420
7861
|
await chmod2(this.statePath, 384);
|
|
7421
7862
|
} finally {
|
|
7422
7863
|
await handle.close();
|
|
7423
|
-
await
|
|
7864
|
+
await rm4(temporaryPath, { force: true });
|
|
7424
7865
|
}
|
|
7425
7866
|
}
|
|
7426
7867
|
}
|
|
@@ -7435,11 +7876,11 @@ var toJsonValue = (value) => {
|
|
|
7435
7876
|
var isJsonObject3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7436
7877
|
var hasCode4 = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
|
|
7437
7878
|
var readNoFollowText2 = async (path) => {
|
|
7438
|
-
if (typeof
|
|
7879
|
+
if (typeof constants5.O_NOFOLLOW !== "number" || constants5.O_NOFOLLOW === 0)
|
|
7439
7880
|
throw productError("path_invalid");
|
|
7440
7881
|
let handle;
|
|
7441
7882
|
try {
|
|
7442
|
-
handle = await
|
|
7883
|
+
handle = await open7(path, constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
7443
7884
|
} catch (error) {
|
|
7444
7885
|
if (hasCode4(error, "ELOOP"))
|
|
7445
7886
|
throw productError("path_invalid");
|
|
@@ -7582,16 +8023,21 @@ var createCliDependencies = (options = {}) => {
|
|
|
7582
8023
|
const apiToken = environment["SCHIFT_KS_API_TOKEN"];
|
|
7583
8024
|
const storageOptions = { environment, ...options.home === undefined ? {} : { home: options.home } };
|
|
7584
8025
|
const store = new KnowledgeScopeStateStore(storageOptions);
|
|
8026
|
+
const localDocuments = new LocalDocumentStore(storageOptions);
|
|
8027
|
+
const httpProvider = environmentProvider(environment);
|
|
7585
8028
|
const authorization = new KnowledgeScopeAuthorization(storageOptions);
|
|
7586
8029
|
const application = new KnowledgeScopeApplication({
|
|
7587
8030
|
store,
|
|
7588
8031
|
authorization,
|
|
7589
|
-
provider: options.provider ??
|
|
8032
|
+
provider: options.provider ?? {
|
|
8033
|
+
execute: (context) => context.capability.provider.kind === "local_documents" ? localDocuments.execute(context) : httpProvider.execute(context)
|
|
8034
|
+
}
|
|
7590
8035
|
});
|
|
7591
8036
|
const embedded = applicationPort(application);
|
|
7592
8037
|
return {
|
|
7593
8038
|
environment,
|
|
7594
8039
|
authoring,
|
|
8040
|
+
localDocuments,
|
|
7595
8041
|
embedded,
|
|
7596
8042
|
remote: (apiUrl) => createRemoteKnowledgeScopeApplication(apiUrl, globalThis.fetch, apiToken),
|
|
7597
8043
|
readJson,
|
|
@@ -7824,11 +8270,11 @@ var createKnowledgeScopeClient = (options) => {
|
|
|
7824
8270
|
};
|
|
7825
8271
|
};
|
|
7826
8272
|
// src/evaluation.ts
|
|
7827
|
-
import { createHash as
|
|
8273
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
7828
8274
|
|
|
7829
8275
|
// src/evaluation/contracts.ts
|
|
7830
8276
|
var id = exports_external.string().min(1).max(256);
|
|
7831
|
-
var
|
|
8277
|
+
var digest3 = exports_external.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
7832
8278
|
var decision = exports_external.enum(["ready", "insufficient_evidence"]);
|
|
7833
8279
|
var evidence = exports_external.object({ evidenceId: id, citation: exports_external.string().min(1).max(4096) }).strict();
|
|
7834
8280
|
var unique = (values) => new Set(values).size === values.length;
|
|
@@ -7836,7 +8282,7 @@ var evidenceList = exports_external.array(evidence).max(100).refine((rows) => un
|
|
|
7836
8282
|
var capturedEvidenceList = exports_external.array(evidence.extend({ citation: exports_external.string().max(4096).nullable() })).max(100).refine((rows) => unique(rows.map((row) => row.evidenceId)), "Duplicate captured evidence IDs");
|
|
7837
8283
|
var question = exports_external.object({
|
|
7838
8284
|
questionId: id,
|
|
7839
|
-
inputFingerprint:
|
|
8285
|
+
inputFingerprint: digest3,
|
|
7840
8286
|
expectedDecision: decision,
|
|
7841
8287
|
expectedEvidence: evidenceList,
|
|
7842
8288
|
forbiddenEvidenceIds: exports_external.array(id).max(100).refine(unique, "Duplicate forbidden evidence IDs")
|
|
@@ -7844,18 +8290,18 @@ var question = exports_external.object({
|
|
|
7844
8290
|
var retrievalDatasetSchema = exports_external.object({
|
|
7845
8291
|
datasetId: id,
|
|
7846
8292
|
provenance: exports_external.enum(["synthetic", "customer_provided"]),
|
|
7847
|
-
packDigest:
|
|
7848
|
-
sourceSnapshotDigest:
|
|
8293
|
+
packDigest: digest3,
|
|
8294
|
+
sourceSnapshotDigest: digest3,
|
|
7849
8295
|
questions: exports_external.array(question).min(1).max(1000).refine((rows) => unique(rows.map((row) => row.questionId)), "Duplicate question IDs")
|
|
7850
8296
|
}).strict();
|
|
7851
8297
|
var retrievalCaptureSchema = exports_external.object({
|
|
7852
8298
|
adapter: id,
|
|
7853
|
-
datasetFingerprint:
|
|
7854
|
-
packDigest:
|
|
7855
|
-
sourceSnapshotDigest:
|
|
8299
|
+
datasetFingerprint: digest3,
|
|
8300
|
+
packDigest: digest3,
|
|
8301
|
+
sourceSnapshotDigest: digest3,
|
|
7856
8302
|
results: exports_external.array(exports_external.object({
|
|
7857
8303
|
questionId: id,
|
|
7858
|
-
inputFingerprint:
|
|
8304
|
+
inputFingerprint: digest3,
|
|
7859
8305
|
decision,
|
|
7860
8306
|
items: capturedEvidenceList
|
|
7861
8307
|
}).strict()).max(1000).refine((rows) => unique(rows.map((row) => row.questionId)), "Duplicate captured question IDs")
|
|
@@ -7868,7 +8314,7 @@ var retrievalEvaluationSchema = exports_external.object({
|
|
|
7868
8314
|
var count = exports_external.number().int().min(0).max(1e5);
|
|
7869
8315
|
var evaluationQuestionReportSchema = exports_external.object({
|
|
7870
8316
|
questionId: id,
|
|
7871
|
-
inputFingerprint:
|
|
8317
|
+
inputFingerprint: digest3,
|
|
7872
8318
|
expectedEvidenceCount: count,
|
|
7873
8319
|
retrievedAtKCount: count,
|
|
7874
8320
|
matchedAtKCount: count,
|
|
@@ -7883,10 +8329,10 @@ var retrievalReportSchema = exports_external.object({
|
|
|
7883
8329
|
datasetId: id,
|
|
7884
8330
|
provenance: exports_external.enum(["synthetic", "customer_provided"]),
|
|
7885
8331
|
adapter: id,
|
|
7886
|
-
datasetFingerprint:
|
|
7887
|
-
inputFingerprint:
|
|
7888
|
-
packDigest:
|
|
7889
|
-
sourceSnapshotDigest:
|
|
8332
|
+
datasetFingerprint: digest3,
|
|
8333
|
+
inputFingerprint: digest3,
|
|
8334
|
+
packDigest: digest3,
|
|
8335
|
+
sourceSnapshotDigest: digest3,
|
|
7890
8336
|
topK: exports_external.number().int().min(1).max(100),
|
|
7891
8337
|
questions: exports_external.array(evaluationQuestionReportSchema).min(1).max(1000).refine((rows) => unique(rows.map((row) => row.questionId)), "Duplicate report question IDs"),
|
|
7892
8338
|
metrics: exports_external.object({
|
|
@@ -7907,7 +8353,7 @@ class RetrievalEvaluationError extends Error {
|
|
|
7907
8353
|
}
|
|
7908
8354
|
|
|
7909
8355
|
// src/evaluation.ts
|
|
7910
|
-
var hash = (value) => `sha256:${
|
|
8356
|
+
var hash = (value) => `sha256:${createHash4("sha256").update(canonicalJson2(value)).digest("hex")}`;
|
|
7911
8357
|
var byId = (left, right) => left.questionId < right.questionId ? -1 : left.questionId > right.questionId ? 1 : 0;
|
|
7912
8358
|
var ratio = (numerator, denominator) => denominator === 0 ? null : numerator / denominator;
|
|
7913
8359
|
function datasetFingerprint(dataset) {
|
|
@@ -8030,6 +8476,9 @@ export {
|
|
|
8030
8476
|
RetrievalEvaluationError,
|
|
8031
8477
|
MAX_CANDIDATES,
|
|
8032
8478
|
MAX_BINDINGS,
|
|
8479
|
+
LocalDocumentStore,
|
|
8480
|
+
LocalDocumentError,
|
|
8481
|
+
LOCAL_LIMITS,
|
|
8033
8482
|
KnowledgeScopeStateStore,
|
|
8034
8483
|
KnowledgeScopeProductError,
|
|
8035
8484
|
KnowledgeScopeClientError,
|