@gmickel/gno 2.10.1 → 2.12.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 +11 -11
- package/assets/skill/SKILL.md +27 -15
- package/assets/skill/cli-reference.md +6 -5
- package/assets/skill/examples.md +16 -12
- package/assets/skill/mcp-reference.md +2 -1
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.10.1.zip → gno-browser-clipper-v2.12.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.12.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +10 -5
- package/spec/cli.md +127 -35
- package/spec/evals-agentic.md +3 -1
- package/spec/mcp.md +36 -6
- package/spec/output-schemas/ask.schema.json +8 -0
- package/spec/output-schemas/claim-verification.schema.json +8 -2
- package/spec/output-schemas/context-capsule-v1.schema.json +6 -1
- package/spec/output-schemas/context-capsule-verification.schema.json +4 -2
- package/spec/output-schemas/get.schema.json +4 -0
- package/spec/output-schemas/metadata-summary.schema.json +60 -0
- package/spec/output-schemas/multi-get.schema.json +4 -0
- package/spec/output-schemas/search-result.schema.json +4 -0
- package/spec/output-schemas/search-results.schema.json +4 -0
- package/spec/output-schemas/status.schema.json +28 -0
- package/src/app/constants.ts +16 -0
- package/src/app/context-agent-projection.ts +10 -1
- package/src/app/context-format.ts +1 -0
- package/src/app/verified-ask.ts +2 -0
- package/src/cli/commands/agents/block.ts +10 -9
- package/src/cli/commands/agents/commands.ts +6 -5
- package/src/cli/commands/agents/engine.ts +15 -2
- package/src/cli/commands/agents/harnesses.ts +21 -14
- package/src/cli/commands/ask-format.ts +18 -1
- package/src/cli/commands/ask.ts +4 -5
- package/src/cli/commands/cleanup.ts +46 -6
- package/src/cli/commands/collection/remove.ts +1 -0
- package/src/cli/commands/collection/rename.ts +1 -0
- package/src/cli/commands/daemon.ts +5 -1
- package/src/cli/commands/doctor.ts +37 -0
- package/src/cli/commands/get.ts +11 -1
- package/src/cli/commands/mcp/paths.ts +2 -1
- package/src/cli/commands/metadata.ts +79 -0
- package/src/cli/commands/multi-get.ts +13 -0
- package/src/cli/commands/setup-semantic.ts +6 -1
- package/src/cli/commands/shared.ts +35 -8
- package/src/cli/commands/skill/paths.ts +20 -2
- package/src/cli/commands/status.ts +89 -15
- package/src/cli/format/search-results.ts +11 -0
- package/src/cli/options.ts +2 -0
- package/src/cli/program.ts +64 -3
- package/src/core/activation-probe-plan.ts +0 -1
- package/src/core/config-mutation.ts +72 -55
- package/src/core/context-capsule-schema.ts +2 -0
- package/src/core/context-capsule-verification.ts +2 -0
- package/src/core/context-compiler.ts +2 -5
- package/src/core/context-evidence.ts +1 -0
- package/src/core/context-verifier.ts +14 -1
- package/src/core/memory-remember.ts +0 -1
- package/src/core/metadata-discovery.ts +169 -0
- package/src/core/module-path.ts +33 -0
- package/src/core/runtime-entrypoint.ts +3 -1
- package/src/ingestion/file-processor.ts +6 -3
- package/src/ingestion/prepare-file.ts +14 -4
- package/src/ingestion/source-availability/root.ts +138 -0
- package/src/ingestion/sync.ts +35 -1
- package/src/ingestion/walker.ts +11 -3
- package/src/llm/native-worker/client.ts +8 -4
- package/src/llm/native-worker/errors.ts +26 -3
- package/src/mcp/http-egress.ts +1 -0
- package/src/mcp/http-transport.ts +7 -2
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-descriptions-core.ts +3 -3
- package/src/mcp/tool-profile.ts +16 -1
- package/src/mcp/tools/get.ts +10 -0
- package/src/mcp/tools/index.ts +68 -4
- package/src/mcp/tools/multi-get.ts +15 -0
- package/src/mcp/tools/query.ts +5 -3
- package/src/mcp/tools/search.ts +5 -3
- package/src/mcp/tools/status.ts +17 -2
- package/src/mcp/tools/vsearch.ts +5 -3
- package/src/mcp/tools/workspace-write.ts +42 -34
- package/src/pipeline/answer-prompt.ts +15 -2
- package/src/pipeline/answer.ts +3 -0
- package/src/pipeline/claim-verification-schema.ts +6 -1
- package/src/pipeline/claim-verification.ts +54 -1
- package/src/pipeline/explain.ts +1 -1
- package/src/pipeline/fusion.ts +24 -4
- package/src/pipeline/hybrid.ts +5 -1
- package/src/pipeline/query-snippet.ts +266 -0
- package/src/pipeline/result-context.ts +29 -0
- package/src/pipeline/search.ts +154 -20
- package/src/pipeline/types.ts +4 -0
- package/src/pipeline/vsearch.ts +5 -1
- package/src/sdk/client.ts +46 -6
- package/src/sdk/documents.ts +5 -0
- package/src/sdk/types.ts +11 -1
- package/src/serve/public/components/ThoroughnessSelector.tsx +1 -1
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/pages/Collections.tsx +8 -0
- package/src/serve/public/pages/Search.tsx +9 -1
- package/src/serve/resident-request.ts +8 -0
- package/src/serve/resident-runtime.ts +25 -3
- package/src/serve/routes/api.ts +29 -1
- package/src/serve/server.ts +12 -0
- package/src/serve/spa-production-build.ts +7 -3
- package/src/serve/status-model.ts +2 -0
- package/src/serve/status.ts +29 -0
- package/src/sessions/config-refresh.ts +6 -0
- package/src/sessions/import-child.ts +4 -1
- package/src/store/sqlite/adapter.ts +241 -20
- package/src/store/types.ts +61 -7
- package/src/store/vector/variant-search.ts +112 -74
- package/browser-extension/artifacts/gno-browser-clipper-v2.10.1.zip.sha256 +0 -1
|
@@ -81,6 +81,8 @@ interface CollectionStats {
|
|
|
81
81
|
documentCount: number;
|
|
82
82
|
chunkCount: number;
|
|
83
83
|
embeddedCount: number;
|
|
84
|
+
/** Set while the folder is missing or unreadable; documents stay indexed. */
|
|
85
|
+
rootError?: { code: string; message: string };
|
|
84
86
|
include?: string[];
|
|
85
87
|
models?: Partial<Record<"embed" | "rerank" | "expand" | "gen", string>>;
|
|
86
88
|
effectiveModels?: Record<"embed" | "rerank" | "expand" | "gen", string>;
|
|
@@ -277,6 +279,12 @@ function CollectionCard({
|
|
|
277
279
|
</Tooltip>
|
|
278
280
|
</TooltipProvider>
|
|
279
281
|
)}
|
|
282
|
+
{collection.rootError && (
|
|
283
|
+
<p className="flex items-start gap-1.5 text-amber-500 text-xs">
|
|
284
|
+
<AlertCircleIcon className="mt-0.5 size-3.5 shrink-0" />
|
|
285
|
+
<span>{collection.rootError.message}</span>
|
|
286
|
+
</p>
|
|
287
|
+
)}
|
|
280
288
|
</CardHeader>
|
|
281
289
|
|
|
282
290
|
<CardContent className="pt-2">
|
|
@@ -174,8 +174,12 @@ export default function Search({ navigate }: PageProps) {
|
|
|
174
174
|
query.trim().length > 0 && !structuredQueryState.ok
|
|
175
175
|
? structuredQueryState.error.message
|
|
176
176
|
: null;
|
|
177
|
+
// Without vectors, Fast is keyword search unless modes or intent need the
|
|
178
|
+
// hybrid endpoint. With vectors, Fast is always hybrid (no expansion,
|
|
179
|
+
// rerank or graph), the same path as CLI `--fast`.
|
|
177
180
|
const forceHybridForModes =
|
|
178
181
|
thoroughness === "fast" &&
|
|
182
|
+
!hybridAvailable &&
|
|
179
183
|
(queryModes.length > 0 ||
|
|
180
184
|
intent.trim().length > 0 ||
|
|
181
185
|
(structuredQueryState.ok &&
|
|
@@ -325,6 +329,7 @@ export default function Search({ navigate }: PageProps) {
|
|
|
325
329
|
|
|
326
330
|
const useBm25 =
|
|
327
331
|
thoroughness === "fast" &&
|
|
332
|
+
!hybridAvailable &&
|
|
328
333
|
queryModes.length === 0 &&
|
|
329
334
|
intent.trim().length === 0 &&
|
|
330
335
|
!structuredQueryState.value.usedStructuredQuerySyntax;
|
|
@@ -403,6 +408,7 @@ export default function Search({ navigate }: PageProps) {
|
|
|
403
408
|
activePreset,
|
|
404
409
|
category,
|
|
405
410
|
exclude,
|
|
411
|
+
hybridAvailable,
|
|
406
412
|
intent,
|
|
407
413
|
query,
|
|
408
414
|
queryModes,
|
|
@@ -448,7 +454,9 @@ export default function Search({ navigate }: PageProps) {
|
|
|
448
454
|
|
|
449
455
|
const thoroughnessDesc =
|
|
450
456
|
thoroughness === "fast"
|
|
451
|
-
?
|
|
457
|
+
? hybridAvailable
|
|
458
|
+
? "Hybrid, no expansion or reranking"
|
|
459
|
+
: "Keyword search (no vectors)"
|
|
452
460
|
: thoroughness === "balanced"
|
|
453
461
|
? resolveDepthPolicy({ presetId: activePreset })
|
|
454
462
|
.balancedExpansionEnabled
|
|
@@ -2,6 +2,7 @@ import type { ResidentRuntime } from "./resident-runtime";
|
|
|
2
2
|
/** Admission and bounded-reader boundary for resident REST reads. */
|
|
3
3
|
|
|
4
4
|
import { withInferenceScope } from "../llm/inference-scope";
|
|
5
|
+
import { StaleCollectionsError } from "../store/sqlite/adapter";
|
|
5
6
|
import { sessionsErrorResponse } from "./routes/sessions";
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -16,6 +17,13 @@ export async function refreshResidentConfig(
|
|
|
16
17
|
await runtime.refreshConfig?.();
|
|
17
18
|
return null;
|
|
18
19
|
} catch (error) {
|
|
20
|
+
// Names collections only (no paths), and says how to resolve it.
|
|
21
|
+
if (error instanceof StaleCollectionsError) {
|
|
22
|
+
return Response.json(
|
|
23
|
+
{ error: { code: "STALE_COLLECTIONS", message: error.message } },
|
|
24
|
+
{ status: 409 }
|
|
25
|
+
);
|
|
26
|
+
}
|
|
19
27
|
return sessionsErrorResponse(error);
|
|
20
28
|
}
|
|
21
29
|
}
|
|
@@ -61,7 +61,11 @@ import {
|
|
|
61
61
|
readInstanceConfig,
|
|
62
62
|
} from "../sessions/config-refresh";
|
|
63
63
|
import { remoteSafeSessionsError } from "../sessions/types";
|
|
64
|
-
import {
|
|
64
|
+
import {
|
|
65
|
+
SqliteAdapter,
|
|
66
|
+
StaleCollectionsError,
|
|
67
|
+
staleCollectionsMessage,
|
|
68
|
+
} from "../store/sqlite/adapter";
|
|
65
69
|
import {
|
|
66
70
|
createServerContext,
|
|
67
71
|
type CreateServerContextOptions,
|
|
@@ -519,10 +523,23 @@ export async function startResidentRuntime(
|
|
|
519
523
|
return undefined;
|
|
520
524
|
}
|
|
521
525
|
};
|
|
526
|
+
// Another client may index a collection the served config omits after this
|
|
527
|
+
// runtime started; requests fail instead of reading it.
|
|
528
|
+
const assertServedCollections = (): void => {
|
|
529
|
+
const stale = store.staleIndexedCollections(
|
|
530
|
+
ctxHolder.config.collections.map(({ name }) => name)
|
|
531
|
+
);
|
|
532
|
+
if (stale.length > 0) {
|
|
533
|
+
throw new StaleCollectionsError(staleCollectionsMessage(stale));
|
|
534
|
+
}
|
|
535
|
+
};
|
|
522
536
|
const refreshConfig = (): Promise<void> => {
|
|
523
537
|
configRefresh ??= (async () => {
|
|
524
538
|
const stamp = await configStamp();
|
|
525
|
-
if (stamp !== undefined && stamp === loadedConfigStamp)
|
|
539
|
+
if (stamp !== undefined && stamp === loadedConfigStamp) {
|
|
540
|
+
assertServedCollections();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
526
543
|
const file = await readInstanceConfig(
|
|
527
544
|
instance,
|
|
528
545
|
deps.loadConfig ?? loadConfig
|
|
@@ -535,6 +552,7 @@ export async function startResidentRuntime(
|
|
|
535
552
|
};
|
|
536
553
|
if (Bun.deepEquals(next, served)) {
|
|
537
554
|
loadedConfigStamp = stamp;
|
|
555
|
+
assertServedCollections();
|
|
538
556
|
return;
|
|
539
557
|
}
|
|
540
558
|
await adoptServedConfig(
|
|
@@ -816,7 +834,11 @@ export async function startResidentRuntime(
|
|
|
816
834
|
await refreshConfig();
|
|
817
835
|
lastRefreshError = undefined;
|
|
818
836
|
} catch (error) {
|
|
819
|
-
|
|
837
|
+
// The stale-collections message names collections, never paths.
|
|
838
|
+
const message =
|
|
839
|
+
error instanceof StaleCollectionsError
|
|
840
|
+
? error.message
|
|
841
|
+
: remoteSafeSessionsError(error).message;
|
|
820
842
|
if (message !== lastRefreshError)
|
|
821
843
|
console.warn(`gno ${mode}: config not reloaded: ${message}`);
|
|
822
844
|
lastRefreshError = message;
|
package/src/serve/routes/api.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* @module src/serve/routes/api
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// node:fs/promises structure ops + realpath have no Bun equivalent
|
|
9
8
|
import { readdir, realpath } from "node:fs/promises";
|
|
10
9
|
// node:path has no Bun equivalent
|
|
11
10
|
import { posix as pathPosix } from "node:path";
|
|
@@ -90,6 +89,7 @@ import {
|
|
|
90
89
|
type RecallInput,
|
|
91
90
|
type RememberInput,
|
|
92
91
|
} from "../../core/memory";
|
|
92
|
+
import { readMetadataSummary } from "../../core/metadata-discovery";
|
|
93
93
|
import {
|
|
94
94
|
hasContentMutation,
|
|
95
95
|
recordContentMutation,
|
|
@@ -167,6 +167,8 @@ import {
|
|
|
167
167
|
import { diagnoseQueryTarget } from "../../pipeline/diagnose";
|
|
168
168
|
import { searchHybrid } from "../../pipeline/hybrid";
|
|
169
169
|
import { validateQueryModes } from "../../pipeline/query-modes";
|
|
170
|
+
// node:fs/promises structure ops + realpath have no Bun equivalent
|
|
171
|
+
import { supersedingUri } from "../../pipeline/result-context";
|
|
170
172
|
import { searchBm25 } from "../../pipeline/search";
|
|
171
173
|
import {
|
|
172
174
|
derivePublishArtifactFilename,
|
|
@@ -2146,7 +2148,9 @@ export async function handleDoc(
|
|
|
2146
2148
|
const record = projectRecordEvidenceMetadata(doc);
|
|
2147
2149
|
const relPath = doc.recordSourcePath ?? doc.relPath;
|
|
2148
2150
|
|
|
2151
|
+
const successor = await supersedingUri(store, doc.uri);
|
|
2149
2152
|
const responseData = {
|
|
2153
|
+
...(successor ? { supersededBy: successor } : {}),
|
|
2150
2154
|
docid: doc.docid,
|
|
2151
2155
|
uri: doc.uri,
|
|
2152
2156
|
title: doc.title,
|
|
@@ -2463,6 +2467,30 @@ export async function handleDocAsset(
|
|
|
2463
2467
|
* Query params: collection, prefix
|
|
2464
2468
|
* Returns tag list with document counts.
|
|
2465
2469
|
*/
|
|
2470
|
+
export async function handleMetadata(
|
|
2471
|
+
store: Pick<StorePort, "getTypedMetadataDocuments">,
|
|
2472
|
+
url: URL
|
|
2473
|
+
): Promise<Response> {
|
|
2474
|
+
const collection = url.searchParams.get("collection") ?? undefined;
|
|
2475
|
+
const valuesParam = url.searchParams.get("values");
|
|
2476
|
+
try {
|
|
2477
|
+
return jsonResponse(
|
|
2478
|
+
await readMetadataSummary(store, {
|
|
2479
|
+
collection,
|
|
2480
|
+
values: valuesParam === null ? undefined : Number(valuesParam),
|
|
2481
|
+
})
|
|
2482
|
+
);
|
|
2483
|
+
} catch (cause) {
|
|
2484
|
+
return cause instanceof RangeError
|
|
2485
|
+
? errorResponse("VALIDATION", cause.message)
|
|
2486
|
+
: errorResponse(
|
|
2487
|
+
"RUNTIME",
|
|
2488
|
+
cause instanceof Error ? cause.message : String(cause),
|
|
2489
|
+
500
|
|
2490
|
+
);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2466
2494
|
export async function handleTags(
|
|
2467
2495
|
store: SqliteAdapter,
|
|
2468
2496
|
url: URL
|
package/src/serve/server.ts
CHANGED
|
@@ -83,6 +83,7 @@ import {
|
|
|
83
83
|
handleSetPreset,
|
|
84
84
|
handleStatus,
|
|
85
85
|
handleSync,
|
|
86
|
+
handleMetadata,
|
|
86
87
|
handleTags,
|
|
87
88
|
handleTrashDoc,
|
|
88
89
|
handleUpdateCollection,
|
|
@@ -1331,6 +1332,17 @@ export async function startServer(
|
|
|
1331
1332
|
}
|
|
1332
1333
|
},
|
|
1333
1334
|
},
|
|
1335
|
+
"/api/metadata": {
|
|
1336
|
+
GET: async (req: Request) => {
|
|
1337
|
+
const url = new URL(req.url);
|
|
1338
|
+
return withSecurityHeaders(
|
|
1339
|
+
await handleResidentRead(runtime as ResidentRuntime, req, () =>
|
|
1340
|
+
handleMetadata(store, url)
|
|
1341
|
+
),
|
|
1342
|
+
isDev
|
|
1343
|
+
);
|
|
1344
|
+
},
|
|
1345
|
+
},
|
|
1334
1346
|
"/api/tags": {
|
|
1335
1347
|
GET: async (req: Request) => {
|
|
1336
1348
|
const url = new URL(req.url);
|
|
@@ -6,6 +6,8 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
// node:path — no Bun path utils.
|
|
7
7
|
import { basename, join, relative } from "node:path";
|
|
8
8
|
|
|
9
|
+
import { moduleDir, modulePath } from "../core/module-path";
|
|
10
|
+
|
|
9
11
|
export const ROOT_MOUNT_MARKER = 'getElementById("root")';
|
|
10
12
|
|
|
11
13
|
export type ProductionSpaFile = {
|
|
@@ -19,7 +21,8 @@ export type ProductionSpaAssets = {
|
|
|
19
21
|
sourceHash: string;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
const productionSpaPublicDir = (): string =>
|
|
24
|
+
const productionSpaPublicDir = (): string =>
|
|
25
|
+
join(moduleDir(import.meta), "public");
|
|
23
26
|
|
|
24
27
|
export const productionSpaEntryPath = (): string =>
|
|
25
28
|
join(productionSpaPublicDir(), "index.html");
|
|
@@ -79,13 +82,14 @@ const contentTypeFor = (path: string): string => {
|
|
|
79
82
|
return "application/octet-stream";
|
|
80
83
|
};
|
|
81
84
|
|
|
82
|
-
export const isBunfsPath = (path: string): boolean => {
|
|
85
|
+
export const isBunfsPath = (path: string | undefined): boolean => {
|
|
86
|
+
if (path === undefined) return false;
|
|
83
87
|
const normalized = path.replaceAll("\\", "/");
|
|
84
88
|
return normalized.includes("/$bunfs/") || normalized.startsWith("B:/~BUN/");
|
|
85
89
|
};
|
|
86
90
|
|
|
87
91
|
export const isStandaloneExecutable = (): boolean =>
|
|
88
|
-
isBunfsPath(import.meta
|
|
92
|
+
isBunfsPath(modulePath(import.meta));
|
|
89
93
|
|
|
90
94
|
const rewriteProductionHtml = (html: string, jsEntryPath: string): string => {
|
|
91
95
|
const script = `<script type="module" src="/${basename(jsEntryPath)}"></script>`;
|
|
@@ -28,6 +28,8 @@ export interface StatusCollection {
|
|
|
28
28
|
documentCount: number;
|
|
29
29
|
chunkCount: number;
|
|
30
30
|
embeddedCount: number;
|
|
31
|
+
/** Present while the collection root is missing or unreadable (path-free). */
|
|
32
|
+
rootError?: { code: string; message: string };
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export interface SuggestedCollection {
|
package/src/serve/status.ts
CHANGED
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
type ActivationStatus,
|
|
21
21
|
buildActivationStatus,
|
|
22
22
|
} from "../core/activation-status";
|
|
23
|
+
import {
|
|
24
|
+
checkCollectionRoots,
|
|
25
|
+
type CollectionRootError,
|
|
26
|
+
} from "../ingestion/source-availability/root";
|
|
23
27
|
import { ModelCache } from "../llm/cache";
|
|
24
28
|
import { envIsSet, resolveDownloadPolicy } from "../llm/policy";
|
|
25
29
|
import { getActivePreset, resolveModelUri } from "../llm/registry";
|
|
@@ -263,6 +267,25 @@ function buildCollectionCheck(status: IndexStatus): HealthCheck {
|
|
|
263
267
|
};
|
|
264
268
|
}
|
|
265
269
|
|
|
270
|
+
/** Folders whose root is missing or unreadable right now; null when none. */
|
|
271
|
+
function buildFolderAccessCheck(
|
|
272
|
+
rootErrors: Record<string, CollectionRootError>
|
|
273
|
+
): HealthCheck | null {
|
|
274
|
+
const names = Object.keys(rootErrors);
|
|
275
|
+
if (names.length === 0) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
id: "folder-access",
|
|
280
|
+
title: "Folder access",
|
|
281
|
+
status: "warn",
|
|
282
|
+
summary: `${names.join(", ")}: folder missing or unreadable`,
|
|
283
|
+
detail: `${names.map((name) => `${name}: ${rootErrors[name]?.message}.`).join(" ")} Reconnect the drive or restore the folder; this clears as soon as it is back. If the folder moved, change the collection's path.`,
|
|
284
|
+
actionLabel: "Manage folders",
|
|
285
|
+
actionKind: "open-collections",
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
266
289
|
function buildIndexingCheck(status: IndexStatus): HealthCheck {
|
|
267
290
|
if (status.recentErrors > 0) {
|
|
268
291
|
return {
|
|
@@ -688,6 +711,7 @@ export async function buildAppStatus(
|
|
|
688
711
|
bootstrap,
|
|
689
712
|
embedCached,
|
|
690
713
|
connectorTargets,
|
|
714
|
+
rootErrors,
|
|
691
715
|
] = await Promise.all([
|
|
692
716
|
buildModelCheck(ctx, deps),
|
|
693
717
|
buildDiskCheck(status, deps),
|
|
@@ -695,6 +719,7 @@ export async function buildAppStatus(
|
|
|
695
719
|
buildBootstrapState(ctx),
|
|
696
720
|
isModelCached(preset.embed),
|
|
697
721
|
(deps.listConnectorTargets ?? getConnectorVerificationTargets)(),
|
|
722
|
+
checkCollectionRoots(ctx.config.collections),
|
|
698
723
|
]);
|
|
699
724
|
const activation = await (deps.buildActivation ?? buildActivationStatus)(
|
|
700
725
|
ctx.store,
|
|
@@ -712,6 +737,7 @@ export async function buildAppStatus(
|
|
|
712
737
|
const backgroundCheck = buildBackgroundCheck(ctx, status);
|
|
713
738
|
const checks = [
|
|
714
739
|
buildCollectionCheck(status),
|
|
740
|
+
buildFolderAccessCheck(rootErrors),
|
|
715
741
|
buildIndexingCheck(status),
|
|
716
742
|
buildActivationCheck(activation),
|
|
717
743
|
buildConnectorActivationCheck(activation),
|
|
@@ -746,6 +772,9 @@ export async function buildAppStatus(
|
|
|
746
772
|
documentCount: collection.activeDocuments,
|
|
747
773
|
chunkCount: collection.totalChunks,
|
|
748
774
|
embeddedCount: collection.embeddedChunks,
|
|
775
|
+
...(rootErrors[collection.name]
|
|
776
|
+
? { rootError: rootErrors[collection.name] }
|
|
777
|
+
: {}),
|
|
749
778
|
})),
|
|
750
779
|
totalDocuments: status.activeDocuments,
|
|
751
780
|
totalChunks: status.totalChunks,
|
|
@@ -13,6 +13,7 @@ import type { SqliteAdapter } from "../store/sqlite/adapter";
|
|
|
13
13
|
import { getIndexDbPath } from "../app/constants";
|
|
14
14
|
import { loadConfig } from "../config";
|
|
15
15
|
import { collectionEgressPolicyEpoch } from "../core/collection-egress-policy-service";
|
|
16
|
+
import { StaleCollectionsError } from "../store/sqlite/adapter";
|
|
16
17
|
import { assertSessionBinding } from "./binding";
|
|
17
18
|
import { SessionsError } from "./types";
|
|
18
19
|
|
|
@@ -76,7 +77,12 @@ export async function adoptServedConfig(
|
|
|
76
77
|
served: ServedSessionsConfig,
|
|
77
78
|
config: Config
|
|
78
79
|
): Promise<void> {
|
|
80
|
+
// A config edit never deletes indexed collections; one it dropped fails
|
|
81
|
+
// the refresh until it is added back or `gno cleanup` deletes it.
|
|
79
82
|
const collections = await served.store.syncCollections(config.collections);
|
|
83
|
+
if (!collections.ok && collections.error.code === "STALE_COLLECTIONS") {
|
|
84
|
+
throw new StaleCollectionsError(collections.error.message);
|
|
85
|
+
}
|
|
80
86
|
if (!collections.ok) {
|
|
81
87
|
throw new Error(
|
|
82
88
|
`Config saved but collection sync failed: ${collections.error.message}`
|
|
@@ -26,6 +26,7 @@ import type { Config } from "../config/types";
|
|
|
26
26
|
import type { SessionImportReceipt } from "./types";
|
|
27
27
|
|
|
28
28
|
import { getIndexDbPath } from "../app/constants";
|
|
29
|
+
import { modulePath } from "../core/module-path";
|
|
29
30
|
import { isStandaloneExecutable } from "../serve/spa-production-build";
|
|
30
31
|
import { SqliteAdapter } from "../store/sqlite/adapter";
|
|
31
32
|
import { assertSessionBinding } from "./binding";
|
|
@@ -113,7 +114,9 @@ export async function importInChildProcess(
|
|
|
113
114
|
// Shared check: covers the POSIX `/$bunfs/` and Windows `B:/~BUN/` roots.
|
|
114
115
|
const compiled = isStandaloneExecutable();
|
|
115
116
|
const child = Bun.spawn({
|
|
116
|
-
cmd: compiled
|
|
117
|
+
cmd: compiled
|
|
118
|
+
? [process.execPath]
|
|
119
|
+
: [process.execPath, modulePath(import.meta)],
|
|
117
120
|
env: compiled ? { ...process.env, [IMPORT_CHILD_ENV]: "1" } : process.env,
|
|
118
121
|
// The request line, then held open as the child's lifeline.
|
|
119
122
|
stdin: "pipe",
|