ont-run 0.0.4 → 0.0.5
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 +8 -4
- package/dist/bin/ont.js +202 -6
- package/dist/index.js +18 -54
- package/dist/src/browser/server.d.ts +2 -0
- package/dist/src/browser/transform.d.ts +0 -1
- package/dist/src/config/define.d.ts +37 -2
- package/dist/src/config/schema.d.ts +8 -8
- package/dist/src/config/types.d.ts +27 -27
- package/dist/src/index.d.ts +3 -2
- package/dist/src/server/api/index.d.ts +0 -2
- package/dist/src/server/api/router.d.ts +1 -1
- package/dist/src/server/mcp/index.d.ts +0 -2
- package/dist/src/server/mcp/tools.d.ts +2 -2
- package/dist/src/server/resolver.d.ts +4 -15
- package/package.json +1 -1
- package/src/browser/server.ts +203 -2
- package/src/browser/transform.ts +0 -2
- package/src/cli/commands/review.ts +2 -1
- package/src/config/define.ts +49 -1
- package/src/config/schema.ts +1 -1
- package/src/config/types.ts +33 -31
- package/src/index.ts +3 -2
- package/src/server/api/index.ts +2 -15
- package/src/server/api/router.ts +3 -6
- package/src/server/mcp/index.ts +2 -4
- package/src/server/mcp/tools.ts +3 -5
- package/src/server/resolver.ts +5 -78
- package/src/server/start.ts +0 -2
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@ A web framework designed for the era of coding agents. You define the ontology
|
|
|
7
7
|
```typescript
|
|
8
8
|
// ontology.config.ts
|
|
9
9
|
import { defineOntology, z } from 'ont-run';
|
|
10
|
+
import getTicket from './resolvers/getTicket.js';
|
|
11
|
+
import assignTicket from './resolvers/assignTicket.js';
|
|
10
12
|
|
|
11
13
|
export default defineOntology({
|
|
12
14
|
name: 'support-desk',
|
|
@@ -23,14 +25,14 @@ export default defineOntology({
|
|
|
23
25
|
access: ['support', 'admin'],
|
|
24
26
|
entities: ['Ticket'],
|
|
25
27
|
inputs: z.object({ ticketId: z.string().uuid() }),
|
|
26
|
-
resolver:
|
|
28
|
+
resolver: getTicket,
|
|
27
29
|
},
|
|
28
30
|
assignTicket: {
|
|
29
31
|
description: 'Assign ticket to an agent',
|
|
30
32
|
access: ['admin'], // If AI tries to add 'public' here, review is triggered
|
|
31
33
|
entities: ['Ticket'],
|
|
32
34
|
inputs: z.object({ ticketId: z.string().uuid(), assignee: z.string() }),
|
|
33
|
-
resolver:
|
|
35
|
+
resolver: assignTicket,
|
|
34
36
|
},
|
|
35
37
|
},
|
|
36
38
|
// ...
|
|
@@ -141,6 +143,7 @@ The resolver context provides:
|
|
|
141
143
|
|
|
142
144
|
```typescript
|
|
143
145
|
import { defineOntology, z } from 'ont-run';
|
|
146
|
+
import getUser from './resolvers/getUser.js';
|
|
144
147
|
|
|
145
148
|
export default defineOntology({
|
|
146
149
|
name: 'my-api',
|
|
@@ -179,7 +182,7 @@ export default defineOntology({
|
|
|
179
182
|
access: ['user', 'admin'],
|
|
180
183
|
entities: ['User'],
|
|
181
184
|
inputs: z.object({ userId: z.string().uuid() }),
|
|
182
|
-
resolver:
|
|
185
|
+
resolver: getUser,
|
|
183
186
|
},
|
|
184
187
|
},
|
|
185
188
|
});
|
|
@@ -191,6 +194,7 @@ The framework handles **group-based access** (user → group → function) out o
|
|
|
191
194
|
|
|
192
195
|
```typescript
|
|
193
196
|
import { defineOntology, userContext, z } from 'ont-run';
|
|
197
|
+
import editPost from './resolvers/editPost.js';
|
|
194
198
|
|
|
195
199
|
export default defineOntology({
|
|
196
200
|
// Auth must return user identity for userContext to work
|
|
@@ -216,7 +220,7 @@ export default defineOntology({
|
|
|
216
220
|
email: z.string(),
|
|
217
221
|
})),
|
|
218
222
|
}),
|
|
219
|
-
resolver:
|
|
223
|
+
resolver: editPost,
|
|
220
224
|
},
|
|
221
225
|
},
|
|
222
226
|
});
|
package/dist/bin/ont.js
CHANGED
|
@@ -10233,6 +10233,10 @@ var Hono2 = class extends Hono {
|
|
|
10233
10233
|
}
|
|
10234
10234
|
};
|
|
10235
10235
|
|
|
10236
|
+
// src/browser/server.ts
|
|
10237
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
10238
|
+
import { basename } from "path";
|
|
10239
|
+
|
|
10236
10240
|
// node_modules/open/index.js
|
|
10237
10241
|
import process7 from "node:process";
|
|
10238
10242
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -10869,7 +10873,6 @@ function transformToGraphData(config) {
|
|
|
10869
10873
|
metadata: {
|
|
10870
10874
|
inputs: safeZodToJsonSchema(fn.inputs),
|
|
10871
10875
|
outputs: fn.outputs ? safeZodToJsonSchema(fn.outputs) : undefined,
|
|
10872
|
-
resolver: fn.resolver,
|
|
10873
10876
|
usesUserContext: usesUserContext || undefined
|
|
10874
10877
|
}
|
|
10875
10878
|
});
|
|
@@ -11103,7 +11106,7 @@ function enhanceWithDiff(graphData, diff) {
|
|
|
11103
11106
|
|
|
11104
11107
|
// src/browser/server.ts
|
|
11105
11108
|
async function startBrowserServer(options) {
|
|
11106
|
-
const { config, diff = null, configDir, port: preferredPort, openBrowser = true } = options;
|
|
11109
|
+
const { config, diff = null, configDir, configPath, port: preferredPort, openBrowser = true } = options;
|
|
11107
11110
|
const baseGraphData = transformToGraphData(config);
|
|
11108
11111
|
const graphData = enhanceWithDiff(baseGraphData, diff);
|
|
11109
11112
|
return new Promise(async (resolve2) => {
|
|
@@ -11150,6 +11153,21 @@ async function startBrowserServer(options) {
|
|
|
11150
11153
|
}, 500);
|
|
11151
11154
|
return c3.json({ success: true });
|
|
11152
11155
|
});
|
|
11156
|
+
app.get("/api/source", (c3) => {
|
|
11157
|
+
if (!configPath) {
|
|
11158
|
+
return c3.json({ error: "Config path not available" }, 400);
|
|
11159
|
+
}
|
|
11160
|
+
try {
|
|
11161
|
+
const source = readFileSync2(configPath, "utf-8");
|
|
11162
|
+
const filename = basename(configPath);
|
|
11163
|
+
return c3.json({ source, filename, path: configPath });
|
|
11164
|
+
} catch (error) {
|
|
11165
|
+
return c3.json({
|
|
11166
|
+
error: "Failed to read config file",
|
|
11167
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
11168
|
+
}, 500);
|
|
11169
|
+
}
|
|
11170
|
+
});
|
|
11153
11171
|
app.get("/", (c3) => c3.html(generateBrowserUI(graphData)));
|
|
11154
11172
|
const port = preferredPort || await findAvailablePort(3457);
|
|
11155
11173
|
const server = await serve2(app, port);
|
|
@@ -11758,6 +11776,89 @@ function generateBrowserUI(graphData) {
|
|
|
11758
11776
|
color: var(--change-added);
|
|
11759
11777
|
}
|
|
11760
11778
|
|
|
11779
|
+
/* Source View */
|
|
11780
|
+
.source-view {
|
|
11781
|
+
display: none;
|
|
11782
|
+
grid-column: 2 / 4;
|
|
11783
|
+
padding: 24px;
|
|
11784
|
+
overflow-y: auto;
|
|
11785
|
+
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.5), rgba(231, 225, 207, 0.3));
|
|
11786
|
+
}
|
|
11787
|
+
|
|
11788
|
+
.source-view.active {
|
|
11789
|
+
display: flex;
|
|
11790
|
+
flex-direction: column;
|
|
11791
|
+
}
|
|
11792
|
+
|
|
11793
|
+
.source-header {
|
|
11794
|
+
display: flex;
|
|
11795
|
+
align-items: center;
|
|
11796
|
+
justify-content: space-between;
|
|
11797
|
+
padding: 12px 20px;
|
|
11798
|
+
background: rgba(2, 61, 96, 0.95);
|
|
11799
|
+
border-radius: 12px 12px 0 0;
|
|
11800
|
+
color: white;
|
|
11801
|
+
}
|
|
11802
|
+
|
|
11803
|
+
.source-filename {
|
|
11804
|
+
font-family: 'Space Mono', monospace;
|
|
11805
|
+
font-size: 13px;
|
|
11806
|
+
font-weight: 500;
|
|
11807
|
+
}
|
|
11808
|
+
|
|
11809
|
+
.copy-btn {
|
|
11810
|
+
display: flex;
|
|
11811
|
+
align-items: center;
|
|
11812
|
+
gap: 6px;
|
|
11813
|
+
padding: 6px 12px;
|
|
11814
|
+
background: rgba(255, 255, 255, 0.1);
|
|
11815
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
11816
|
+
border-radius: 6px;
|
|
11817
|
+
color: white;
|
|
11818
|
+
font-family: 'Space Grotesk', sans-serif;
|
|
11819
|
+
font-size: 12px;
|
|
11820
|
+
cursor: pointer;
|
|
11821
|
+
transition: all 0.2s ease;
|
|
11822
|
+
}
|
|
11823
|
+
|
|
11824
|
+
.copy-btn:hover {
|
|
11825
|
+
background: rgba(255, 255, 255, 0.2);
|
|
11826
|
+
}
|
|
11827
|
+
|
|
11828
|
+
.copy-btn.copied {
|
|
11829
|
+
background: rgba(21, 168, 168, 0.3);
|
|
11830
|
+
border-color: var(--vanna-teal);
|
|
11831
|
+
}
|
|
11832
|
+
|
|
11833
|
+
.source-code {
|
|
11834
|
+
flex: 1;
|
|
11835
|
+
margin: 0;
|
|
11836
|
+
padding: 20px;
|
|
11837
|
+
background: #1e1e1e;
|
|
11838
|
+
border-radius: 0 0 12px 12px;
|
|
11839
|
+
overflow: auto;
|
|
11840
|
+
font-family: 'Space Mono', monospace;
|
|
11841
|
+
font-size: 13px;
|
|
11842
|
+
line-height: 1.6;
|
|
11843
|
+
color: #d4d4d4;
|
|
11844
|
+
tab-size: 2;
|
|
11845
|
+
}
|
|
11846
|
+
|
|
11847
|
+
.source-code code {
|
|
11848
|
+
display: block;
|
|
11849
|
+
white-space: pre;
|
|
11850
|
+
}
|
|
11851
|
+
|
|
11852
|
+
/* Syntax highlighting classes */
|
|
11853
|
+
.source-code .keyword { color: #569cd6; }
|
|
11854
|
+
.source-code .string { color: #ce9178; }
|
|
11855
|
+
.source-code .number { color: #b5cea8; }
|
|
11856
|
+
.source-code .comment { color: #6a9955; }
|
|
11857
|
+
.source-code .function { color: #dcdcaa; }
|
|
11858
|
+
.source-code .type { color: #4ec9b0; }
|
|
11859
|
+
.source-code .property { color: #9cdcfe; }
|
|
11860
|
+
.source-code .punctuation { color: #d4d4d4; }
|
|
11861
|
+
|
|
11761
11862
|
/* No Changes State */
|
|
11762
11863
|
.no-changes {
|
|
11763
11864
|
text-align: center;
|
|
@@ -12486,6 +12587,7 @@ function generateBrowserUI(graphData) {
|
|
|
12486
12587
|
<div class="view-tabs">
|
|
12487
12588
|
<button class="view-tab active" data-view="graph">Graph</button>
|
|
12488
12589
|
<button class="view-tab" data-view="table">Table</button>
|
|
12590
|
+
<button class="view-tab" data-view="source">Source</button>
|
|
12489
12591
|
</div>
|
|
12490
12592
|
|
|
12491
12593
|
<div class="filter-buttons" id="graphFilters">
|
|
@@ -12606,6 +12708,20 @@ function generateBrowserUI(graphData) {
|
|
|
12606
12708
|
<div class="table-view" id="tableView">
|
|
12607
12709
|
<div id="tableContent"></div>
|
|
12608
12710
|
</div>
|
|
12711
|
+
|
|
12712
|
+
<!-- Source View -->
|
|
12713
|
+
<div class="source-view" id="sourceView">
|
|
12714
|
+
<div class="source-header">
|
|
12715
|
+
<span class="source-filename" id="sourceFilename">ontology.config.ts</span>
|
|
12716
|
+
<button class="copy-btn" id="copySourceBtn" title="Copy to clipboard">
|
|
12717
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
12718
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
12719
|
+
</svg>
|
|
12720
|
+
Copy
|
|
12721
|
+
</button>
|
|
12722
|
+
</div>
|
|
12723
|
+
<pre class="source-code" id="sourceCode"><code>Loading...</code></pre>
|
|
12724
|
+
</div>
|
|
12609
12725
|
</div>
|
|
12610
12726
|
|
|
12611
12727
|
<!-- Review Footer -->
|
|
@@ -13482,6 +13598,7 @@ function generateBrowserUI(graphData) {
|
|
|
13482
13598
|
const graphContainer = document.querySelector('.graph-container');
|
|
13483
13599
|
const detailPanel = document.getElementById('detailPanel');
|
|
13484
13600
|
const tableView = document.getElementById('tableView');
|
|
13601
|
+
const sourceView = document.getElementById('sourceView');
|
|
13485
13602
|
const graphFilters = document.getElementById('graphFilters');
|
|
13486
13603
|
const layoutSelector = document.querySelector('.layout-selector');
|
|
13487
13604
|
|
|
@@ -13489,15 +13606,25 @@ function generateBrowserUI(graphData) {
|
|
|
13489
13606
|
graphContainer.style.display = 'block';
|
|
13490
13607
|
detailPanel.style.display = 'block';
|
|
13491
13608
|
tableView.classList.remove('active');
|
|
13609
|
+
sourceView.classList.remove('active');
|
|
13492
13610
|
if (graphFilters) graphFilters.style.display = 'flex';
|
|
13493
13611
|
if (layoutSelector) layoutSelector.style.display = 'flex';
|
|
13494
|
-
} else {
|
|
13612
|
+
} else if (view === 'table') {
|
|
13495
13613
|
graphContainer.style.display = 'none';
|
|
13496
13614
|
detailPanel.style.display = 'none';
|
|
13497
13615
|
tableView.classList.add('active');
|
|
13616
|
+
sourceView.classList.remove('active');
|
|
13498
13617
|
if (graphFilters) graphFilters.style.display = 'none';
|
|
13499
13618
|
if (layoutSelector) layoutSelector.style.display = 'none';
|
|
13500
13619
|
renderTableView();
|
|
13620
|
+
} else if (view === 'source') {
|
|
13621
|
+
graphContainer.style.display = 'none';
|
|
13622
|
+
detailPanel.style.display = 'none';
|
|
13623
|
+
tableView.classList.remove('active');
|
|
13624
|
+
sourceView.classList.add('active');
|
|
13625
|
+
if (graphFilters) graphFilters.style.display = 'none';
|
|
13626
|
+
if (layoutSelector) layoutSelector.style.display = 'none';
|
|
13627
|
+
loadSourceView();
|
|
13501
13628
|
}
|
|
13502
13629
|
}
|
|
13503
13630
|
|
|
@@ -13539,6 +13666,74 @@ function generateBrowserUI(graphData) {
|
|
|
13539
13666
|
});
|
|
13540
13667
|
}
|
|
13541
13668
|
|
|
13669
|
+
// Source view
|
|
13670
|
+
let sourceLoaded = false;
|
|
13671
|
+
let sourceContent = '';
|
|
13672
|
+
|
|
13673
|
+
async function loadSourceView() {
|
|
13674
|
+
if (sourceLoaded) return;
|
|
13675
|
+
|
|
13676
|
+
const codeEl = document.getElementById('sourceCode').querySelector('code');
|
|
13677
|
+
const filenameEl = document.getElementById('sourceFilename');
|
|
13678
|
+
|
|
13679
|
+
try {
|
|
13680
|
+
const res = await fetch('/api/source');
|
|
13681
|
+
if (!res.ok) throw new Error('Failed to load source');
|
|
13682
|
+
const data = await res.json();
|
|
13683
|
+
|
|
13684
|
+
sourceContent = data.source;
|
|
13685
|
+
filenameEl.textContent = data.filename;
|
|
13686
|
+
codeEl.innerHTML = highlightTypeScript(data.source);
|
|
13687
|
+
sourceLoaded = true;
|
|
13688
|
+
} catch (err) {
|
|
13689
|
+
codeEl.textContent = 'Error loading source: ' + err.message;
|
|
13690
|
+
}
|
|
13691
|
+
}
|
|
13692
|
+
|
|
13693
|
+
function highlightTypeScript(code) {
|
|
13694
|
+
// Escape HTML first
|
|
13695
|
+
code = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
13696
|
+
|
|
13697
|
+
// Comments (single and multi-line)
|
|
13698
|
+
code = code.replace(/(\\/\\/.*$)/gm, '<span class="comment">$1</span>');
|
|
13699
|
+
code = code.replace(/(\\/\\*[\\s\\S]*?\\*\\/)/g, '<span class="comment">$1</span>');
|
|
13700
|
+
|
|
13701
|
+
// Strings (double, single, and template)
|
|
13702
|
+
code = code.replace(/("(?:[^"\\\\]|\\\\.)*")/g, '<span class="string">$1</span>');
|
|
13703
|
+
code = code.replace(/('(?:[^'\\\\]|\\\\.)*')/g, '<span class="string">$1</span>');
|
|
13704
|
+
code = code.replace(/(\`(?:[^\`\\\\]|\\\\.)*\`)/g, '<span class="string">$1</span>');
|
|
13705
|
+
|
|
13706
|
+
// Keywords
|
|
13707
|
+
const keywords = ['import', 'export', 'from', 'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'class', 'extends', 'new', 'this', 'true', 'false', 'null', 'undefined', 'typeof', 'instanceof', 'async', 'await', 'default', 'as', 'type', 'interface'];
|
|
13708
|
+
keywords.forEach(kw => {
|
|
13709
|
+
code = code.replace(new RegExp('\\\\b(' + kw + ')\\\\b', 'g'), '<span class="keyword">$1</span>');
|
|
13710
|
+
});
|
|
13711
|
+
|
|
13712
|
+
// Numbers
|
|
13713
|
+
code = code.replace(/\\b(\\d+\\.?\\d*)\\b/g, '<span class="number">$1</span>');
|
|
13714
|
+
|
|
13715
|
+
// Function calls
|
|
13716
|
+
code = code.replace(/\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(/g, '<span class="function">$1</span>(');
|
|
13717
|
+
|
|
13718
|
+
return code;
|
|
13719
|
+
}
|
|
13720
|
+
|
|
13721
|
+
// Copy source button
|
|
13722
|
+
document.getElementById('copySourceBtn').addEventListener('click', async () => {
|
|
13723
|
+
const btn = document.getElementById('copySourceBtn');
|
|
13724
|
+
try {
|
|
13725
|
+
await navigator.clipboard.writeText(sourceContent);
|
|
13726
|
+
btn.classList.add('copied');
|
|
13727
|
+
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> Copied!';
|
|
13728
|
+
setTimeout(() => {
|
|
13729
|
+
btn.classList.remove('copied');
|
|
13730
|
+
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> Copy';
|
|
13731
|
+
}, 2000);
|
|
13732
|
+
} catch (err) {
|
|
13733
|
+
console.error('Failed to copy:', err);
|
|
13734
|
+
}
|
|
13735
|
+
});
|
|
13736
|
+
|
|
13542
13737
|
function renderTableSection(title, items, type) {
|
|
13543
13738
|
const changedCount = items.filter(n => n.changeStatus !== 'unchanged').length;
|
|
13544
13739
|
|
|
@@ -13712,7 +13907,7 @@ var reviewCommand = defineCommand({
|
|
|
13712
13907
|
process.exit(0);
|
|
13713
13908
|
}
|
|
13714
13909
|
consola.info("Loading ontology config...");
|
|
13715
|
-
const { config, configDir } = await loadConfig();
|
|
13910
|
+
const { config, configDir, configPath } = await loadConfig();
|
|
13716
13911
|
const { ontology: newOntology, hash: newHash } = computeOntologyHash(config);
|
|
13717
13912
|
const lockfile = await readLockfile(configDir);
|
|
13718
13913
|
const oldOntology = lockfile?.ontology || null;
|
|
@@ -13760,7 +13955,8 @@ var reviewCommand = defineCommand({
|
|
|
13760
13955
|
const result = await startBrowserServer({
|
|
13761
13956
|
config,
|
|
13762
13957
|
diff: diff.hasChanges ? diff : null,
|
|
13763
|
-
configDir
|
|
13958
|
+
configDir,
|
|
13959
|
+
configPath
|
|
13764
13960
|
});
|
|
13765
13961
|
if (diff.hasChanges) {
|
|
13766
13962
|
if (result.approved) {
|
|
@@ -13780,7 +13976,7 @@ var reviewCommand = defineCommand({
|
|
|
13780
13976
|
// package.json
|
|
13781
13977
|
var package_default = {
|
|
13782
13978
|
name: "ont-run",
|
|
13783
|
-
version: "0.0.
|
|
13979
|
+
version: "0.0.5",
|
|
13784
13980
|
description: "Ontology-enforced API framework for AI coding agents",
|
|
13785
13981
|
type: "module",
|
|
13786
13982
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -13489,7 +13489,7 @@ var FunctionDefinitionSchema = exports_external.object({
|
|
|
13489
13489
|
outputs: exports_external.custom(isZodSchema, {
|
|
13490
13490
|
message: "outputs must be a Zod schema"
|
|
13491
13491
|
}).optional(),
|
|
13492
|
-
resolver: exports_external.
|
|
13492
|
+
resolver: exports_external.function()
|
|
13493
13493
|
});
|
|
13494
13494
|
var AuthFunctionSchema = exports_external.function().args(exports_external.custom()).returns(exports_external.union([exports_external.array(exports_external.string()), exports_external.promise(exports_external.array(exports_external.string()))]));
|
|
13495
13495
|
var OntologyConfigSchema = exports_external.object({
|
|
@@ -13606,6 +13606,13 @@ function defineOntology(config) {
|
|
|
13606
13606
|
validateFieldFromReferences(parsed);
|
|
13607
13607
|
return config;
|
|
13608
13608
|
}
|
|
13609
|
+
function defineFunction(config) {
|
|
13610
|
+
return {
|
|
13611
|
+
...config,
|
|
13612
|
+
access: [...config.access],
|
|
13613
|
+
entities: [...config.entities]
|
|
13614
|
+
};
|
|
13615
|
+
}
|
|
13609
13616
|
|
|
13610
13617
|
// src/index.ts
|
|
13611
13618
|
init_categorical();
|
|
@@ -16301,39 +16308,6 @@ var Factory = class {
|
|
|
16301
16308
|
var createMiddleware = (middleware) => middleware;
|
|
16302
16309
|
|
|
16303
16310
|
// src/server/resolver.ts
|
|
16304
|
-
import { join as join3, isAbsolute } from "path";
|
|
16305
|
-
import { existsSync as existsSync3 } from "fs";
|
|
16306
|
-
var resolverCache = new Map;
|
|
16307
|
-
async function loadResolver(resolverPath, configDir) {
|
|
16308
|
-
const fullPath = isAbsolute(resolverPath) ? resolverPath : join3(configDir, resolverPath);
|
|
16309
|
-
if (resolverCache.has(fullPath)) {
|
|
16310
|
-
return resolverCache.get(fullPath);
|
|
16311
|
-
}
|
|
16312
|
-
try {
|
|
16313
|
-
const module = await import(fullPath);
|
|
16314
|
-
const resolver = module.default;
|
|
16315
|
-
if (typeof resolver !== "function") {
|
|
16316
|
-
throw new Error(`Resolver at ${resolverPath} must export a default function`);
|
|
16317
|
-
}
|
|
16318
|
-
resolverCache.set(fullPath, resolver);
|
|
16319
|
-
return resolver;
|
|
16320
|
-
} catch (error) {
|
|
16321
|
-
if (error.code === "ERR_MODULE_NOT_FOUND") {
|
|
16322
|
-
throw new Error(`Resolver not found: ${resolverPath}`);
|
|
16323
|
-
}
|
|
16324
|
-
throw error;
|
|
16325
|
-
}
|
|
16326
|
-
}
|
|
16327
|
-
function findMissingResolvers(config, configDir) {
|
|
16328
|
-
const missing = [];
|
|
16329
|
-
for (const [name, fn] of Object.entries(config.functions)) {
|
|
16330
|
-
const fullPath = isAbsolute(fn.resolver) ? fn.resolver : join3(configDir, fn.resolver);
|
|
16331
|
-
if (!existsSync3(fullPath)) {
|
|
16332
|
-
missing.push(fn.resolver);
|
|
16333
|
-
}
|
|
16334
|
-
}
|
|
16335
|
-
return missing;
|
|
16336
|
-
}
|
|
16337
16311
|
function createLogger(debug = false) {
|
|
16338
16312
|
return {
|
|
16339
16313
|
info: (message, ...args) => {
|
|
@@ -16422,7 +16396,7 @@ function errorHandler2() {
|
|
|
16422
16396
|
|
|
16423
16397
|
// src/server/api/router.ts
|
|
16424
16398
|
init_categorical();
|
|
16425
|
-
function createApiRoutes(config
|
|
16399
|
+
function createApiRoutes(config) {
|
|
16426
16400
|
const router = new Hono2;
|
|
16427
16401
|
for (const [name, fn] of Object.entries(config.functions)) {
|
|
16428
16402
|
const path = `/${name}`;
|
|
@@ -16465,8 +16439,7 @@ function createApiRoutes(config, configDir) {
|
|
|
16465
16439
|
args = parsed.data;
|
|
16466
16440
|
}
|
|
16467
16441
|
try {
|
|
16468
|
-
const
|
|
16469
|
-
const result = await resolver(resolverContext, args);
|
|
16442
|
+
const result = await fn.resolver(resolverContext, args);
|
|
16470
16443
|
return c3.json(result);
|
|
16471
16444
|
} catch (error) {
|
|
16472
16445
|
console.error(`Error in resolver ${name}:`, error);
|
|
@@ -16490,18 +16463,11 @@ function getFunctionsInfo(config) {
|
|
|
16490
16463
|
|
|
16491
16464
|
// src/server/api/index.ts
|
|
16492
16465
|
function createApiApp(options) {
|
|
16493
|
-
const { config,
|
|
16466
|
+
const { config, env: env2, cors: enableCors = true } = options;
|
|
16494
16467
|
const envConfig = config.environments[env2];
|
|
16495
16468
|
if (!envConfig) {
|
|
16496
16469
|
throw new Error(`Unknown environment "${env2}". Available: ${Object.keys(config.environments).join(", ")}`);
|
|
16497
16470
|
}
|
|
16498
|
-
const missingResolvers = findMissingResolvers(config, configDir);
|
|
16499
|
-
if (missingResolvers.length > 0) {
|
|
16500
|
-
consola.warn(`Missing resolvers (${missingResolvers.length}):`);
|
|
16501
|
-
for (const resolver of missingResolvers) {
|
|
16502
|
-
consola.warn(` - ${resolver}`);
|
|
16503
|
-
}
|
|
16504
|
-
}
|
|
16505
16471
|
const app = new Hono2;
|
|
16506
16472
|
if (enableCors) {
|
|
16507
16473
|
app.use("*", cors());
|
|
@@ -16527,7 +16493,7 @@ function createApiApp(options) {
|
|
|
16527
16493
|
functions: accessibleFunctions
|
|
16528
16494
|
});
|
|
16529
16495
|
});
|
|
16530
|
-
const apiRoutes = createApiRoutes(config
|
|
16496
|
+
const apiRoutes = createApiRoutes(config);
|
|
16531
16497
|
app.route("/api", apiRoutes);
|
|
16532
16498
|
return app;
|
|
16533
16499
|
}
|
|
@@ -23017,7 +22983,7 @@ function generateMcpTools(config2) {
|
|
|
23017
22983
|
function filterToolsByAccess(tools, accessGroups) {
|
|
23018
22984
|
return tools.filter((tool) => tool.access.some((group) => accessGroups.includes(group)));
|
|
23019
22985
|
}
|
|
23020
|
-
function createToolExecutor(config2,
|
|
22986
|
+
function createToolExecutor(config2, env2, envConfig, logger) {
|
|
23021
22987
|
const userContextFieldsCache = new Map;
|
|
23022
22988
|
for (const [name, fn] of Object.entries(config2.functions)) {
|
|
23023
22989
|
userContextFieldsCache.set(name, getUserContextFields(fn.inputs));
|
|
@@ -23049,8 +23015,7 @@ function createToolExecutor(config2, configDir, env2, envConfig, logger) {
|
|
|
23049
23015
|
logger,
|
|
23050
23016
|
accessGroups: authResult.groups
|
|
23051
23017
|
};
|
|
23052
|
-
|
|
23053
|
-
return resolver(resolverContext, parsed.data);
|
|
23018
|
+
return fn.resolver(resolverContext, parsed.data);
|
|
23054
23019
|
};
|
|
23055
23020
|
}
|
|
23056
23021
|
|
|
@@ -23103,14 +23068,14 @@ function getAuthResult(authInfo) {
|
|
|
23103
23068
|
return authInfo.extra.authResult;
|
|
23104
23069
|
}
|
|
23105
23070
|
function createMcpServer(options) {
|
|
23106
|
-
const { config: config2,
|
|
23071
|
+
const { config: config2, env: env2 } = options;
|
|
23107
23072
|
const envConfig = config2.environments[env2];
|
|
23108
23073
|
if (!envConfig) {
|
|
23109
23074
|
throw new Error(`Unknown environment "${env2}". Available: ${Object.keys(config2.environments).join(", ")}`);
|
|
23110
23075
|
}
|
|
23111
23076
|
const logger = createLogger(envConfig.debug);
|
|
23112
23077
|
const allTools = generateMcpTools(config2);
|
|
23113
|
-
const executeToolWithAccess = createToolExecutor(config2,
|
|
23078
|
+
const executeToolWithAccess = createToolExecutor(config2, env2, envConfig, logger);
|
|
23114
23079
|
const server = new Server({
|
|
23115
23080
|
name: config2.name,
|
|
23116
23081
|
version: "1.0.0"
|
|
@@ -23267,7 +23232,6 @@ Run \`bun run review\` to approve the changes.`;
|
|
|
23267
23232
|
if (!mcpOnly) {
|
|
23268
23233
|
const api2 = createApiApp({
|
|
23269
23234
|
config: config2,
|
|
23270
|
-
configDir,
|
|
23271
23235
|
env: env2
|
|
23272
23236
|
});
|
|
23273
23237
|
const server = await serve2(api2, port);
|
|
@@ -23286,7 +23250,6 @@ Run \`bun run review\` to approve the changes.`;
|
|
|
23286
23250
|
if (!apiOnly) {
|
|
23287
23251
|
const mcpServer = await startMcpServer({
|
|
23288
23252
|
config: config2,
|
|
23289
|
-
configDir,
|
|
23290
23253
|
env: env2,
|
|
23291
23254
|
port: mcpPort
|
|
23292
23255
|
});
|
|
@@ -23305,5 +23268,6 @@ export {
|
|
|
23305
23268
|
userContext,
|
|
23306
23269
|
startOnt,
|
|
23307
23270
|
fieldFrom,
|
|
23308
|
-
defineOntology
|
|
23271
|
+
defineOntology,
|
|
23272
|
+
defineFunction
|
|
23309
23273
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { OntologyConfig, FunctionDefinition, AccessGroupConfig, EnvironmentConfig, EntityDefinition, AuthFunction, ResolverFunction } from "./types.js";
|
|
2
3
|
/**
|
|
3
4
|
* Define an Ontology configuration with full type inference.
|
|
4
5
|
*
|
|
@@ -6,6 +7,7 @@ import type { OntologyConfig, FunctionDefinition, AccessGroupConfig, Environment
|
|
|
6
7
|
* ```ts
|
|
7
8
|
* import { defineOntology, fieldFrom } from 'ont-run';
|
|
8
9
|
* import { z } from 'zod';
|
|
10
|
+
* import { getUser } from './resolvers/getUser.js';
|
|
9
11
|
*
|
|
10
12
|
* export default defineOntology({
|
|
11
13
|
* name: 'my-api',
|
|
@@ -30,7 +32,7 @@ import type { OntologyConfig, FunctionDefinition, AccessGroupConfig, Environment
|
|
|
30
32
|
* access: ['public', 'admin'],
|
|
31
33
|
* entities: ['User'],
|
|
32
34
|
* inputs: z.object({ id: z.string() }),
|
|
33
|
-
* resolver:
|
|
35
|
+
* resolver: getUser, // Direct function reference for type safety
|
|
34
36
|
* },
|
|
35
37
|
* },
|
|
36
38
|
* });
|
|
@@ -44,3 +46,36 @@ export declare function defineOntology<TGroups extends string, TEntities extends
|
|
|
44
46
|
entities?: Record<TEntities, EntityDefinition>;
|
|
45
47
|
functions: TFunctions;
|
|
46
48
|
}): OntologyConfig<TGroups, TEntities, TFunctions>;
|
|
49
|
+
/**
|
|
50
|
+
* Define a function with full type inference for resolver type safety.
|
|
51
|
+
*
|
|
52
|
+
* This helper ensures that the resolver function's return type matches
|
|
53
|
+
* the outputs Zod schema at compile time.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { defineFunction, z } from 'ont-run';
|
|
58
|
+
* import type { ResolverContext } from 'ont-run';
|
|
59
|
+
*
|
|
60
|
+
* const getUser = defineFunction({
|
|
61
|
+
* description: 'Get a user by ID',
|
|
62
|
+
* access: ['public', 'admin'] as const,
|
|
63
|
+
* entities: ['User'] as const,
|
|
64
|
+
* inputs: z.object({ id: z.string() }),
|
|
65
|
+
* outputs: z.object({ id: z.string(), name: z.string() }),
|
|
66
|
+
* resolver: async (ctx, args) => {
|
|
67
|
+
* // TypeScript knows args is { id: string }
|
|
68
|
+
* // TypeScript enforces return type is { id: string, name: string }
|
|
69
|
+
* return { id: args.id, name: 'Example User' };
|
|
70
|
+
* },
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function defineFunction<TGroups extends string, TEntities extends string, TInputs extends z.ZodType, TOutputs extends z.ZodType>(config: {
|
|
75
|
+
description: string;
|
|
76
|
+
access: readonly TGroups[];
|
|
77
|
+
entities: readonly TEntities[];
|
|
78
|
+
inputs: TInputs;
|
|
79
|
+
outputs?: TOutputs;
|
|
80
|
+
resolver: ResolverFunction<z.infer<TInputs>, z.infer<TOutputs>>;
|
|
81
|
+
}): FunctionDefinition<TGroups, TEntities, TInputs, TOutputs>;
|
|
@@ -39,20 +39,20 @@ export declare const FunctionDefinitionSchema: z.ZodObject<{
|
|
|
39
39
|
entities: z.ZodArray<z.ZodString, "many">;
|
|
40
40
|
inputs: z.ZodType<z.ZodType<any, z.ZodTypeDef, any>, z.ZodTypeDef, z.ZodType<any, z.ZodTypeDef, any>>;
|
|
41
41
|
outputs: z.ZodOptional<z.ZodType<z.ZodType<any, z.ZodTypeDef, any>, z.ZodTypeDef, z.ZodType<any, z.ZodTypeDef, any>>>;
|
|
42
|
-
resolver: z.
|
|
42
|
+
resolver: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
|
|
43
43
|
}, "strip", z.ZodTypeAny, {
|
|
44
44
|
description: string;
|
|
45
45
|
access: string[];
|
|
46
46
|
entities: string[];
|
|
47
47
|
inputs: z.ZodType<any, z.ZodTypeDef, any>;
|
|
48
|
-
resolver:
|
|
48
|
+
resolver: (...args: unknown[]) => unknown;
|
|
49
49
|
outputs?: z.ZodType<any, z.ZodTypeDef, any> | undefined;
|
|
50
50
|
}, {
|
|
51
51
|
description: string;
|
|
52
52
|
access: string[];
|
|
53
53
|
entities: string[];
|
|
54
54
|
inputs: z.ZodType<any, z.ZodTypeDef, any>;
|
|
55
|
-
resolver:
|
|
55
|
+
resolver: (...args: unknown[]) => unknown;
|
|
56
56
|
outputs?: z.ZodType<any, z.ZodTypeDef, any> | undefined;
|
|
57
57
|
}>;
|
|
58
58
|
/**
|
|
@@ -92,20 +92,20 @@ export declare const OntologyConfigSchema: z.ZodObject<{
|
|
|
92
92
|
entities: z.ZodArray<z.ZodString, "many">;
|
|
93
93
|
inputs: z.ZodType<z.ZodType<any, z.ZodTypeDef, any>, z.ZodTypeDef, z.ZodType<any, z.ZodTypeDef, any>>;
|
|
94
94
|
outputs: z.ZodOptional<z.ZodType<z.ZodType<any, z.ZodTypeDef, any>, z.ZodTypeDef, z.ZodType<any, z.ZodTypeDef, any>>>;
|
|
95
|
-
resolver: z.
|
|
95
|
+
resolver: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
|
|
96
96
|
}, "strip", z.ZodTypeAny, {
|
|
97
97
|
description: string;
|
|
98
98
|
access: string[];
|
|
99
99
|
entities: string[];
|
|
100
100
|
inputs: z.ZodType<any, z.ZodTypeDef, any>;
|
|
101
|
-
resolver:
|
|
101
|
+
resolver: (...args: unknown[]) => unknown;
|
|
102
102
|
outputs?: z.ZodType<any, z.ZodTypeDef, any> | undefined;
|
|
103
103
|
}, {
|
|
104
104
|
description: string;
|
|
105
105
|
access: string[];
|
|
106
106
|
entities: string[];
|
|
107
107
|
inputs: z.ZodType<any, z.ZodTypeDef, any>;
|
|
108
|
-
resolver:
|
|
108
|
+
resolver: (...args: unknown[]) => unknown;
|
|
109
109
|
outputs?: z.ZodType<any, z.ZodTypeDef, any> | undefined;
|
|
110
110
|
}>>;
|
|
111
111
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -122,7 +122,7 @@ export declare const OntologyConfigSchema: z.ZodObject<{
|
|
|
122
122
|
access: string[];
|
|
123
123
|
entities: string[];
|
|
124
124
|
inputs: z.ZodType<any, z.ZodTypeDef, any>;
|
|
125
|
-
resolver:
|
|
125
|
+
resolver: (...args: unknown[]) => unknown;
|
|
126
126
|
outputs?: z.ZodType<any, z.ZodTypeDef, any> | undefined;
|
|
127
127
|
}>;
|
|
128
128
|
entities?: Record<string, {
|
|
@@ -142,7 +142,7 @@ export declare const OntologyConfigSchema: z.ZodObject<{
|
|
|
142
142
|
access: string[];
|
|
143
143
|
entities: string[];
|
|
144
144
|
inputs: z.ZodType<any, z.ZodTypeDef, any>;
|
|
145
|
-
resolver:
|
|
145
|
+
resolver: (...args: unknown[]) => unknown;
|
|
146
146
|
outputs?: z.ZodType<any, z.ZodTypeDef, any> | undefined;
|
|
147
147
|
}>;
|
|
148
148
|
entities?: Record<string, {
|