ont-run 0.0.3 → 0.0.4
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 +70 -3
- package/dist/bin/ont.js +102 -5
- package/dist/index.js +3 -2
- package/dist/src/browser/transform.d.ts +1 -0
- package/dist/src/config/categorical.d.ts +3 -0
- package/package.json +1 -1
- package/src/browser/server.ts +31 -0
- package/src/browser/transform.ts +6 -0
- package/src/cli/index.ts +2 -1
- package/src/config/categorical.ts +7 -2
package/README.md
CHANGED
|
@@ -150,11 +150,16 @@ export default defineOntology({
|
|
|
150
150
|
prod: { debug: false },
|
|
151
151
|
},
|
|
152
152
|
|
|
153
|
+
// Auth returns access groups (and optional user identity)
|
|
153
154
|
auth: async (req: Request) => {
|
|
154
155
|
const token = req.headers.get('Authorization');
|
|
155
|
-
if (!token) return ['public'];
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
if (!token) return { groups: ['public'] };
|
|
157
|
+
|
|
158
|
+
const user = await verifyToken(token);
|
|
159
|
+
return {
|
|
160
|
+
groups: user.isAdmin ? ['admin', 'user', 'public'] : ['user', 'public'],
|
|
161
|
+
user: { id: user.id, email: user.email }, // Optional: for row-level access
|
|
162
|
+
};
|
|
158
163
|
},
|
|
159
164
|
|
|
160
165
|
accessGroups: {
|
|
@@ -180,6 +185,68 @@ export default defineOntology({
|
|
|
180
185
|
});
|
|
181
186
|
```
|
|
182
187
|
|
|
188
|
+
## Row-Level Access Control
|
|
189
|
+
|
|
190
|
+
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()`:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { defineOntology, userContext, z } from 'ont-run';
|
|
194
|
+
|
|
195
|
+
export default defineOntology({
|
|
196
|
+
// Auth must return user identity for userContext to work
|
|
197
|
+
auth: async (req) => {
|
|
198
|
+
const user = await verifyToken(req);
|
|
199
|
+
return {
|
|
200
|
+
groups: ['user'],
|
|
201
|
+
user: { id: user.id, email: user.email },
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
functions: {
|
|
206
|
+
editPost: {
|
|
207
|
+
description: 'Edit a post',
|
|
208
|
+
access: ['user', 'admin'],
|
|
209
|
+
entities: ['Post'],
|
|
210
|
+
inputs: z.object({
|
|
211
|
+
postId: z.string(),
|
|
212
|
+
title: z.string(),
|
|
213
|
+
// currentUser is injected at runtime, hidden from API callers
|
|
214
|
+
currentUser: userContext(z.object({
|
|
215
|
+
id: z.string(),
|
|
216
|
+
email: z.string(),
|
|
217
|
+
})),
|
|
218
|
+
}),
|
|
219
|
+
resolver: './resolvers/editPost.ts',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
In the resolver, you receive the typed user object:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// resolvers/editPost.ts
|
|
229
|
+
export default async function editPost(
|
|
230
|
+
ctx: ResolverContext,
|
|
231
|
+
args: { postId: string; title: string; currentUser: { id: string; email: string } }
|
|
232
|
+
) {
|
|
233
|
+
const post = await db.posts.findById(args.postId);
|
|
234
|
+
|
|
235
|
+
// Row-level check: only author or admin can edit
|
|
236
|
+
if (args.currentUser.id !== post.authorId && !ctx.accessGroups.includes('admin')) {
|
|
237
|
+
throw new Error('Not authorized to edit this post');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return db.posts.update(args.postId, { title: args.title });
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Key points:**
|
|
245
|
+
- `userContext()` fields are **injected** from `auth()` result's `user` field
|
|
246
|
+
- They're **hidden** from public API/MCP schemas (callers don't see or provide them)
|
|
247
|
+
- They're **type-safe** in resolvers
|
|
248
|
+
- The review UI shows a badge for functions using user context
|
|
249
|
+
|
|
183
250
|
## The Lockfile
|
|
184
251
|
|
|
185
252
|
`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
|
});
|
|
@@ -10840,6 +10840,7 @@ function transformToGraphData(config) {
|
|
|
10840
10840
|
const edges = [];
|
|
10841
10841
|
const accessGroupCounts = {};
|
|
10842
10842
|
const entityCounts = {};
|
|
10843
|
+
let userContextFunctionCount = 0;
|
|
10843
10844
|
for (const groupName of Object.keys(config.accessGroups)) {
|
|
10844
10845
|
accessGroupCounts[groupName] = 0;
|
|
10845
10846
|
}
|
|
@@ -10857,6 +10858,9 @@ function transformToGraphData(config) {
|
|
|
10857
10858
|
}
|
|
10858
10859
|
const userContextFields = getUserContextFields(fn.inputs);
|
|
10859
10860
|
const usesUserContext = userContextFields.length > 0;
|
|
10861
|
+
if (usesUserContext) {
|
|
10862
|
+
userContextFunctionCount++;
|
|
10863
|
+
}
|
|
10860
10864
|
nodes.push({
|
|
10861
10865
|
id: `function:${name}`,
|
|
10862
10866
|
type: "function",
|
|
@@ -10927,7 +10931,8 @@ function transformToGraphData(config) {
|
|
|
10927
10931
|
ontologyName: config.name,
|
|
10928
10932
|
totalFunctions: Object.keys(config.functions).length,
|
|
10929
10933
|
totalEntities: config.entities ? Object.keys(config.entities).length : 0,
|
|
10930
|
-
totalAccessGroups: Object.keys(config.accessGroups).length
|
|
10934
|
+
totalAccessGroups: Object.keys(config.accessGroups).length,
|
|
10935
|
+
totalUserContextFunctions: userContextFunctionCount
|
|
10931
10936
|
}
|
|
10932
10937
|
};
|
|
10933
10938
|
}
|
|
@@ -11173,6 +11178,9 @@ Ontology ${hasChanges ? "Review" : "Browser"} available at: ${url}`);
|
|
|
11173
11178
|
});
|
|
11174
11179
|
}
|
|
11175
11180
|
function generateBrowserUI(graphData) {
|
|
11181
|
+
const userContextFilterBtn = graphData.meta.totalUserContextFunctions > 0 ? `<button class="filter-btn" data-filter="userContext" title="Functions using userContext()">
|
|
11182
|
+
<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})
|
|
11183
|
+
</button>` : "";
|
|
11176
11184
|
return `<!DOCTYPE html>
|
|
11177
11185
|
<html lang="en">
|
|
11178
11186
|
<head>
|
|
@@ -12491,6 +12499,7 @@ function generateBrowserUI(graphData) {
|
|
|
12491
12499
|
<button class="filter-btn" data-filter="accessGroup">
|
|
12492
12500
|
<span class="dot access"></span> Access
|
|
12493
12501
|
</button>
|
|
12502
|
+
${userContextFilterBtn}
|
|
12494
12503
|
</div>
|
|
12495
12504
|
|
|
12496
12505
|
<div class="layout-selector">
|
|
@@ -12635,6 +12644,7 @@ function generateBrowserUI(graphData) {
|
|
|
12635
12644
|
metadata: node.metadata,
|
|
12636
12645
|
changeStatus: node.changeStatus || 'unchanged',
|
|
12637
12646
|
changeDetails: node.changeDetails || null,
|
|
12647
|
+
usesUserContext: node.metadata?.usesUserContext || false,
|
|
12638
12648
|
},
|
|
12639
12649
|
});
|
|
12640
12650
|
}
|
|
@@ -12687,6 +12697,19 @@ function generateBrowserUI(graphData) {
|
|
|
12687
12697
|
'height': 55,
|
|
12688
12698
|
},
|
|
12689
12699
|
},
|
|
12700
|
+
// Function nodes with userContext - show indicator below label
|
|
12701
|
+
{
|
|
12702
|
+
selector: 'node[type="function"][?usesUserContext]',
|
|
12703
|
+
style: {
|
|
12704
|
+
'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>'),
|
|
12705
|
+
'background-width': '18px',
|
|
12706
|
+
'background-height': '18px',
|
|
12707
|
+
'background-position-x': '50%',
|
|
12708
|
+
'background-position-y': '75%',
|
|
12709
|
+
'text-valign': 'center',
|
|
12710
|
+
'text-margin-y': -8,
|
|
12711
|
+
},
|
|
12712
|
+
},
|
|
12690
12713
|
// Entity nodes - Teal
|
|
12691
12714
|
{
|
|
12692
12715
|
selector: 'node[type="entity"]',
|
|
@@ -13228,6 +13251,16 @@ function generateBrowserUI(graphData) {
|
|
|
13228
13251
|
if (filter === 'all') {
|
|
13229
13252
|
cy.nodes().removeClass('hidden');
|
|
13230
13253
|
cy.edges().removeClass('hidden');
|
|
13254
|
+
} else if (filter === 'userContext') {
|
|
13255
|
+
// Special filter: show only functions with userContext
|
|
13256
|
+
cy.nodes().forEach(node => {
|
|
13257
|
+
if (node.data('type') === 'function' && node.data('usesUserContext')) {
|
|
13258
|
+
node.removeClass('hidden');
|
|
13259
|
+
} else {
|
|
13260
|
+
node.addClass('hidden');
|
|
13261
|
+
}
|
|
13262
|
+
});
|
|
13263
|
+
cy.edges().addClass('hidden');
|
|
13231
13264
|
} else {
|
|
13232
13265
|
cy.nodes().forEach(node => {
|
|
13233
13266
|
if (node.data('type') === filter) {
|
|
@@ -13744,13 +13777,77 @@ var reviewCommand = defineCommand({
|
|
|
13744
13777
|
}
|
|
13745
13778
|
}
|
|
13746
13779
|
});
|
|
13780
|
+
// package.json
|
|
13781
|
+
var package_default = {
|
|
13782
|
+
name: "ont-run",
|
|
13783
|
+
version: "0.0.4",
|
|
13784
|
+
description: "Ontology-enforced API framework for AI coding agents",
|
|
13785
|
+
type: "module",
|
|
13786
|
+
bin: {
|
|
13787
|
+
"ont-run": "./dist/bin/ont.js"
|
|
13788
|
+
},
|
|
13789
|
+
exports: {
|
|
13790
|
+
".": {
|
|
13791
|
+
types: "./dist/src/index.d.ts",
|
|
13792
|
+
bun: "./src/index.ts",
|
|
13793
|
+
default: "./dist/index.js"
|
|
13794
|
+
}
|
|
13795
|
+
},
|
|
13796
|
+
files: [
|
|
13797
|
+
"dist",
|
|
13798
|
+
"src",
|
|
13799
|
+
"bin"
|
|
13800
|
+
],
|
|
13801
|
+
scripts: {
|
|
13802
|
+
dev: "bun run bin/ont.ts",
|
|
13803
|
+
build: "bun build ./src/index.ts --outdir ./dist --target node",
|
|
13804
|
+
"build:cli": "bun build ./bin/ont.ts --outfile ./dist/bin/ont.js --target node --external @modelcontextprotocol/sdk",
|
|
13805
|
+
"build:types": "tsc --declaration --emitDeclarationOnly",
|
|
13806
|
+
prepublishOnly: "bun run build && bun run build:cli && bun run build:types",
|
|
13807
|
+
docs: "cd docs && bun run dev",
|
|
13808
|
+
"docs:build": "cd docs && bun run build",
|
|
13809
|
+
"docs:preview": "cd docs && bun run preview"
|
|
13810
|
+
},
|
|
13811
|
+
dependencies: {
|
|
13812
|
+
"@hono/node-server": "^1.19.8",
|
|
13813
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
13814
|
+
citty: "^0.1.6",
|
|
13815
|
+
consola: "^3.2.0",
|
|
13816
|
+
hono: "^4.6.0",
|
|
13817
|
+
open: "^10.0.0",
|
|
13818
|
+
zod: "^3.24.0",
|
|
13819
|
+
"zod-to-json-schema": "^3.23.0"
|
|
13820
|
+
},
|
|
13821
|
+
devDependencies: {
|
|
13822
|
+
"@types/bun": "latest",
|
|
13823
|
+
typescript: "^5.5.0"
|
|
13824
|
+
},
|
|
13825
|
+
peerDependencies: {
|
|
13826
|
+
bun: ">=1.0.0"
|
|
13827
|
+
},
|
|
13828
|
+
peerDependenciesMeta: {
|
|
13829
|
+
bun: {
|
|
13830
|
+
optional: true
|
|
13831
|
+
}
|
|
13832
|
+
},
|
|
13833
|
+
keywords: [
|
|
13834
|
+
"api",
|
|
13835
|
+
"framework",
|
|
13836
|
+
"access-control",
|
|
13837
|
+
"ai-agents",
|
|
13838
|
+
"hono",
|
|
13839
|
+
"zod",
|
|
13840
|
+
"typescript"
|
|
13841
|
+
],
|
|
13842
|
+
license: "MIT"
|
|
13843
|
+
};
|
|
13747
13844
|
|
|
13748
13845
|
// src/cli/index.ts
|
|
13749
13846
|
var main = defineCommand({
|
|
13750
13847
|
meta: {
|
|
13751
13848
|
name: "ont",
|
|
13752
13849
|
description: "Ontology - Ontology-first backends with human-approved AI access & edits",
|
|
13753
|
-
version:
|
|
13850
|
+
version: package_default.version
|
|
13754
13851
|
},
|
|
13755
13852
|
subCommands: {
|
|
13756
13853
|
init: initCommand,
|
package/dist/index.js
CHANGED
|
@@ -4017,8 +4017,9 @@ function hasUserContextMetadata(schema) {
|
|
|
4017
4017
|
}
|
|
4018
4018
|
function getUserContextFields(schema) {
|
|
4019
4019
|
const fields = [];
|
|
4020
|
-
|
|
4021
|
-
|
|
4020
|
+
const def = schema._def;
|
|
4021
|
+
if (def?.typeName === "ZodObject" && typeof def.shape === "function") {
|
|
4022
|
+
const shape = def.shape();
|
|
4022
4023
|
for (const [key, value] of Object.entries(shape)) {
|
|
4023
4024
|
if (hasUserContextMetadata(value)) {
|
|
4024
4025
|
fields.push(key);
|
|
@@ -131,5 +131,8 @@ export declare function userContext<T extends z.ZodType>(schema: T): UserContext
|
|
|
131
131
|
export declare function hasUserContextMetadata(schema: unknown): schema is UserContextSchema<z.ZodType>;
|
|
132
132
|
/**
|
|
133
133
|
* Get all userContext field names from a Zod object schema
|
|
134
|
+
*
|
|
135
|
+
* Note: Uses _def.typeName check instead of instanceof to work across
|
|
136
|
+
* module boundaries in bundled CLI.
|
|
134
137
|
*/
|
|
135
138
|
export declare function getUserContextFields(schema: z.ZodType): string[];
|
package/package.json
CHANGED
package/src/browser/server.ts
CHANGED
|
@@ -122,6 +122,12 @@ export async function startBrowserServer(options: BrowserServerOptions): Promise
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
125
|
+
const userContextFilterBtn = graphData.meta.totalUserContextFunctions > 0
|
|
126
|
+
? `<button class="filter-btn" data-filter="userContext" title="Functions using userContext()">
|
|
127
|
+
<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})
|
|
128
|
+
</button>`
|
|
129
|
+
: '';
|
|
130
|
+
|
|
125
131
|
return `<!DOCTYPE html>
|
|
126
132
|
<html lang="en">
|
|
127
133
|
<head>
|
|
@@ -1440,6 +1446,7 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
1440
1446
|
<button class="filter-btn" data-filter="accessGroup">
|
|
1441
1447
|
<span class="dot access"></span> Access
|
|
1442
1448
|
</button>
|
|
1449
|
+
${userContextFilterBtn}
|
|
1443
1450
|
</div>
|
|
1444
1451
|
|
|
1445
1452
|
<div class="layout-selector">
|
|
@@ -1584,6 +1591,7 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
1584
1591
|
metadata: node.metadata,
|
|
1585
1592
|
changeStatus: node.changeStatus || 'unchanged',
|
|
1586
1593
|
changeDetails: node.changeDetails || null,
|
|
1594
|
+
usesUserContext: node.metadata?.usesUserContext || false,
|
|
1587
1595
|
},
|
|
1588
1596
|
});
|
|
1589
1597
|
}
|
|
@@ -1636,6 +1644,19 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
1636
1644
|
'height': 55,
|
|
1637
1645
|
},
|
|
1638
1646
|
},
|
|
1647
|
+
// Function nodes with userContext - show indicator below label
|
|
1648
|
+
{
|
|
1649
|
+
selector: 'node[type="function"][?usesUserContext]',
|
|
1650
|
+
style: {
|
|
1651
|
+
'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>'),
|
|
1652
|
+
'background-width': '18px',
|
|
1653
|
+
'background-height': '18px',
|
|
1654
|
+
'background-position-x': '50%',
|
|
1655
|
+
'background-position-y': '75%',
|
|
1656
|
+
'text-valign': 'center',
|
|
1657
|
+
'text-margin-y': -8,
|
|
1658
|
+
},
|
|
1659
|
+
},
|
|
1639
1660
|
// Entity nodes - Teal
|
|
1640
1661
|
{
|
|
1641
1662
|
selector: 'node[type="entity"]',
|
|
@@ -2177,6 +2198,16 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
2177
2198
|
if (filter === 'all') {
|
|
2178
2199
|
cy.nodes().removeClass('hidden');
|
|
2179
2200
|
cy.edges().removeClass('hidden');
|
|
2201
|
+
} else if (filter === 'userContext') {
|
|
2202
|
+
// Special filter: show only functions with userContext
|
|
2203
|
+
cy.nodes().forEach(node => {
|
|
2204
|
+
if (node.data('type') === 'function' && node.data('usesUserContext')) {
|
|
2205
|
+
node.removeClass('hidden');
|
|
2206
|
+
} else {
|
|
2207
|
+
node.addClass('hidden');
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2210
|
+
cy.edges().addClass('hidden');
|
|
2180
2211
|
} else {
|
|
2181
2212
|
cy.nodes().forEach(node => {
|
|
2182
2213
|
if (node.data('type') === filter) {
|
package/src/browser/transform.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface GraphData {
|
|
|
38
38
|
totalFunctions: number;
|
|
39
39
|
totalEntities: number;
|
|
40
40
|
totalAccessGroups: number;
|
|
41
|
+
totalUserContextFunctions: number;
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -132,6 +133,7 @@ export function transformToGraphData(config: OntologyConfig): GraphData {
|
|
|
132
133
|
// Track function counts for access groups and entities
|
|
133
134
|
const accessGroupCounts: Record<string, number> = {};
|
|
134
135
|
const entityCounts: Record<string, number> = {};
|
|
136
|
+
let userContextFunctionCount = 0;
|
|
135
137
|
|
|
136
138
|
// Initialize counts
|
|
137
139
|
for (const groupName of Object.keys(config.accessGroups)) {
|
|
@@ -158,6 +160,9 @@ export function transformToGraphData(config: OntologyConfig): GraphData {
|
|
|
158
160
|
// Check if function uses userContext
|
|
159
161
|
const userContextFields = getUserContextFields(fn.inputs);
|
|
160
162
|
const usesUserContext = userContextFields.length > 0;
|
|
163
|
+
if (usesUserContext) {
|
|
164
|
+
userContextFunctionCount++;
|
|
165
|
+
}
|
|
161
166
|
|
|
162
167
|
// Create function node
|
|
163
168
|
nodes.push({
|
|
@@ -242,6 +247,7 @@ export function transformToGraphData(config: OntologyConfig): GraphData {
|
|
|
242
247
|
totalFunctions: Object.keys(config.functions).length,
|
|
243
248
|
totalEntities: config.entities ? Object.keys(config.entities).length : 0,
|
|
244
249
|
totalAccessGroups: Object.keys(config.accessGroups).length,
|
|
250
|
+
totalUserContextFunctions: userContextFunctionCount,
|
|
245
251
|
},
|
|
246
252
|
};
|
|
247
253
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { defineCommand, runMain } from "citty";
|
|
2
2
|
import { initCommand } from "./commands/init.js";
|
|
3
3
|
import { reviewCommand } from "./commands/review.js";
|
|
4
|
+
import pkg from "../../package.json";
|
|
4
5
|
|
|
5
6
|
const main = defineCommand({
|
|
6
7
|
meta: {
|
|
7
8
|
name: "ont",
|
|
8
9
|
description: "Ontology - Ontology-first backends with human-approved AI access & edits",
|
|
9
|
-
version:
|
|
10
|
+
version: pkg.version,
|
|
10
11
|
},
|
|
11
12
|
subCommands: {
|
|
12
13
|
init: initCommand,
|
|
@@ -174,12 +174,17 @@ export function hasUserContextMetadata(
|
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* Get all userContext field names from a Zod object schema
|
|
177
|
+
*
|
|
178
|
+
* Note: Uses _def.typeName check instead of instanceof to work across
|
|
179
|
+
* module boundaries in bundled CLI.
|
|
177
180
|
*/
|
|
178
181
|
export function getUserContextFields(schema: z.ZodType): string[] {
|
|
179
182
|
const fields: string[] = [];
|
|
180
183
|
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
// Use _def.typeName check for bundler compatibility (instanceof fails across module boundaries)
|
|
185
|
+
const def = (schema as unknown as { _def?: { typeName?: string; shape?: () => Record<string, unknown> } })._def;
|
|
186
|
+
if (def?.typeName === "ZodObject" && typeof def.shape === "function") {
|
|
187
|
+
const shape = def.shape();
|
|
183
188
|
for (const [key, value] of Object.entries(shape)) {
|
|
184
189
|
if (hasUserContextMetadata(value)) {
|
|
185
190
|
fields.push(key);
|