@warpgogol/werkstatt-shared 0.6.0 → 0.8.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/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@warpgogol/werkstatt-shared",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=24 <25"
8
+ },
6
9
  "description": "Stack-agnostic shared infrastructure extracted from werkstatt-site (RFC-0868).",
7
10
  "repository": {
8
11
  "type": "git",
@@ -1329,6 +1332,8 @@
1329
1332
  }
1330
1333
  },
1331
1334
  "scripts": {
1335
+ "lint": "pnpm exec eslint \"src/**/*.ts\"",
1336
+ "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
1332
1337
  "build": "pnpm exec tsc -p tsconfig.json --noEmit",
1333
1338
  "build:check": "pnpm exec tsc -p tsconfig.json --noEmit",
1334
1339
  "prepublishOnly": "pnpm exec tsc -p tsconfig.json --noEmit",
@@ -1348,21 +1353,27 @@
1348
1353
  "ajv": "^8.20.0",
1349
1354
  "bs58": "^6.0.0",
1350
1355
  "expr-eval": "^2.0.2",
1356
+ "gray-matter": "^4.0.3",
1351
1357
  "hls.js": "^1.6.16",
1352
1358
  "lenis": "^1.3.25",
1353
1359
  "plyr": "^3.8.4",
1354
- "gray-matter": "^4.0.3",
1355
1360
  "yaml": "^2.9.0",
1356
1361
  "zod": "^4.4.3"
1357
1362
  },
1358
1363
  "optionalDependencies": {
1359
- "@syrokomskyi/axiom-study": "*"
1364
+ "@syrokomskyi/axiom-study": "link:../../../pipelines/packages/axiom/axiom-study"
1360
1365
  },
1361
1366
  "devDependencies": {
1367
+ "@astrojs/markdown-remark": "^7.0.0",
1362
1368
  "@cloudflare/workers-types": "^5.20260801.1",
1363
1369
  "@types/node": "^24.0.0",
1370
+ "@warpgogol/werkstatt": "1.4.0",
1364
1371
  "astro": "^7.2.0",
1372
+ "eslint": "^10.8.0",
1365
1373
  "fast-check": "^4.9.0",
1374
+ "gsap": "^3.15.0",
1375
+ "typescript": "^6.0.3",
1376
+ "typescript-eslint": "8.65.0",
1366
1377
  "vitest": "^4.1.10"
1367
1378
  },
1368
1379
  "packageManager": "pnpm@11.10.0"
@@ -141,7 +141,7 @@ describe("PassportSchema", () => {
141
141
  });
142
142
 
