@ferrule-io/ok-fine 0.0.0-stage → 0.3.7

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.
Files changed (87) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +93 -2
  3. package/dist/auth/discovery.js +67 -0
  4. package/dist/auth/http-auth.js +50 -0
  5. package/dist/auth/verifier.js +176 -0
  6. package/dist/cli-options.js +99 -0
  7. package/dist/cli.js +115 -0
  8. package/dist/config.js +320 -0
  9. package/dist/data-dir-lock.js +60 -0
  10. package/dist/errors.js +13 -0
  11. package/dist/http/mcp-route.js +32 -0
  12. package/dist/http/rest.js +243 -0
  13. package/dist/http/ui.js +118 -0
  14. package/dist/main.js +32 -0
  15. package/dist/mcp/server.js +203 -0
  16. package/dist/okf/concept.js +99 -0
  17. package/dist/okf/frontmatter.js +180 -0
  18. package/dist/okf/index-file.js +74 -0
  19. package/dist/okf/lint.js +480 -0
  20. package/dist/okf/log-file.js +74 -0
  21. package/dist/okf/markdown.js +106 -0
  22. package/dist/okf/paths.js +99 -0
  23. package/dist/okf/repository.js +53 -0
  24. package/dist/okf/semantics.js +121 -0
  25. package/dist/server.js +343 -0
  26. package/dist/service/feedback.js +40 -0
  27. package/dist/service/knowledge-service.js +819 -0
  28. package/dist/service/principal.js +22 -0
  29. package/dist/store/archive.js +108 -0
  30. package/dist/store/backend.js +4 -0
  31. package/dist/store/bundle.js +250 -0
  32. package/dist/store/catalog.js +295 -0
  33. package/dist/store/fs-util.js +120 -0
  34. package/dist/store/git-backend.js +713 -0
  35. package/dist/store/git.js +274 -0
  36. package/dist/store/mutex.js +19 -0
  37. package/dist/store/path-index.js +54 -0
  38. package/dist/ui/assets/bash-BiGZg9Om.js +1 -0
  39. package/dist/ui/assets/css-BGIxQoYs.js +1 -0
  40. package/dist/ui/assets/css-DUjzNfgv.js +1 -0
  41. package/dist/ui/assets/diff-woXpYk--.js +1 -0
  42. package/dist/ui/assets/dockerfile-IyjqRm3v.js +1 -0
  43. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  44. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  45. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  46. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  47. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  48. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  49. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  50. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  51. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  52. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  53. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  54. package/dist/ui/assets/github-dark-default-DXG-b-1a.js +1 -0
  55. package/dist/ui/assets/github-light-default-BXViO-2h.js +1 -0
  56. package/dist/ui/assets/go-rLFTqkRN.js +1 -0
  57. package/dist/ui/assets/highlight-CW19vWvw.js +153 -0
  58. package/dist/ui/assets/html-BMn1ogTm.js +1 -0
  59. package/dist/ui/assets/html-BlpXnbzK.js +1 -0
  60. package/dist/ui/assets/index-0b2NNRC5.js +127 -0
  61. package/dist/ui/assets/index-CFuTAZQM.css +1 -0
  62. package/dist/ui/assets/java-BtIQNnN0.js +1 -0
  63. package/dist/ui/assets/java-DxlWrXwe.js +1 -0
  64. package/dist/ui/assets/javascript-BzbQS41l.js +1 -0
  65. package/dist/ui/assets/javascript-Yelw-5ZN.js +1 -0
  66. package/dist/ui/assets/json-qhed-kSA.js +1 -0
  67. package/dist/ui/assets/jsx-D3KKoC5L.js +1 -0
  68. package/dist/ui/assets/jsx-hEbJ82HF.js +1 -0
  69. package/dist/ui/assets/markdown-BYOwaDjH.js +1 -0
  70. package/dist/ui/assets/python-gzcpVVnB.js +1 -0
  71. package/dist/ui/assets/ruby-Di5fYh6S.js +1 -0
  72. package/dist/ui/assets/rust-DpiIf5JO.js +1 -0
  73. package/dist/ui/assets/shellscript-CqHdAlCp.js +1 -0
  74. package/dist/ui/assets/sql-BOsskAmu.js +1 -0
  75. package/dist/ui/assets/sql-CNu3Or2W.js +1 -0
  76. package/dist/ui/assets/toml-CcmNWLt0.js +1 -0
  77. package/dist/ui/assets/tsx-5f3ulCja.js +1 -0
  78. package/dist/ui/assets/tsx-BOvsFOI6.js +1 -0
  79. package/dist/ui/assets/typescript-Utq2Cl8c.js +1 -0
  80. package/dist/ui/assets/typescript-j_1H8WHN.js +1 -0
  81. package/dist/ui/assets/yaml-CxbubiML.js +1 -0
  82. package/dist/ui/assets/yaml-DijMHRDA.js +1 -0
  83. package/dist/ui/favicon.svg +11 -0
  84. package/dist/ui/index.html +16 -0
  85. package/dist/ui/theme-init.js +10 -0
  86. package/dist/version.js +2 -0
  87. package/package.json +77 -4
