@canonical/pragma-cli 0.32.0 → 0.34.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 +72 -0
- package/dist/pragma +0 -0
- package/package.json +19 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +15 -6
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.test.ts +10 -1
- package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
- package/src/capabilities/capabilities/catalog.ts +20 -8
- package/src/capabilities/capabilities/hints.ts +21 -36
- package/src/capabilities/capabilities/types.ts +2 -0
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.render.test.ts +31 -4
- package/src/capabilities/colophon/colophon.render.ts +13 -4
- package/src/capabilities/colophon/colophon.test.ts +3 -3
- package/src/capabilities/colophon/colophon.verb.ts +11 -4
- package/src/capabilities/colophon/markdownTerminal.ts +33 -14
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +10 -108
- package/src/capabilities/config/set.test.ts +6 -6
- package/src/capabilities/config/set.verb.ts +12 -8
- package/src/capabilities/config/show.render.test.ts +137 -0
- package/src/capabilities/config/show.render.ts +57 -16
- package/src/capabilities/config/show.verb.ts +9 -8
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
- package/src/capabilities/create/byteEquality.test.ts +82 -101
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +192 -16
- package/src/capabilities/create/create.verb.ts +208 -79
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
- package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
- package/src/capabilities/doctor/doctor.render.test.ts +170 -0
- package/src/capabilities/doctor/doctor.render.ts +117 -34
- package/src/capabilities/doctor/doctor.test.ts +68 -9
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +33 -9
- package/src/capabilities/doctor/types.ts +16 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/index.ts +10 -3
- package/src/capabilities/ontology/ontology.test.ts +157 -5
- package/src/capabilities/ontology/verbs.ts +203 -104
- package/src/capabilities/prompt/prompt.render.test.ts +94 -0
- package/src/capabilities/prompt/prompt.render.ts +23 -6
- package/src/capabilities/prompt/prompt.test.ts +68 -1
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/reference.test.ts +32 -0
- package/src/capabilities/resources/provider.ts +58 -25
- package/src/capabilities/resources/resources.test.ts +176 -21
- package/src/capabilities/scopeTypeSync.test.ts +37 -0
- package/src/capabilities/setup/index.ts +7 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
- package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
- package/src/capabilities/setup/operations/setupLsp.ts +56 -12
- package/src/capabilities/setup/operations/setupMcp.ts +216 -39
- package/src/capabilities/setup/operations/setupSkills.ts +10 -4
- package/src/capabilities/setup/setup.render.test.ts +109 -0
- package/src/capabilities/setup/setup.render.ts +61 -14
- package/src/capabilities/setup/setup.test.ts +352 -1
- package/src/capabilities/setup/setup.verb.ts +68 -9
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/setup/types.ts +59 -2
- package/src/capabilities/shared/bands.ts +16 -0
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +69 -49
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +146 -98
- package/src/capabilities/sources/sources.test.ts +264 -84
- package/src/capabilities/sources/status.render.test.ts +94 -0
- package/src/capabilities/sources/status.render.ts +55 -33
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +65 -17
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +9 -7
- package/src/constants.ts +49 -23
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
- package/src/kernel/completion/complete.test.ts +3 -5
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +131 -113
- package/src/kernel/completion/model.test.ts +3 -3
- package/src/kernel/completion/model.ts +11 -1
- package/src/kernel/completion/parse.test.ts +2 -4
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +5 -5
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/renderError.ts +1 -1
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +60 -11
- package/src/kernel/project/cli/buildProgram.ts +11 -0
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/globalFlags.test.ts +12 -4
- package/src/kernel/project/cli/globalFlags.ts +14 -14
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +109 -107
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +12 -7
- package/src/kernel/project/mcp/mcpError.ts +52 -0
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
- package/src/kernel/project/mcp/prompts/provider.ts +51 -7
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/formatters.ts +5 -4
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +35 -1
- package/src/kernel/render/renderers.ts +25 -7
- package/src/kernel/render/style.test.ts +31 -0
- package/src/kernel/render/style.ts +74 -0
- package/src/kernel/runtime/graphpack/build.ts +57 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +32 -22
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +72 -45
- package/src/kernel/runtime/loadSession.ts +4 -12
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +141 -65
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +62 -0
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/types.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
- package/src/kernel/spec/emitReference.test.ts +196 -0
- package/src/kernel/spec/emitReference.ts +624 -0
- package/src/kernel/spec/emitSurface.ts +13 -7
- package/src/kernel/spec/index.ts +2 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
- package/src/testing/behavioral/completion.test.ts +21 -8
- package/src/testing/behavioral/disclosure.test.ts +12 -0
- package/src/testing/behavioral/docExamples.test.ts +283 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -73
- package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
- package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +10 -5
- package/src/testing/behavioral/staleCommands.test.ts +86 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
- package/src/testing/eval/cases/assembly.ts +16 -9
- package/src/testing/eval/cases/effectful.ts +31 -16
- package/src/testing/eval/cases/readNouns.ts +23 -18
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +201 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +179 -0
- package/src/testing/fixtures/graph/owlExport.ts +97 -0
- package/src/testing/helpers/extractSnippets.ts +29 -0
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/golden.ts +1 -1
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.verb.ts +0 -150
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -155
- package/src/capabilities/block/parity.test.ts +0 -180
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/config/field.test.ts +0 -188
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.ts +0 -34
- package/src/capabilities/tier/lookup.test.ts +0 -68
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -59
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -16,24 +16,19 @@ import { readFileSync } from "node:fs";
|
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { createStore } from "@canonical/ke";
|
|
18
18
|
import { compileFromExtraction } from "@canonical/ke-graphql";
|
|
19
|
-
import { RECOVERY_CLI_PREFIX } from "../../../constants.js";
|
|
20
19
|
import { PragmaError } from "../../error/PragmaError.js";
|
|
21
20
|
import { cliRecovery } from "../../error/recovery.js";
|
|
22
21
|
import type { StoreSession } from "../types.js";
|
|
23
22
|
import { readManifest } from "./manifest.js";
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
INDEX_FILE,
|
|
27
|
-
packIndexSchema,
|
|
28
|
-
SCHEMA_FILE,
|
|
29
|
-
} from "./types.js";
|
|
23
|
+
import { packIndexSchema } from "./schemas.js";
|
|
24
|
+
import { DATA_FILE, INDEX_FILE, SCHEMA_FILE } from "./types.js";
|
|
30
25
|
|
|
31
26
|
/** STORE_UNAVAILABLE with the canonical `pragma sources update` recovery (CLI + MCP). */
|
|
32
27
|
function packUnavailable(reason: string): PragmaError {
|
|
33
28
|
return PragmaError.storeUnavailable(reason, {
|
|
34
29
|
recovery: cliRecovery(
|
|
35
|
-
|
|
36
|
-
"Rebuild the local store from the configured
|
|
30
|
+
"sources update",
|
|
31
|
+
"Rebuild the local store from the configured packs.",
|
|
37
32
|
// An agent recovers by calling the tool, then retrying (PR9 C1 cold-store
|
|
38
33
|
// retry makes the post-update retry succeed).
|
|
39
34
|
{ tool: "sources_update" },
|
|
@@ -73,13 +68,28 @@ export async function readPack(dir: string): Promise<StoreSession> {
|
|
|
73
68
|
JSON.parse(readFileSync(join(dir, INDEX_FILE), "utf-8")),
|
|
74
69
|
);
|
|
75
70
|
|
|
76
|
-
// Guard against a corrupt
|
|
77
|
-
// ke silently
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
71
|
+
// Guard against a corrupt or TRUNCATED `data.nq` beside an intact manifest:
|
|
72
|
+
// ke silently serves whatever loaded (falling back to an EMPTY store on a
|
|
73
|
+
// parse failure, and rewriting an empty dump). When the manifest records the
|
|
74
|
+
// build-time triple count (A9), require the booted store to hold AT LEAST it:
|
|
75
|
+
// a SHORTFALL means the dump was truncated (fewer triples than were built) —
|
|
76
|
+
// it passes the mere size>0 completeness gate yet would serve a partial graph
|
|
77
|
+
// silently, so it must surface STORE_UNAVAILABLE here. A benign SUPERSET (more
|
|
78
|
+
// triples than recorded — e.g. a future ke counting change) is TOLERATED
|
|
79
|
+
// rather than treated as corruption, so such a change can't trip a fleet-wide
|
|
80
|
+
// false STORE_UNAVAILABLE. Older packs without the count fall back to the
|
|
81
|
+
// "populated index but empty store" check (via the now-empty dump,
|
|
82
|
+
// `packIsComplete` turns false so recovery rebuilds) rather than serving an
|
|
83
|
+
// empty graph as the pack.
|
|
84
|
+
const actualTriples = await countTriples(store);
|
|
85
|
+
if (manifest.tripleCount !== undefined) {
|
|
86
|
+
if (actualTriples < manifest.tripleCount) {
|
|
87
|
+
store.dispose();
|
|
88
|
+
throw packUnavailable(
|
|
89
|
+
`The pack at ${dir} has a corrupt data cache (expected at least ${manifest.tripleCount} triples, loaded ${actualTriples}).`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
} else if (index.entities.length > 0 && actualTriples === 0) {
|
|
83
93
|
store.dispose();
|
|
84
94
|
throw packUnavailable(`The pack at ${dir} has a corrupt data cache.`);
|
|
85
95
|
}
|
|
@@ -93,10 +103,10 @@ export async function readPack(dir: string): Promise<StoreSession> {
|
|
|
93
103
|
};
|
|
94
104
|
}
|
|
95
105
|
|
|
96
|
-
/**
|
|
97
|
-
async function
|
|
98
|
-
const result = (await store.query(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return result.
|
|
106
|
+
/** Count the booted store's triples (a cheap aggregate over the union graph). */
|
|
107
|
+
async function countTriples(store: StoreSession["store"]): Promise<number> {
|
|
108
|
+
const result = (await store.query(
|
|
109
|
+
"SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }" as never,
|
|
110
|
+
)) as import("@canonical/ke").SelectResult;
|
|
111
|
+
return Number(result.bindings.at(0)?.n ?? 0);
|
|
102
112
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zod schemas validating a pack's persisted artifacts.
|
|
3
|
+
*
|
|
4
|
+
* They live HERE, apart from the contracts in `types.ts`, for one reason: zod is
|
|
5
|
+
* a value dependency, and `types.ts` is reachable from the storeless fast path.
|
|
6
|
+
* `capabilities/index.ts` → `resolveSources` → `packIsComplete` → `readManifest`
|
|
7
|
+
* put `manifest.json`'s schema on the graph `bin.ts` evaluates for `__complete`,
|
|
8
|
+
* costing ~3–4 ms of a ~30 ms budget to build a validator for a ~1 KB file the
|
|
9
|
+
* boot decision only needs to look at. `readManifest` now validates that file
|
|
10
|
+
* structurally, by hand, and this module is imported only by the two readers
|
|
11
|
+
* that are already off the fast path (`read.ts` boots the store; `embedded.ts`
|
|
12
|
+
* materializes the 1.9 MB embedded pack).
|
|
13
|
+
*
|
|
14
|
+
* The schemas remain the EXECUTABLE SPECIFICATION of what a valid artifact is:
|
|
15
|
+
* `graphpack.test.ts` pins `readManifest`'s hand validator to accept exactly
|
|
16
|
+
* what {@link manifestSchema} accepts, in both directions, so the two cannot
|
|
17
|
+
* drift. Change a rule here and that test fails until the hand validator
|
|
18
|
+
* follows. `capabilities/lazy.test.ts` pins the other half — that NO module on
|
|
19
|
+
* the storeless graph imports zod at all — so this split cannot silently close.
|
|
20
|
+
*
|
|
21
|
+
* Each schema is annotated `z.ZodType<T>` against the hand-written contract it
|
|
22
|
+
* validates, so a field added to one and not the other is a type error here
|
|
23
|
+
* rather than a runtime surprise at a read.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
import type { Manifest, PackIndex, PackIndexEntity } from "./types.js";
|
|
28
|
+
|
|
29
|
+
/** zod schema validating a persisted {@link PackIndexEntity}. */
|
|
30
|
+
export const packIndexEntitySchema: z.ZodType<PackIndexEntity> = z.object({
|
|
31
|
+
name: z.string(),
|
|
32
|
+
type: z.string(),
|
|
33
|
+
uri: z.string().optional(),
|
|
34
|
+
prefixed: z.string().optional(),
|
|
35
|
+
types: z.array(z.string()).optional(),
|
|
36
|
+
label: z.string().nullable().optional(),
|
|
37
|
+
altNames: z.array(z.string()).optional(),
|
|
38
|
+
box: z.enum(["tbox", "abox"]).optional(),
|
|
39
|
+
description: z.string().nullable().optional(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/** zod schema validating a persisted {@link PackIndex}. */
|
|
43
|
+
export const packIndexSchema: z.ZodType<PackIndex> = z.object({
|
|
44
|
+
version: z.union([z.literal(1), z.literal(2)]),
|
|
45
|
+
contentHash: z.string(),
|
|
46
|
+
prefixes: z.record(z.string(), z.string()),
|
|
47
|
+
entities: z.array(packIndexEntitySchema),
|
|
48
|
+
instanceCountByType: z.record(z.string(), z.number()),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** zod schema validating a persisted `manifest.json`. */
|
|
52
|
+
export const manifestSchema: z.ZodType<Manifest> = z.object({
|
|
53
|
+
name: z.string(),
|
|
54
|
+
version: z.string(),
|
|
55
|
+
/** The config `packs` ref this pack was built from (verbatim), or a label. */
|
|
56
|
+
sourceRef: z.string(),
|
|
57
|
+
contentHash: z.string(),
|
|
58
|
+
prefixes: z.record(z.string(), z.string()),
|
|
59
|
+
createdAt: z.string(),
|
|
60
|
+
/**
|
|
61
|
+
* The store's triple count at build time. Cross-checked against the booted
|
|
62
|
+
* store so a truncated-but-non-empty `data.nq` (a partial graph that passes
|
|
63
|
+
* the mere size>0 completeness gate) surfaces as STORE_UNAVAILABLE rather
|
|
64
|
+
* than being served silently (A9). Optional — packs built before this field
|
|
65
|
+
* skip the check.
|
|
66
|
+
*/
|
|
67
|
+
tripleCount: z.number().optional(),
|
|
68
|
+
/**
|
|
69
|
+
* The distinct abox entity count (matches `entityTotal`). Lets
|
|
70
|
+
* `sources status` report the figure without parsing the whole `index.json`
|
|
71
|
+
* (A10). Optional — packs built before this field fall back to the index read.
|
|
72
|
+
*/
|
|
73
|
+
entityCount: z.number().optional(),
|
|
74
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the read stories the ANSWERING pack carries.
|
|
3
|
+
*
|
|
4
|
+
* It CONSUMES the one boot decision ({@link resolveSources}) rather than
|
|
5
|
+
* re-deriving it, so the nouns a project's reads answer with can never differ
|
|
6
|
+
* from the nouns its pack carries: a project pointed at its own pack gets that
|
|
7
|
+
* pack's stories, a fresh install gets the embedded snapshot's, and a
|
|
8
|
+
* configured-but-unbuilt project gets none (its reads refuse too).
|
|
9
|
+
*
|
|
10
|
+
* Total: any unreadable or unparseable `stories.json` yields `[]`. The records
|
|
11
|
+
* are RAW TEXT — validating them is `kernel/packs/collect.validateStories`'s
|
|
12
|
+
* job, behind one guard, because a third-party story must never break a command.
|
|
13
|
+
*
|
|
14
|
+
* The embedded snapshot's records come from their OWN generated module, so
|
|
15
|
+
* reading them costs a small string and never loads `pack.generated.ts`'s
|
|
16
|
+
* ~1.9 MB of n-quads (a measured +28 ms on every invocation).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import type { SourcesDecision } from "../resolveSources.js";
|
|
22
|
+
import { storiesJson } from "./embedded/pack.stories.generated.js";
|
|
23
|
+
import { STORIES_FILE } from "./types.js";
|
|
24
|
+
|
|
25
|
+
/** One package-declared story as the pack carries it: the file it came from, verbatim. */
|
|
26
|
+
export interface PackStoryRecord {
|
|
27
|
+
/** Where the story was declared, e.g. `@canonical/design-system/stories/block.json`. */
|
|
28
|
+
readonly source: string;
|
|
29
|
+
/** The file's raw text — unparsed, uninterpreted. */
|
|
30
|
+
readonly content: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A record shaped as this module promises — the file is a user-writable cache. */
|
|
34
|
+
function isRecord(value: unknown): value is PackStoryRecord {
|
|
35
|
+
return (
|
|
36
|
+
typeof value === "object" &&
|
|
37
|
+
value !== null &&
|
|
38
|
+
typeof (value as PackStoryRecord).source === "string" &&
|
|
39
|
+
typeof (value as PackStoryRecord).content === "string"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse a `stories.json` body; anything malformed degrades to no stories.
|
|
45
|
+
*
|
|
46
|
+
* The elements are checked, not just the container: a record that is not
|
|
47
|
+
* `{ source, content }` would otherwise reach `validateStories` and be reported
|
|
48
|
+
* as `Ignored story undefined: …` — a diagnostic that names no file.
|
|
49
|
+
*/
|
|
50
|
+
function parseRecords(json: string): readonly PackStoryRecord[] {
|
|
51
|
+
try {
|
|
52
|
+
const parsed: unknown = JSON.parse(json);
|
|
53
|
+
return Array.isArray(parsed) ? parsed.filter(isRecord) : [];
|
|
54
|
+
} catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The read stories the answering pack carries.
|
|
61
|
+
*
|
|
62
|
+
* @param decision - The boot decision from `resolveSources`.
|
|
63
|
+
* @returns The carried story records — `[]` when the pack carries none, no pack
|
|
64
|
+
* answers, or the file is unreadable.
|
|
65
|
+
* @note Impure — reads `stories.json` from the answering pack directory.
|
|
66
|
+
*/
|
|
67
|
+
export function activeStories(
|
|
68
|
+
decision: SourcesDecision,
|
|
69
|
+
): readonly PackStoryRecord[] {
|
|
70
|
+
switch (decision.kind) {
|
|
71
|
+
case "pack":
|
|
72
|
+
try {
|
|
73
|
+
return parseRecords(
|
|
74
|
+
readFileSync(join(decision.dir, STORIES_FILE), "utf-8"),
|
|
75
|
+
);
|
|
76
|
+
} catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
case "embedded":
|
|
80
|
+
return parseRecords(storiesJson);
|
|
81
|
+
case "unavailable":
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -1,28 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Graphpack artifact contracts — the
|
|
3
|
-
*
|
|
2
|
+
* Graphpack artifact contracts — the five files a built pack directory holds,
|
|
3
|
+
* as hand-written types with NO runtime dependency.
|
|
4
4
|
*
|
|
5
5
|
* A pack is the content-addressed, boot-ready form of a set of RDF sources:
|
|
6
6
|
* `data.nq` (the store's n-quads dump — boots via ke's cache path, no TTL
|
|
7
7
|
* parse), `schema.json` (the serialized ke-graphql extraction — boots via
|
|
8
8
|
* `compileFromExtraction`, no live 7-pass compile), `index.json` (the storeless
|
|
9
|
-
* entity index the completion tier and reads consume),
|
|
9
|
+
* entity index the completion tier and reads consume), `stories.json` (the read
|
|
10
|
+
* stories the packages ship, carried verbatim as raw text), and `manifest.json`
|
|
10
11
|
* (provenance + the prefixes the store was built with). A directory missing
|
|
11
12
|
* `manifest.json` is treated as absent (a torn build), so writes are always
|
|
12
13
|
* temp-dir + atomic rename.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
15
|
+
* FIVE AND ONLY FIVE: the constants below are the single place the artifact set
|
|
16
|
+
* is named, and three modules must agree with them — `buildPack` writes them,
|
|
17
|
+
* `packIsComplete` gates on them, and `materializeEmbeddedPack` writes them back
|
|
18
|
+
* out. A sixth artifact added to only some of those makes a pack whose content
|
|
19
|
+
* hash claims more than its directory holds, which the next build then reuses,
|
|
20
|
+
* silently dropping the difference. The agreement is pinned by `graphpack.test.ts`'s
|
|
21
|
+
* "the committed embedded pack (PROTECTED) > materializes exactly the files
|
|
22
|
+
* buildPack produces" — extend the set here and that test fails until every
|
|
23
|
+
* side follows.
|
|
24
|
+
*
|
|
25
|
+
* This module is on the STORELESS FAST PATH — `capabilities/index.ts` →
|
|
26
|
+
* `resolveSources` → `packIsComplete` → `readManifest` reaches it while
|
|
27
|
+
* `__complete` builds the command tree — so it carries NO runtime import at
|
|
28
|
+
* all. It used to hold the zod schemas too, which put zod on that graph through
|
|
29
|
+
* a genuine value import and cost ~3–4 ms of a ~30 ms budget; they now live in
|
|
30
|
+
* `schemas.ts`, imported only by the two readers that are already off the fast
|
|
31
|
+
* path. `capabilities/lazy.test.ts` pins the result — no module on that graph
|
|
32
|
+
* imports zod — so the split cannot silently close.
|
|
16
33
|
*/
|
|
17
34
|
|
|
18
|
-
import { z } from "zod";
|
|
19
|
-
|
|
20
35
|
/** The n-quads store dump — ke boots it via `createStore({ cache })`. */
|
|
21
36
|
export const DATA_FILE = "data.nq";
|
|
22
37
|
/** The serialized ke-graphql extraction — boots via `compileFromExtraction`. */
|
|
23
38
|
export const SCHEMA_FILE = "schema.json";
|
|
24
39
|
/** The storeless entity index (PR-C's dynamic-completion contract). */
|
|
25
40
|
export const INDEX_FILE = "index.json";
|
|
41
|
+
/**
|
|
42
|
+
* The read stories the packages shipped, as raw text: one
|
|
43
|
+
* `{ source, content }` record per `stories/*.json` file, in a JSON array.
|
|
44
|
+
*
|
|
45
|
+
* Written ALWAYS, even as `[]`, and gated by `packIsComplete` alongside the
|
|
46
|
+
* other three — an optional artifact would put the same condition in all three
|
|
47
|
+
* modules below, which is exactly how a pack ends up claiming stories its
|
|
48
|
+
* directory does not hold. Raw text rather than parsed definitions so the pack stays a
|
|
49
|
+
* faithful carrier of the package's bytes and EVERY interpretation failure
|
|
50
|
+
* (malformed JSON and schema-invalid JSON alike) is caught behind the one guard
|
|
51
|
+
* in `kernel/packs/collect.validateStories`.
|
|
52
|
+
*/
|
|
53
|
+
export const STORIES_FILE = "stories.json";
|
|
26
54
|
/** Provenance + prefixes; its presence marks a pack directory as complete. */
|
|
27
55
|
export const MANIFEST_FILE = "manifest.json";
|
|
28
56
|
|
|
@@ -45,9 +73,10 @@ export interface PackIndexEntity {
|
|
|
45
73
|
/** Human label (rdfs:label / skos:prefLabel / dcterms:title / schema:name). */
|
|
46
74
|
readonly label?: string | null;
|
|
47
75
|
/**
|
|
48
|
-
* Alternative completable names — the
|
|
49
|
-
*
|
|
50
|
-
*
|
|
76
|
+
* Alternative completable names — the values of the distribution's declared
|
|
77
|
+
* alternative-name property (`kernel/vocabulary.ts`), which is what a
|
|
78
|
+
* bespoke lookup matches on, when they differ from `name`/`label`.
|
|
79
|
+
* Enrichment for the storeless name-completion sources.
|
|
51
80
|
*/
|
|
52
81
|
readonly altNames?: readonly string[];
|
|
53
82
|
/** Schema (`tbox`) vs individual (`abox`). */
|
|
@@ -74,38 +103,36 @@ export interface PackIndex {
|
|
|
74
103
|
readonly instanceCountByType: Readonly<Record<string, number>>;
|
|
75
104
|
}
|
|
76
105
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/** Pack provenance and the prefixes the store was built with. */
|
|
111
|
-
export type Manifest = z.infer<typeof manifestSchema>;
|
|
106
|
+
/**
|
|
107
|
+
* Pack provenance and the prefixes the store was built with — the parsed shape
|
|
108
|
+
* of `manifest.json`.
|
|
109
|
+
*
|
|
110
|
+
* Hand-written rather than inferred from a zod schema, so reading a manifest
|
|
111
|
+
* needs no validator library (see the module docblock). `schemas.ts`'s
|
|
112
|
+
* {@link manifestSchema} is annotated against this type, so the two cannot
|
|
113
|
+
* drift in their FIELDS; `graphpack.test.ts` pins that they do not drift in
|
|
114
|
+
* what they ACCEPT either.
|
|
115
|
+
*/
|
|
116
|
+
export interface Manifest {
|
|
117
|
+
readonly name: string;
|
|
118
|
+
readonly version: string;
|
|
119
|
+
/** The config `packs` ref this pack was built from (verbatim), or a label. */
|
|
120
|
+
readonly sourceRef: string;
|
|
121
|
+
readonly contentHash: string;
|
|
122
|
+
readonly prefixes: Readonly<Record<string, string>>;
|
|
123
|
+
readonly createdAt: string;
|
|
124
|
+
/**
|
|
125
|
+
* The store's triple count at build time. Cross-checked against the booted
|
|
126
|
+
* store so a truncated-but-non-empty `data.nq` (a partial graph that passes
|
|
127
|
+
* the mere size>0 completeness gate) surfaces as STORE_UNAVAILABLE rather
|
|
128
|
+
* than being served silently (A9). Optional — packs built before this field
|
|
129
|
+
* skip the check.
|
|
130
|
+
*/
|
|
131
|
+
readonly tripleCount?: number;
|
|
132
|
+
/**
|
|
133
|
+
* The distinct abox entity count (matches `entityTotal`). Lets
|
|
134
|
+
* `sources status` report the figure without parsing the whole `index.json`
|
|
135
|
+
* (A10). Optional — packs built before this field fall back to the index read.
|
|
136
|
+
*/
|
|
137
|
+
readonly entityCount?: number;
|
|
138
|
+
}
|
|
@@ -9,13 +9,11 @@
|
|
|
9
9
|
* than silently reaching out.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { RECOVERY_CLI_PREFIX } from "../../constants.js";
|
|
13
12
|
import type { ConfigLayers } from "../config/types.js";
|
|
14
|
-
import { PragmaError } from "../error/PragmaError.js";
|
|
15
|
-
import { cliRecovery } from "../error/recovery.js";
|
|
16
13
|
import { materializeEmbeddedPack } from "./graphpack/embedded.js";
|
|
17
14
|
import { readPack } from "./graphpack/read.js";
|
|
18
15
|
import { resolveSources } from "./resolveSources.js";
|
|
16
|
+
import { storeUnavailable } from "./storeReadiness.js";
|
|
19
17
|
import type { StoreSession } from "./types.js";
|
|
20
18
|
|
|
21
19
|
/** What {@link loadStoreSession} needs from the runtime. */
|
|
@@ -39,15 +37,9 @@ export async function loadStoreSession(
|
|
|
39
37
|
const decision = resolveSources(layers, ctx.cwd);
|
|
40
38
|
|
|
41
39
|
if (decision.kind === "unavailable") {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"Build the local store from the configured packages.",
|
|
46
|
-
// An agent recovers by calling the tool (then retrying — PR9's C1 cold-
|
|
47
|
-
// store retry makes a post-update retry succeed).
|
|
48
|
-
{ tool: "sources_update" },
|
|
49
|
-
),
|
|
50
|
-
});
|
|
40
|
+
// The SAME cold-store diagnostic the native MCP surfaces raise (shared via
|
|
41
|
+
// storeReadiness), so a cold store reads identically on every surface.
|
|
42
|
+
throw storeUnavailable(decision.reason);
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
const dir =
|
|
@@ -1,27 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Filesystem locations for the store layer: the content-addressed pack cache,
|
|
3
|
-
* the git-ref checkout cache, and the project
|
|
3
|
+
* the git-ref checkout cache, and the per-project active-pack pointer.
|
|
4
4
|
*
|
|
5
|
-
* The pack cache lives under `$XDG_CACHE_HOME
|
|
5
|
+
* The pack cache lives under `$XDG_CACHE_HOME/<bin>/packs/<contentHash>/`
|
|
6
6
|
* (content-addressed, so a new source set is a new directory and invalidation
|
|
7
|
-
* is free); ref checkouts under
|
|
8
|
-
* project
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* is free); ref checkouts under `.../<bin>/refs/`. Which pack answers a
|
|
8
|
+
* project's reads is recorded by a one-line POINTER under
|
|
9
|
+
* `.../<bin>/projects/` — the content hash `sources update` last built there,
|
|
10
|
+
* and nothing else. It lives in the cache rather than the repo because the pack
|
|
11
|
+
* it names is machine-local: a committed pointer would promise a store the next
|
|
12
|
+
* machine does not have.
|
|
13
|
+
*
|
|
14
|
+
* A LEAF module on purpose: it imports node builtins and the distribution's
|
|
15
|
+
* {@link BIN_NAME}, and owns the cache root's XDG resolution outright
|
|
16
|
+
* (`kernel/config/paths.ts` keeps config and state). The storeless `__complete`
|
|
17
|
+
* fast path (`kernel/completion/entitySource.ts`) has to read the pointer, and
|
|
18
|
+
* `completion/safety.test.ts` forbids the config LAYER anywhere on that import
|
|
19
|
+
* graph — so the cache root is resolved here rather than imported. `constants.ts`
|
|
20
|
+
* is not the config layer: it reads inert data, and that same guard positive-lists
|
|
21
|
+
* it.
|
|
11
22
|
*/
|
|
12
23
|
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join, resolve } from "node:path";
|
|
28
|
+
import { BIN_NAME } from "../../constants.js";
|
|
15
29
|
|
|
16
|
-
/**
|
|
17
|
-
|
|
30
|
+
/** `$XDG_CACHE_HOME/<bin>` (default `~/.cache/<bin>`) — the cache root. */
|
|
31
|
+
function cacheDir(): string {
|
|
32
|
+
const base = process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
|
|
33
|
+
return join(base, BIN_NAME);
|
|
34
|
+
}
|
|
18
35
|
|
|
19
|
-
/** `$XDG_CACHE_HOME
|
|
36
|
+
/** `$XDG_CACHE_HOME/<bin>/packs` — the content-addressed pack cache root. */
|
|
20
37
|
export function packsCacheDir(): string {
|
|
21
38
|
return join(cacheDir(), "packs");
|
|
22
39
|
}
|
|
23
40
|
|
|
24
|
-
/** `$XDG_CACHE_HOME
|
|
41
|
+
/** `$XDG_CACHE_HOME/<bin>/refs` — cached git-ref checkouts. */
|
|
25
42
|
export function refsCacheDir(): string {
|
|
26
43
|
return join(cacheDir(), "refs");
|
|
27
44
|
}
|
|
@@ -31,7 +48,39 @@ export function packDir(contentHash: string): string {
|
|
|
31
48
|
return join(packsCacheDir(), contentHash);
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The active-pack pointer for a project: `$XDG_CACHE_HOME/<bin>/projects/<key>`.
|
|
53
|
+
*
|
|
54
|
+
* The key is the SHA-256 of the resolved `cwd` — a hash rather than the path
|
|
55
|
+
* itself so a deep project directory can never exceed the 255-byte filename
|
|
56
|
+
* limit, and a cryptographic one so two projects can never collide onto each
|
|
57
|
+
* other's graph.
|
|
58
|
+
*/
|
|
59
|
+
export function activePackPath(cwd: string): string {
|
|
60
|
+
return join(
|
|
61
|
+
cacheDir(),
|
|
62
|
+
"projects",
|
|
63
|
+
createHash("sha256").update(resolve(cwd)).digest("hex"),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The content hash of the pack `sources update` built for a project.
|
|
69
|
+
*
|
|
70
|
+
* @param cwd - The project directory.
|
|
71
|
+
* @returns The 64-hex content hash, or `undefined` when the project was never
|
|
72
|
+
* built. A pointer that is unreadable, or that does not hold exactly one
|
|
73
|
+
* hash, is treated as absent — a corrupt pointer must never name an arbitrary
|
|
74
|
+
* cache directory.
|
|
75
|
+
* @note Impure — reads the pointer from the cache.
|
|
76
|
+
*/
|
|
77
|
+
export function readActivePack(cwd: string): string | undefined {
|
|
78
|
+
const path = activePackPath(cwd);
|
|
79
|
+
if (!existsSync(path)) return undefined;
|
|
80
|
+
try {
|
|
81
|
+
const hash = readFileSync(path, "utf-8").trim();
|
|
82
|
+
return /^[0-9a-f]{64}$/.test(hash) ? hash : undefined;
|
|
83
|
+
} catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
37
86
|
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { embeddedManifest } from "./graphpack/embedded.js";
|
|
2
3
|
import { runStoreProbe } from "./probe.js";
|
|
3
4
|
|
|
4
5
|
describe("store probe (in-process)", () => {
|
|
5
|
-
it("boots the embedded pack and
|
|
6
|
+
it("boots the embedded pack and finds the triples its manifest claims", async () => {
|
|
6
7
|
const out = JSON.parse(await runStoreProbe()) as {
|
|
7
8
|
ok: boolean;
|
|
8
9
|
entities: number;
|
|
9
10
|
triples: string;
|
|
10
11
|
};
|
|
11
12
|
expect(out.ok).toBe(true);
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Against the COMMITTED manifest, not a recomputation: a store booted from
|
|
14
|
+
// `data.nq` must really hold what `manifest.json` says was built into it.
|
|
15
|
+
// That catches a regenerated dump that disagrees with its own provenance,
|
|
16
|
+
// and needs no literal that churns whenever upstream moves.
|
|
17
|
+
expect(Number(out.triples)).toBe(embeddedManifest().tripleCount);
|
|
18
|
+
expect(out.entities).toBeGreaterThan(0);
|
|
15
19
|
});
|
|
16
20
|
});
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { PragmaError } from "../error/PragmaError.js";
|
|
19
|
+
import { cliRecovery } from "../error/recovery.js";
|
|
19
20
|
import { resolveUri } from "../packs/iri.js";
|
|
20
21
|
import type { PragmaRuntime } from "./types.js";
|
|
21
22
|
|
|
@@ -57,11 +58,11 @@ export async function readEntity(
|
|
|
57
58
|
|
|
58
59
|
if (result.type !== "select" || result.bindings.length === 0) {
|
|
59
60
|
throw PragmaError.notFound("entity", uri, {
|
|
60
|
-
recovery:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
recovery: cliRecovery(
|
|
62
|
+
`graph query 'SELECT ?s WHERE { ?s ?p ?o } LIMIT 10'`,
|
|
63
|
+
"Check the URI, or list known entities.",
|
|
64
|
+
{ tool: "graph_query" },
|
|
65
|
+
),
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { execFileSync } from "node:child_process";
|
|
12
|
-
import {
|
|
12
|
+
import { mkdirSync } from "node:fs";
|
|
13
13
|
import { dirname } from "node:path";
|
|
14
14
|
|
|
15
15
|
/** Whether a ref looks like a commit SHA (7-40 hex chars). */
|
|
@@ -64,24 +64,6 @@ export function fetchRef(url: string, ref: string, dest: string): string {
|
|
|
64
64
|
return gitOutput(dest, ["rev-parse", "HEAD"]);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/** Check out an exact commit in an existing clone (fetching it if needed). */
|
|
68
|
-
export function checkoutCommit(
|
|
69
|
-
url: string,
|
|
70
|
-
commit: string,
|
|
71
|
-
dest: string,
|
|
72
|
-
): void {
|
|
73
|
-
if (!existsSync(dest)) {
|
|
74
|
-
cloneRef(url, commit, dest);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
git(dest, ["checkout", commit]);
|
|
79
|
-
} catch {
|
|
80
|
-
git(dest, ["fetch", "--depth", "1", url, commit]);
|
|
81
|
-
git(dest, ["checkout", commit]);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
67
|
/** The resolved HEAD commit of a checkout. */
|
|
86
68
|
export function headCommit(dest: string): string {
|
|
87
69
|
return gitOutput(dest, ["rev-parse", "HEAD"]);
|