143
143
  test("defaults stars to empty array when omitted", () => {
144
- const { stars, ...withoutStars } = validPassport.composition;
144
+ const { stars: _stars, ...withoutStars } = validPassport.composition;
145
145
  const result = PassportSchema.safeParse({
146
146
  ...validPassport,
147
147
  composition: withoutStars,
@@ -0,0 +1,133 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>
4
+ ARD (Agentic Resource Discovery) ai-catalog.json projection. Pure, dependency-free
5
+ formatter that projects a site's Agent Surface Manifest into the ai-catalog data model
6
+ for /.well-known/ai-catalog.json. No I/O — the kernel command loads the manifest and
7
+ passes it here.
8
+ </purpose>
9
+ <non-goals>
10
+ <item>Do not read files — callers load and pass the manifest.</item>
11
+ <item>Do not duplicate logic from api-catalog or mcp-card — this is a separate spec.</item>
12
+ </non-goals>
13
+ </MODULE_CONTRACT>
14
+ <CHANGE_SUMMARY>
15
+ <item>Initial ARD ai-catalog.json projection for isitagentready.com ARD check.</item>
16
+ </CHANGE_SUMMARY>
17
+ */
18
+
19
+ import type { AgentSurfaceManifest } from "./manifest.ts";
20
+
21
+ /** ARD ai-catalog entry — one per discoverable resource. */
22
+ export interface ArdCatalogEntry {
23
+ identifier: string;
24
+ displayName: string;
25
+ type: string;
26
+ url: string;
27
+ representativeQueries: string[];
28
+ }
29
+
30
+ /** ARD ai-catalog host object. */
31
+ export interface ArdCatalogHost {
32
+ displayName: string;
33
+ identifier: string;
34
+ }
35
+
36
+ /** ARD ai-catalog top-level document. */
37
+ export interface ArdCatalog {
38
+ specVersion: string;
39
+ host: ArdCatalogHost;
40
+ entries: ArdCatalogEntry[];
41
+ }
42
+
43
+ /**
44
+ * Pure: project Agent Surface Manifest into ARD ai-catalog.json.
45
+ * Deterministic — entries are sorted by identifier for byte-identical output (DNA-58).
46
+ */
47
+ export function buildArdCatalog(
48
+ manifest: AgentSurfaceManifest,
49
+ hostDisplayName?: string,
50
+ ): ArdCatalog {
51
+ const domain = manifest.baseUrl.replace(/^https?:\/\//, "").replace(/\/+$/, "");
52
+ const displayName = hostDisplayName ?? manifest.site;
53
+
54
+ const entries: ArdCatalogEntry[] = [];
55
+
56
+ // MCP server card entry
57
+ if (manifest.interfaces.mcp) {
58
+ entries.push({
59
+ identifier: `urn:air:${domain}:server:mcp`,
60
+ displayName: "MCP Server",
61
+ type: "application/mcp-server-card+json",
62
+ url: "/.well-known/mcp/server-card.json",
63
+ representativeQueries: [
64
+ "What tools are available on this MCP server",
65
+ "List MCP server capabilities and endpoints",
66
+ ],
67
+ });
68
+ }
69
+
70
+ // A2A Agent Card entry
71
+ entries.push({
72
+ identifier: `urn:air:${domain}:agent:a2a`,
73
+ displayName: "A2A Agent Card",
74
+ type: "application/a2a+json",
75
+ url: "/.well-known/agent-card.json",
76
+ representativeQueries: [
77
+ "What agent capabilities does this site offer",
78
+ "Get the A2A agent card for this service",
79
+ ],
80
+ });
81
+
82
+ // OpenAPI entry
83
+ if (manifest.interfaces.openapi) {
84
+ entries.push({
85
+ identifier: `urn:air:${domain}:api:openapi`,
86
+ displayName: "OpenAPI Specification",
87
+ type: "application/vnd.oai.openapi+json",
88
+ url: manifest.interfaces.openapi,
89
+ representativeQueries: [
90
+ "What API endpoints are available on this site",
91
+ "Get the OpenAPI specification for agent integration",
92
+ ],
93
+ });
94
+ }
95
+
96
+ // Agent Surface Manifest entry
97
+ entries.push({
98
+ identifier: `urn:air:${domain}:manifest:agent-surface`,
99
+ displayName: "Agent Surface Manifest",
100
+ type: "application/json",
101
+ url: "/.well-known/agent.json",
102
+ representativeQueries: [
103
+ "What agent discovery endpoints does this site provide",
104
+ "Get the agent surface manifest for this site",
105
+ ],
106
+ });
107
+
108
+ // Knowledge domain entries
109
+ for (const ref of manifest.knowledge) {
110
+ entries.push({
111
+ identifier: `urn:air:${domain}:knowledge:${ref.domain}`,
112
+ displayName: `${ref.domain.charAt(0).toUpperCase() + ref.domain.slice(1)} Knowledge`,
113
+ type: "application/json",
114
+ url: ref.url,
115
+ representativeQueries: [
116
+ `What ${ref.domain} information is available on this site`,
117
+ `Get structured ${ref.domain} data for agent consumption`,
118
+ ],
119
+ });
120
+ }
121
+
122
+ // Sort entries by identifier for deterministic output
123
+ entries.sort((a, b) => a.identifier.localeCompare(b.identifier));
124
+
125
+ return {
126
+ specVersion: "1.0",
127
+ host: {
128
+ displayName,
129
+ identifier: `did:web:${domain}`,
130
+ },
131
+ entries,
132
+ };
133
+ }
@@ -27,3 +27,4 @@ export * from "./fleet-catalog.ts";
27
27
  export * from "./api-catalog.ts";
28
28
  export * from "./mcp-card.ts";
29
29
  export * from "./dns-aid.ts";
30
+ export * from "./ard-catalog.ts";
@@ -205,6 +205,7 @@ export function resolveReferencesInString(
205
205
  lang: string,
206
206
  defaultLang: string,
207
207
  sourceRef?: SourceRef,
208
+ onUnresolved?: (ref: string, error: string) => void,
208
209
  ): string {
209
210
  // RFC-0731: Check for this. as a pure reference (entire text is this.field.path)
210
211
  if (
@@ -219,6 +220,7 @@ export function resolveReferencesInString(
219
220
  if (result.resolved) {
220
221
  return formatValue(result.value);
221
222
  }
223
+ onUnresolved?.(text.trim(), result.error ?? "unresolved this. reference");
222
224
  }
223
225
  }
224
226
  THIS_SCAN_PATTERN.lastIndex = 0;
@@ -228,6 +230,7 @@ export function resolveReferencesInString(
228
230
  if (result.resolved) {
229
231
  return formatValue(result.value);
230
232
  }
233
+ onUnresolved?.(text, result.error ?? "unresolved reference");
231
234
  return text;
232
235
  }
233
236
 
@@ -310,9 +313,12 @@ export async function resolveReferencesDeep(
310
313
  lang: string,
311
314
  defaultLang: string,
312
315
  sourceRef?: SourceRef,
316
+ onUnresolved?: (ref: string, error: string) => void,
313
317
  ): Promise<unknown> {
314
318
  return substituteRefsDeep(data, (value) =>
315
- Promise.resolve(resolveReferencesInString(index, value, lang, defaultLang, sourceRef)),
319
+ Promise.resolve(
320
+ resolveReferencesInString(index, value, lang, defaultLang, sourceRef, onUnresolved),
321
+ ),
316
322
  );
317
323
  }
318
324
 
@@ -32,6 +32,7 @@ export function createLanguageRedirectMiddleware(options: {
32
32
  supportedLangs: readonly string[];
33
33
  defaultLang: string;
34
34
  }) {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
36
  return defineMiddleware((ctx: any, next: any) => {
36
37
  const url = new URL(ctx.request.url);
37
38
  const segments = url.pathname.split("/").filter(Boolean);
@@ -0,0 +1,129 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>ARD ai-catalog.json projection tests: determinism, shape, entry coverage.</purpose>
4
+ <keywords>ARD, ai-catalog, agent surface, test</keywords>
5
+ </MODULE_CONTRACT>
6
+ <CHANGE_SUMMARY>
7
+ <item>Initial ARD ai-catalog.json projection tests.</item>
8
+ </CHANGE_SUMMARY>
9
+ */
10
+
11
+ import { test, expect } from "vitest";
12
+ import { buildArdCatalog } from "../agent/ard-catalog.ts";
13
+ import { buildAgentSurfaceManifest } from "../agent/manifest.ts";
14
+
15
+ test("buildArdCatalog: produces specVersion, host, and entries array", () => {
16
+ const manifest = buildAgentSurfaceManifest({
17
+ site: "s",
18
+ baseUrl: "https://s.example",
19
+ languages: { default: "de", supported: ["de"] },
20
+ });
21
+ const catalog = buildArdCatalog(manifest, "My Site");
22
+ expect(catalog.specVersion).toBe("1.0");
23
+ expect(catalog.host.displayName).toBe("My Site");
24
+ expect(catalog.host.identifier).toBe("did:web:s.example");
25
+ expect(catalog.entries.length).toBeGreaterThan(0);
26
+ });
27
+
28
+ test("buildArdCatalog: each entry has identifier, displayName, type, url, representativeQueries", () => {
29
+ const manifest = buildAgentSurfaceManifest({
30
+ site: "s",
31
+ baseUrl: "https://s.example",
32
+ languages: { default: "de", supported: ["de"] },
33
+ mcp: { url: "/api/agent/mcp", protocolVersion: "2025-06-18" },
34
+ knowledge: [
35
+ { domain: "offer", url: "/api/agent/v1/offer.json", schema: "gogol.agent.knowledge/offer@1" },
36
+ ],
37
+ });
38
+ const catalog = buildArdCatalog(manifest);
39
+ for (const entry of catalog.entries) {
40
+ expect(entry.identifier).toMatch(/^urn:air:s\.example:/);
41
+ expect(entry.displayName).toBeTruthy();
42
+ expect(entry.type).toBeTruthy();
43
+ expect(entry.url).toBeTruthy();
44
+ expect(entry.representativeQueries.length).toBeGreaterThanOrEqual(2);
45
+ expect(entry.representativeQueries.length).toBeLessThanOrEqual(5);
46
+ }
47
+ });
48
+
49
+ test("buildArdCatalog: MCP interface adds MCP server entry", () => {
50
+ const manifest = buildAgentSurfaceManifest({
51
+ site: "s",
52
+ baseUrl: "https://s.example",
53
+ languages: { default: "de", supported: ["de"] },
54
+ mcp: { url: "/api/agent/mcp", protocolVersion: "2025-06-18" },
55
+ });
56
+ const catalog = buildArdCatalog(manifest);
57
+ const mcpEntry = catalog.entries.find((e) => e.identifier.includes(":server:mcp"));
58
+ expect(mcpEntry).toBeDefined();
59
+ expect(mcpEntry?.type).toBe("application/mcp-server-card+json");
60
+ expect(mcpEntry?.url).toBe("/.well-known/mcp/server-card.json");
61
+ });
62
+
63
+ test("buildArdCatalog: knowledge refs produce entries", () => {
64
+ const manifest = buildAgentSurfaceManifest({
65
+ site: "s",
66
+ baseUrl: "https://s.example",
67
+ languages: { default: "de", supported: ["de"] },
68
+ knowledge: [
69
+ { domain: "offer", url: "/api/agent/v1/offer.json", schema: "gogol.agent.knowledge/offer@1" },
70
+ { domain: "team", url: "/api/agent/v1/team.json", schema: "gogol.agent.knowledge/team@1" },
71
+ ],
72
+ });
73
+ const catalog = buildArdCatalog(manifest);
74
+ const knowledgeEntries = catalog.entries.filter((e) => e.identifier.includes(":knowledge:"));
75
+ expect(knowledgeEntries).toHaveLength(2);
76
+ expect(knowledgeEntries[0].url).toBe("/api/agent/v1/offer.json");
77
+ });
78
+
79
+ test("buildArdCatalog: entries are sorted by identifier", () => {
80
+ const manifest = buildAgentSurfaceManifest({
81
+ site: "s",
82
+ baseUrl: "https://s.example",
83
+ languages: { default: "de", supported: ["de"] },
84
+ knowledge: [
85
+ { domain: "zeta", url: "/api/agent/v1/zeta.json", schema: "gogol.agent.knowledge/zeta@1" },
86
+ { domain: "alpha", url: "/api/agent/v1/alpha.json", schema: "gogol.agent.knowledge/alpha@1" },
87
+ ],
88
+ });
89
+ const catalog = buildArdCatalog(manifest);
90
+ const identifiers = catalog.entries.map((e) => e.identifier);
91
+ const sorted = [...identifiers].sort();
92
+ expect(identifiers).toEqual(sorted);
93
+ });
94
+
95
+ test("buildArdCatalog: determinism — same input produces identical output", () => {
96
+ const input = {
97
+ site: "s",
98
+ baseUrl: "https://s.example",
99
+ languages: { default: "de", supported: ["de"] },
100
+ knowledge: [
101
+ { domain: "offer", url: "/api/agent/v1/offer.json", schema: "gogol.agent.knowledge/offer@1" },
102
+ ],
103
+ mcp: { url: "/api/agent/mcp", protocolVersion: "2025-06-18" },
104
+ };
105
+ const a = buildArdCatalog(buildAgentSurfaceManifest(input), "My Site");
106
+ const b = buildArdCatalog(buildAgentSurfaceManifest(input), "My Site");
107
+ expect(JSON.stringify(a)).toBe(JSON.stringify(b));
108
+ });
109
+
110
+ test("buildArdCatalog: host identifier uses did:web format with domain", () => {
111
+ const manifest = buildAgentSurfaceManifest({
112
+ site: "warpgogol-com",
113
+ baseUrl: "https://warpgogol.com",
114
+ languages: { default: "de", supported: ["de"] },
115
+ });
116
+ const catalog = buildArdCatalog(manifest, "Warpgogol");
117
+ expect(catalog.host.identifier).toBe("did:web:warpgogol.com");
118
+ expect(catalog.host.displayName).toBe("Warpgogol");
119
+ });
120
+
121
+ test("buildArdCatalog: default hostDisplayName falls back to site name", () => {
122
+ const manifest = buildAgentSurfaceManifest({
123
+ site: "my-site",
124
+ baseUrl: "https://example.com",
125
+ languages: { default: "de", supported: ["de"] },
126
+ });
127
+ const catalog = buildArdCatalog(manifest);
128
+ expect(catalog.host.displayName).toBe("my-site");
129
+ });
@@ -0,0 +1,14 @@
1
+ declare module "@syrokomskyi/axiom-study" {
2
+ export interface Finding {
3
+ findingId: string;
4
+ semanticFingerprint: string;
5
+ methodologyId: string;
6
+ ruleId: string;
7
+ affectedSubjectId: string;
8
+ title: string;
9
+ severity: "info" | "low" | "medium" | "high" | "critical";
10
+ evidence: Array<{ evidenceId: string; digest: string }>;
11
+ uncertainty: unknown[];
12
+ extension: Record<string, unknown>;
13
+ }
14
+ }
package/tsconfig.json CHANGED
@@ -2,19 +2,22 @@
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
4
  "module": "preserve",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "isolatedModules": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
5
12
  "noEmit": true,
13
+ "allowImportingTsExtensions": true,
6
14
  "declaration": true,
7
15
  "rewriteRelativeImportExtensions": true,
8
- "types": [
9
- "node"
10
- ],
16
+ "types": ["node"],
11
17
  "rootDir": ".",
12
- "outDir": "dist",
13
- "allowImportingTsExtensions": true
18
+ "outDir": "dist"
14
19
  },
15
- "include": [
16
- "src/**/*.ts"
17
- ],
20
+ "include": ["src/**/*.ts"],
18
21
  "exclude": [
19
22
  "src/**/*.template.*",
20
23
  "src/**/*.test.ts",