@@ -0,0 +1,99 @@
1
+ import { parseFrontmatter, splitFrontmatter } from "./frontmatter.js";
2
+ import { extractLinks } from "./markdown.js";
3
+ import { blobRevision } from "./paths.js";
4
+ import { displayTitle, effectiveStatus, generatedAt, generatedBy, lastVerifiedAt, staleAfter, trustTier, } from "./semantics.js";
5
+ export function parseConcept(project, id, file, buf) {
6
+ const text = buf.toString("utf8");
7
+ const revision = blobRevision(buf);
8
+ const split = splitFrontmatter(text);
9
+ if (!split) {
10
+ return {
11
+ project,
12
+ id,
13
+ file,
14
+ revision,
15
+ frontmatter: null,
16
+ body: text,
17
+ parseError: "missing_frontmatter",
18
+ title: displayTitle(null, id),
19
+ description: null,
20
+ type: null,
21
+ tags: [],
22
+ status: "stable",
23
+ trustTier: "unverified",
24
+ staleAfter: null,
25
+ generatedAt: null,
26
+ generatedBy: null,
27
+ lastVerifiedAt: null,
28
+ outbound: [],
29
+ };
30
+ }
31
+ const parsed = parseFrontmatter(split.yaml);
32
+ if ("error" in parsed) {
33
+ return {
34
+ project,
35
+ id,
36
+ file,
37
+ revision,
38
+ frontmatter: null,
39
+ body: split.body,
40
+ parseError: parsed.error,
41
+ title: displayTitle(null, id),
42
+ description: null,
43
+ type: null,
44
+ tags: [],
45
+ status: "stable",
46
+ trustTier: "unverified",
47
+ staleAfter: null,
48
+ generatedAt: null,
49
+ generatedBy: null,
50
+ lastVerifiedAt: null,
51
+ outbound: [],
52
+ };
53
+ }
54
+ const { data } = parsed;
55
+ if (typeof data.type !== "string" || data.type.trim().length === 0) {
56
+ return {
57
+ project,
58
+ id,
59
+ file,
60
+ revision,
61
+ frontmatter: null,
62
+ body: split.body,
63
+ parseError: "missing_type",
64
+ title: displayTitle(data, id),
65
+ description: typeof data.description === "string" ? data.description : null,
66
+ type: null,
67
+ tags: [],
68
+ status: effectiveStatus(data),
69
+ trustTier: trustTier(data),
70
+ staleAfter: staleAfter(data),
71
+ generatedAt: generatedAt(data),
72
+ generatedBy: generatedBy(data),
73
+ lastVerifiedAt: lastVerifiedAt(data),
74
+ outbound: extractLinks(split.body, id),
75
+ };
76
+ }
77
+ const tags = Array.isArray(data.tags) ? data.tags.filter((t) => typeof t === "string") : [];
78
+ return {
79
+ project,
80
+ id,
81
+ file,
82
+ revision,
83
+ frontmatter: data,
84
+ body: split.body,
85
+ parseError: null,
86
+ title: displayTitle(data, id),
87
+ description: typeof data.description === "string" ? data.description : null,
88
+ type: data.type,
89
+ tags,
90
+ status: effectiveStatus(data),
91
+ trustTier: trustTier(data),
92
+ staleAfter: staleAfter(data),
93
+ generatedAt: generatedAt(data),
94
+ generatedBy: generatedBy(data),
95
+ lastVerifiedAt: lastVerifiedAt(data),
96
+ outbound: extractLinks(split.body, id),
97
+ };
98
+ }
99
+ //# sourceMappingURL=concept.js.map
@@ -0,0 +1,180 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { Document, isMap, parseDocument } from "yaml";
3
+ export function splitFrontmatter(text) {
4
+ if (text.charCodeAt(0) === 0xfeff) {
5
+ text = text.slice(1);
6
+ }
7
+ const match = /^---\r?\n/.exec(text);
8
+ if (!match) {
9
+ return null;
10
+ }
11
+ const yamlStart = match[0].length;
12
+ const closingRegex = /\r?\n---\r?\n|\r?\n---$/g;
13
+ closingRegex.lastIndex = yamlStart;
14
+ const closeMatch = closingRegex.exec(text);
15
+ if (!closeMatch) {
16
+ return null;
17
+ }
18
+ const yamlEnd = closeMatch.index;
19
+ const bodyStart = closeMatch.index + closeMatch[0].length;
20
+ const yaml = text.slice(yamlStart, yamlEnd);
21
+ let body = text.slice(bodyStart);
22
+ body = body.replace(/^(\r?\n)+/, "");
23
+ return { yaml, body };
24
+ }
25
+ export function parseFrontmatter(yaml) {
26
+ const doc = parseDocument(yaml);
27
+ if (doc.errors.length > 0) {
28
+ return {
29
+ error: "invalid_yaml",
30
+ message: doc.errors.map((e) => e.message).join("; "),
31
+ };
32
+ }
33
+ if (!doc.contents || !isMap(doc.contents)) {
34
+ return {
35
+ error: "frontmatter_not_mapping",
36
+ message: "frontmatter must be a YAML mapping",
37
+ };
38
+ }
39
+ const data = (doc.toJS() ?? {});
40
+ return { doc, data };
41
+ }
42
+ export function serializeConcept(doc, body) {
43
+ const normalizedBody = body.replace(/\r\n/g, "\n").replace(/^\n+/, "").replace(/\s*$/, "");
44
+ return `---\n${doc.toString({ lineWidth: 0 })}---\n\n${normalizedBody}\n`;
45
+ }
46
+ function createFormattedNode(doc, key, val) {
47
+ if (key === "tags" && Array.isArray(val)) {
48
+ return doc.createNode(val, { flow: true });
49
+ }
50
+ if (key === "usage_window" && val && typeof val === "object" && !Array.isArray(val)) {
51
+ return doc.createNode(val, { flow: true });
52
+ }
53
+ if (key === "parameters" && Array.isArray(val)) {
54
+ return doc.createNode(val.map((item) => item && typeof item === "object" && !Array.isArray(item) ? doc.createNode(item, { flow: true }) : item));
55
+ }
56
+ if (key === "generated" && val && typeof val === "object") {
57
+ return doc.createNode(val, { flow: true });
58
+ }
59
+ return val;
60
+ }
61
+ export function applyFrontmatter(existing, input, server) {
62
+ const ignoredKeys = [];
63
+ if (existing !== null) {
64
+ const existingJs = (existing.toJS() ?? {});
65
+ if ("generated" in input && !isDeepStrictEqual(input.generated, existingJs.generated)) {
66
+ ignoredKeys.push("generated");
67
+ }
68
+ if ("verified" in input && !isDeepStrictEqual(input.verified, existingJs.verified)) {
69
+ ignoredKeys.push("verified");
70
+ }
71
+ const cleanInput = {};
72
+ for (const [k, v] of Object.entries(input)) {
73
+ if (k !== "generated" && k !== "verified") {
74
+ cleanInput[k] = v;
75
+ }
76
+ }
77
+ const keysToDelete = [];
78
+ if (isMap(existing.contents)) {
79
+ for (const item of existing.contents.items) {
80
+ if (!item || typeof item !== "object" || !("key" in item) || item.key == null) {
81
+ continue;
82
+ }
83
+ let keyName = "";
84
+ const rawKey = item.key;
85
+ if (typeof rawKey === "string") {
86
+ keyName = rawKey;
87
+ }
88
+ else if (typeof rawKey === "object" && "value" in rawKey && typeof rawKey.value === "string") {
89
+ keyName = rawKey.value;
90
+ }
91
+ else {
92
+ keyName = String(rawKey);
93
+ }
94
+ if (keyName && keyName !== "generated" && keyName !== "verified" && !(keyName in cleanInput)) {
95
+ keysToDelete.push(keyName);
96
+ }
97
+ }
98
+ }
99
+ for (const k of keysToDelete) {
100
+ existing.delete(k);
101
+ }
102
+ for (const [k, v] of Object.entries(cleanInput)) {
103
+ if (existing.has(k)) {
104
+ if (!isDeepStrictEqual(existingJs[k], v)) {
105
+ existing.set(k, createFormattedNode(existing, k, v));
106
+ }
107
+ }
108
+ else {
109
+ existing.set(k, createFormattedNode(existing, k, v));
110
+ }
111
+ }
112
+ existing.set("generated", existing.createNode(server.generated, { flow: true }));
113
+ return { doc: existing, ignoredKeys };
114
+ }
115
+ else {
116
+ if ("generated" in input) {
117
+ ignoredKeys.push("generated");
118
+ }
119
+ if ("verified" in input) {
120
+ ignoredKeys.push("verified");
121
+ }
122
+ const cleanInput = {};
123
+ for (const [k, v] of Object.entries(input)) {
124
+ if (k !== "generated" && k !== "verified") {
125
+ cleanInput[k] = v;
126
+ }
127
+ }
128
+ const doc = new Document();
129
+ const firstKeys = ["type", "title", "description", "resource", "tags", "status"];
130
+ const endKeys = ["stale_after", "sources", "usage_window"];
131
+ for (const k of firstKeys) {
132
+ if (k in cleanInput) {
133
+ doc.set(k, createFormattedNode(doc, k, cleanInput[k]));
134
+ }
135
+ }
136
+ for (const [k, v] of Object.entries(cleanInput)) {
137
+ if (!firstKeys.includes(k) && !endKeys.includes(k)) {
138
+ doc.set(k, createFormattedNode(doc, k, v));
139
+ }
140
+ }
141
+ doc.set("generated", doc.createNode(server.generated, { flow: true }));
142
+ for (const k of endKeys) {
143
+ if (k in cleanInput) {
144
+ doc.set(k, createFormattedNode(doc, k, cleanInput[k]));
145
+ }
146
+ }
147
+ return { doc, ignoredKeys };
148
+ }
149
+ }
150
+ export function appendVerification(doc, entry) {
151
+ const js = (doc.toJS() ?? {});
152
+ const raw = js.verified;
153
+ let list = [];
154
+ if (raw && typeof raw === "object") {
155
+ if (Array.isArray(raw)) {
156
+ list = raw.filter((item) => Boolean(item && typeof item === "object" && typeof item.by === "string" && item.by.length > 0));
157
+ }
158
+ else {
159
+ if ("by" in raw && typeof raw.by === "string" && raw.by.length > 0) {
160
+ const atVal = "at" in raw && typeof raw.at === "string" ? raw.at : undefined;
161
+ list = [{ by: raw.by, at: atVal }];
162
+ }
163
+ }
164
+ }
165
+ list.push(entry);
166
+ if (list.length === 1) {
167
+ doc.set("verified", doc.createNode(list[0], { flow: true }));
168
+ }
169
+ else {
170
+ doc.set("verified", doc.createNode(list.map((e) => doc.createNode(e, { flow: true }))));
171
+ }
172
+ }
173
+ export function nowIso() {
174
+ return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
175
+ }
176
+ export function isoAfterDays(now, days) {
177
+ const d = new Date(now.getTime() + days * 86_400_000);
178
+ return d.toISOString().replace(/\.\d{3}Z$/, "Z");
179
+ }
180
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1,74 @@
1
+ export function renderIndex(listing) {
2
+ const sections = [];
3
+ // Group concepts by type
4
+ const byType = new Map();
5
+ for (const c of listing.concepts) {
6
+ const typeKey = c.type && c.type.length > 0 ? c.type : "Other";
7
+ let list = byType.get(typeKey);
8
+ if (!list) {
9
+ list = [];
10
+ byType.set(typeKey, list);
11
+ }
12
+ list.push({ file: c.file, title: c.title, description: c.description });
13
+ }
14
+ const sortedTypes = Array.from(byType).sort(([a], [b]) => a.localeCompare(b, "en"));
15
+ for (const [type, items] of sortedTypes) {
16
+ items.sort((a, b) => {
17
+ const titleCmp = a.title.toLowerCase().localeCompare(b.title.toLowerCase(), "en");
18
+ if (titleCmp !== 0) {
19
+ return titleCmp;
20
+ }
21
+ return a.file.localeCompare(b.file, "en");
22
+ });
23
+ const lines = [`# ${type}`];
24
+ for (const item of items) {
25
+ const uri = encodeURI(item.file);
26
+ if (item.description != null) {
27
+ const collapsedDesc = item.description.replace(/\s+/g, " ").trim();
28
+ lines.push(`* [${item.title}](${uri}) - ${collapsedDesc}`);
29
+ }
30
+ else {
31
+ lines.push(`* [${item.title}](${uri})`);
32
+ }
33
+ }
34
+ sections.push(lines.join("\n"));
35
+ }
36
+ // Non-markdown files
37
+ if (listing.files.length > 0) {
38
+ const sortedFiles = listing.files.slice().sort((a, b) => a.localeCompare(b, "en"));
39
+ const lines = ["# Files"];
40
+ for (const f of sortedFiles) {
41
+ lines.push(`* [${f}](${encodeURI(f)})`);
42
+ }
43
+ sections.push(lines.join("\n"));
44
+ }
45
+ // Directories
46
+ if (listing.dirs.length > 0) {
47
+ const sortedDirs = listing.dirs.slice().sort((a, b) => a.name.localeCompare(b.name, "en"));
48
+ const lines = ["# Directories"];
49
+ for (const d of sortedDirs) {
50
+ const uri = `${encodeURI(d.name)}/`;
51
+ let countText;
52
+ if (d.conceptCount > 0) {
53
+ countText = d.conceptCount === 1 ? "1 concept" : `${d.conceptCount} concepts`;
54
+ }
55
+ else {
56
+ countText = d.fileCount === 1 ? "1 file" : `${d.fileCount} files`;
57
+ }
58
+ lines.push(`* [${d.name}](${uri}) - ${countText}`);
59
+ }
60
+ sections.push(lines.join("\n"));
61
+ }
62
+ let body;
63
+ if (sections.length === 0) {
64
+ body = "# Concepts\n";
65
+ }
66
+ else {
67
+ body = `${sections.join("\n\n")}\n`;
68
+ }
69
+ if (listing.isRoot) {
70
+ return `---\nokf_version: "0.2"\n---\n\n${body}`;
71
+ }
72
+ return body;
73
+ }
74
+ //# sourceMappingURL=index-file.js.map