@canonical/pragma-cli 0.29.0 → 0.30.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 +67 -15
- package/dist/pragma +0 -0
- package/package.json +13 -13
- package/src/config/defaultConfig.test.ts +33 -0
- package/src/config/defaultConfig.ts +59 -0
- package/src/config/defaults.json +17 -0
- package/src/config/findProjectConfigPath.test.ts +53 -0
- package/src/config/findProjectConfigPath.ts +36 -0
- package/src/config/index.ts +17 -2
- package/src/config/mergeConfigFileUpdate.ts +58 -0
- package/src/config/parseConfigValues.ts +215 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -153
- package/src/config/readConfigLayers.test.ts +107 -0
- package/src/config/readConfigLayers.ts +115 -0
- package/src/config/resolveGlobalConfigPath.ts +13 -0
- package/src/config/resolveWriteConfigPath.test.ts +53 -0
- package/src/config/resolveWriteConfigPath.ts +31 -0
- package/src/config/types.ts +76 -1
- package/src/config/writeConfig.ts +25 -55
- package/src/domains/block/commands/list.ts +5 -90
- package/src/domains/block/commands/lookup.ts +8 -167
- package/src/domains/block/mcp/specs.ts +16 -196
- package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +254 -0
- package/src/domains/config/commands/channel.test.ts +14 -3
- package/src/domains/config/commands/channel.ts +11 -7
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +18 -1
- package/src/domains/config/commands/framework.ts +13 -7
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +13 -2
- package/src/domains/config/commands/tier.ts +11 -7
- package/src/domains/config/commands/trace.test.ts +19 -2
- package/src/domains/config/commands/trace.ts +6 -3
- package/src/domains/config/formatters/channel.test.ts +20 -6
- package/src/domains/config/formatters/channel.ts +14 -11
- package/src/domains/config/formatters/framework.ts +14 -11
- package/src/domains/config/formatters/show.test.ts +27 -0
- package/src/domains/config/formatters/show.ts +27 -4
- package/src/domains/config/formatters/tier.test.ts +26 -10
- package/src/domains/config/formatters/tier.ts +19 -11
- package/src/domains/config/formatters/trace.ts +7 -7
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/show.test.ts +18 -0
- package/src/domains/config/operations/show.ts +7 -1
- package/src/domains/config/operations/types.ts +7 -0
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +1 -0
- package/src/domains/config/tasks/setChannel.ts +13 -28
- package/src/domains/config/tasks/setFramework.ts +11 -30
- package/src/domains/config/tasks/setTier.ts +10 -29
- package/src/domains/config/tasks/setTrace.ts +10 -28
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/commands/component.ts +5 -5
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +130 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +2 -14
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/checks.test.ts +158 -7
- package/src/domains/doctor/operations/runChecks.test.ts +15 -6
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graphql/commands/build.test.ts +5 -3
- package/src/domains/graphql/commands/build.ts +8 -16
- package/src/domains/graphql/commands/check.test.ts +5 -3
- package/src/domains/graphql/commands/check.ts +7 -13
- package/src/domains/graphql/commands/index.ts +1 -0
- package/src/domains/graphql/commands/serve.test.ts +40 -0
- package/src/domains/graphql/commands/serve.ts +131 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
- package/src/domains/graphql/helpers/gatherSources.ts +58 -0
- package/src/domains/graphql/helpers/index.ts +2 -0
- package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +9 -6
- package/src/domains/graphql/operations/compileSchema.test.ts +21 -45
- package/src/domains/graphql/operations/compileSchema.ts +23 -25
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/index.ts +5 -0
- package/src/domains/info/operations/collectInfo.test.ts +14 -3
- package/src/domains/info/operations/collectInfo.ts +12 -15
- package/src/domains/llm/capabilities.test.ts +4 -4
- package/src/domains/llm/data/toolCatalog.ts +6 -0
- package/src/domains/modifier/commands/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/mcp/specs.ts +15 -94
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +77 -0
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/show.ts +7 -1
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +108 -0
- package/src/domains/refs/operations/updateRefs.ts +2 -37
- package/src/domains/setup/commands/skills.test.ts +6 -2
- package/src/domains/setup/commands/skills.ts +6 -2
- package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
- package/src/domains/setup/helpers/runSetupTask.ts +62 -70
- package/src/domains/setup/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.ts +6 -3
- package/src/domains/shared/compactUri.test.ts +11 -0
- package/src/domains/shared/compactUri.ts +10 -5
- package/src/domains/shared/context.ts +2 -2
- package/src/domains/shared/contracts.ts +11 -0
- package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
- package/src/domains/shared/createInteractivePromptSession.ts +236 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +110 -12
- package/src/domains/shared/loaders/gitLoader.ts +2 -2
- package/src/domains/shared/loaders/index.ts +4 -1
- package/src/domains/shared/loaders/localLoader.ts +18 -4
- package/src/domains/shared/loaders/readPackageDir.ts +36 -1
- package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
- package/src/domains/shared/lookupMany.test.ts +120 -0
- package/src/domains/shared/lookupMany.ts +34 -2
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/packages.ts +10 -17
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/runtime.ts +4 -63
- package/src/domains/shared/semanticPackage.ts +17 -1
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +100 -0
- package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
- package/src/domains/shared/stories/compileLookupTool.ts +96 -0
- package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
- package/src/domains/shared/stories/compileReadCommand.ts +48 -0
- package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
- package/src/domains/shared/stories/compileReadTool.ts +38 -0
- package/src/domains/shared/stories/condense.test.ts +18 -0
- package/src/domains/shared/stories/condense.ts +15 -0
- package/src/domains/shared/stories/index.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.ts +22 -0
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +102 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +76 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +51 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +80 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +108 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +76 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +40 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +146 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +253 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +43 -0
- package/src/domains/shared/stories/pack/index.ts +28 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +308 -0
- package/src/domains/shared/stories/pack/types.ts +104 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +240 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +408 -0
- package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
- package/src/domains/shared/stories/projectCliParameters.ts +57 -0
- package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
- package/src/domains/shared/stories/projectToolParams.ts +32 -0
- package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
- package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
- package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
- package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
- package/src/domains/shared/stories/types.ts +171 -0
- package/src/domains/shared/suggestions/expandGlob.ts +1 -0
- package/src/domains/shared/suggestions/suggestNames.ts +43 -10
- package/src/domains/skill/commands/index.ts +3 -1
- package/src/domains/skill/commands/list.test.ts +6 -2
- package/src/domains/skill/commands/list.ts +5 -63
- package/src/domains/skill/commands/lookup.test.ts +118 -0
- package/src/domains/skill/commands/lookup.ts +14 -0
- package/src/domains/skill/formatters/index.ts +1 -0
- package/src/domains/skill/formatters/lookup.test.ts +46 -0
- package/src/domains/skill/formatters/lookup.ts +57 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
- package/src/domains/skill/index.ts +7 -3
- package/src/domains/skill/mcp/specs.ts +10 -29
- package/src/domains/skill/operations/discover.test.ts +61 -30
- package/src/domains/skill/operations/discover.ts +28 -37
- package/src/domains/skill/operations/index.ts +2 -1
- package/src/domains/skill/operations/list.test.ts +15 -7
- package/src/domains/skill/operations/lookup.test.ts +91 -0
- package/src/domains/skill/operations/lookup.ts +70 -0
- package/src/domains/skill/stories.test.ts +43 -0
- package/src/domains/skill/stories.ts +103 -0
- package/src/domains/skill/types.ts +8 -0
- package/src/domains/standard/commands/list.ts +5 -84
- package/src/domains/standard/commands/lookup.ts +8 -93
- package/src/domains/standard/mcp/specs.ts +16 -171
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +2 -1
- package/src/domains/standard/stories.test.ts +24 -0
- package/src/domains/standard/stories.ts +151 -0
- package/src/domains/tier/commands/list.ts +5 -55
- package/src/domains/tier/mcp/specs.ts +5 -31
- package/src/domains/tier/stories.test.ts +22 -0
- package/src/domains/tier/stories.ts +52 -0
- package/src/domains/token/mcp/specs.ts +5 -1
- package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
- package/src/domains/trace/tracePlugin.test.ts +1 -1
- package/src/mcp/registerResources.test.ts +382 -74
- package/src/mcp/registerResources.ts +5 -348
- package/src/mcp/resources/buildGraphIndex.ts +167 -0
- package/src/mcp/resources/buildResourceList.test.ts +97 -0
- package/src/mcp/resources/buildResourceList.ts +176 -0
- package/src/mcp/resources/classifyEntity.test.ts +40 -0
- package/src/mcp/resources/classifyEntity.ts +27 -0
- package/src/mcp/resources/constants.ts +105 -0
- package/src/mcp/resources/displayPredicates.ts +62 -0
- package/src/mcp/resources/pickFirstValue.test.ts +29 -0
- package/src/mcp/resources/pickFirstValue.ts +26 -0
- package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
- package/src/mcp/resources/pickPrimaryType.ts +26 -0
- package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
- package/src/mcp/resources/rankUriCompletions.ts +64 -0
- package/src/mcp/resources/readEntity.ts +169 -0
- package/src/mcp/resources/registerResources.ts +126 -0
- package/src/mcp/resources/types.ts +128 -0
- package/src/mcp/tools/index.ts +9 -0
- package/src/mcp/tools/registerTools.test.ts +65 -8
- package/src/mcp/types.ts +4 -0
- package/src/pipeline/collectCommands.ts +8 -1
- package/src/pipeline/runCli.ts +40 -6
- package/src/testing/createTestMcpClient.ts +4 -1
- package/src/testing/index.ts +10 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/executor-execution.test.ts +101 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +1 -1
- package/src/testing/integration/parity.test.ts +17 -12
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +15 -0
- package/src/testing/standardParityFixtures.ts +92 -0
- package/src/testing/storyFixtures.ts +63 -0
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route an incoming request for the graphql dev server: `/graphql` to the
|
|
3
|
+
* GraphQL + GraphiQL handler, `/` to a redirect there, and everything else
|
|
4
|
+
* to 404. Mounting is intentionally the host's job, which keeps the handler
|
|
5
|
+
* itself path-agnostic.
|
|
6
|
+
*
|
|
7
|
+
* @param request - The incoming fetch Request.
|
|
8
|
+
* @param handler - The GraphQL fetch handler for `/graphql`.
|
|
9
|
+
* @param port - The listen port, used to build the root redirect target.
|
|
10
|
+
* @returns The response (or a promise of one) for the route.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
type GraphQLHandler = (request: Request) => Response | Promise<Response>;
|
|
14
|
+
|
|
15
|
+
export default function routeGraphql(
|
|
16
|
+
request: Request,
|
|
17
|
+
handler: GraphQLHandler,
|
|
18
|
+
port: number,
|
|
19
|
+
): Response | Promise<Response> {
|
|
20
|
+
const { pathname } = new URL(request.url);
|
|
21
|
+
if (pathname === "/graphql") {
|
|
22
|
+
return handler(request);
|
|
23
|
+
}
|
|
24
|
+
if (pathname === "/") {
|
|
25
|
+
return Response.redirect(`http://localhost:${port}/graphql`, 302);
|
|
26
|
+
}
|
|
27
|
+
return new Response(null, { status: 404 });
|
|
28
|
+
}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module GraphQL domain barrel.
|
|
3
3
|
*
|
|
4
|
-
* Compiles TTL ontologies into GraphQL schema artifacts via the
|
|
5
|
-
*
|
|
6
|
-
* TTL sources,
|
|
4
|
+
* Compiles TTL ontologies into GraphQL schema artifacts via the ke-graphql
|
|
5
|
+
* pipeline, and serves them locally. Commands boot their own ke store —
|
|
6
|
+
* from explicit TTL sources or, when none are given, the semantic packages
|
|
7
|
+
* configured in `pragma.config.json` — so they run on the store-skip
|
|
8
|
+
* pipeline path.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import type { CommandDefinition } from "@canonical/cli-core";
|
|
10
|
-
import { buildCommand, checkCommand } from "./commands/index.js";
|
|
12
|
+
import { buildCommand, checkCommand, serveCommand } from "./commands/index.js";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Returns all command definitions for the graphql domain.
|
|
14
16
|
*
|
|
15
|
-
* @returns
|
|
17
|
+
* @returns The `graphql build`, `graphql check`, and `graphql serve`
|
|
18
|
+
* command definitions.
|
|
16
19
|
*/
|
|
17
20
|
export function commands(): CommandDefinition[] {
|
|
18
|
-
return [buildCommand, checkCommand];
|
|
21
|
+
return [buildCommand, checkCommand, serveCommand];
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
export { compileSchema } from "./operations/index.js";
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { hashSources } from "@canonical/ke-graphql";
|
|
5
|
-
import {
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
6
3
|
import {
|
|
7
4
|
EX_NAMESPACE,
|
|
8
5
|
GRAPHQL_CLEAN_TTL,
|
|
@@ -14,60 +11,47 @@ import compileSchema from "./compileSchema.js";
|
|
|
14
11
|
|
|
15
12
|
const PREFIXES = { ...PREFIX_MAP, ex: EX_NAMESPACE };
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
writeFileSync(join(dir, "clean.ttl"), GRAPHQL_CLEAN_TTL);
|
|
23
|
-
writeFileSync(join(dir, "error.ttl"), GRAPHQL_ERROR_TTL);
|
|
24
|
-
writeFileSync(join(dir, "fatal.ttl"), GRAPHQL_FATAL_TTL);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
rmSync(dir, { recursive: true, force: true });
|
|
29
|
-
});
|
|
14
|
+
const inline = (content: string, path: string) => ({
|
|
15
|
+
content,
|
|
16
|
+
format: "turtle" as const,
|
|
17
|
+
path,
|
|
18
|
+
});
|
|
30
19
|
|
|
20
|
+
describe("compileSchema", () => {
|
|
31
21
|
it("compiles a clean ontology into SDL", async () => {
|
|
32
22
|
const outcome = await compileSchema({
|
|
33
|
-
sources: ["clean.ttl"],
|
|
23
|
+
sources: [inline(GRAPHQL_CLEAN_TTL, "clean.ttl")],
|
|
34
24
|
prefixes: PREFIXES,
|
|
35
|
-
cwd: dir,
|
|
36
25
|
});
|
|
37
26
|
|
|
38
27
|
expect(outcome.status).toBe("ok");
|
|
39
28
|
expect(outcome.compiled?.sdl).toContain("type Thing");
|
|
40
29
|
expect(outcome.diagnostics).toEqual([]);
|
|
41
|
-
expect(outcome.files).toEqual([
|
|
30
|
+
expect(outcome.files).toEqual(["clean.ttl"]);
|
|
42
31
|
});
|
|
43
32
|
|
|
44
|
-
it("hashes the exact raw
|
|
33
|
+
it("hashes the exact raw source contents", async () => {
|
|
45
34
|
const outcome = await compileSchema({
|
|
46
|
-
sources: ["clean.ttl"],
|
|
35
|
+
sources: [inline(GRAPHQL_CLEAN_TTL, "clean.ttl")],
|
|
47
36
|
prefixes: PREFIXES,
|
|
48
|
-
cwd: dir,
|
|
49
37
|
});
|
|
50
38
|
|
|
51
|
-
|
|
52
|
-
expect(outcome.sourcesHash).toBe(hashSources([raw]));
|
|
39
|
+
expect(outcome.sourcesHash).toBe(hashSources([GRAPHQL_CLEAN_TTL]));
|
|
53
40
|
});
|
|
54
41
|
|
|
55
|
-
it("
|
|
42
|
+
it("labels a source without a path as 'inline'", async () => {
|
|
56
43
|
const outcome = await compileSchema({
|
|
57
|
-
sources: ["
|
|
44
|
+
sources: [{ content: GRAPHQL_CLEAN_TTL, format: "turtle" }],
|
|
58
45
|
prefixes: PREFIXES,
|
|
59
|
-
cwd: dir,
|
|
60
46
|
});
|
|
61
47
|
|
|
62
|
-
expect(outcome.
|
|
63
|
-
expect(outcome.files).toEqual([join(dir, "clean.ttl")]);
|
|
48
|
+
expect(outcome.files).toEqual(["inline"]);
|
|
64
49
|
});
|
|
65
50
|
|
|
66
51
|
it("surfaces error diagnostics without failing the compile", async () => {
|
|
67
52
|
const outcome = await compileSchema({
|
|
68
|
-
sources: ["error.ttl"],
|
|
53
|
+
sources: [inline(GRAPHQL_ERROR_TTL, "error.ttl")],
|
|
69
54
|
prefixes: PREFIXES,
|
|
70
|
-
cwd: dir,
|
|
71
55
|
});
|
|
72
56
|
|
|
73
57
|
expect(outcome.status).toBe("ok");
|
|
@@ -81,9 +65,8 @@ describe("compileSchema", () => {
|
|
|
81
65
|
|
|
82
66
|
it("converts CompilationError into a failed result with diagnostics", async () => {
|
|
83
67
|
const outcome = await compileSchema({
|
|
84
|
-
sources: ["fatal.ttl"],
|
|
68
|
+
sources: [inline(GRAPHQL_FATAL_TTL, "fatal.ttl")],
|
|
85
69
|
prefixes: PREFIXES,
|
|
86
|
-
cwd: dir,
|
|
87
70
|
});
|
|
88
71
|
|
|
89
72
|
expect(outcome.status).toBe("failed");
|
|
@@ -95,24 +78,17 @@ describe("compileSchema", () => {
|
|
|
95
78
|
).toBe(true);
|
|
96
79
|
});
|
|
97
80
|
|
|
98
|
-
it("throws structured error when no sources
|
|
81
|
+
it("throws a structured error when given no sources", async () => {
|
|
99
82
|
await expect(
|
|
100
|
-
compileSchema({
|
|
101
|
-
sources: ["missing.ttl"],
|
|
102
|
-
prefixes: PREFIXES,
|
|
103
|
-
cwd: dir,
|
|
104
|
-
}),
|
|
83
|
+
compileSchema({ sources: [], prefixes: PREFIXES }),
|
|
105
84
|
).rejects.toMatchObject({ code: "INVALID_INPUT" });
|
|
106
85
|
});
|
|
107
86
|
|
|
108
|
-
it("throws structured error on invalid TTL syntax", async () => {
|
|
109
|
-
writeFileSync(join(dir, "broken.ttl"), "this is not turtle .");
|
|
110
|
-
|
|
87
|
+
it("throws a structured error on invalid TTL syntax", async () => {
|
|
111
88
|
await expect(
|
|
112
89
|
compileSchema({
|
|
113
|
-
sources: ["broken.ttl"],
|
|
90
|
+
sources: [inline("this is not turtle .", "broken.ttl")],
|
|
114
91
|
prefixes: PREFIXES,
|
|
115
|
-
cwd: dir,
|
|
116
92
|
}),
|
|
117
93
|
).rejects.toMatchObject({ code: "STORE_ERROR" });
|
|
118
94
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createStore } from "@canonical/ke";
|
|
1
|
+
import { createStore, type InlineSource } from "@canonical/ke";
|
|
3
2
|
import {
|
|
4
3
|
CompilationError,
|
|
5
4
|
type CompilerResult,
|
|
@@ -9,15 +8,14 @@ import {
|
|
|
9
8
|
hashSources,
|
|
10
9
|
} from "@canonical/ke-graphql";
|
|
11
10
|
import { PragmaError } from "#error";
|
|
12
|
-
import { resolveSourceFiles } from "../helpers/index.js";
|
|
13
11
|
|
|
14
12
|
export interface CompileSchemaOptions {
|
|
15
|
-
/** TTL
|
|
16
|
-
readonly sources: readonly
|
|
13
|
+
/** In-memory TTL sources (content + provenance path) to compile. */
|
|
14
|
+
readonly sources: readonly InlineSource[];
|
|
17
15
|
/** Prefix map handed to both the ke store and the compiler. */
|
|
18
16
|
readonly prefixes: Readonly<Record<string, string>>;
|
|
19
|
-
/** Working directory
|
|
20
|
-
readonly cwd
|
|
17
|
+
/** Working directory, passed through to the store (unused for inline). */
|
|
18
|
+
readonly cwd?: string;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export interface CompileSchemaResult {
|
|
@@ -25,7 +23,7 @@ export interface CompileSchemaResult {
|
|
|
25
23
|
readonly status: "ok" | "failed";
|
|
26
24
|
/** All compiler diagnostics, including those carried by a CompilationError. */
|
|
27
25
|
readonly diagnostics: readonly Diagnostic[];
|
|
28
|
-
/**
|
|
26
|
+
/** Provenance paths of the sources that were loaded. */
|
|
29
27
|
readonly files: readonly string[];
|
|
30
28
|
/** Fingerprint of the raw source contents (matches ke's onLoad hashing). */
|
|
31
29
|
readonly sourcesHash: string;
|
|
@@ -34,42 +32,42 @@ export interface CompileSchemaResult {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
/**
|
|
37
|
-
* Compile TTL sources into a GraphQL schema via the ke-graphql
|
|
35
|
+
* Compile in-memory TTL sources into a GraphQL schema via the ke-graphql
|
|
36
|
+
* pipeline.
|
|
38
37
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* diagnostics; the store is always disposed.
|
|
38
|
+
* Hashes the raw source contents (the exact bytes ke loads, so the
|
|
39
|
+
* artifact's `sourcesHash` matches what `createSchemaPlugin` computes from
|
|
40
|
+
* ke's onLoad), boots a throwaway ke store, and runs the compiler. A
|
|
41
|
+
* {@link CompilationError} (schema composition failed) is converted into a
|
|
42
|
+
* `"failed"` result carrying its diagnostics; the store is always disposed.
|
|
45
43
|
*
|
|
46
|
-
* @note Impure —
|
|
44
|
+
* @note Impure — boots a ke store (WASM + TTL).
|
|
47
45
|
*
|
|
48
|
-
* @param options -
|
|
46
|
+
* @param options - In-memory sources and prefixes.
|
|
49
47
|
* @returns The compile outcome with diagnostics, files, and sources hash.
|
|
50
|
-
* @throws PragmaError with code `INVALID_INPUT` when no sources
|
|
48
|
+
* @throws PragmaError with code `INVALID_INPUT` when given no sources.
|
|
51
49
|
* @throws PragmaError with code `STORE_ERROR` when the store fails to load.
|
|
52
50
|
*/
|
|
53
51
|
export default async function compileSchema(
|
|
54
52
|
options: CompileSchemaOptions,
|
|
55
53
|
): Promise<CompileSchemaResult> {
|
|
56
|
-
const
|
|
54
|
+
const { sources } = options;
|
|
57
55
|
|
|
58
|
-
if (
|
|
59
|
-
throw PragmaError.invalidInput("sources",
|
|
56
|
+
if (sources.length === 0) {
|
|
57
|
+
throw PragmaError.invalidInput("sources", "(empty)", {
|
|
60
58
|
recovery: {
|
|
61
|
-
message: "Provide at least one
|
|
59
|
+
message: "Provide at least one TTL source.",
|
|
62
60
|
},
|
|
63
61
|
});
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
const
|
|
67
|
-
const
|
|
64
|
+
const sourcesHash = hashSources(sources.map((source) => source.content));
|
|
65
|
+
const files = sources.map((source) => source.path ?? "inline");
|
|
68
66
|
|
|
69
67
|
let store: Awaited<ReturnType<typeof createStore>>;
|
|
70
68
|
try {
|
|
71
69
|
store = await createStore({
|
|
72
|
-
sources: [...
|
|
70
|
+
sources: [...sources],
|
|
73
71
|
prefixes: options.prefixes,
|
|
74
72
|
cwd: options.cwd,
|
|
75
73
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { EX_NAMESPACE, GRAPHQL_CLEAN_TTL } from "#testing";
|
|
3
|
+
import { PREFIX_MAP } from "../../shared/prefixes.js";
|
|
4
|
+
import createSchemaServer, { type SchemaServer } from "./createSchemaServer.js";
|
|
5
|
+
|
|
6
|
+
const PREFIXES = { ...PREFIX_MAP, ex: EX_NAMESPACE };
|
|
7
|
+
|
|
8
|
+
describe("createSchemaServer", () => {
|
|
9
|
+
let server: SchemaServer | undefined;
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
server?.dispose();
|
|
13
|
+
server = undefined;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("compiles inline sources and serves an introspectable schema", async () => {
|
|
17
|
+
server = await createSchemaServer({
|
|
18
|
+
sources: [
|
|
19
|
+
{ content: GRAPHQL_CLEAN_TTL, format: "turtle", path: "clean.ttl" },
|
|
20
|
+
],
|
|
21
|
+
prefixes: PREFIXES,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const response = await server.handler(
|
|
25
|
+
new Request("http://localhost/graphql", {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: { "content-type": "application/json" },
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
query: "{ __schema { queryType { name } } }",
|
|
30
|
+
}),
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(response.status).toBe(200);
|
|
35
|
+
const body = (await response.json()) as {
|
|
36
|
+
data?: { __schema?: { queryType?: { name?: string } } };
|
|
37
|
+
};
|
|
38
|
+
expect(body.data?.__schema?.queryType?.name).toBe("Query");
|
|
39
|
+
expect(Array.isArray(server.diagnostics)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("throws a STORE_ERROR on invalid TTL", async () => {
|
|
43
|
+
await expect(
|
|
44
|
+
createSchemaServer({
|
|
45
|
+
sources: [{ content: "not turtle .", format: "turtle", path: "x.ttl" }],
|
|
46
|
+
prefixes: PREFIXES,
|
|
47
|
+
}),
|
|
48
|
+
).rejects.toMatchObject({ code: "STORE_ERROR" });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot a persistent ke store over in-memory TTL sources with the
|
|
3
|
+
* ke-graphql schema plugin attached, and build the `/http` fetch handler
|
|
4
|
+
* (GraphiQL + GraphQL, CORS, incremental delivery) over the compiled
|
|
5
|
+
* schema. The caller owns the returned `dispose`.
|
|
6
|
+
*
|
|
7
|
+
* @note Impure — boots a ke store (WASM + TTL) and compiles the schema.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createStore, type InlineSource, type Plugin } from "@canonical/ke";
|
|
11
|
+
import {
|
|
12
|
+
createSchemaPlugin,
|
|
13
|
+
type Diagnostic,
|
|
14
|
+
type SchemaPluginApi,
|
|
15
|
+
} from "@canonical/ke-graphql";
|
|
16
|
+
import { createGraphQLHandler } from "@canonical/ke-graphql/http";
|
|
17
|
+
import { PragmaError } from "#error";
|
|
18
|
+
|
|
19
|
+
export interface CreateSchemaServerOptions {
|
|
20
|
+
/** In-memory TTL sources to load into the store. */
|
|
21
|
+
readonly sources: readonly InlineSource[];
|
|
22
|
+
/** Prefix map handed to both the ke store and the compiler. */
|
|
23
|
+
readonly prefixes: Readonly<Record<string, string>>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SchemaServer {
|
|
27
|
+
/** Runtime-neutral fetch handler over the compiled schema. */
|
|
28
|
+
readonly handler: ReturnType<typeof createGraphQLHandler>;
|
|
29
|
+
/** Compiler diagnostics produced while building the schema. */
|
|
30
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
31
|
+
/** Releases the underlying ke store. */
|
|
32
|
+
readonly dispose: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default async function createSchemaServer(
|
|
36
|
+
options: CreateSchemaServerOptions,
|
|
37
|
+
): Promise<SchemaServer> {
|
|
38
|
+
const plugin = createSchemaPlugin({ incremental: true });
|
|
39
|
+
|
|
40
|
+
let store: Awaited<ReturnType<typeof createStore>>;
|
|
41
|
+
try {
|
|
42
|
+
store = await createStore({
|
|
43
|
+
sources: [...options.sources],
|
|
44
|
+
prefixes: options.prefixes,
|
|
45
|
+
// biome-ignore lint/suspicious/noExplicitAny: Plugin generic variance
|
|
46
|
+
plugins: [plugin as Plugin<any>],
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw PragmaError.storeError(
|
|
50
|
+
error instanceof Error ? error.message : String(error),
|
|
51
|
+
{ recovery: { message: "Check the TTL syntax of the sources." } },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const api = store.api<SchemaPluginApi>("ke-graphql");
|
|
56
|
+
if (!api) {
|
|
57
|
+
store.dispose();
|
|
58
|
+
throw PragmaError.storeError("ke-graphql plugin did not register its API", {
|
|
59
|
+
recovery: { message: "This is an internal error; please report it." },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const handler = createGraphQLHandler(api.schema, {
|
|
64
|
+
context: () => api.createContext(store),
|
|
65
|
+
graphiql: true,
|
|
66
|
+
cors: true,
|
|
67
|
+
incremental: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
handler,
|
|
72
|
+
diagnostics: api.diagnostics,
|
|
73
|
+
dispose: () => store.dispose(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -5,3 +5,8 @@ export type {
|
|
|
5
5
|
CompileSchemaResult,
|
|
6
6
|
} from "./compileSchema.js";
|
|
7
7
|
export { default as compileSchema } from "./compileSchema.js";
|
|
8
|
+
export type {
|
|
9
|
+
CreateSchemaServerOptions,
|
|
10
|
+
SchemaServer,
|
|
11
|
+
} from "./createSchemaServer.js";
|
|
12
|
+
export { default as createSchemaServer } from "./createSchemaServer.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const readConfigLayersMock = vi.fn();
|
|
4
4
|
const detectInstallSourceMock = vi.fn();
|
|
5
5
|
const bootStoreMock = vi.fn();
|
|
6
6
|
const checkRegistryVersionMock = vi.fn();
|
|
@@ -8,7 +8,7 @@ const collectStoreSummaryMock = vi.fn();
|
|
|
8
8
|
|
|
9
9
|
vi.mock("#config", async (importOriginal) => ({
|
|
10
10
|
...(await importOriginal<typeof import("#config")>()),
|
|
11
|
-
|
|
11
|
+
readConfigLayers: readConfigLayersMock,
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
14
|
vi.mock("#package-manager", async (importOriginal) => {
|
|
@@ -46,7 +46,18 @@ describe("collectInfo", () => {
|
|
|
46
46
|
scope: "global",
|
|
47
47
|
label: "bun (global)",
|
|
48
48
|
});
|
|
49
|
-
|
|
49
|
+
readConfigLayersMock.mockReturnValue({
|
|
50
|
+
config: { tier: "apps/lxd", channel: "normal" },
|
|
51
|
+
origins: {
|
|
52
|
+
tier: "project",
|
|
53
|
+
channel: "default",
|
|
54
|
+
packages: "default",
|
|
55
|
+
trace: "default",
|
|
56
|
+
framework: "default",
|
|
57
|
+
},
|
|
58
|
+
global: { path: "/home/u/.config/pragma/config.json", exists: false },
|
|
59
|
+
project: { path: "/repo/pragma.config.json", exists: true },
|
|
60
|
+
});
|
|
50
61
|
});
|
|
51
62
|
|
|
52
63
|
it("includes update information and store summary when available", async () => {
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readConfigLayers } from "#config";
|
|
2
2
|
import { VERSION } from "#constants";
|
|
3
3
|
import { detectInstallSource, PM_COMMANDS } from "#package-manager";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import type {
|
|
5
|
+
PackageRef,
|
|
6
|
+
RawPackageEntry,
|
|
7
7
|
} from "../../refs/operations/parseRef.js";
|
|
8
|
-
import readGlobalRefs from "../../refs/operations/readGlobalRefs.js";
|
|
9
8
|
import { bootStore } from "../../shared/bootStore.js";
|
|
10
9
|
import { CHANNEL_RELEASES } from "../../shared/filters/buildChannelFilter.js";
|
|
11
10
|
import { resolveTierChain } from "../../shared/filters/buildTierFilter.js";
|
|
12
|
-
import {
|
|
11
|
+
import { mergeAndParseRefs } from "../../shared/mergeAndParseRefs.js";
|
|
13
12
|
import type { InfoData, PackageRefSummary } from "../types.js";
|
|
14
13
|
import checkRegistryVersion from "./checkRegistryVersion.js";
|
|
15
14
|
import { collectStoreSummary } from "./collectStoreSummary.js";
|
|
@@ -28,7 +27,8 @@ import { collectStoreSummary } from "./collectStoreSummary.js";
|
|
|
28
27
|
export default async function collectInfo(cwd: string): Promise<InfoData> {
|
|
29
28
|
const install = detectInstallSource();
|
|
30
29
|
const pm = install.packageManager;
|
|
31
|
-
const
|
|
30
|
+
const layers = readConfigLayers(cwd);
|
|
31
|
+
const config = layers.config;
|
|
32
32
|
const tierChain = resolveTierChain(config.tier);
|
|
33
33
|
const channelReleases = CHANNEL_RELEASES[config.channel];
|
|
34
34
|
|
|
@@ -65,7 +65,9 @@ export default async function collectInfo(cwd: string): Promise<InfoData> {
|
|
|
65
65
|
version: VERSION,
|
|
66
66
|
pm,
|
|
67
67
|
installSource: install.label,
|
|
68
|
-
configPath:
|
|
68
|
+
configPath: layers.project.exists
|
|
69
|
+
? layers.project.path
|
|
70
|
+
: layers.global.path,
|
|
69
71
|
tier: config.tier,
|
|
70
72
|
tierChain,
|
|
71
73
|
channel: config.channel,
|
|
@@ -78,14 +80,9 @@ export default async function collectInfo(cwd: string): Promise<InfoData> {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
function collectPackageRefSummaries(
|
|
81
|
-
|
|
82
|
-
string | { readonly name: string; readonly source?: string }
|
|
83
|
-
>,
|
|
83
|
+
configPackages?: ReadonlyArray<RawPackageEntry>,
|
|
84
84
|
): PackageRefSummary[] {
|
|
85
|
-
|
|
86
|
-
const raw = entries.length > 0 ? entries : DEFAULT_PACKAGES.map((pkg) => pkg);
|
|
87
|
-
|
|
88
|
-
return raw.map((entry) => refToSummary(parsePackageEntry(entry)));
|
|
85
|
+
return mergeAndParseRefs(configPackages).map(refToSummary);
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
function refToSummary(ref: PackageRef): PackageRefSummary {
|
|
@@ -26,10 +26,10 @@ describe("buildCapabilitiesData", () => {
|
|
|
26
26
|
expect(data.discovery_sequence[2]?.tool).toBe("domain tools");
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
it("includes
|
|
29
|
+
it("includes every tool from TOOL_CATALOG", () => {
|
|
30
30
|
const data = buildCapabilitiesData();
|
|
31
31
|
expect(data.tools).toHaveLength(TOOL_CATALOG.length);
|
|
32
|
-
expect(data.tools).toHaveLength(
|
|
32
|
+
expect(data.tools).toHaveLength(30);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it("every tool has a non-empty use_when", () => {
|
|
@@ -41,7 +41,7 @@ describe("buildCapabilitiesData", () => {
|
|
|
41
41
|
|
|
42
42
|
it("counts match tool catalog category lengths", () => {
|
|
43
43
|
const data = buildCapabilitiesData();
|
|
44
|
-
expect(data.counts.total).toBe(
|
|
44
|
+
expect(data.counts.total).toBe(30);
|
|
45
45
|
expect(data.counts.read).toBe(
|
|
46
46
|
TOOL_CATALOG.filter((t) => t.category === "read").length,
|
|
47
47
|
);
|
|
@@ -79,7 +79,7 @@ describe("buildCapabilitiesCommand", () => {
|
|
|
79
79
|
expect(text).toContain("Discovery Sequence");
|
|
80
80
|
expect(text).toContain("block_list");
|
|
81
81
|
expect(text).toContain("read");
|
|
82
|
-
expect(text).toContain("
|
|
82
|
+
expect(text).toContain("30 tools");
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
it("does not require a store or runtime", async () => {
|
|
@@ -100,6 +100,12 @@ export const TOOL_CATALOG: readonly ToolEntry[] = [
|
|
|
100
100
|
category: "read",
|
|
101
101
|
use_when: "Discovering agent skills provided by installed packages",
|
|
102
102
|
},
|
|
103
|
+
{
|
|
104
|
+
name: "skill_lookup",
|
|
105
|
+
category: "read",
|
|
106
|
+
use_when:
|
|
107
|
+
"Loading full SKILL.md instructions for a specific agent skill by name",
|
|
108
|
+
},
|
|
103
109
|
{
|
|
104
110
|
name: "block_sample",
|
|
105
111
|
category: "read",
|
|
@@ -1,42 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
* Wires the `pragma modifier list` CLI command.
|
|
3
|
-
*
|
|
4
|
-
* Lists all modifier families with their allowed values. Throws a
|
|
5
|
-
* structured recovery error when no families are found.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
type CommandDefinition,
|
|
10
|
-
createOutputResult,
|
|
11
|
-
} from "@canonical/cli-core";
|
|
12
|
-
import { createListView } from "#tui";
|
|
1
|
+
import type { CommandDefinition } from "@canonical/cli-core";
|
|
13
2
|
import type { PragmaContext } from "../../shared/context.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { modifierConfig } from "../modifierConfig.js";
|
|
17
|
-
import { resolveModifierList } from "../orchestration/index.js";
|
|
3
|
+
import { compileReadCommand } from "../../shared/stories/index.js";
|
|
4
|
+
import { modifierListStory } from "../stories.js";
|
|
18
5
|
|
|
6
|
+
/** Wire the `pragma modifier list` CLI command from the modifier list story. */
|
|
19
7
|
export default function listCommand(ctx: PragmaContext): CommandDefinition {
|
|
20
|
-
return
|
|
21
|
-
path: ["modifier", "list"],
|
|
22
|
-
description: "List all modifier families",
|
|
23
|
-
parameters: [],
|
|
24
|
-
meta: {
|
|
25
|
-
examples: ["pragma modifier list", "pragma modifier list --llm"],
|
|
26
|
-
},
|
|
27
|
-
async execute() {
|
|
28
|
-
const resolution = await resolveModifierList(ctx);
|
|
29
|
-
|
|
30
|
-
return createOutputResult([...resolution.items], {
|
|
31
|
-
plain: selectFormatter(ctx, listFormatters),
|
|
32
|
-
ink: (data) =>
|
|
33
|
-
createListView({
|
|
34
|
-
heading: "Modifiers",
|
|
35
|
-
domain: "modifier",
|
|
36
|
-
items: data,
|
|
37
|
-
columns: modifierConfig.listColumns,
|
|
38
|
-
}),
|
|
39
|
-
});
|
|
40
|
-
},
|
|
41
|
-
};
|
|
8
|
+
return compileReadCommand(ctx, modifierListStory);
|
|
42
9
|
}
|