@gmickel/gno 1.27.1 → 1.29.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 +46 -20
- package/assets/skill/SKILL.md +59 -1
- package/browser-extension/artifacts/gno-browser-clipper-v1.29.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.29.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-b2zm0jjd.js +50 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +6 -6
- package/package.json +2 -2
- package/spec/cli.md +82 -5
- package/spec/db/schema.sql +31 -0
- package/spec/mcp.md +71 -0
- package/spec/output-schemas/ask.schema.json +156 -1
- package/spec/output-schemas/browser-clip-preview.schema.json +30 -0
- package/spec/output-schemas/collection-egress-check.schema.json +91 -0
- package/spec/output-schemas/collection-egress-policy-set.schema.json +56 -0
- package/spec/output-schemas/collection-egress-policy.schema.json +41 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +183 -3
- package/spec/output-schemas/context-capsule-verification.schema.json +1 -1
- package/spec/output-schemas/egress-audit-management.schema.json +88 -0
- package/spec/output-schemas/get.schema.json +94 -0
- package/spec/output-schemas/mcp-http-error.schema.json +113 -2
- package/spec/output-schemas/mcp-job-status.schema.json +28 -0
- package/spec/output-schemas/multi-get.schema.json +128 -0
- package/spec/output-schemas/publish-artifact.schema.json +32 -0
- package/spec/output-schemas/record-import.schema.json +223 -0
- package/spec/output-schemas/retrieval-trace-common.schema.json +31 -0
- package/spec/output-schemas/retrieval-trace-export.schema.json +3 -0
- package/spec/output-schemas/retrieval-trace-qrels.schema.json +3 -0
- package/spec/output-schemas/retrieval-trace-summary.schema.json +4 -0
- package/spec/output-schemas/search-result.schema.json +94 -0
- package/spec/output-schemas/search-results.schema.json +155 -0
- package/spec/project-profile.schema.json +66 -0
- package/src/app/context-format.ts +1 -1
- package/src/app/context-runtime-contract.ts +16 -7
- package/src/cli/commands/ask.ts +7 -0
- package/src/cli/commands/collection/index.ts +5 -0
- package/src/cli/commands/collection/policy.ts +126 -0
- package/src/cli/commands/context-build.ts +5 -0
- package/src/cli/commands/daemon.ts +45 -3
- package/src/cli/commands/egress-audit.ts +68 -0
- package/src/cli/commands/embed.ts +2 -0
- package/src/cli/commands/get.ts +12 -2
- package/src/cli/commands/index-cmd.ts +13 -0
- package/src/cli/commands/multi-get.ts +9 -2
- package/src/cli/commands/query.ts +12 -0
- package/src/cli/commands/replay.ts +13 -3
- package/src/cli/commands/shared.ts +28 -0
- package/src/cli/commands/update.ts +5 -0
- package/src/cli/commands/vsearch.ts +3 -1
- package/src/cli/program.ts +127 -0
- package/src/config/index.ts +9 -0
- package/src/config/project-profile.ts +2 -0
- package/src/config/types.ts +66 -0
- package/src/converters/adapters/browser-export/adapter.ts +199 -0
- package/src/converters/adapters/browser-export/formats.ts +358 -0
- package/src/converters/adapters/email/adapter.ts +429 -0
- package/src/converters/adapters/email/html.ts +162 -0
- package/src/converters/adapters/email/mime.ts +454 -0
- package/src/converters/adapters/email/parameters.ts +77 -0
- package/src/converters/adapters/ical/adapter.ts +475 -0
- package/src/converters/adapters/ical/recurrence.ts +186 -0
- package/src/converters/adapters/jsonl/adapter.ts +238 -0
- package/src/converters/adapters/jsonl/config.ts +105 -0
- package/src/converters/adapters/shared/html-text.ts +165 -0
- package/src/converters/adapters/shared/record-utils.ts +79 -0
- package/src/converters/adapters/shared/utf8-lines.ts +141 -0
- package/src/converters/adapters/transcript/adapter.ts +295 -0
- package/src/converters/adapters/transcript/json.ts +184 -0
- package/src/converters/adapters/transcript/model.ts +171 -0
- package/src/converters/adapters/transcript/text.ts +58 -0
- package/src/converters/adapters/transcript/timed.ts +152 -0
- package/src/converters/index.ts +11 -1
- package/src/converters/mime.ts +8 -0
- package/src/converters/pipeline.ts +15 -1
- package/src/converters/registry.ts +37 -1
- package/src/converters/types.ts +136 -0
- package/src/core/browser-clip.ts +16 -1
- package/src/core/collection-egress-policy-projection.ts +28 -0
- package/src/core/collection-egress-policy-service.ts +443 -0
- package/src/core/collection-egress-policy-validation.ts +242 -0
- package/src/core/config-mutation.ts +31 -19
- package/src/core/context-capsule-schema.ts +198 -1
- package/src/core/context-capsule-validation.ts +4 -4
- package/src/core/context-capsule-verification.ts +5 -1
- package/src/core/context-capsule.ts +177 -112
- package/src/core/context-evidence.ts +93 -15
- package/src/core/destination-classifier.ts +402 -0
- package/src/core/document-capabilities.ts +12 -0
- package/src/core/egress-audit.ts +239 -0
- package/src/core/egress-authorization.ts +50 -0
- package/src/core/egress-enforcement.ts +264 -0
- package/src/core/egress-policy.ts +440 -0
- package/src/core/egress-provenance.ts +336 -0
- package/src/core/job-manager.ts +37 -0
- package/src/core/network-boundary-inventory.ts +261 -0
- package/src/core/project-profile-apply-state.ts +5 -0
- package/src/core/project-profile.ts +4 -0
- package/src/core/record-metadata.ts +49 -0
- package/src/core/retrieval-qrels.ts +6 -0
- package/src/core/retrieval-replay.ts +18 -4
- package/src/core/retrieval-trace-export.ts +23 -4
- package/src/core/retrieval-trace-management-helpers.ts +1 -0
- package/src/core/retrieval-trace-management-types.ts +3 -0
- package/src/core/retrieval-trace-management.ts +9 -1
- package/src/core/retrieval-trace-session.ts +94 -0
- package/src/core/retrieval-trace.ts +22 -0
- package/src/ingestion/record-adapter-canonical.ts +433 -0
- package/src/ingestion/record-adapter.ts +448 -0
- package/src/ingestion/record-container.ts +704 -0
- package/src/ingestion/record-path.ts +20 -0
- package/src/ingestion/record-sync.ts +70 -0
- package/src/ingestion/sync.ts +243 -36
- package/src/ingestion/types.ts +71 -1
- package/src/ingestion/walker.ts +31 -11
- package/src/llm/errors.ts +10 -0
- package/src/llm/http-inference.ts +175 -0
- package/src/llm/http-policy.ts +537 -0
- package/src/llm/httpEmbedding.ts +47 -28
- package/src/llm/httpGeneration.ts +31 -18
- package/src/llm/httpRerank.ts +30 -18
- package/src/llm/index.ts +1 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +55 -20
- package/src/llm/pinned-http-connection.ts +177 -0
- package/src/mcp/context.ts +53 -1
- package/src/mcp/http-egress.ts +220 -0
- package/src/mcp/http-security.ts +32 -21
- package/src/mcp/http-session.ts +13 -2
- package/src/mcp/http-transport.ts +96 -6
- package/src/mcp/sync-egress.ts +24 -0
- package/src/mcp/tools/add-collection.ts +4 -0
- package/src/mcp/tools/ask.ts +1 -1
- package/src/mcp/tools/context.ts +9 -2
- package/src/mcp/tools/egress.ts +247 -0
- package/src/mcp/tools/embed.ts +1 -0
- package/src/mcp/tools/get.ts +10 -2
- package/src/mcp/tools/index-cmd.ts +7 -0
- package/src/mcp/tools/index.ts +98 -0
- package/src/mcp/tools/multi-get.ts +9 -2
- package/src/mcp/tools/query.ts +10 -0
- package/src/mcp/tools/remove-collection.ts +4 -0
- package/src/mcp/tools/sync.ts +6 -0
- package/src/mcp/tools/trace.ts +7 -1
- package/src/mcp/tools/vsearch.ts +1 -0
- package/src/mcp/tools/workspace-write.ts +2 -0
- package/src/pipeline/egress-lineage.ts +124 -0
- package/src/pipeline/filters.ts +1 -1
- package/src/pipeline/graph-retrieval.ts +7 -4
- package/src/pipeline/hybrid.ts +21 -4
- package/src/pipeline/result-context.ts +12 -4
- package/src/pipeline/search.ts +23 -4
- package/src/pipeline/types.ts +6 -0
- package/src/pipeline/vsearch.ts +67 -28
- package/src/publish/artifact.ts +31 -3
- package/src/publish/export-service.ts +25 -0
- package/src/sdk/client.ts +119 -2
- package/src/sdk/documents.ts +13 -5
- package/src/sdk/embed.ts +1 -0
- package/src/sdk/types.ts +33 -0
- package/src/serve/browse-tree.ts +4 -2
- package/src/serve/clipper-capture.ts +5 -1
- package/src/serve/closed-json.ts +61 -0
- package/src/serve/config-sync.ts +4 -1
- package/src/serve/context.ts +1 -0
- package/src/serve/doc-events.ts +110 -39
- package/src/serve/public/components/CollectionEgressCheckPanel.tsx +411 -0
- package/src/serve/public/components/CollectionEgressPolicyEditor.tsx +78 -0
- package/src/serve/public/components/CollectionModelDialog.tsx +93 -9
- package/src/serve/public/components/EgressAuditPanel.tsx +278 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/pages/Collections.tsx +3 -0
- package/src/serve/resident-request.ts +80 -3
- package/src/serve/resident-runtime.ts +63 -1
- package/src/serve/routes/api.ts +237 -65
- package/src/serve/routes/clipper.ts +64 -1
- package/src/serve/routes/mcp.ts +6 -0
- package/src/serve/routes/traces.ts +27 -4
- package/src/serve/server.ts +118 -7
- package/src/store/migrations/022-record-export-lineage.ts +42 -0
- package/src/store/migrations/023-collection-egress-policy.ts +61 -0
- package/src/store/migrations/024-egress-derived-lineage.ts +121 -0
- package/src/store/migrations/025-collection-egress-policy-revision.ts +40 -0
- package/src/store/migrations/index.ts +8 -0
- package/src/store/retrieval-trace-codec.ts +15 -0
- package/src/store/sqlite/adapter.ts +284 -14
- package/src/store/sqlite/change-journal-store.ts +41 -2
- package/src/store/sqlite/egress-audit-store.ts +485 -0
- package/src/store/sqlite/retrieval-trace-management-store.ts +47 -6
- package/src/store/sqlite/retrieval-trace-retention.ts +2 -0
- package/src/store/sqlite/retrieval-trace-rows.ts +8 -1
- package/src/store/sqlite/retrieval-trace-store.ts +104 -5
- package/src/store/types.ts +166 -1
- package/browser-extension/artifacts/gno-browser-clipper-v1.27.1.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.27.1.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-vn5f663b.js +0 -50
package/src/publish/artifact.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module src/publish/artifact
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { EgressLineage } from "../core/egress-provenance";
|
|
7
8
|
import type { DocumentRow } from "../store/types";
|
|
8
9
|
|
|
9
10
|
import { deriveDocid } from "../app/constants";
|
|
@@ -11,6 +12,10 @@ import {
|
|
|
11
12
|
contextCapsuleEvidenceIdentity,
|
|
12
13
|
sha256Text,
|
|
13
14
|
} from "../core/context-capsule-validation";
|
|
15
|
+
import {
|
|
16
|
+
egressLineageSchema,
|
|
17
|
+
legacyLocalOnlyEgressLineage,
|
|
18
|
+
} from "../core/egress-provenance";
|
|
14
19
|
import { stripFrontmatter } from "../ingestion/frontmatter";
|
|
15
20
|
import {
|
|
16
21
|
MAX_PUBLISH_SLUG_LENGTH,
|
|
@@ -71,6 +76,7 @@ export interface PublicPublishDocument {
|
|
|
71
76
|
export interface PublicPublishManifest {
|
|
72
77
|
capabilities: typeof PUBLIC_PUBLISH_CAPABILITIES;
|
|
73
78
|
documents: PublicPublishDocument[];
|
|
79
|
+
egressLineage: EgressLineage;
|
|
74
80
|
generatedAt: string;
|
|
75
81
|
projectionRevision: string;
|
|
76
82
|
schemaVersion: typeof PUBLIC_PUBLISH_MANIFEST_SCHEMA_VERSION;
|
|
@@ -116,6 +122,7 @@ export interface EncryptedPublishArtifactSpace {
|
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
export interface PublishArtifactV1 {
|
|
125
|
+
egressLineage: EgressLineage;
|
|
119
126
|
exportedAt: string;
|
|
120
127
|
source: string;
|
|
121
128
|
spaces: PublishArtifactSpace[];
|
|
@@ -123,6 +130,7 @@ export interface PublishArtifactV1 {
|
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
export interface PublishArtifactV2 {
|
|
133
|
+
egressLineage: EgressLineage;
|
|
126
134
|
exportedAt: string;
|
|
127
135
|
source: string;
|
|
128
136
|
spaces: EncryptedPublishArtifactSpace[];
|
|
@@ -279,6 +287,7 @@ const buildPublicPublishDocument = (
|
|
|
279
287
|
};
|
|
280
288
|
|
|
281
289
|
export const buildPublicPublishManifest = (input: {
|
|
290
|
+
egressLineage?: EgressLineage;
|
|
282
291
|
exportedAt: string;
|
|
283
292
|
homeNoteSlug?: string;
|
|
284
293
|
notes: PublishArtifactNote[];
|
|
@@ -288,7 +297,11 @@ export const buildPublicPublishManifest = (input: {
|
|
|
288
297
|
title: string;
|
|
289
298
|
visibility: "public";
|
|
290
299
|
}): PublicPublishManifest => {
|
|
291
|
-
const
|
|
300
|
+
const { egressLineage: providedLineage, ...spaceInput } = input;
|
|
301
|
+
const validated = validateAndProjectPublishSpaceInput(spaceInput);
|
|
302
|
+
const egressLineage = egressLineageSchema.parse(
|
|
303
|
+
providedLineage ?? legacyLocalOnlyEgressLineage("legacy")
|
|
304
|
+
);
|
|
292
305
|
if (validated.visibility !== "public") {
|
|
293
306
|
throw new Error("Public publish manifests require public visibility");
|
|
294
307
|
}
|
|
@@ -304,6 +317,7 @@ export const buildPublicPublishManifest = (input: {
|
|
|
304
317
|
const revisionProjection = {
|
|
305
318
|
capabilities: PUBLIC_PUBLISH_CAPABILITIES,
|
|
306
319
|
documents,
|
|
320
|
+
egressLineage,
|
|
307
321
|
homeNoteSlug: validated.homeNoteSlug ?? null,
|
|
308
322
|
notes: validated.notes
|
|
309
323
|
.map((note) => ({
|
|
@@ -324,6 +338,7 @@ export const buildPublicPublishManifest = (input: {
|
|
|
324
338
|
return {
|
|
325
339
|
capabilities: { ...PUBLIC_PUBLISH_CAPABILITIES },
|
|
326
340
|
documents,
|
|
341
|
+
egressLineage,
|
|
327
342
|
generatedAt: exportedAt,
|
|
328
343
|
projectionRevision: sha256Text(canonicalJson(revisionProjection)),
|
|
329
344
|
schemaVersion: PUBLIC_PUBLISH_MANIFEST_SCHEMA_VERSION,
|
|
@@ -332,6 +347,7 @@ export const buildPublicPublishManifest = (input: {
|
|
|
332
347
|
};
|
|
333
348
|
|
|
334
349
|
export const buildPublishArtifact = (input: {
|
|
350
|
+
egressLineage?: EgressLineage;
|
|
335
351
|
homeNoteSlug?: string;
|
|
336
352
|
notes: PublishArtifactNote[];
|
|
337
353
|
routeSlug: string;
|
|
@@ -340,7 +356,11 @@ export const buildPublishArtifact = (input: {
|
|
|
340
356
|
title: string;
|
|
341
357
|
visibility: Exclude<PublishVisibility, "encrypted">;
|
|
342
358
|
}): PublishArtifactV1 => {
|
|
343
|
-
const
|
|
359
|
+
const { egressLineage: providedLineage, ...spaceInput } = input;
|
|
360
|
+
const validated = validateAndProjectPublishSpaceInput(spaceInput);
|
|
361
|
+
const egressLineage = egressLineageSchema.parse(
|
|
362
|
+
providedLineage ?? legacyLocalOnlyEgressLineage("legacy")
|
|
363
|
+
);
|
|
344
364
|
const exportedAt = new Date().toISOString();
|
|
345
365
|
const base = {
|
|
346
366
|
...(validated.homeNoteSlug === undefined
|
|
@@ -357,6 +377,7 @@ export const buildPublishArtifact = (input: {
|
|
|
357
377
|
? {
|
|
358
378
|
...base,
|
|
359
379
|
manifest: buildPublicPublishManifest({
|
|
380
|
+
egressLineage,
|
|
360
381
|
exportedAt,
|
|
361
382
|
homeNoteSlug: validated.homeNoteSlug,
|
|
362
383
|
notes: validated.notes,
|
|
@@ -374,6 +395,7 @@ export const buildPublishArtifact = (input: {
|
|
|
374
395
|
};
|
|
375
396
|
|
|
376
397
|
return {
|
|
398
|
+
egressLineage,
|
|
377
399
|
exportedAt,
|
|
378
400
|
source: validated.routeSlug,
|
|
379
401
|
spaces: [space],
|
|
@@ -382,14 +404,20 @@ export const buildPublishArtifact = (input: {
|
|
|
382
404
|
};
|
|
383
405
|
|
|
384
406
|
export const buildEncryptedPublishArtifact = (input: {
|
|
407
|
+
egressLineage?: EgressLineage;
|
|
385
408
|
encryptedPayload: EncryptedArtifactPayload;
|
|
386
409
|
routeSlug: string;
|
|
387
410
|
secretToken: string;
|
|
388
411
|
sourceType: "note" | "collection";
|
|
389
412
|
}): PublishArtifactV2 => {
|
|
390
|
-
const
|
|
413
|
+
const { egressLineage: providedLineage, ...encryptedInput } = input;
|
|
414
|
+
const validated = validateAndProjectEncryptedPublishInput(encryptedInput);
|
|
415
|
+
const egressLineage = egressLineageSchema.parse(
|
|
416
|
+
providedLineage ?? legacyLocalOnlyEgressLineage("legacy")
|
|
417
|
+
);
|
|
391
418
|
const exportedAt = requirePublishDateTime(new Date().toISOString());
|
|
392
419
|
return {
|
|
420
|
+
egressLineage,
|
|
393
421
|
exportedAt,
|
|
394
422
|
source: validated.routeSlug,
|
|
395
423
|
spaces: [
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { Collection } from "../config/types";
|
|
8
8
|
import type { DocumentRow, StorePort, TagRow } from "../store/types";
|
|
9
9
|
|
|
10
|
+
import { enforceCollectionEgressWithAudit } from "../core/egress-enforcement";
|
|
10
11
|
import { parseRef } from "../core/ref-parser";
|
|
11
12
|
import { parseFrontmatter, stripFrontmatter } from "../ingestion/frontmatter";
|
|
12
13
|
import { getContentBatch } from "../store/content-batch";
|
|
@@ -217,6 +218,15 @@ async function exportCollectionArtifact(
|
|
|
217
218
|
target,
|
|
218
219
|
]);
|
|
219
220
|
const visibility = resolveVisibility(options.visibility);
|
|
221
|
+
const { lineage } = await enforceCollectionEgressWithAudit({
|
|
222
|
+
collections,
|
|
223
|
+
collectionNames: [collection.name],
|
|
224
|
+
action: "export",
|
|
225
|
+
destinationZone: "local_process",
|
|
226
|
+
caller: { authenticated: true, operationAuthorized: true },
|
|
227
|
+
contentClass: "source",
|
|
228
|
+
store,
|
|
229
|
+
});
|
|
220
230
|
|
|
221
231
|
if (visibility === "encrypted") {
|
|
222
232
|
if (!options.encryptionPassphrase) {
|
|
@@ -237,6 +247,7 @@ async function exportCollectionArtifact(
|
|
|
237
247
|
});
|
|
238
248
|
|
|
239
249
|
return buildEncryptedPublishArtifact({
|
|
250
|
+
egressLineage: lineage,
|
|
240
251
|
encryptedPayload: encrypted.encryptedPayload,
|
|
241
252
|
routeSlug,
|
|
242
253
|
secretToken: encrypted.secretToken,
|
|
@@ -245,6 +256,7 @@ async function exportCollectionArtifact(
|
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
return buildPublishArtifact({
|
|
259
|
+
egressLineage: lineage,
|
|
248
260
|
homeNoteSlug: chooseHomeNoteSlug(notes),
|
|
249
261
|
notes,
|
|
250
262
|
routeSlug,
|
|
@@ -257,6 +269,7 @@ async function exportCollectionArtifact(
|
|
|
257
269
|
|
|
258
270
|
async function exportDocumentArtifact(
|
|
259
271
|
store: StorePort,
|
|
272
|
+
collections: Collection[],
|
|
260
273
|
target: string,
|
|
261
274
|
options: PublishExportCoreOptions,
|
|
262
275
|
warnings: SanitizeWarning[]
|
|
@@ -283,6 +296,15 @@ async function exportDocumentArtifact(
|
|
|
283
296
|
const slug = deriveExportedSlug(doc);
|
|
284
297
|
const visibility = resolveVisibility(options.visibility);
|
|
285
298
|
const routeSlug = derivePublishSlug([options.routeSlug ?? "", slug, target]);
|
|
299
|
+
const { lineage } = await enforceCollectionEgressWithAudit({
|
|
300
|
+
collections,
|
|
301
|
+
collectionNames: [doc.collection],
|
|
302
|
+
action: "export",
|
|
303
|
+
destinationZone: "local_process",
|
|
304
|
+
caller: { authenticated: true, operationAuthorized: true },
|
|
305
|
+
contentClass: "source",
|
|
306
|
+
store,
|
|
307
|
+
});
|
|
286
308
|
|
|
287
309
|
if (visibility === "encrypted") {
|
|
288
310
|
if (!options.encryptionPassphrase) {
|
|
@@ -310,6 +332,7 @@ async function exportDocumentArtifact(
|
|
|
310
332
|
});
|
|
311
333
|
|
|
312
334
|
return buildEncryptedPublishArtifact({
|
|
335
|
+
egressLineage: lineage,
|
|
313
336
|
encryptedPayload: encrypted.encryptedPayload,
|
|
314
337
|
routeSlug,
|
|
315
338
|
secretToken: encrypted.secretToken,
|
|
@@ -318,6 +341,7 @@ async function exportDocumentArtifact(
|
|
|
318
341
|
}
|
|
319
342
|
|
|
320
343
|
return buildPublishArtifact({
|
|
344
|
+
egressLineage: lineage,
|
|
321
345
|
notes: [
|
|
322
346
|
{
|
|
323
347
|
markdown,
|
|
@@ -357,6 +381,7 @@ export async function exportPublishArtifact(input: {
|
|
|
357
381
|
)) ??
|
|
358
382
|
(await exportDocumentArtifact(
|
|
359
383
|
input.store,
|
|
384
|
+
input.collections,
|
|
360
385
|
input.target,
|
|
361
386
|
input.options,
|
|
362
387
|
warnings
|
package/src/sdk/client.ts
CHANGED
|
@@ -79,6 +79,11 @@ import {
|
|
|
79
79
|
planCapture,
|
|
80
80
|
} from "../core/capture";
|
|
81
81
|
import { writeCapturePlanFile } from "../core/capture-write";
|
|
82
|
+
import { projectCollectionEgressPolicy } from "../core/collection-egress-policy-projection";
|
|
83
|
+
import { CollectionEgressPolicyService } from "../core/collection-egress-policy-service";
|
|
84
|
+
import { applyConfigChange } from "../core/config-mutation";
|
|
85
|
+
import { EgressAuditService } from "../core/egress-audit";
|
|
86
|
+
import { authorizeCurrentEgress } from "../core/egress-authorization";
|
|
82
87
|
import {
|
|
83
88
|
atomicWrite,
|
|
84
89
|
copyFilePath,
|
|
@@ -270,7 +275,7 @@ async function resolveClientState(
|
|
|
270
275
|
}
|
|
271
276
|
|
|
272
277
|
class GnoClientImpl implements GnoClient {
|
|
273
|
-
|
|
278
|
+
config: Config;
|
|
274
279
|
readonly dbPath: string;
|
|
275
280
|
readonly configPath: string | null;
|
|
276
281
|
readonly configSource: "file" | "inline";
|
|
@@ -331,6 +336,9 @@ class GnoClientImpl implements GnoClient {
|
|
|
331
336
|
rerankModel?: string;
|
|
332
337
|
}): Promise<RuntimePorts> {
|
|
333
338
|
this.assertOpen();
|
|
339
|
+
const egressCollections = options.collection
|
|
340
|
+
? [options.collection]
|
|
341
|
+
: ("all" as const);
|
|
334
342
|
|
|
335
343
|
let embedPort: EmbeddingPort | null = null;
|
|
336
344
|
let expandPort: GenerationPort | null = null;
|
|
@@ -347,6 +355,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
347
355
|
options.collection
|
|
348
356
|
),
|
|
349
357
|
{
|
|
358
|
+
egressCollections,
|
|
350
359
|
policy: this.downloadPolicy,
|
|
351
360
|
}
|
|
352
361
|
);
|
|
@@ -391,6 +400,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
391
400
|
options.collection
|
|
392
401
|
),
|
|
393
402
|
{
|
|
403
|
+
egressCollections,
|
|
394
404
|
policy: this.downloadPolicy,
|
|
395
405
|
}
|
|
396
406
|
);
|
|
@@ -415,6 +425,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
415
425
|
options.collection
|
|
416
426
|
),
|
|
417
427
|
{
|
|
428
|
+
egressCollections,
|
|
418
429
|
policy: this.downloadPolicy,
|
|
419
430
|
}
|
|
420
431
|
);
|
|
@@ -442,6 +453,7 @@ class GnoClientImpl implements GnoClient {
|
|
|
442
453
|
options.collection
|
|
443
454
|
),
|
|
444
455
|
{
|
|
456
|
+
egressCollections,
|
|
445
457
|
policy: this.downloadPolicy,
|
|
446
458
|
}
|
|
447
459
|
);
|
|
@@ -1230,7 +1242,19 @@ class GnoClientImpl implements GnoClient {
|
|
|
1230
1242
|
) {
|
|
1231
1243
|
this.assertOpen();
|
|
1232
1244
|
return unwrapTraceStore(
|
|
1233
|
-
await new RetrievalTraceManagementService(this.store
|
|
1245
|
+
await new RetrievalTraceManagementService(this.store, {
|
|
1246
|
+
authorizeExport: async (lineage) => {
|
|
1247
|
+
return authorizeCurrentEgress({
|
|
1248
|
+
store: this.store,
|
|
1249
|
+
config: this.config,
|
|
1250
|
+
lineage,
|
|
1251
|
+
action: "export",
|
|
1252
|
+
destinationZone: "local_process",
|
|
1253
|
+
caller: { authenticated: true, operationAuthorized: true },
|
|
1254
|
+
contentClass: "retrieval_trace",
|
|
1255
|
+
});
|
|
1256
|
+
},
|
|
1257
|
+
}).export(input)
|
|
1234
1258
|
);
|
|
1235
1259
|
}
|
|
1236
1260
|
|
|
@@ -1248,6 +1272,99 @@ class GnoClientImpl implements GnoClient {
|
|
|
1248
1272
|
);
|
|
1249
1273
|
}
|
|
1250
1274
|
|
|
1275
|
+
async getCollectionEgressPolicy(collection: string) {
|
|
1276
|
+
this.assertOpen();
|
|
1277
|
+
const state = new CollectionEgressPolicyService({
|
|
1278
|
+
getConfig: () => this.config,
|
|
1279
|
+
}).get(collection);
|
|
1280
|
+
if (!state.ok) {
|
|
1281
|
+
throw sdkError(
|
|
1282
|
+
state.code === "NOT_FOUND" ? "NOT_FOUND" : "VALIDATION",
|
|
1283
|
+
state.error
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
return state.value;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
async setCollectionEgressPolicy(
|
|
1290
|
+
collection: string,
|
|
1291
|
+
policy: import("../config/types").EgressPolicy,
|
|
1292
|
+
confirmation?: import("../core/collection-egress-policy-service").EgressRelaxationConfirmation
|
|
1293
|
+
) {
|
|
1294
|
+
this.assertOpen();
|
|
1295
|
+
if (!this.configPath) {
|
|
1296
|
+
throw sdkError(
|
|
1297
|
+
"VALIDATION",
|
|
1298
|
+
"Inline-config clients cannot persist collection policy changes"
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
const service = new CollectionEgressPolicyService({
|
|
1302
|
+
getConfig: () => this.config,
|
|
1303
|
+
mutateConfig: (mutate) =>
|
|
1304
|
+
applyConfigChange(
|
|
1305
|
+
{
|
|
1306
|
+
store: this.store,
|
|
1307
|
+
configPath: this.configPath ?? undefined,
|
|
1308
|
+
onConfigUpdated: (config) => {
|
|
1309
|
+
this.config = config;
|
|
1310
|
+
},
|
|
1311
|
+
projectStore: (store, config) =>
|
|
1312
|
+
projectCollectionEgressPolicy(store, config, collection),
|
|
1313
|
+
},
|
|
1314
|
+
mutate
|
|
1315
|
+
),
|
|
1316
|
+
});
|
|
1317
|
+
const result = await service.set({
|
|
1318
|
+
collection,
|
|
1319
|
+
policy,
|
|
1320
|
+
confirmation,
|
|
1321
|
+
});
|
|
1322
|
+
if (!result.ok) throw sdkError("VALIDATION", result.error);
|
|
1323
|
+
return result.value;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
async checkEgress(
|
|
1327
|
+
input: import("../core/collection-egress-policy-service").CollectionEgressCheckInput
|
|
1328
|
+
) {
|
|
1329
|
+
this.assertOpen();
|
|
1330
|
+
const result = new CollectionEgressPolicyService({
|
|
1331
|
+
getConfig: () => this.config,
|
|
1332
|
+
}).check(input);
|
|
1333
|
+
if (!result.ok) throw sdkError("VALIDATION", result.error);
|
|
1334
|
+
return result.value;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
async listEgressAudits(options: { limit?: number; cursor?: string } = {}) {
|
|
1338
|
+
this.assertOpen();
|
|
1339
|
+
return unwrapTraceStore(
|
|
1340
|
+
await new EgressAuditService(this.store).list(options)
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
async getEgressAudit(auditId: string) {
|
|
1345
|
+
this.assertOpen();
|
|
1346
|
+
return unwrapTraceStore(
|
|
1347
|
+
await new EgressAuditService(this.store).show(auditId)
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
async getEgressAuditStatus() {
|
|
1352
|
+
this.assertOpen();
|
|
1353
|
+
return unwrapTraceStore(await new EgressAuditService(this.store).status());
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
async deleteEgressAudit(auditId: string) {
|
|
1357
|
+
this.assertOpen();
|
|
1358
|
+
return unwrapTraceStore(
|
|
1359
|
+
await new EgressAuditService(this.store).delete(auditId)
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
async purgeEgressAudits() {
|
|
1364
|
+
this.assertOpen();
|
|
1365
|
+
return unwrapTraceStore(await new EgressAuditService(this.store).purge());
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1251
1368
|
async update(options: GnoUpdateOptions = {}): Promise<SyncResult> {
|
|
1252
1369
|
this.assertOpen();
|
|
1253
1370
|
const collections = this.getCollections(options.collection);
|
package/src/sdk/documents.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
} from "./types";
|
|
24
24
|
|
|
25
25
|
import { getDocumentCapabilities } from "../core/document-capabilities";
|
|
26
|
+
import { projectRecordEvidenceMetadata } from "../core/record-metadata";
|
|
26
27
|
import { isGlobPattern, parseRef, splitRefs } from "../core/ref-parser";
|
|
27
28
|
import {
|
|
28
29
|
attachRetrievalTraceMetadata,
|
|
@@ -53,9 +54,10 @@ function buildSourceMeta(
|
|
|
53
54
|
config: Config
|
|
54
55
|
): GetResponse["source"] {
|
|
55
56
|
const coll = config.collections.find((c) => c.name === doc.collection);
|
|
57
|
+
const relPath = doc.recordSourcePath ?? doc.relPath;
|
|
56
58
|
return {
|
|
57
|
-
absPath: coll ? `${coll.path}/${
|
|
58
|
-
relPath
|
|
59
|
+
absPath: coll ? `${coll.path}/${relPath}` : undefined,
|
|
60
|
+
relPath,
|
|
59
61
|
mime: doc.sourceMime,
|
|
60
62
|
ext: doc.sourceExt,
|
|
61
63
|
sizeBytes: doc.sourceSize,
|
|
@@ -129,10 +131,12 @@ async function getDocumentByRefUntraced(
|
|
|
129
131
|
language: doc.languageHint ?? undefined,
|
|
130
132
|
source: buildSourceMeta(doc, config),
|
|
131
133
|
conversion: buildConversionMeta(doc),
|
|
134
|
+
record: projectRecordEvidenceMetadata(doc),
|
|
132
135
|
capabilities: getDocumentCapabilities({
|
|
133
136
|
sourceExt: doc.sourceExt,
|
|
134
137
|
sourceMime: doc.sourceMime,
|
|
135
138
|
contentAvailable: doc.mirrorHash !== null,
|
|
139
|
+
recordKey: doc.recordKey,
|
|
136
140
|
}),
|
|
137
141
|
};
|
|
138
142
|
}
|
|
@@ -156,10 +160,12 @@ async function getDocumentByRefUntraced(
|
|
|
156
160
|
language: doc.languageHint ?? undefined,
|
|
157
161
|
source: buildSourceMeta(doc, config),
|
|
158
162
|
conversion: buildConversionMeta(doc),
|
|
163
|
+
record: projectRecordEvidenceMetadata(doc),
|
|
159
164
|
capabilities: getDocumentCapabilities({
|
|
160
165
|
sourceExt: doc.sourceExt,
|
|
161
166
|
sourceMime: doc.sourceMime,
|
|
162
167
|
contentAvailable: doc.mirrorHash !== null,
|
|
168
|
+
recordKey: doc.recordKey,
|
|
163
169
|
}),
|
|
164
170
|
};
|
|
165
171
|
}
|
|
@@ -318,6 +324,7 @@ export async function multiGetDocuments(
|
|
|
318
324
|
maxBytes
|
|
319
325
|
);
|
|
320
326
|
const coll = config.collections.find((c) => c.name === doc.collection);
|
|
327
|
+
const sourceRelPath = doc.recordSourcePath ?? doc.relPath;
|
|
321
328
|
documents.push({
|
|
322
329
|
docid: doc.docid,
|
|
323
330
|
uri: doc.uri,
|
|
@@ -326,11 +333,12 @@ export async function multiGetDocuments(
|
|
|
326
333
|
truncated: truncated || undefined,
|
|
327
334
|
totalLines: content.split("\n").length,
|
|
328
335
|
source: {
|
|
329
|
-
absPath: coll ? `${coll.path}/${
|
|
330
|
-
relPath:
|
|
336
|
+
absPath: coll ? `${coll.path}/${sourceRelPath}` : undefined,
|
|
337
|
+
relPath: sourceRelPath,
|
|
331
338
|
mime: doc.sourceMime,
|
|
332
339
|
ext: doc.sourceExt,
|
|
333
340
|
},
|
|
341
|
+
record: projectRecordEvidenceMetadata(doc),
|
|
334
342
|
});
|
|
335
343
|
}
|
|
336
344
|
|
|
@@ -385,7 +393,7 @@ export async function listDocuments(
|
|
|
385
393
|
uri: d.uri,
|
|
386
394
|
title: d.title ?? undefined,
|
|
387
395
|
source: {
|
|
388
|
-
relPath: d.relPath,
|
|
396
|
+
relPath: d.recordSourcePath ?? d.relPath,
|
|
389
397
|
mime: d.sourceMime,
|
|
390
398
|
ext: d.sourceExt,
|
|
391
399
|
},
|
package/src/sdk/embed.ts
CHANGED
|
@@ -283,6 +283,7 @@ export async function runEmbed(
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
const embedResult = await runtime.llm.createEmbeddingPort(modelUri, {
|
|
286
|
+
egressCollections: options.collection ? [options.collection] : "all",
|
|
286
287
|
policy: runtime.downloadPolicy,
|
|
287
288
|
});
|
|
288
289
|
if (!embedResult.ok) {
|
package/src/sdk/types.ts
CHANGED
|
@@ -11,6 +11,13 @@ import type {
|
|
|
11
11
|
import type { ContentTypeBoostStatus } from "../config/content-types";
|
|
12
12
|
import type { Config } from "../config/types";
|
|
13
13
|
import type { CaptureInput, CaptureReceipt } from "../core/capture";
|
|
14
|
+
import type {
|
|
15
|
+
CollectionEgressCheckInput,
|
|
16
|
+
CollectionEgressCheckResult,
|
|
17
|
+
CollectionEgressPolicySetResult,
|
|
18
|
+
CollectionEgressPolicyState,
|
|
19
|
+
EgressRelaxationConfirmation,
|
|
20
|
+
} from "../core/collection-egress-policy-service";
|
|
14
21
|
import type {
|
|
15
22
|
ContextCapsuleErrorCode,
|
|
16
23
|
ContextCapsuleV1,
|
|
@@ -18,6 +25,13 @@ import type {
|
|
|
18
25
|
} from "../core/context-capsule";
|
|
19
26
|
import type { ContextEvidenceErrorCode } from "../core/context-evidence";
|
|
20
27
|
import type { ContextVerifierErrorCode } from "../core/context-verifier";
|
|
28
|
+
import type {
|
|
29
|
+
EgressAuditDeleteResult,
|
|
30
|
+
EgressAuditListResult,
|
|
31
|
+
EgressAuditPurgeManagementResult,
|
|
32
|
+
EgressAuditShowResult,
|
|
33
|
+
EgressAuditStatusResult,
|
|
34
|
+
} from "../core/egress-audit";
|
|
21
35
|
import type {
|
|
22
36
|
KnowledgeChangesResult,
|
|
23
37
|
KnowledgeDiffResult,
|
|
@@ -300,6 +314,25 @@ export interface GnoClient {
|
|
|
300
314
|
): Promise<RetrievalTraceExportResult>;
|
|
301
315
|
deleteRetrievalTrace(traceId: string): Promise<RetrievalTraceDeleteResult>;
|
|
302
316
|
purgeRetrievalTraces(): Promise<RetrievalTraceManagementPurgeResult>;
|
|
317
|
+
getCollectionEgressPolicy(
|
|
318
|
+
collection: string
|
|
319
|
+
): Promise<CollectionEgressPolicyState>;
|
|
320
|
+
setCollectionEgressPolicy(
|
|
321
|
+
collection: string,
|
|
322
|
+
policy: import("../config/types").EgressPolicy,
|
|
323
|
+
confirmation?: EgressRelaxationConfirmation
|
|
324
|
+
): Promise<CollectionEgressPolicySetResult>;
|
|
325
|
+
checkEgress(
|
|
326
|
+
input: CollectionEgressCheckInput
|
|
327
|
+
): Promise<CollectionEgressCheckResult>;
|
|
328
|
+
listEgressAudits(options?: {
|
|
329
|
+
limit?: number;
|
|
330
|
+
cursor?: string;
|
|
331
|
+
}): Promise<EgressAuditListResult>;
|
|
332
|
+
getEgressAudit(auditId: string): Promise<EgressAuditShowResult>;
|
|
333
|
+
getEgressAuditStatus(): Promise<EgressAuditStatusResult>;
|
|
334
|
+
deleteEgressAudit(auditId: string): Promise<EgressAuditDeleteResult>;
|
|
335
|
+
purgeEgressAudits(): Promise<EgressAuditPurgeManagementResult>;
|
|
303
336
|
update(options?: GnoUpdateOptions): Promise<SyncResult>;
|
|
304
337
|
embed(options?: GnoEmbedOptions): Promise<GnoEmbedResult>;
|
|
305
338
|
index(options?: GnoIndexOptions): Promise<GnoIndexResult>;
|
package/src/serve/browse-tree.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface BrowseTreeNode {
|
|
|
15
15
|
type BrowseCollectionLike = Pick<CollectionRow, "name">;
|
|
16
16
|
type BrowseDocumentLike = Pick<
|
|
17
17
|
DocumentRow,
|
|
18
|
-
"collection" | "relPath" | "active"
|
|
18
|
+
"collection" | "relPath" | "recordSourcePath" | "active"
|
|
19
19
|
>;
|
|
20
20
|
type BrowseFolderLike = {
|
|
21
21
|
collection: string;
|
|
@@ -128,7 +128,9 @@ export function buildBrowseTree(
|
|
|
128
128
|
|
|
129
129
|
root.documentCount += 1;
|
|
130
130
|
|
|
131
|
-
const normalizedRelPath = normalizeBrowsePath(
|
|
131
|
+
const normalizedRelPath = normalizeBrowsePath(
|
|
132
|
+
doc.recordSourcePath ?? doc.relPath
|
|
133
|
+
);
|
|
132
134
|
const parts = normalizedRelPath.split("/").filter(Boolean);
|
|
133
135
|
const folderParts = parts.slice(0, -1);
|
|
134
136
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { Database } from "bun:sqlite";
|
|
4
4
|
|
|
5
5
|
import type { PreparedBrowserClip } from "../core/browser-clip";
|
|
6
|
+
import type { EgressLineage } from "../core/egress-provenance";
|
|
6
7
|
import type { SqliteAdapter } from "../store/sqlite/adapter";
|
|
7
8
|
import type { ClipperIdempotencyReplay } from "../store/sqlite/clipper-store-types";
|
|
8
9
|
import type { ResidentCaptureContext } from "./capture-service";
|
|
@@ -35,6 +36,7 @@ interface ExecuteClipperCaptureInput {
|
|
|
35
36
|
context: ResidentCaptureContext;
|
|
36
37
|
store: SqliteAdapter;
|
|
37
38
|
pairing: ClipperPairingService;
|
|
39
|
+
egressLineage?: EgressLineage;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const inFlightRequests = new Set<string>();
|
|
@@ -153,7 +155,9 @@ export const executeClipperCapture = async (
|
|
|
153
155
|
);
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
const prepared = prepareBrowserClip(parsed.data.payload
|
|
158
|
+
const prepared = prepareBrowserClip(parsed.data.payload, {
|
|
159
|
+
egressLineage: input.egressLineage,
|
|
160
|
+
});
|
|
157
161
|
if (prepared.preview.digest !== parsed.data.previewDigest) {
|
|
158
162
|
return clipperErrorResponse(
|
|
159
163
|
"CLIPPER_PREVIEW_MISMATCH",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Bounded, content-free JSON request parsing for closed REST contracts. */
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MAX_BYTES = 16 * 1024;
|
|
4
|
+
|
|
5
|
+
export type ClosedJsonResult =
|
|
6
|
+
| { ok: true; value: unknown }
|
|
7
|
+
| { ok: false; code: "VALIDATION"; error: string };
|
|
8
|
+
|
|
9
|
+
export const parseClosedJson = async (
|
|
10
|
+
request: Request,
|
|
11
|
+
maxBytes = DEFAULT_MAX_BYTES
|
|
12
|
+
): Promise<ClosedJsonResult> => {
|
|
13
|
+
try {
|
|
14
|
+
const contentLength = Number(request.headers.get("content-length"));
|
|
15
|
+
if (Number.isFinite(contentLength) && contentLength > maxBytes) {
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
code: "VALIDATION",
|
|
19
|
+
error: "JSON body exceeds the allowed size",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const reader = request.body?.getReader();
|
|
23
|
+
if (!reader) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
code: "VALIDATION",
|
|
27
|
+
error: "Invalid JSON body",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const chunks: Uint8Array[] = [];
|
|
31
|
+
let totalBytes = 0;
|
|
32
|
+
while (true) {
|
|
33
|
+
const chunk = await reader.read();
|
|
34
|
+
if (chunk.done) break;
|
|
35
|
+
totalBytes += chunk.value.byteLength;
|
|
36
|
+
if (totalBytes > maxBytes) {
|
|
37
|
+
await reader.cancel("JSON body exceeds the allowed size");
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
code: "VALIDATION",
|
|
41
|
+
error: "JSON body exceeds the allowed size",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
chunks.push(chunk.value);
|
|
45
|
+
}
|
|
46
|
+
const bytes = new Uint8Array(totalBytes);
|
|
47
|
+
let offset = 0;
|
|
48
|
+
for (const chunk of chunks) {
|
|
49
|
+
bytes.set(chunk, offset);
|
|
50
|
+
offset += chunk.byteLength;
|
|
51
|
+
}
|
|
52
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
53
|
+
return { ok: true, value: JSON.parse(text) as unknown };
|
|
54
|
+
} catch {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
code: "VALIDATION",
|
|
58
|
+
error: "Invalid JSON body",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
};
|
package/src/serve/config-sync.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { ContextHolder } from "./routes/api";
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
applyConfigChange as applyConfigChangeCore,
|
|
14
|
+
type ConfigMutationContext,
|
|
14
15
|
type ApplyConfigResult,
|
|
15
16
|
type MutationResult,
|
|
16
17
|
} from "../core/config-mutation";
|
|
@@ -67,11 +68,13 @@ export async function applyConfigChangeTyped<T>(
|
|
|
67
68
|
ctxHolder: ContextHolder,
|
|
68
69
|
store: SqliteAdapter,
|
|
69
70
|
mutate: (config: Config) => Promise<MutationResult<T>> | MutationResult<T>,
|
|
70
|
-
configPath?: string
|
|
71
|
+
configPath?: string,
|
|
72
|
+
options: Pick<ConfigMutationContext, "projectStore"> = {}
|
|
71
73
|
): Promise<ApplyConfigResult<T>> {
|
|
72
74
|
const result = await applyConfigChangeCore(
|
|
73
75
|
{
|
|
74
76
|
store,
|
|
77
|
+
projectStore: options.projectStore,
|
|
75
78
|
configPath: configPath ?? ctxHolder.actualConfigPath,
|
|
76
79
|
onConfigUpdated: (config) => {
|
|
77
80
|
ctxHolder.config = config;
|
package/src/serve/context.ts
CHANGED
|
@@ -115,6 +115,7 @@ export async function createServerContext(
|
|
|
115
115
|
|
|
116
116
|
// Progress callback updates downloadState for WebUI polling
|
|
117
117
|
const createPortOptions = (type: ModelType): CreatePortOptions => ({
|
|
118
|
+
egressCollections: "all",
|
|
118
119
|
policy,
|
|
119
120
|
onProgress: (progress) => {
|
|
120
121
|
downloadState.active = true;
|