ont-run 0.0.3 → 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 +77 -6
- package/dist/bin/ont.js +303 -10
- package/dist/index.js +21 -56
- package/dist/src/browser/server.d.ts +2 -0
- package/dist/src/browser/transform.d.ts +1 -1
- package/dist/src/config/categorical.d.ts +3 -0
- 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 +234 -2
- package/src/browser/transform.ts +6 -2
- package/src/cli/commands/review.ts +2 -1
- package/src/cli/index.ts +2 -1
- package/src/config/categorical.ts +7 -2
- 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',
|
|
@@ -150,11 +153,16 @@ export default defineOntology({
|
|
|
150
153
|
prod: { debug: false },
|
|
151
154
|
},
|
|
152
155
|
|
|
156
|
+
// Auth returns access groups (and optional user identity)
|
|
153
157
|
auth: async (req: Request) => {
|
|
154
158
|
const token = req.headers.get('Authorization');
|
|
155
|
-
if (!token) return ['public'];
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
if (!token) return { groups: ['public'] };
|
|
160
|
+
|
|
161
|
+
const user = await verifyToken(token);
|
|
162
|
+
return {
|
|
163
|
+
groups: user.isAdmin ? ['admin', 'user', 'public'] : ['user', 'public'],
|
|
164
|
+
user: { id: user.id, email: user.email }, // Optional: for row-level access
|
|
165
|
+
};
|
|
158
166
|
},
|
|
159
167
|
|
|
160
168
|
accessGroups: {
|
|
@@ -174,12 +182,75 @@ export default defineOntology({
|
|
|
174
182
|
access: ['user', 'admin'],
|
|
175
183
|
entities: ['User'],
|
|
176
184
|
inputs: z.object({ userId: z.string().uuid() }),
|
|
177
|
-
resolver:
|
|
185
|
+
resolver: getUser,
|
|
178
186
|
},
|
|
179
187
|
},
|
|
180
188
|
});
|
|
181
189
|
```
|
|
182
190
|
|
|
191
|
+
## Row-Level Access Control
|
|
192
|
+
|
|
193
|
+
The framework handles **group-based access** (user → group → function) out of the box. For **row-level ownership** (e.g., "users can only edit their own posts"), use `userContext()`:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { defineOntology, userContext, z } from 'ont-run';
|
|
197
|
+
import editPost from './resolvers/editPost.js';
|
|
198
|
+
|
|
199
|
+
export default defineOntology({
|
|
200
|
+
// Auth must return user identity for userContext to work
|
|
201
|
+
auth: async (req) => {
|
|
202
|
+
const user = await verifyToken(req);
|
|
203
|
+
return {
|
|
204
|
+
groups: ['user'],
|
|
205
|
+
user: { id: user.id, email: user.email },
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
functions: {
|
|
210
|
+
editPost: {
|
|
211
|
+
description: 'Edit a post',
|
|
212
|
+
access: ['user', 'admin'],
|
|
213
|
+
entities: ['Post'],
|
|
214
|
+
inputs: z.object({
|
|
215
|
+
postId: z.string(),
|
|
216
|
+
title: z.string(),
|
|
217
|
+
// currentUser is injected at runtime, hidden from API callers
|
|
218
|
+
currentUser: userContext(z.object({
|
|
219
|
+
id: z.string(),
|
|
220
|
+
email: z.string(),
|
|
221
|
+
})),
|
|
222
|
+
}),
|
|
223
|
+
resolver: editPost,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
In the resolver, you receive the typed user object:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// resolvers/editPost.ts
|
|
233
|
+
export default async function editPost(
|
|
234
|
+
ctx: ResolverContext,
|
|
235
|
+
args: { postId: string; title: string; currentUser: { id: string; email: string } }
|
|
236
|
+
) {
|
|
237
|
+
const post = await db.posts.findById(args.postId);
|
|
238
|
+
|
|
239
|
+
// Row-level check: only author or admin can edit
|
|
240
|
+
if (args.currentUser.id !== post.authorId && !ctx.accessGroups.includes('admin')) {
|
|
241
|
+
throw new Error('Not authorized to edit this post');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return db.posts.update(args.postId, { title: args.title });
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Key points:**
|
|
249
|
+
- `userContext()` fields are **injected** from `auth()` result's `user` field
|
|
250
|
+
- They're **hidden** from public API/MCP schemas (callers don't see or provide them)
|
|
251
|
+
- They're **type-safe** in resolvers
|
|
252
|
+
- The review UI shows a badge for functions using user context
|
|
253
|
+
|
|
183
254
|
## The Lockfile
|
|
184
255
|
|
|
185
256
|
`ont.lock` is the enforcement mechanism. It contains a hash of your ontology:
|
package/dist/bin/ont.js
CHANGED
|
@@ -6170,8 +6170,9 @@ function hasUserContextMetadata(schema) {
|
|
|
6170
6170
|
}
|
|
6171
6171
|
function getUserContextFields(schema) {
|
|
6172
6172
|
const fields = [];
|
|
6173
|
-
|
|
6174
|
-
|
|
6173
|
+
const def = schema._def;
|
|
6174
|
+
if (def?.typeName === "ZodObject" && typeof def.shape === "function") {
|
|
6175
|
+
const shape = def.shape();
|
|
6175
6176
|
for (const [key, value] of Object.entries(shape)) {
|
|
6176
6177
|
if (hasUserContextMetadata(value)) {
|
|
6177
6178
|
fields.push(key);
|
|
@@ -6182,7 +6183,6 @@ function getUserContextFields(schema) {
|
|
|
6182
6183
|
}
|
|
6183
6184
|
var FIELD_FROM_METADATA, USER_CONTEXT_METADATA;
|
|
6184
6185
|
var init_categorical = __esm(() => {
|
|
6185
|
-
init_zod();
|
|
6186
6186
|
FIELD_FROM_METADATA = Symbol.for("ont:fieldFrom");
|
|
6187
6187
|
USER_CONTEXT_METADATA = Symbol.for("ont:userContext");
|
|
6188
6188
|
});
|
|
@@ -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";
|
|
@@ -10840,6 +10844,7 @@ function transformToGraphData(config) {
|
|
|
10840
10844
|
const edges = [];
|
|
10841
10845
|
const accessGroupCounts = {};
|
|
10842
10846
|
const entityCounts = {};
|
|
10847
|
+
let userContextFunctionCount = 0;
|
|
10843
10848
|
for (const groupName of Object.keys(config.accessGroups)) {
|
|
10844
10849
|
accessGroupCounts[groupName] = 0;
|
|
10845
10850
|
}
|
|
@@ -10857,6 +10862,9 @@ function transformToGraphData(config) {
|
|
|
10857
10862
|
}
|
|
10858
10863
|
const userContextFields = getUserContextFields(fn.inputs);
|
|
10859
10864
|
const usesUserContext = userContextFields.length > 0;
|
|
10865
|
+
if (usesUserContext) {
|
|
10866
|
+
userContextFunctionCount++;
|
|
10867
|
+
}
|
|
10860
10868
|
nodes.push({
|
|
10861
10869
|
id: `function:${name}`,
|
|
10862
10870
|
type: "function",
|
|
@@ -10865,7 +10873,6 @@ function transformToGraphData(config) {
|
|
|
10865
10873
|
metadata: {
|
|
10866
10874
|
inputs: safeZodToJsonSchema(fn.inputs),
|
|
10867
10875
|
outputs: fn.outputs ? safeZodToJsonSchema(fn.outputs) : undefined,
|
|
10868
|
-
resolver: fn.resolver,
|
|
10869
10876
|
usesUserContext: usesUserContext || undefined
|
|
10870
10877
|
}
|
|
10871
10878
|
});
|
|
@@ -10927,7 +10934,8 @@ function transformToGraphData(config) {
|
|
|
10927
10934
|
ontologyName: config.name,
|
|
10928
10935
|
totalFunctions: Object.keys(config.functions).length,
|
|
10929
10936
|
totalEntities: config.entities ? Object.keys(config.entities).length : 0,
|
|
10930
|
-
totalAccessGroups: Object.keys(config.accessGroups).length
|
|
10937
|
+
totalAccessGroups: Object.keys(config.accessGroups).length,
|
|
10938
|
+
totalUserContextFunctions: userContextFunctionCount
|
|
10931
10939
|
}
|
|
10932
10940
|
};
|
|
10933
10941
|
}
|
|
@@ -11098,7 +11106,7 @@ function enhanceWithDiff(graphData, diff) {
|
|
|
11098
11106
|
|
|
11099
11107
|
// src/browser/server.ts
|
|
11100
11108
|
async function startBrowserServer(options) {
|
|
11101
|
-
const { config, diff = null, configDir, port: preferredPort, openBrowser = true } = options;
|
|
11109
|
+
const { config, diff = null, configDir, configPath, port: preferredPort, openBrowser = true } = options;
|
|
11102
11110
|
const baseGraphData = transformToGraphData(config);
|
|
11103
11111
|
const graphData = enhanceWithDiff(baseGraphData, diff);
|
|
11104
11112
|
return new Promise(async (resolve2) => {
|
|
@@ -11145,6 +11153,21 @@ async function startBrowserServer(options) {
|
|
|
11145
11153
|
}, 500);
|
|
11146
11154
|
return c3.json({ success: true });
|
|
11147
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
|
+
});
|
|
11148
11171
|
app.get("/", (c3) => c3.html(generateBrowserUI(graphData)));
|
|
11149
11172
|
const port = preferredPort || await findAvailablePort(3457);
|
|
11150
11173
|
const server = await serve2(app, port);
|
|
@@ -11173,6 +11196,9 @@ Ontology ${hasChanges ? "Review" : "Browser"} available at: ${url}`);
|
|
|
11173
11196
|
});
|
|
11174
11197
|
}
|
|
11175
11198
|
function generateBrowserUI(graphData) {
|
|
11199
|
+
const userContextFilterBtn = graphData.meta.totalUserContextFunctions > 0 ? `<button class="filter-btn" data-filter="userContext" title="Functions using userContext()">
|
|
11200
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;vertical-align:middle;margin-right:4px;"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>User Context (${graphData.meta.totalUserContextFunctions})
|
|
11201
|
+
</button>` : "";
|
|
11176
11202
|
return `<!DOCTYPE html>
|
|
11177
11203
|
<html lang="en">
|
|
11178
11204
|
<head>
|
|
@@ -11750,6 +11776,89 @@ function generateBrowserUI(graphData) {
|
|
|
11750
11776
|
color: var(--change-added);
|
|
11751
11777
|
}
|
|
11752
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
|
+
|
|
11753
11862
|
/* No Changes State */
|
|
11754
11863
|
.no-changes {
|
|
11755
11864
|
text-align: center;
|
|
@@ -12478,6 +12587,7 @@ function generateBrowserUI(graphData) {
|
|
|
12478
12587
|
<div class="view-tabs">
|
|
12479
12588
|
<button class="view-tab active" data-view="graph">Graph</button>
|
|
12480
12589
|
<button class="view-tab" data-view="table">Table</button>
|
|
12590
|
+
<button class="view-tab" data-view="source">Source</button>
|
|
12481
12591
|
</div>
|
|
12482
12592
|
|
|
12483
12593
|
<div class="filter-buttons" id="graphFilters">
|
|
@@ -12491,6 +12601,7 @@ function generateBrowserUI(graphData) {
|
|
|
12491
12601
|
<button class="filter-btn" data-filter="accessGroup">
|
|
12492
12602
|
<span class="dot access"></span> Access
|
|
12493
12603
|
</button>
|
|
12604
|
+
${userContextFilterBtn}
|
|
12494
12605
|
</div>
|
|
12495
12606
|
|
|
12496
12607
|
<div class="layout-selector">
|
|
@@ -12597,6 +12708,20 @@ function generateBrowserUI(graphData) {
|
|
|
12597
12708
|
<div class="table-view" id="tableView">
|
|
12598
12709
|
<div id="tableContent"></div>
|
|
12599
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>
|
|
12600
12725
|
</div>
|
|
12601
12726
|
|
|
12602
12727
|
<!-- Review Footer -->
|
|
@@ -12635,6 +12760,7 @@ function generateBrowserUI(graphData) {
|
|
|
12635
12760
|
metadata: node.metadata,
|
|
12636
12761
|
changeStatus: node.changeStatus || 'unchanged',
|
|
12637
12762
|
changeDetails: node.changeDetails || null,
|
|
12763
|
+
usesUserContext: node.metadata?.usesUserContext || false,
|
|
12638
12764
|
},
|
|
12639
12765
|
});
|
|
12640
12766
|
}
|
|
@@ -12687,6 +12813,19 @@ function generateBrowserUI(graphData) {
|
|
|
12687
12813
|
'height': 55,
|
|
12688
12814
|
},
|
|
12689
12815
|
},
|
|
12816
|
+
// Function nodes with userContext - show indicator below label
|
|
12817
|
+
{
|
|
12818
|
+
selector: 'node[type="function"][?usesUserContext]',
|
|
12819
|
+
style: {
|
|
12820
|
+
'background-image': 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><circle cx="16" cy="16" r="14" fill="#e8f4f8" stroke="#023d60" stroke-width="1.5"/><g transform="translate(4, 4)" fill="none" stroke="#023d60" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></g></svg>'),
|
|
12821
|
+
'background-width': '18px',
|
|
12822
|
+
'background-height': '18px',
|
|
12823
|
+
'background-position-x': '50%',
|
|
12824
|
+
'background-position-y': '75%',
|
|
12825
|
+
'text-valign': 'center',
|
|
12826
|
+
'text-margin-y': -8,
|
|
12827
|
+
},
|
|
12828
|
+
},
|
|
12690
12829
|
// Entity nodes - Teal
|
|
12691
12830
|
{
|
|
12692
12831
|
selector: 'node[type="entity"]',
|
|
@@ -13228,6 +13367,16 @@ function generateBrowserUI(graphData) {
|
|
|
13228
13367
|
if (filter === 'all') {
|
|
13229
13368
|
cy.nodes().removeClass('hidden');
|
|
13230
13369
|
cy.edges().removeClass('hidden');
|
|
13370
|
+
} else if (filter === 'userContext') {
|
|
13371
|
+
// Special filter: show only functions with userContext
|
|
13372
|
+
cy.nodes().forEach(node => {
|
|
13373
|
+
if (node.data('type') === 'function' && node.data('usesUserContext')) {
|
|
13374
|
+
node.removeClass('hidden');
|
|
13375
|
+
} else {
|
|
13376
|
+
node.addClass('hidden');
|
|
13377
|
+
}
|
|
13378
|
+
});
|
|
13379
|
+
cy.edges().addClass('hidden');
|
|
13231
13380
|
} else {
|
|
13232
13381
|
cy.nodes().forEach(node => {
|
|
13233
13382
|
if (node.data('type') === filter) {
|
|
@@ -13449,6 +13598,7 @@ function generateBrowserUI(graphData) {
|
|
|
13449
13598
|
const graphContainer = document.querySelector('.graph-container');
|
|
13450
13599
|
const detailPanel = document.getElementById('detailPanel');
|
|
13451
13600
|
const tableView = document.getElementById('tableView');
|
|
13601
|
+
const sourceView = document.getElementById('sourceView');
|
|
13452
13602
|
const graphFilters = document.getElementById('graphFilters');
|
|
13453
13603
|
const layoutSelector = document.querySelector('.layout-selector');
|
|
13454
13604
|
|
|
@@ -13456,15 +13606,25 @@ function generateBrowserUI(graphData) {
|
|
|
13456
13606
|
graphContainer.style.display = 'block';
|
|
13457
13607
|
detailPanel.style.display = 'block';
|
|
13458
13608
|
tableView.classList.remove('active');
|
|
13609
|
+
sourceView.classList.remove('active');
|
|
13459
13610
|
if (graphFilters) graphFilters.style.display = 'flex';
|
|
13460
13611
|
if (layoutSelector) layoutSelector.style.display = 'flex';
|
|
13461
|
-
} else {
|
|
13612
|
+
} else if (view === 'table') {
|
|
13462
13613
|
graphContainer.style.display = 'none';
|
|
13463
13614
|
detailPanel.style.display = 'none';
|
|
13464
13615
|
tableView.classList.add('active');
|
|
13616
|
+
sourceView.classList.remove('active');
|
|
13465
13617
|
if (graphFilters) graphFilters.style.display = 'none';
|
|
13466
13618
|
if (layoutSelector) layoutSelector.style.display = 'none';
|
|
13467
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();
|
|
13468
13628
|
}
|
|
13469
13629
|
}
|
|
13470
13630
|
|
|
@@ -13506,6 +13666,74 @@ function generateBrowserUI(graphData) {
|
|
|
13506
13666
|
});
|
|
13507
13667
|
}
|
|
13508
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
|
+
|
|
13509
13737
|
function renderTableSection(title, items, type) {
|
|
13510
13738
|
const changedCount = items.filter(n => n.changeStatus !== 'unchanged').length;
|
|
13511
13739
|
|
|
@@ -13679,7 +13907,7 @@ var reviewCommand = defineCommand({
|
|
|
13679
13907
|
process.exit(0);
|
|
13680
13908
|
}
|
|
13681
13909
|
consola.info("Loading ontology config...");
|
|
13682
|
-
const { config, configDir } = await loadConfig();
|
|
13910
|
+
const { config, configDir, configPath } = await loadConfig();
|
|
13683
13911
|
const { ontology: newOntology, hash: newHash } = computeOntologyHash(config);
|
|
13684
13912
|
const lockfile = await readLockfile(configDir);
|
|
13685
13913
|
const oldOntology = lockfile?.ontology || null;
|
|
@@ -13727,7 +13955,8 @@ var reviewCommand = defineCommand({
|
|
|
13727
13955
|
const result = await startBrowserServer({
|
|
13728
13956
|
config,
|
|
13729
13957
|
diff: diff.hasChanges ? diff : null,
|
|
13730
|
-
configDir
|
|
13958
|
+
configDir,
|
|
13959
|
+
configPath
|
|
13731
13960
|
});
|
|
13732
13961
|
if (diff.hasChanges) {
|
|
13733
13962
|
if (result.approved) {
|
|
@@ -13744,13 +13973,77 @@ var reviewCommand = defineCommand({
|
|
|
13744
13973
|
}
|
|
13745
13974
|
}
|
|
13746
13975
|
});
|
|
13976
|
+
// package.json
|
|
13977
|
+
var package_default = {
|
|
13978
|
+
name: "ont-run",
|
|
13979
|
+
version: "0.0.5",
|
|
13980
|
+
description: "Ontology-enforced API framework for AI coding agents",
|
|
13981
|
+
type: "module",
|
|
13982
|
+
bin: {
|
|
13983
|
+
"ont-run": "./dist/bin/ont.js"
|
|
13984
|
+
},
|
|
13985
|
+
exports: {
|
|
13986
|
+
".": {
|
|
13987
|
+
types: "./dist/src/index.d.ts",
|
|
13988
|
+
bun: "./src/index.ts",
|
|
13989
|
+
default: "./dist/index.js"
|
|
13990
|
+
}
|
|
13991
|
+
},
|
|
13992
|
+
files: [
|
|
13993
|
+
"dist",
|
|
13994
|
+
"src",
|
|
13995
|
+
"bin"
|
|
13996
|
+
],
|
|
13997
|
+
scripts: {
|
|
13998
|
+
dev: "bun run bin/ont.ts",
|
|
13999
|
+
build: "bun build ./src/index.ts --outdir ./dist --target node",
|
|
14000
|
+
"build:cli": "bun build ./bin/ont.ts --outfile ./dist/bin/ont.js --target node --external @modelcontextprotocol/sdk",
|
|
14001
|
+
"build:types": "tsc --declaration --emitDeclarationOnly",
|
|
14002
|
+
prepublishOnly: "bun run build && bun run build:cli && bun run build:types",
|
|
14003
|
+
docs: "cd docs && bun run dev",
|
|
14004
|
+
"docs:build": "cd docs && bun run build",
|
|
14005
|
+
"docs:preview": "cd docs && bun run preview"
|
|
14006
|
+
},
|
|
14007
|
+
dependencies: {
|
|
14008
|
+
"@hono/node-server": "^1.19.8",
|
|
14009
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
14010
|
+
citty: "^0.1.6",
|
|
14011
|
+
consola: "^3.2.0",
|
|
14012
|
+
hono: "^4.6.0",
|
|
14013
|
+
open: "^10.0.0",
|
|
14014
|
+
zod: "^3.24.0",
|
|
14015
|
+
"zod-to-json-schema": "^3.23.0"
|
|
14016
|
+
},
|
|
14017
|
+
devDependencies: {
|
|
14018
|
+
"@types/bun": "latest",
|
|
14019
|
+
typescript: "^5.5.0"
|
|
14020
|
+
},
|
|
14021
|
+
peerDependencies: {
|
|
14022
|
+
bun: ">=1.0.0"
|
|
14023
|
+
},
|
|
14024
|
+
peerDependenciesMeta: {
|
|
14025
|
+
bun: {
|
|
14026
|
+
optional: true
|
|
14027
|
+
}
|
|
14028
|
+
},
|
|
14029
|
+
keywords: [
|
|
14030
|
+
"api",
|
|
14031
|
+
"framework",
|
|
14032
|
+
"access-control",
|
|
14033
|
+
"ai-agents",
|
|
14034
|
+
"hono",
|
|
14035
|
+
"zod",
|
|
14036
|
+
"typescript"
|
|
14037
|
+
],
|
|
14038
|
+
license: "MIT"
|
|
14039
|
+
};
|
|
13747
14040
|
|
|
13748
14041
|
// src/cli/index.ts
|
|
13749
14042
|
var main = defineCommand({
|
|
13750
14043
|
meta: {
|
|
13751
14044
|
name: "ont",
|
|
13752
14045
|
description: "Ontology - Ontology-first backends with human-approved AI access & edits",
|
|
13753
|
-
version:
|
|
14046
|
+
version: package_default.version
|
|
13754
14047
|
},
|
|
13755
14048
|
subCommands: {
|
|
13756
14049
|
init: initCommand,
|