@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/main.js
CHANGED
|
@@ -15,8 +15,8 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/main.ts
|
|
18
|
-
import { constants as
|
|
19
|
-
import { open as
|
|
18
|
+
import { constants as constants5 } from "node:fs";
|
|
19
|
+
import { open as open7 } from "node:fs/promises";
|
|
20
20
|
|
|
21
21
|
// ../node_modules/zod/v3/external.js
|
|
22
22
|
var exports_external = {};
|
|
@@ -4125,6 +4125,10 @@ var SearchProviderSchema = exports_external.object({
|
|
|
4125
4125
|
kind: exports_external.literal("schift_search"),
|
|
4126
4126
|
indexRef: KnowledgeScopeIdentifierSchema
|
|
4127
4127
|
}).strict();
|
|
4128
|
+
var LocalDocumentsProviderSchema = exports_external.object({
|
|
4129
|
+
kind: exports_external.literal("local_documents"),
|
|
4130
|
+
indexRef: KnowledgeScopeIdentifierSchema
|
|
4131
|
+
}).strict();
|
|
4128
4132
|
var WebProviderSchema = exports_external.object({
|
|
4129
4133
|
kind: exports_external.literal("web_search"),
|
|
4130
4134
|
provider: exports_external.enum(["customer", "schift"])
|
|
@@ -4133,6 +4137,7 @@ var QueryProviderSchema = exports_external.discriminatedUnion("kind", [
|
|
|
4133
4137
|
RecordsProviderSchema,
|
|
4134
4138
|
ConnectorProviderSchema,
|
|
4135
4139
|
SearchProviderSchema,
|
|
4140
|
+
LocalDocumentsProviderSchema,
|
|
4136
4141
|
WebProviderSchema
|
|
4137
4142
|
]).readonly();
|
|
4138
4143
|
var QueryCapabilitySchema = exports_external.object({
|
|
@@ -4241,6 +4246,7 @@ var CandidateProviderEvidenceSchema = exports_external.discriminatedUnion("kind"
|
|
|
4241
4246
|
auditPersisted: exports_external.boolean()
|
|
4242
4247
|
}).strict(),
|
|
4243
4248
|
SearchProviderSchema,
|
|
4249
|
+
LocalDocumentsProviderSchema,
|
|
4244
4250
|
WebProviderSchema
|
|
4245
4251
|
]).readonly();
|
|
4246
4252
|
var CandidateEnvelopeSchema = exports_external.object({
|
|
@@ -4442,6 +4448,10 @@ var ProviderEvidenceSchema = exports_external.discriminatedUnion("kind", [
|
|
|
4442
4448
|
kind: exports_external.literal("schift_search"),
|
|
4443
4449
|
indexRef: KnowledgeScopeIdentifierSchema
|
|
4444
4450
|
}).strict(),
|
|
4451
|
+
exports_external.object({
|
|
4452
|
+
kind: exports_external.literal("local_documents"),
|
|
4453
|
+
indexRef: KnowledgeScopeIdentifierSchema
|
|
4454
|
+
}).strict(),
|
|
4445
4455
|
exports_external.object({
|
|
4446
4456
|
kind: exports_external.literal("web_search"),
|
|
4447
4457
|
provider: exports_external.enum(["customer", "schift"])
|
|
@@ -4571,6 +4581,8 @@ var providerEvidenceDenial = (capability, binding, candidate) => {
|
|
|
4571
4581
|
return evidence.auditPersisted ? undefined : "connector_audit_missing";
|
|
4572
4582
|
case "schift_search":
|
|
4573
4583
|
return evidence.kind === "schift_search" && evidence.indexRef === capability.provider.indexRef ? undefined : "provider_evidence_mismatch";
|
|
4584
|
+
case "local_documents":
|
|
4585
|
+
return evidence.kind === "local_documents" && evidence.indexRef === capability.provider.indexRef ? undefined : "provider_evidence_mismatch";
|
|
4574
4586
|
case "web_search":
|
|
4575
4587
|
return evidence.kind === "web_search" && evidence.provider === capability.provider.provider ? undefined : "provider_evidence_mismatch";
|
|
4576
4588
|
}
|
|
@@ -5378,6 +5390,8 @@ var providerReference = (capability) => {
|
|
|
5378
5390
|
return capability.provider.actionId;
|
|
5379
5391
|
case "schift_search":
|
|
5380
5392
|
return capability.provider.indexRef;
|
|
5393
|
+
case "local_documents":
|
|
5394
|
+
return capability.provider.indexRef;
|
|
5381
5395
|
case "web_search":
|
|
5382
5396
|
return capability.provider.provider;
|
|
5383
5397
|
}
|
|
@@ -6214,6 +6228,7 @@ var createHttpProviderExecutionPort = (options) => ({
|
|
|
6214
6228
|
return parsed.data;
|
|
6215
6229
|
});
|
|
6216
6230
|
}
|
|
6231
|
+
case "local_documents":
|
|
6217
6232
|
case "web_search":
|
|
6218
6233
|
throw new HttpProviderAdapterError("invalid_configuration");
|
|
6219
6234
|
}
|
|
@@ -6224,6 +6239,154 @@ var createHttpProviderExecutionPort = (options) => ({
|
|
|
6224
6239
|
import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
6225
6240
|
import { createServer } from "node:http";
|
|
6226
6241
|
|
|
6242
|
+
// src/local-documents/files.ts
|
|
6243
|
+
import { constants as constants2 } from "node:fs";
|
|
6244
|
+
import { lstat as lstat2, open as open2, opendir, realpath } from "node:fs/promises";
|
|
6245
|
+
import { dirname, extname, join as join2, relative, resolve, sep } from "node:path";
|
|
6246
|
+
class LocalDocumentError extends Error {
|
|
6247
|
+
code;
|
|
6248
|
+
name = "LocalDocumentError";
|
|
6249
|
+
constructor(code) {
|
|
6250
|
+
super({
|
|
6251
|
+
local_source_invalid: "Select regular UTF-8 Markdown (.md) or text (.txt) files; symlinks, hardlinks and binary files are unsupported.",
|
|
6252
|
+
local_limit_exceeded: "Local documents exceed the supported file, byte, or chunk limits.",
|
|
6253
|
+
local_snapshot_invalid: "Local document snapshot is missing, unsafe, or corrupt; ingest the source again.",
|
|
6254
|
+
local_scope_invalid: "Local documents require the mounted tenant and document binding; filters and narrowed scopes are unsupported."
|
|
6255
|
+
}[code]);
|
|
6256
|
+
this.code = code;
|
|
6257
|
+
}
|
|
6258
|
+
}
|
|
6259
|
+
var LOCAL_LIMITS = { files: 100, fileBytes: 1048576, totalBytes: 8388608, snapshotBytes: 16777216, chunks: 4000, entries: 2000, depth: 16, chunkCharacters: 2000 };
|
|
6260
|
+
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;
|
|
6261
|
+
var checkDirectories = async (directories) => {
|
|
6262
|
+
for (const directory of directories) {
|
|
6263
|
+
const current = await lstat2(directory.path);
|
|
6264
|
+
if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== directory.dev || current.ino !== directory.ino || await realpath(directory.path) !== directory.path)
|
|
6265
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6266
|
+
}
|
|
6267
|
+
};
|
|
6268
|
+
var mapLocalFileError = (error, ownerOnly) => {
|
|
6269
|
+
if (error instanceof Error && "code" in error && typeof error.code === "string" && ["ENOENT", "ELOOP", "EACCES", "EPERM", "ENOTDIR", "EISDIR"].includes(error.code)) {
|
|
6270
|
+
throw new LocalDocumentError(ownerOnly ? "local_snapshot_invalid" : "local_source_invalid");
|
|
6271
|
+
}
|
|
6272
|
+
throw error;
|
|
6273
|
+
};
|
|
6274
|
+
var readBounded = async (source, maxBytes, ownerOnly = false) => {
|
|
6275
|
+
try {
|
|
6276
|
+
return await readFileContents(source, maxBytes, ownerOnly);
|
|
6277
|
+
} catch (error) {
|
|
6278
|
+
return mapLocalFileError(error, ownerOnly);
|
|
6279
|
+
}
|
|
6280
|
+
};
|
|
6281
|
+
var readFileContents = async (source, maxBytes, ownerOnly) => {
|
|
6282
|
+
if (!constants2.O_NOFOLLOW)
|
|
6283
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6284
|
+
const path = typeof source === "string" ? source : source.path;
|
|
6285
|
+
if (typeof source !== "string") {
|
|
6286
|
+
await checkDirectories(source.ancestors);
|
|
6287
|
+
const currentPath = await realpath(path);
|
|
6288
|
+
const inside = relative(source.root, currentPath);
|
|
6289
|
+
if (currentPath !== path || inside === ".." || inside.startsWith(`..${sep}`))
|
|
6290
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6291
|
+
}
|
|
6292
|
+
const handle = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
6293
|
+
try {
|
|
6294
|
+
const before = await handle.stat();
|
|
6295
|
+
if (typeof source !== "string") {
|
|
6296
|
+
if (!identityMatches(source.identity, before))
|
|
6297
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6298
|
+
await checkDirectories(source.ancestors);
|
|
6299
|
+
}
|
|
6300
|
+
if (!before.isFile() || ownerOnly && ((before.mode & 63) !== 0 || process.getuid !== undefined && before.uid !== process.getuid()))
|
|
6301
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6302
|
+
if (before.size > maxBytes)
|
|
6303
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6304
|
+
const buffer = Buffer.alloc(maxBytes + 1);
|
|
6305
|
+
let length = 0;
|
|
6306
|
+
while (length < buffer.length) {
|
|
6307
|
+
const { bytesRead } = await handle.read(buffer, length, buffer.length - length, length);
|
|
6308
|
+
if (bytesRead === 0)
|
|
6309
|
+
break;
|
|
6310
|
+
length += bytesRead;
|
|
6311
|
+
}
|
|
6312
|
+
const after = await handle.stat();
|
|
6313
|
+
if (length > maxBytes)
|
|
6314
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6315
|
+
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || typeof source !== "string" && !identityMatches(source.identity, after))
|
|
6316
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6317
|
+
if (typeof source !== "string")
|
|
6318
|
+
await checkDirectories(source.ancestors);
|
|
6319
|
+
const text = decodeUtf8Strict(buffer.subarray(0, length));
|
|
6320
|
+
if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/u.test(text))
|
|
6321
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6322
|
+
return text;
|
|
6323
|
+
} finally {
|
|
6324
|
+
await handle.close();
|
|
6325
|
+
}
|
|
6326
|
+
};
|
|
6327
|
+
var collectFiles = async (source) => {
|
|
6328
|
+
try {
|
|
6329
|
+
return await collectSourceFiles(source);
|
|
6330
|
+
} catch (error) {
|
|
6331
|
+
return mapLocalFileError(error, false);
|
|
6332
|
+
}
|
|
6333
|
+
};
|
|
6334
|
+
var collectSourceFiles = async (source) => {
|
|
6335
|
+
const selected = resolve(source);
|
|
6336
|
+
const selectedMetadata = await lstat2(selected);
|
|
6337
|
+
if (selectedMetadata.isSymbolicLink())
|
|
6338
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6339
|
+
const root = await realpath(selected);
|
|
6340
|
+
const rootMetadata = await lstat2(root);
|
|
6341
|
+
if (selectedMetadata.dev !== rootMetadata.dev || selectedMetadata.ino !== rootMetadata.ino)
|
|
6342
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6343
|
+
const boundary = rootMetadata.isDirectory() ? root : dirname(root);
|
|
6344
|
+
const parentMetadata = await lstat2(dirname(boundary));
|
|
6345
|
+
const parents = [{ path: dirname(boundary), dev: parentMetadata.dev, ino: parentMetadata.ino }];
|
|
6346
|
+
const files = [];
|
|
6347
|
+
let entries = 1;
|
|
6348
|
+
const visit = async (path, depth, ancestors) => {
|
|
6349
|
+
await checkDirectories(ancestors);
|
|
6350
|
+
if (entries > LOCAL_LIMITS.entries || depth > LOCAL_LIMITS.depth)
|
|
6351
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6352
|
+
const metadata = await lstat2(path);
|
|
6353
|
+
if (metadata.isSymbolicLink())
|
|
6354
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6355
|
+
if (metadata.isDirectory()) {
|
|
6356
|
+
const directories = [...ancestors, { path, dev: metadata.dev, ino: metadata.ino }];
|
|
6357
|
+
await checkDirectories(directories);
|
|
6358
|
+
const directory = await opendir(path);
|
|
6359
|
+
for await (const child of directory) {
|
|
6360
|
+
entries += 1;
|
|
6361
|
+
if (entries > LOCAL_LIMITS.entries)
|
|
6362
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6363
|
+
if (child.name.startsWith(".") || child.name === "node_modules")
|
|
6364
|
+
continue;
|
|
6365
|
+
await visit(join2(path, child.name), depth + 1, directories);
|
|
6366
|
+
}
|
|
6367
|
+
await checkDirectories(directories);
|
|
6368
|
+
return;
|
|
6369
|
+
}
|
|
6370
|
+
if (!metadata.isFile())
|
|
6371
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6372
|
+
if (![".md", ".txt"].includes(extname(path).toLowerCase())) {
|
|
6373
|
+
if (depth === 0)
|
|
6374
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6375
|
+
return;
|
|
6376
|
+
}
|
|
6377
|
+
if (metadata.nlink !== 1 || await realpath(path) !== path)
|
|
6378
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6379
|
+
files.push({ path, root: boundary, identity: metadata, ancestors });
|
|
6380
|
+
if (files.length > LOCAL_LIMITS.files)
|
|
6381
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6382
|
+
};
|
|
6383
|
+
const boundaryMetadata = await lstat2(boundary);
|
|
6384
|
+
await visit(root, 0, rootMetadata.isDirectory() ? parents : [...parents, { path: boundary, dev: boundaryMetadata.dev, ino: boundaryMetadata.ino }]);
|
|
6385
|
+
if (files.length === 0)
|
|
6386
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6387
|
+
return files.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
6388
|
+
};
|
|
6389
|
+
|
|
6227
6390
|
// src/api-contract.ts
|
|
6228
6391
|
var JsonValueSchema3 = exports_external.lazy(() => exports_external.union([
|
|
6229
6392
|
exports_external.null(),
|
|
@@ -6388,6 +6551,8 @@ var createKnowledgeScopeApi = (options) => {
|
|
|
6388
6551
|
return errorResponse(error.code, error.status);
|
|
6389
6552
|
if (error instanceof HttpProviderAdapterError)
|
|
6390
6553
|
return errorResponse(error.code, 502);
|
|
6554
|
+
if (error instanceof LocalDocumentError)
|
|
6555
|
+
return errorResponse(error.code, error.code === "local_snapshot_invalid" ? 503 : 400);
|
|
6391
6556
|
if (error instanceof KnowledgeScopeProductError)
|
|
6392
6557
|
return errorResponse(error.code, 400);
|
|
6393
6558
|
if (error instanceof SyntaxError || error instanceof URIError)
|
|
@@ -6447,11 +6612,11 @@ var serveKnowledgeScopeApi = async (options) => {
|
|
|
6447
6612
|
writeNodeResponse(outgoing, errorResponse(code, status));
|
|
6448
6613
|
});
|
|
6449
6614
|
});
|
|
6450
|
-
await new Promise((
|
|
6615
|
+
await new Promise((resolve2, reject) => {
|
|
6451
6616
|
server.once("error", reject);
|
|
6452
6617
|
server.listen(options.port ?? 8787, host, () => {
|
|
6453
6618
|
server.off("error", reject);
|
|
6454
|
-
|
|
6619
|
+
resolve2();
|
|
6455
6620
|
});
|
|
6456
6621
|
});
|
|
6457
6622
|
const address = server.address();
|
|
@@ -6529,8 +6694,8 @@ var createRemoteKnowledgeScopeApplication = (apiUrl, request = globalThis.fetch,
|
|
|
6529
6694
|
var isJsonObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6530
6695
|
|
|
6531
6696
|
// src/cli.ts
|
|
6532
|
-
import { mkdir as
|
|
6533
|
-
import { basename, resolve as
|
|
6697
|
+
import { mkdir as mkdir5, open as open4 } from "node:fs/promises";
|
|
6698
|
+
import { basename as basename2, resolve as resolve4 } from "node:path";
|
|
6534
6699
|
|
|
6535
6700
|
// src/cli-options.ts
|
|
6536
6701
|
class CliUsageError extends Error {
|
|
@@ -6547,12 +6712,13 @@ var specifications = {
|
|
|
6547
6712
|
lock: { count: 1, values: [] },
|
|
6548
6713
|
mount: { count: 1, values: ["--bindings", "--api-url"] },
|
|
6549
6714
|
inspect: { count: 1, values: ["--api-url"] },
|
|
6715
|
+
query: { count: 1, values: ["--query", "--api-url"] },
|
|
6550
6716
|
run: { count: 2, values: ["--input", "--api-url"] },
|
|
6551
6717
|
"run-batch": { count: 1, values: ["--input", "--api-url"] },
|
|
6552
6718
|
admit: { count: 1, values: ["--candidate", "--api-url"] },
|
|
6553
6719
|
unmount: { count: 1, values: ["--expected-revision", "--api-url"] },
|
|
6554
6720
|
serve: { count: 0, values: ["--host", "--port"] },
|
|
6555
|
-
quickstart: { count: 1, values: ["--index", "--tenant", "--query"] },
|
|
6721
|
+
quickstart: { count: 1, values: ["--source", "--index", "--tenant", "--query"] },
|
|
6556
6722
|
doctor: { count: 1, values: ["--query"], flags: ["--probe"] }
|
|
6557
6723
|
};
|
|
6558
6724
|
var parseCliOptions = (command, args) => {
|
|
@@ -6587,12 +6753,14 @@ var parseCliOptions = (command, args) => {
|
|
|
6587
6753
|
throw new CliUsageError("argument_invalid");
|
|
6588
6754
|
if (command === "doctor" && seen.has("--probe") !== seen.has("--query"))
|
|
6589
6755
|
throw new CliUsageError("argument_invalid");
|
|
6756
|
+
if (command === "quickstart" && seen.has("--source") && seen.has("--index"))
|
|
6757
|
+
throw new CliUsageError("argument_invalid");
|
|
6590
6758
|
return positional;
|
|
6591
6759
|
};
|
|
6592
6760
|
|
|
6593
6761
|
// src/quickstart.ts
|
|
6594
6762
|
import { mkdir as mkdir2, writeFile } from "node:fs/promises";
|
|
6595
|
-
import { join as
|
|
6763
|
+
import { join as join3, resolve as resolve2 } from "node:path";
|
|
6596
6764
|
|
|
6597
6765
|
// src/onboarding-config.ts
|
|
6598
6766
|
class OnboardingError extends Error {
|
|
@@ -6639,7 +6807,7 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6639
6807
|
});
|
|
6640
6808
|
if (!definition.success)
|
|
6641
6809
|
throw new OnboardingError("argument_invalid");
|
|
6642
|
-
const directory =
|
|
6810
|
+
const directory = resolve2(request.directory);
|
|
6643
6811
|
try {
|
|
6644
6812
|
await mkdir2(directory, { mode: 448 });
|
|
6645
6813
|
} catch (error) {
|
|
@@ -6647,20 +6815,20 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6647
6815
|
throw new OnboardingError("directory_exists");
|
|
6648
6816
|
throw error;
|
|
6649
6817
|
}
|
|
6650
|
-
const pack =
|
|
6651
|
-
const bindingsPath =
|
|
6652
|
-
const inputPath =
|
|
6818
|
+
const pack = join3(directory, "pack");
|
|
6819
|
+
const bindingsPath = join3(directory, "bindings.json");
|
|
6820
|
+
const inputPath = join3(directory, "input.json");
|
|
6653
6821
|
let installationId;
|
|
6654
6822
|
let artifactsComplete = false;
|
|
6655
6823
|
const recovery = () => installationId === undefined ? ["schift-ks", "mount", pack, "--bindings", bindingsPath] : ["schift-ks", "run", installationId, "search", "--input", inputPath];
|
|
6656
6824
|
try {
|
|
6657
|
-
await mkdir2(
|
|
6825
|
+
await mkdir2(join3(pack, "schemas"), { recursive: true, mode: 448 });
|
|
6658
6826
|
const bindings = { scopeAuthority: scopeAuthority.data, sourceBindings: [{ sourceId: "project-documents", sourceClass: "document", authority: "approved", permissionMode: "mirrored", providerRef: request.index, operationIds: ["search"] }] };
|
|
6659
6827
|
const input = { effectiveScope: { tenant: request.tenant }, input: { query: request.query } };
|
|
6660
6828
|
const files = {
|
|
6661
|
-
[
|
|
6662
|
-
[
|
|
6663
|
-
[
|
|
6829
|
+
[join3(pack, "scope.json")]: json(definition.data),
|
|
6830
|
+
[join3(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
|
|
6831
|
+
[join3(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
|
|
6664
6832
|
[bindingsPath]: json(bindings),
|
|
6665
6833
|
[inputPath]: input
|
|
6666
6834
|
};
|
|
@@ -6671,7 +6839,7 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6671
6839
|
artifactsComplete = true;
|
|
6672
6840
|
const mounted = KnowledgeScopeMountSchema.parse(await dependencies.embedded.mount(await dependencies.authoring.mountPayload(pack, bindingsPath)));
|
|
6673
6841
|
installationId = mounted.installationId;
|
|
6674
|
-
await writeFile(
|
|
6842
|
+
await writeFile(join3(directory, "installation.json"), `${JSON.stringify(mounted)}
|
|
6675
6843
|
`, { flag: "wx", mode: 384 });
|
|
6676
6844
|
const result = await dependencies.embedded.run({ installationId, operationId: "search", ...input });
|
|
6677
6845
|
return { directory, installationId, operationId: "search", result, recovery: recovery(), status: "completed" };
|
|
@@ -6681,6 +6849,266 @@ var quickstart = async (request, dependencies, environment) => {
|
|
|
6681
6849
|
}
|
|
6682
6850
|
};
|
|
6683
6851
|
|
|
6852
|
+
// src/local-quickstart.ts
|
|
6853
|
+
import { mkdir as mkdir4, writeFile as writeFile2 } from "node:fs/promises";
|
|
6854
|
+
import { join as join5, resolve as resolve3 } from "node:path";
|
|
6855
|
+
|
|
6856
|
+
// src/local-documents/index.ts
|
|
6857
|
+
import { mkdir as mkdir3, lstat as lstat3, open as open3, link as link2, rm as rm2 } from "node:fs/promises";
|
|
6858
|
+
import { basename, join as join4 } from "node:path";
|
|
6859
|
+
import { fileURLToPath } from "node:url";
|
|
6860
|
+
import { homedir as homedir2 } from "node:os";
|
|
6861
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
6862
|
+
|
|
6863
|
+
// src/local-documents/snapshot.ts
|
|
6864
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
6865
|
+
import { pathToFileURL } from "node:url";
|
|
6866
|
+
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();
|
|
6867
|
+
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();
|
|
6868
|
+
var digest2 = (value) => createHash3("sha256").update(value).digest("hex");
|
|
6869
|
+
var snapshotId = (snapshot) => `local:${digest2(canonicalJson2(snapshot))}`;
|
|
6870
|
+
var buildSnapshot = async (source) => {
|
|
6871
|
+
const documents = [];
|
|
6872
|
+
let bytes = 0;
|
|
6873
|
+
for (const file of await collectFiles(source)) {
|
|
6874
|
+
const text = await readBounded(file, LOCAL_LIMITS.fileBytes);
|
|
6875
|
+
bytes += Buffer.byteLength(text);
|
|
6876
|
+
if (bytes > LOCAL_LIMITS.totalBytes)
|
|
6877
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6878
|
+
documents.push({ uri: pathToFileURL(file.path).href, text, revision: `sha256:${digest2(text)}` });
|
|
6879
|
+
}
|
|
6880
|
+
return { version: 1, capturedAt: new Date().toISOString(), documents };
|
|
6881
|
+
};
|
|
6882
|
+
var chunksFor = (snapshot) => {
|
|
6883
|
+
const chunks = [];
|
|
6884
|
+
let bytes = 0;
|
|
6885
|
+
for (const document of snapshot.documents) {
|
|
6886
|
+
const uri = new URL(document.uri);
|
|
6887
|
+
if (uri.protocol !== "file:" || uri.host !== "" || uri.search !== "" || uri.hash !== "" || document.revision !== `sha256:${digest2(document.text)}`)
|
|
6888
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6889
|
+
bytes += Buffer.byteLength(document.text);
|
|
6890
|
+
if (bytes > LOCAL_LIMITS.totalBytes)
|
|
6891
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6892
|
+
const lines = document.text.split(/\r?\n/u);
|
|
6893
|
+
let text = "";
|
|
6894
|
+
let start = 1;
|
|
6895
|
+
let end = 1;
|
|
6896
|
+
const flush = () => {
|
|
6897
|
+
if (text.trim().length > 0)
|
|
6898
|
+
chunks.push({ document, text, lineStart: start, lineEnd: end, id: `chunk:${digest2(`${document.uri}:${start}:${end}:${text}`)}` });
|
|
6899
|
+
text = "";
|
|
6900
|
+
if (chunks.length > LOCAL_LIMITS.chunks)
|
|
6901
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6902
|
+
};
|
|
6903
|
+
lines.forEach((line, offset) => {
|
|
6904
|
+
if (line.length > LOCAL_LIMITS.chunkCharacters)
|
|
6905
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6906
|
+
if (text.length + line.length + 1 > LOCAL_LIMITS.chunkCharacters)
|
|
6907
|
+
flush();
|
|
6908
|
+
if (text.length === 0)
|
|
6909
|
+
start = offset + 1;
|
|
6910
|
+
text += `${text.length === 0 ? "" : `
|
|
6911
|
+
`}${line}`;
|
|
6912
|
+
end = offset + 1;
|
|
6913
|
+
});
|
|
6914
|
+
flush();
|
|
6915
|
+
}
|
|
6916
|
+
return chunks;
|
|
6917
|
+
};
|
|
6918
|
+
var lexicalTerms = (text) => {
|
|
6919
|
+
const words = text.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
6920
|
+
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]))];
|
|
6921
|
+
};
|
|
6922
|
+
|
|
6923
|
+
// src/local-documents/index.ts
|
|
6924
|
+
var QuerySchema = exports_external.object({ query: exports_external.string().min(1).max(8192) }).strict();
|
|
6925
|
+
var assertPrivateDirectory = async (path) => {
|
|
6926
|
+
await mkdir3(path, { recursive: true, mode: 448 });
|
|
6927
|
+
const info = await lstat3(path);
|
|
6928
|
+
if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 63) !== 0 || process.getuid !== undefined && info.uid !== process.getuid())
|
|
6929
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6930
|
+
};
|
|
6931
|
+
|
|
6932
|
+
class LocalDocumentStore {
|
|
6933
|
+
home;
|
|
6934
|
+
constructor(options = {}) {
|
|
6935
|
+
this.home = options.home ?? (options.environment ?? process.env)["SCHIFT_KS_HOME"] ?? join4(homedir2(), ".schift", "knowledge-scope");
|
|
6936
|
+
}
|
|
6937
|
+
async directory() {
|
|
6938
|
+
try {
|
|
6939
|
+
await assertPrivateDirectory(this.home);
|
|
6940
|
+
const directory = join4(this.home, "local-documents");
|
|
6941
|
+
await assertPrivateDirectory(directory);
|
|
6942
|
+
return directory;
|
|
6943
|
+
} catch (error) {
|
|
6944
|
+
return mapLocalFileError(error, true);
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6947
|
+
async ingest(sourcePath) {
|
|
6948
|
+
const snapshot = await buildSnapshot(sourcePath);
|
|
6949
|
+
const chunks = chunksFor(snapshot);
|
|
6950
|
+
if (chunks.length === 0)
|
|
6951
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6952
|
+
const indexRef = snapshotId(snapshot);
|
|
6953
|
+
const directory = await this.directory();
|
|
6954
|
+
const temporary = join4(directory, `.ingest-${randomUUID3()}`);
|
|
6955
|
+
const serialized = canonicalJson2(snapshot);
|
|
6956
|
+
if (Buffer.byteLength(serialized) > LOCAL_LIMITS.snapshotBytes)
|
|
6957
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
6958
|
+
const handle = await open3(temporary, "wx", 384);
|
|
6959
|
+
try {
|
|
6960
|
+
await handle.writeFile(serialized, "utf8");
|
|
6961
|
+
await handle.sync();
|
|
6962
|
+
try {
|
|
6963
|
+
await link2(temporary, join4(directory, `${indexRef.slice(6)}.json`));
|
|
6964
|
+
} catch (error) {
|
|
6965
|
+
if (!(error instanceof Error && ("code" in error) && error.code === "EEXIST"))
|
|
6966
|
+
throw error;
|
|
6967
|
+
}
|
|
6968
|
+
} finally {
|
|
6969
|
+
await handle.close();
|
|
6970
|
+
await rm2(temporary, { force: true });
|
|
6971
|
+
}
|
|
6972
|
+
return { indexRef, documentCount: snapshot.documents.length, chunkCount: chunks.length };
|
|
6973
|
+
}
|
|
6974
|
+
async read(indexRef) {
|
|
6975
|
+
if (!/^local:[a-f0-9]{64}$/u.test(indexRef))
|
|
6976
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6977
|
+
const directory = await this.directory();
|
|
6978
|
+
const text = await readBounded(join4(directory, `${indexRef.slice(6)}.json`), LOCAL_LIMITS.snapshotBytes, true);
|
|
6979
|
+
const parsed = SnapshotSchema.safeParse(parseJsonText(text, "local snapshot", { maxBytes: LOCAL_LIMITS.snapshotBytes }));
|
|
6980
|
+
if (!parsed.success || snapshotId(parsed.data) !== indexRef)
|
|
6981
|
+
throw new LocalDocumentError("local_snapshot_invalid");
|
|
6982
|
+
return parsed.data;
|
|
6983
|
+
}
|
|
6984
|
+
async execute(context) {
|
|
6985
|
+
const { capability, binding, request, mount } = context;
|
|
6986
|
+
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)
|
|
6987
|
+
throw new LocalDocumentError("local_scope_invalid");
|
|
6988
|
+
const query = QuerySchema.safeParse(request.input);
|
|
6989
|
+
if (!query.success || !context.validateInput(request.input).valid)
|
|
6990
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
6991
|
+
const snapshot = await this.read(capability.provider.indexRef);
|
|
6992
|
+
const terms = lexicalTerms(query.data.query);
|
|
6993
|
+
const ranked = chunksFor(snapshot).map((chunk) => {
|
|
6994
|
+
const tokens = new Set(lexicalTerms(chunk.text));
|
|
6995
|
+
return { chunk, score: terms.filter((term) => tokens.has(term)).length };
|
|
6996
|
+
}).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.chunk.id.localeCompare(b.chunk.id));
|
|
6997
|
+
const results = [];
|
|
6998
|
+
let bytes = 0;
|
|
6999
|
+
for (const { chunk, score } of ranked.slice(0, Math.min(capability.limits?.maxRows ?? 8, 100))) {
|
|
7000
|
+
const sourcePath = fileURLToPath(chunk.document.uri);
|
|
7001
|
+
const documentId = `doc:${digest2(chunk.document.uri)}`;
|
|
7002
|
+
const payload = { text: chunk.text, sourceId: binding.sourceId, documentId, sourcePath, chunkId: chunk.id, lineStart: chunk.lineStart, lineEnd: chunk.lineEnd, score };
|
|
7003
|
+
if (!context.validateResult(payload).valid)
|
|
7004
|
+
throw new LocalDocumentError("local_source_invalid");
|
|
7005
|
+
const result = ProviderResultSchema.parse({
|
|
7006
|
+
resultId: chunk.id,
|
|
7007
|
+
srn: `srn:local:${chunk.id.slice(6)}`,
|
|
7008
|
+
revision: chunk.document.revision,
|
|
7009
|
+
freshness: snapshot.capturedAt,
|
|
7010
|
+
payload,
|
|
7011
|
+
citation: { uri: `schift://local-documents/${capability.provider.indexRef}/${documentId}#L${chunk.lineStart}-L${chunk.lineEnd}`, label: `${basename(sourcePath)}:L${chunk.lineStart}-L${chunk.lineEnd}` },
|
|
7012
|
+
providerScopes: [],
|
|
7013
|
+
providerEvidence: { kind: "local_documents", indexRef: capability.provider.indexRef }
|
|
7014
|
+
});
|
|
7015
|
+
bytes += Buffer.byteLength(canonicalJson2(result));
|
|
7016
|
+
if (bytes > Math.min(capability.limits?.maxResultBytes ?? 1048576, 8388608))
|
|
7017
|
+
throw new LocalDocumentError("local_limit_exceeded");
|
|
7018
|
+
results.push(result);
|
|
7019
|
+
}
|
|
7020
|
+
return results;
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
|
|
7024
|
+
// src/local-quickstart.ts
|
|
7025
|
+
var localQuickstart = async (request, dependencies) => {
|
|
7026
|
+
const importer = dependencies.localDocuments;
|
|
7027
|
+
if (importer === undefined)
|
|
7028
|
+
throw new OnboardingError("local_import_unavailable");
|
|
7029
|
+
const scopeAuthority = ScopeAuthoritySchema.safeParse({ organizationId: "local-organization", tenant: request.tenant });
|
|
7030
|
+
if (!scopeAuthority.success || request.query.trim().length === 0 || request.query.length > 8192)
|
|
7031
|
+
throw new OnboardingError("argument_invalid");
|
|
7032
|
+
const directory = resolve3(request.directory);
|
|
7033
|
+
try {
|
|
7034
|
+
await mkdir4(directory, { mode: 448 });
|
|
7035
|
+
} catch (error) {
|
|
7036
|
+
if (error !== null && typeof error === "object" && "code" in error && error.code === "EEXIST")
|
|
7037
|
+
throw new OnboardingError("directory_exists", { nextAction: "Choose a new workspace directory; your existing files were not changed." });
|
|
7038
|
+
throw error;
|
|
7039
|
+
}
|
|
7040
|
+
const pack = join5(directory, "pack");
|
|
7041
|
+
const bindingsPath = join5(directory, "bindings.json");
|
|
7042
|
+
let installationId;
|
|
7043
|
+
let artifactsComplete = false;
|
|
7044
|
+
try {
|
|
7045
|
+
const imported = await importer.ingest(request.source);
|
|
7046
|
+
const definition = KnowledgeScopeDefinitionSchema.parse({
|
|
7047
|
+
packId: "local-project",
|
|
7048
|
+
version: "0.1.0",
|
|
7049
|
+
responsibility: "project-context",
|
|
7050
|
+
scope: { root: "tenant", descendants: ["namespace", "subject", "session"] },
|
|
7051
|
+
authority: { allowed: ["read", "draft"], forbidden: ["send", "approve", "mutate_source", "workflow"], precedence: ["primary", "operational", "approved", "observed", "derived"] },
|
|
7052
|
+
capabilities: [{ operationId: "search", provider: { kind: "local_documents", indexRef: imported.indexRef }, inputSchemaRef: "schemas/input.json", resultSchemaRef: "schemas/result.json", limits: { maxRows: 8, maxResultBytes: 131072 } }],
|
|
7053
|
+
contextPolicy: { mustConsider: [{ id: "project-evidence", minEvidence: 1, selector: { sourceClasses: ["document"] } }], mayConsider: [], mustNotUse: [{ id: "derived-context", selector: { authorities: ["derived"] } }] },
|
|
7054
|
+
evidence: { requireCitation: true, freshness: { defaultMaxAgeSeconds: 86400 }, coverageAssertions: ["project-evidence"] }
|
|
7055
|
+
});
|
|
7056
|
+
const bindings = { scopeAuthority: scopeAuthority.data, sourceBindings: [{ sourceId: "project-documents", sourceClass: "document", authority: "approved", permissionMode: "static", providerRef: imported.indexRef, operationIds: ["search"] }] };
|
|
7057
|
+
const input = { effectiveScope: { tenant: request.tenant }, input: { query: request.query } };
|
|
7058
|
+
await mkdir4(join5(pack, "schemas"), { recursive: true, mode: 448 });
|
|
7059
|
+
const files = {
|
|
7060
|
+
[join5(pack, "scope.json")]: parseJsonText(JSON.stringify(definition), "local definition"),
|
|
7061
|
+
[join5(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
|
|
7062
|
+
[join5(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
|
|
7063
|
+
[bindingsPath]: bindings,
|
|
7064
|
+
[join5(directory, "input.json")]: input
|
|
7065
|
+
};
|
|
7066
|
+
for (const [path, value] of Object.entries(files))
|
|
7067
|
+
await writeFile2(path, `${canonicalJson2(value)}
|
|
7068
|
+
`, { flag: "wx", mode: 384 });
|
|
7069
|
+
await dependencies.authoring.lock(pack);
|
|
7070
|
+
artifactsComplete = true;
|
|
7071
|
+
const mounted = KnowledgeScopeMountSchema.parse(await dependencies.embedded.mount(await dependencies.authoring.mountPayload(pack, bindingsPath)));
|
|
7072
|
+
installationId = mounted.installationId;
|
|
7073
|
+
await writeFile2(join5(directory, "installation.json"), `${JSON.stringify(mounted)}
|
|
7074
|
+
`, { flag: "wx", mode: 384 });
|
|
7075
|
+
const result = await dependencies.embedded.run({ installationId, operationId: "search", expectedRevision: mounted.revision, ...input });
|
|
7076
|
+
return {
|
|
7077
|
+
directory,
|
|
7078
|
+
installationId,
|
|
7079
|
+
tenant: request.tenant,
|
|
7080
|
+
operationId: "search",
|
|
7081
|
+
documentCount: imported.documentCount,
|
|
7082
|
+
chunkCount: imported.chunkCount,
|
|
7083
|
+
result,
|
|
7084
|
+
nextQuery: ["schift-ks", "query", installationId, "--query", "Your next question"],
|
|
7085
|
+
status: "completed",
|
|
7086
|
+
storage: "local_snapshot",
|
|
7087
|
+
note: "Documents stay on this computer. Search uses keywords, not semantic embeddings. Re-import into a new workspace to refresh the snapshot."
|
|
7088
|
+
};
|
|
7089
|
+
} catch (error) {
|
|
7090
|
+
const code = error instanceof LocalDocumentError || error instanceof OnboardingError || error instanceof KnowledgeScopeProductError || error instanceof KnowledgeScopeApiError ? error.code : "local_quickstart_failed";
|
|
7091
|
+
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>"];
|
|
7092
|
+
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 } });
|
|
7093
|
+
}
|
|
7094
|
+
};
|
|
7095
|
+
|
|
7096
|
+
// src/local-query.ts
|
|
7097
|
+
var queryProject = async (request, application) => {
|
|
7098
|
+
if (request.query.trim().length === 0 || request.query.length > 8192)
|
|
7099
|
+
throw new OnboardingError("argument_invalid");
|
|
7100
|
+
const { mount } = exports_external.object({ mount: KnowledgeScopeMountSchema }).parse(await application.inspect(request.installationId));
|
|
7101
|
+
if (mount.installationId !== request.installationId)
|
|
7102
|
+
throw new OnboardingError("installation_mismatch");
|
|
7103
|
+
return application.run({
|
|
7104
|
+
installationId: mount.installationId,
|
|
7105
|
+
operationId: "search",
|
|
7106
|
+
effectiveScope: { tenant: mount.scopeAuthority.tenant },
|
|
7107
|
+
expectedRevision: mount.revision,
|
|
7108
|
+
input: { query: request.query }
|
|
7109
|
+
});
|
|
7110
|
+
};
|
|
7111
|
+
|
|
6684
7112
|
// src/doctor-config.ts
|
|
6685
7113
|
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);
|
|
6686
7114
|
var doctorConfiguration = (inspection, environment) => {
|
|
@@ -6689,6 +7117,10 @@ var doctorConfiguration = (inspection, environment) => {
|
|
|
6689
7117
|
for (const capability of definition.capabilities.filter((entry) => mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)))) {
|
|
6690
7118
|
let scopeEnvironment;
|
|
6691
7119
|
switch (capability.provider.kind) {
|
|
7120
|
+
case "local_documents":
|
|
7121
|
+
if ((capability.requiredProviderScopes?.length ?? 0) > 0)
|
|
7122
|
+
issues.push("local_documents_provider_scopes_unsupported");
|
|
7123
|
+
break;
|
|
6692
7124
|
case "schift_search":
|
|
6693
7125
|
issues.push(...searchConfiguration(environment));
|
|
6694
7126
|
if (environment["SCHIFT_KS_SEARCH_ORGANIZATION_ID"] !== mount.scopeAuthority.organizationId)
|
|
@@ -6739,8 +7171,8 @@ var doctor = async (request, application, environment) => {
|
|
|
6739
7171
|
throw new OnboardingError("argument_invalid");
|
|
6740
7172
|
const inspection = InspectionSchema.parse(await application.inspect(request.installationId));
|
|
6741
7173
|
const { definition, lock, mount } = inspection;
|
|
6742
|
-
const
|
|
6743
|
-
const integrity = lock.definitionDigest ===
|
|
7174
|
+
const digest3 = await digestKnowledgeScopeDefinition(definition);
|
|
7175
|
+
const integrity = lock.definitionDigest === digest3 && mount.definitionDigest === digest3 && mount.installationId === request.installationId;
|
|
6744
7176
|
const issues = doctorConfiguration(inspection, environment);
|
|
6745
7177
|
const configured = integrity && mount.state === "mounted" && issues.length === 0;
|
|
6746
7178
|
const report = { installationId: mount.installationId, integrity, state: mount.state, environment: [...new Set(issues)], configured };
|
|
@@ -6748,7 +7180,7 @@ var doctor = async (request, application, environment) => {
|
|
|
6748
7180
|
return { ...report, status: configured ? "configured" : "attention_required", evidenceVerified: false };
|
|
6749
7181
|
if (!configured)
|
|
6750
7182
|
throw new OnboardingError("configuration_invalid", { ...report });
|
|
6751
|
-
const capability = definition.capabilities.find((entry) => entry.provider.kind === "schift_search" && mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)));
|
|
7183
|
+
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)));
|
|
6752
7184
|
if (capability === undefined)
|
|
6753
7185
|
throw new OnboardingError("probe_operation_unavailable");
|
|
6754
7186
|
const result = await application.run({ installationId: mount.installationId, operationId: capability.operationId, effectiveScope: { tenant: mount.scopeAuthority.tenant }, expectedRevision: mount.revision, input: { query: request.query } });
|
|
@@ -6756,10 +7188,12 @@ var doctor = async (request, application, environment) => {
|
|
|
6756
7188
|
};
|
|
6757
7189
|
|
|
6758
7190
|
// src/cli.ts
|
|
6759
|
-
var COMMANDS = ["init", "validate", "lock", "mount", "inspect", "run", "run-batch", "admit", "unmount", "serve", "quickstart", "doctor"];
|
|
7191
|
+
var COMMANDS = ["init", "validate", "lock", "mount", "inspect", "run", "run-batch", "admit", "unmount", "serve", "quickstart", "query", "doctor"];
|
|
6760
7192
|
var HELP = {
|
|
6761
7193
|
bin: "schift-ks",
|
|
6762
7194
|
commands: COMMANDS,
|
|
7195
|
+
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." },
|
|
7196
|
+
followUp: "schift-ks query <installation-id> --query 'Your next question'",
|
|
6763
7197
|
serve: { apiTokenEnvironment: "SCHIFT_KS_API_TOKEN", loopbackOnly: true, tokenRequired: true }
|
|
6764
7198
|
};
|
|
6765
7199
|
var option = (args, name) => {
|
|
@@ -6800,7 +7234,7 @@ var candidateBatch = (value) => {
|
|
|
6800
7234
|
throw new CliUsageError("input_invalid");
|
|
6801
7235
|
};
|
|
6802
7236
|
var initialDefinition = (directory) => {
|
|
6803
|
-
const inferred =
|
|
7237
|
+
const inferred = basename2(resolve4(directory)).toLowerCase().replace(/[^a-z0-9._:-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6804
7238
|
const packId = inferred.length >= 2 ? inferred.slice(0, 128) : "example-scope";
|
|
6805
7239
|
return {
|
|
6806
7240
|
authority: {
|
|
@@ -6826,8 +7260,8 @@ var initialDefinition = (directory) => {
|
|
|
6826
7260
|
};
|
|
6827
7261
|
};
|
|
6828
7262
|
var initialize = async (directory) => {
|
|
6829
|
-
await
|
|
6830
|
-
const file = await
|
|
7263
|
+
await mkdir5(directory, { recursive: true, mode: 448 });
|
|
7264
|
+
const file = await open4(resolve4(directory, "scope.json"), "wx", 384);
|
|
6831
7265
|
try {
|
|
6832
7266
|
await file.writeFile(`${canonicalJson2(initialDefinition(directory))}
|
|
6833
7267
|
`, "utf8");
|
|
@@ -6848,8 +7282,14 @@ var execute = async (argv, dependencies) => {
|
|
|
6848
7282
|
const apiUrl = option(args, "--api-url");
|
|
6849
7283
|
const application = apiUrl === undefined ? dependencies.embedded : dependencies.remote(apiUrl);
|
|
6850
7284
|
switch (command) {
|
|
6851
|
-
case "quickstart":
|
|
7285
|
+
case "quickstart": {
|
|
7286
|
+
const source = option(args, "--source");
|
|
7287
|
+
if (source !== undefined)
|
|
7288
|
+
return localQuickstart({ directory: positional[0] ?? "", source, tenant: option(args, "--tenant") ?? "local-tenant", query: requiredOption(args, "--query") }, dependencies);
|
|
6852
7289
|
return quickstart({ directory: positional[0] ?? "", index: requiredOption(args, "--index"), tenant: requiredOption(args, "--tenant"), query: requiredOption(args, "--query") }, dependencies, dependencies.environment ?? {});
|
|
7290
|
+
}
|
|
7291
|
+
case "query":
|
|
7292
|
+
return queryProject({ installationId: positional[0] ?? "", query: requiredOption(args, "--query") }, application);
|
|
6853
7293
|
case "doctor":
|
|
6854
7294
|
return doctor({ installationId: positional[0] ?? "", ...args.includes("--probe") ? { query: requiredOption(args, "--query") } : {} }, application, dependencies.environment ?? {});
|
|
6855
7295
|
case "init":
|
|
@@ -6930,7 +7370,7 @@ var execute = async (argv, dependencies) => {
|
|
|
6930
7370
|
}
|
|
6931
7371
|
};
|
|
6932
7372
|
var errorCode = (error) => {
|
|
6933
|
-
if (error instanceof OnboardingError || error instanceof CliUsageError || error instanceof KnowledgeScopeApiError || error instanceof KnowledgeScopeProductError || error instanceof HttpProviderAdapterError) {
|
|
7373
|
+
if (error instanceof LocalDocumentError || error instanceof OnboardingError || error instanceof CliUsageError || error instanceof KnowledgeScopeApiError || error instanceof KnowledgeScopeProductError || error instanceof HttpProviderAdapterError) {
|
|
6934
7374
|
return error.code;
|
|
6935
7375
|
}
|
|
6936
7376
|
return "internal_error";
|
|
@@ -6946,12 +7386,12 @@ var runKnowledgeScopeCli = async (argv, dependencies, streams) => {
|
|
|
6946
7386
|
};
|
|
6947
7387
|
|
|
6948
7388
|
// src/portable.ts
|
|
6949
|
-
import { constants as
|
|
6950
|
-
import { lstat as
|
|
6951
|
-
import { dirname, join as
|
|
6952
|
-
import { randomUUID as
|
|
7389
|
+
import { constants as constants3 } from "node:fs";
|
|
7390
|
+
import { lstat as lstat4, mkdir as mkdir6, open as open5, readdir, rename, rm as rm3 } from "node:fs/promises";
|
|
7391
|
+
import { dirname as dirname2, join as join6, relative as relative2, resolve as resolve5, sep as sep2 } from "node:path";
|
|
7392
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
6953
7393
|
var normalizeRelativePath = (path) => {
|
|
6954
|
-
const normalized = path.split(
|
|
7394
|
+
const normalized = path.split(sep2).join("/");
|
|
6955
7395
|
if (normalized.length === 0 || normalized.startsWith("/") || normalized.includes("\\") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
6956
7396
|
throw productError("path_invalid");
|
|
6957
7397
|
}
|
|
@@ -6959,12 +7399,12 @@ var normalizeRelativePath = (path) => {
|
|
|
6959
7399
|
};
|
|
6960
7400
|
var hasCode2 = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
|
|
6961
7401
|
var readNoFollowText = async (path, maxBytes = DEFAULT_MAX_JSON_BYTES) => {
|
|
6962
|
-
if (typeof
|
|
7402
|
+
if (typeof constants3.O_NOFOLLOW !== "number" || constants3.O_NOFOLLOW === 0) {
|
|
6963
7403
|
throw productError("path_invalid");
|
|
6964
7404
|
}
|
|
6965
7405
|
let handle;
|
|
6966
7406
|
try {
|
|
6967
|
-
handle = await
|
|
7407
|
+
handle = await open5(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
6968
7408
|
} catch (error) {
|
|
6969
7409
|
if (hasCode2(error, "ELOOP"))
|
|
6970
7410
|
throw productError("path_invalid");
|
|
@@ -6994,18 +7434,18 @@ var readNoFollowText = async (path, maxBytes = DEFAULT_MAX_JSON_BYTES) => {
|
|
|
6994
7434
|
}
|
|
6995
7435
|
};
|
|
6996
7436
|
var walkJsonFiles = async (root, current = root) => {
|
|
6997
|
-
const metadata = await
|
|
7437
|
+
const metadata = await lstat4(current);
|
|
6998
7438
|
if (metadata.isSymbolicLink())
|
|
6999
7439
|
throw productError("path_invalid");
|
|
7000
7440
|
if (!metadata.isDirectory())
|
|
7001
7441
|
throw productError("path_invalid");
|
|
7002
7442
|
const paths = [];
|
|
7003
7443
|
for (const entry of await readdir(current, { withFileTypes: true })) {
|
|
7004
|
-
const absolute =
|
|
7005
|
-
const entryMetadata = await
|
|
7444
|
+
const absolute = join6(current, entry.name);
|
|
7445
|
+
const entryMetadata = await lstat4(absolute);
|
|
7006
7446
|
if (entryMetadata.isSymbolicLink())
|
|
7007
7447
|
throw productError("path_invalid");
|
|
7008
|
-
const path = normalizeRelativePath(
|
|
7448
|
+
const path = normalizeRelativePath(relative2(root, absolute));
|
|
7009
7449
|
if (entryMetadata.isDirectory()) {
|
|
7010
7450
|
if (path !== ".schift")
|
|
7011
7451
|
paths.push(...await walkJsonFiles(root, absolute));
|
|
@@ -7020,8 +7460,8 @@ var walkJsonFiles = async (root, current = root) => {
|
|
|
7020
7460
|
};
|
|
7021
7461
|
var readJsonFile = async (root, path) => {
|
|
7022
7462
|
const normalized = normalizeRelativePath(path);
|
|
7023
|
-
const absolute =
|
|
7024
|
-
const prefix = `${
|
|
7463
|
+
const absolute = resolve5(root, normalized);
|
|
7464
|
+
const prefix = `${resolve5(root)}${sep2}`;
|
|
7025
7465
|
if (!absolute.startsWith(prefix))
|
|
7026
7466
|
throw productError("path_invalid");
|
|
7027
7467
|
const text = await readNoFollowText(absolute).catch((error) => {
|
|
@@ -7040,7 +7480,7 @@ var declaredPaths = (definition) => [
|
|
|
7040
7480
|
]))
|
|
7041
7481
|
].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
7042
7482
|
var loadPortableScope = async (directory, options = {}) => {
|
|
7043
|
-
const root =
|
|
7483
|
+
const root = resolve5(directory);
|
|
7044
7484
|
const discovered = await walkJsonFiles(root);
|
|
7045
7485
|
if (!discovered.includes("scope.json"))
|
|
7046
7486
|
throw productError("portable_file_missing");
|
|
@@ -7060,7 +7500,7 @@ var loadPortableScope = async (directory, options = {}) => {
|
|
|
7060
7500
|
compileSchemaSubset(files[path]);
|
|
7061
7501
|
if (options.readLock === false)
|
|
7062
7502
|
return { directory: root, definition: parsedDefinition.data, files };
|
|
7063
|
-
const lockPath =
|
|
7503
|
+
const lockPath = join6(root, "scope.lock.json");
|
|
7064
7504
|
try {
|
|
7065
7505
|
const rawLock = parseJsonText(await readNoFollowText(lockPath), "scope.lock.json");
|
|
7066
7506
|
const parsedLock = KnowledgeScopeLockSchema.safeParse(rawLock);
|
|
@@ -7077,10 +7517,10 @@ var loadPortableScope = async (directory, options = {}) => {
|
|
|
7077
7517
|
var createPortableLock = async (directory) => {
|
|
7078
7518
|
const portable = await loadPortableScope(directory, { readLock: false });
|
|
7079
7519
|
const lock = await buildKnowledgeScopeLock(portable.definition, portable.files);
|
|
7080
|
-
const lockPath =
|
|
7081
|
-
const temporaryPath =
|
|
7082
|
-
await
|
|
7083
|
-
const handle = await
|
|
7520
|
+
const lockPath = join6(portable.directory, "scope.lock.json");
|
|
7521
|
+
const temporaryPath = join6(dirname2(lockPath), `.scope-lock-${randomUUID4()}.tmp`);
|
|
7522
|
+
await mkdir6(dirname2(lockPath), { recursive: true });
|
|
7523
|
+
const handle = await open5(temporaryPath, "wx", 420);
|
|
7084
7524
|
try {
|
|
7085
7525
|
await handle.writeFile(`${canonicalJson2(lock)}
|
|
7086
7526
|
`, "utf8");
|
|
@@ -7088,7 +7528,7 @@ var createPortableLock = async (directory) => {
|
|
|
7088
7528
|
await rename(temporaryPath, lockPath);
|
|
7089
7529
|
} finally {
|
|
7090
7530
|
await handle.close();
|
|
7091
|
-
await
|
|
7531
|
+
await rm3(temporaryPath, { force: true });
|
|
7092
7532
|
}
|
|
7093
7533
|
return lock;
|
|
7094
7534
|
};
|
|
@@ -7100,11 +7540,11 @@ var verifyPortableLock = async (directory) => {
|
|
|
7100
7540
|
};
|
|
7101
7541
|
|
|
7102
7542
|
// src/state-store.ts
|
|
7103
|
-
import { constants as
|
|
7104
|
-
import { chmod as chmod2, link as
|
|
7105
|
-
import { homedir as
|
|
7106
|
-
import { dirname as
|
|
7107
|
-
import { randomUUID as
|
|
7543
|
+
import { constants as constants4 } from "node:fs";
|
|
7544
|
+
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";
|
|
7545
|
+
import { homedir as homedir3 } from "node:os";
|
|
7546
|
+
import { dirname as dirname3, join as join7 } from "node:path";
|
|
7547
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
7108
7548
|
|
|
7109
7549
|
// src/state-contract.ts
|
|
7110
7550
|
var StateJsonValueSchema = exports_external.lazy(() => exports_external.union([
|
|
@@ -7170,21 +7610,21 @@ var hasCode3 = (value, code) => {
|
|
|
7170
7610
|
return value.code === code;
|
|
7171
7611
|
};
|
|
7172
7612
|
var assertOwnerOnly2 = async (path, expectedDirectory) => {
|
|
7173
|
-
const metadata = await
|
|
7613
|
+
const metadata = await lstat5(path);
|
|
7174
7614
|
if (metadata.isSymbolicLink() || (metadata.mode & 63) !== 0 || (expectedDirectory ? !metadata.isDirectory() : !metadata.isFile())) {
|
|
7175
7615
|
throw productError("state_permissions_invalid");
|
|
7176
7616
|
}
|
|
7177
7617
|
};
|
|
7178
7618
|
var noFollowReadFlags = () => {
|
|
7179
|
-
if (typeof
|
|
7619
|
+
if (typeof constants4.O_NOFOLLOW !== "number" || constants4.O_NOFOLLOW === 0) {
|
|
7180
7620
|
throw productError("state_permissions_invalid");
|
|
7181
7621
|
}
|
|
7182
|
-
return
|
|
7622
|
+
return constants4.O_RDONLY | constants4.O_NOFOLLOW;
|
|
7183
7623
|
};
|
|
7184
7624
|
var readOwnerOnlyFile = async (path, maxBytes) => {
|
|
7185
7625
|
let handle;
|
|
7186
7626
|
try {
|
|
7187
|
-
handle = await
|
|
7627
|
+
handle = await open6(path, noFollowReadFlags());
|
|
7188
7628
|
} catch (error) {
|
|
7189
7629
|
if (hasCode3(error, "ELOOP"))
|
|
7190
7630
|
throw productError("state_permissions_invalid");
|
|
@@ -7222,13 +7662,13 @@ class KnowledgeScopeStateStore {
|
|
|
7222
7662
|
faults;
|
|
7223
7663
|
constructor(options = {}) {
|
|
7224
7664
|
const environment = options.environment ?? process.env;
|
|
7225
|
-
this.home = options.home ?? environment["SCHIFT_KS_HOME"] ??
|
|
7226
|
-
this.statePath =
|
|
7227
|
-
this.lockPath =
|
|
7665
|
+
this.home = options.home ?? environment["SCHIFT_KS_HOME"] ?? join7(homedir3(), ".schift", "knowledge-scope");
|
|
7666
|
+
this.statePath = join7(this.home, "state.json");
|
|
7667
|
+
this.lockPath = join7(this.home, "state.lock");
|
|
7228
7668
|
this.faults = options.faults;
|
|
7229
7669
|
}
|
|
7230
7670
|
async initialize() {
|
|
7231
|
-
const created = await
|
|
7671
|
+
const created = await mkdir7(this.home, { recursive: true, mode: 448 });
|
|
7232
7672
|
if (created === undefined)
|
|
7233
7673
|
await assertOwnerOnly2(this.home, true);
|
|
7234
7674
|
else
|
|
@@ -7246,21 +7686,21 @@ class KnowledgeScopeStateStore {
|
|
|
7246
7686
|
}
|
|
7247
7687
|
}
|
|
7248
7688
|
async createInitialState() {
|
|
7249
|
-
const temporaryPath =
|
|
7250
|
-
const handle = await
|
|
7689
|
+
const temporaryPath = join7(dirname3(this.statePath), `.state-init-${process.pid}-${randomUUID5()}.tmp`);
|
|
7690
|
+
const handle = await open6(temporaryPath, "wx", 384);
|
|
7251
7691
|
try {
|
|
7252
7692
|
await handle.writeFile(serializeState(EMPTY_KNOWLEDGE_SCOPE_STATE), "utf8");
|
|
7253
7693
|
await handle.sync();
|
|
7254
7694
|
if (this.faults !== undefined)
|
|
7255
7695
|
await this.faults.beforeRename();
|
|
7256
7696
|
try {
|
|
7257
|
-
await
|
|
7697
|
+
await link3(temporaryPath, this.statePath);
|
|
7258
7698
|
} catch (error) {
|
|
7259
7699
|
if (!hasCode3(error, "EEXIST"))
|
|
7260
7700
|
throw error;
|
|
7261
7701
|
return;
|
|
7262
7702
|
}
|
|
7263
|
-
const directory = await
|
|
7703
|
+
const directory = await open6(this.home, "r");
|
|
7264
7704
|
try {
|
|
7265
7705
|
await directory.sync();
|
|
7266
7706
|
} finally {
|
|
@@ -7268,7 +7708,7 @@ class KnowledgeScopeStateStore {
|
|
|
7268
7708
|
}
|
|
7269
7709
|
} finally {
|
|
7270
7710
|
await handle.close();
|
|
7271
|
-
await
|
|
7711
|
+
await rm4(temporaryPath, { force: true });
|
|
7272
7712
|
}
|
|
7273
7713
|
}
|
|
7274
7714
|
async read() {
|
|
@@ -7300,13 +7740,13 @@ class KnowledgeScopeStateStore {
|
|
|
7300
7740
|
return mutation.value;
|
|
7301
7741
|
} finally {
|
|
7302
7742
|
await lockHandle.close();
|
|
7303
|
-
await
|
|
7743
|
+
await rm4(this.lockPath, { force: true });
|
|
7304
7744
|
}
|
|
7305
7745
|
}
|
|
7306
7746
|
async acquireLock() {
|
|
7307
7747
|
let handle;
|
|
7308
7748
|
try {
|
|
7309
|
-
handle = await
|
|
7749
|
+
handle = await open6(this.lockPath, "wx", 384);
|
|
7310
7750
|
} catch (error) {
|
|
7311
7751
|
if (!hasCode3(error, "EEXIST"))
|
|
7312
7752
|
throw error;
|
|
@@ -7322,28 +7762,28 @@ class KnowledgeScopeStateStore {
|
|
|
7322
7762
|
throw productError("lock_conflict");
|
|
7323
7763
|
}
|
|
7324
7764
|
try {
|
|
7325
|
-
const owner = { pid: process.pid, createdAt: Date.now(), nonce:
|
|
7765
|
+
const owner = { pid: process.pid, createdAt: Date.now(), nonce: randomUUID5() };
|
|
7326
7766
|
await handle.writeFile(`${canonicalJson2(owner)}
|
|
7327
7767
|
`, "utf8");
|
|
7328
7768
|
await handle.sync();
|
|
7329
7769
|
return handle;
|
|
7330
7770
|
} catch (error) {
|
|
7331
7771
|
await handle.close();
|
|
7332
|
-
await
|
|
7772
|
+
await rm4(this.lockPath, { force: true });
|
|
7333
7773
|
throw error;
|
|
7334
7774
|
}
|
|
7335
7775
|
}
|
|
7336
7776
|
async writeAtomically(state) {
|
|
7337
7777
|
const serialized = serializeState(state);
|
|
7338
|
-
const temporaryPath =
|
|
7339
|
-
const handle = await
|
|
7778
|
+
const temporaryPath = join7(dirname3(this.statePath), `.state-${process.pid}-${randomUUID5()}.tmp`);
|
|
7779
|
+
const handle = await open6(temporaryPath, "wx", 384);
|
|
7340
7780
|
try {
|
|
7341
7781
|
await handle.writeFile(serialized, "utf8");
|
|
7342
7782
|
await handle.sync();
|
|
7343
7783
|
if (this.faults !== undefined)
|
|
7344
7784
|
await this.faults.beforeRename();
|
|
7345
7785
|
await rename2(temporaryPath, this.statePath);
|
|
7346
|
-
const directory = await
|
|
7786
|
+
const directory = await open6(this.home, "r");
|
|
7347
7787
|
try {
|
|
7348
7788
|
await directory.sync();
|
|
7349
7789
|
} finally {
|
|
@@ -7352,7 +7792,7 @@ class KnowledgeScopeStateStore {
|
|
|
7352
7792
|
await chmod2(this.statePath, 384);
|
|
7353
7793
|
} finally {
|
|
7354
7794
|
await handle.close();
|
|
7355
|
-
await
|
|
7795
|
+
await rm4(temporaryPath, { force: true });
|
|
7356
7796
|
}
|
|
7357
7797
|
}
|
|
7358
7798
|
}
|
|
@@ -7367,11 +7807,11 @@ var toJsonValue = (value) => {
|
|
|
7367
7807
|
var isJsonObject3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7368
7808
|
var hasCode4 = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
|
|
7369
7809
|
var readNoFollowText2 = async (path) => {
|
|
7370
|
-
if (typeof
|
|
7810
|
+
if (typeof constants5.O_NOFOLLOW !== "number" || constants5.O_NOFOLLOW === 0)
|
|
7371
7811
|
throw productError("path_invalid");
|
|
7372
7812
|
let handle;
|
|
7373
7813
|
try {
|
|
7374
|
-
handle = await
|
|
7814
|
+
handle = await open7(path, constants5.O_RDONLY | constants5.O_NOFOLLOW);
|
|
7375
7815
|
} catch (error) {
|
|
7376
7816
|
if (hasCode4(error, "ELOOP"))
|
|
7377
7817
|
throw productError("path_invalid");
|
|
@@ -7514,16 +7954,21 @@ var createCliDependencies = (options = {}) => {
|
|
|
7514
7954
|
const apiToken = environment["SCHIFT_KS_API_TOKEN"];
|
|
7515
7955
|
const storageOptions = { environment, ...options.home === undefined ? {} : { home: options.home } };
|
|
7516
7956
|
const store = new KnowledgeScopeStateStore(storageOptions);
|
|
7957
|
+
const localDocuments = new LocalDocumentStore(storageOptions);
|
|
7958
|
+
const httpProvider = environmentProvider(environment);
|
|
7517
7959
|
const authorization = new KnowledgeScopeAuthorization(storageOptions);
|
|
7518
7960
|
const application = new KnowledgeScopeApplication({
|
|
7519
7961
|
store,
|
|
7520
7962
|
authorization,
|
|
7521
|
-
provider: options.provider ??
|
|
7963
|
+
provider: options.provider ?? {
|
|
7964
|
+
execute: (context) => context.capability.provider.kind === "local_documents" ? localDocuments.execute(context) : httpProvider.execute(context)
|
|
7965
|
+
}
|
|
7522
7966
|
});
|
|
7523
7967
|
const embedded = applicationPort(application);
|
|
7524
7968
|
return {
|
|
7525
7969
|
environment,
|
|
7526
7970
|
authoring,
|
|
7971
|
+
localDocuments,
|
|
7527
7972
|
embedded,
|
|
7528
7973
|
remote: (apiUrl) => createRemoteKnowledgeScopeApplication(apiUrl, globalThis.fetch, apiToken),
|
|
7529
7974
|
readJson,
|