ozmoz 0.1.45 → 0.1.48
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 +59 -163
- package/dist/cli.cjs +47 -47
- package/dist/index.js +1 -1
- package/dist/ozmoz-mcp.js +14 -14
- package/dist/ozmoz.iife.js +1 -1
- package/package.json +2 -1
- package/scripts/setup-lib.cjs +2 -2
package/README.md
CHANGED
|
@@ -1,230 +1,126 @@
|
|
|
1
1
|
# OZmoz
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The Data OS for AI agents.** A ready-to-use backend for any application, and Alex AI to count, calculate and present your data in plain language.
|
|
4
4
|
|
|
5
|
-
OZmoz
|
|
5
|
+
OZmoz is a deterministic and secure software layer on top of MongoDB. Your AI agent builds the interface; OZmoz gives it a backend that is modeled once, sealed, and enforced.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Why OZmoz
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **100% Frontend Focus**: Zero servers, zero custom REST APIs, and zero SQL migrations. Build full-stack applications purely from the client.
|
|
16
|
-
- **AI Agent Accelerator**: Eliminates hallucinations and cuts token usage by 90% via deterministic contracts (`svro.schema.json`, `svro.workflow.json`). Agents (Claude Code, Cursor, Windsurf) focus 100% of their reasoning on UX.
|
|
17
|
-
- **Deterministic Edge Gatekeeper**: Sub-millisecond $O(1)$ schema validation at the cloud edge before writes hit the database.
|
|
18
|
-
- **Zero-Crash Native Web Components**: 7 built-in, framework-agnostic Custom Elements (`<oz-*>`) preventing white-screen crashes on missing or partial data.
|
|
19
|
-
- **Zero-Rewrite Philosophy**: Write the exact same frontend code in trial mode (`OZ_PROTOTYPE=true`) and production (`OZ_PROTOTYPE=false`).
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
<!-- STREAMING_CHUNK:Documenting installation and setup commands... -->
|
|
24
|
-
|
|
25
|
-
## Quick Start
|
|
9
|
+
- **Frontend focus**: no server to run, no REST API to write, no SQL migrations.
|
|
10
|
+
- **Built for AI agents**: your agent spends its effort on the UI and UX instead of backend plumbing. Works with Claude Code, Cursor, Codex, Windsurf, GitHub Copilot, Gemini CLI, Antigravity and more.
|
|
11
|
+
- **A sealed data contract**: Alex AI models your data, you validate it, and the contract is sealed. Reads and writes then follow that contract.
|
|
12
|
+
- **Zero-crash components**: seven native `<oz-*>` web components that handle missing or partial data without breaking the screen.
|
|
13
|
+
- **Zero-rewrite**: write the same code in trial mode and in live mode.
|
|
14
|
+
- **Talk to your data**: ask Alex questions in plain language. No SQL, no BI tool to set up.
|
|
26
15
|
|
|
27
|
-
|
|
16
|
+
## Quick start
|
|
28
17
|
|
|
29
|
-
|
|
18
|
+
### New project with an AI agent
|
|
30
19
|
|
|
31
20
|
```bash
|
|
32
|
-
npx ozmoz start claude "
|
|
21
|
+
npx -y ozmoz start claude "your app idea"
|
|
33
22
|
```
|
|
34
23
|
|
|
35
|
-
Or
|
|
24
|
+
Or connect your agent once, then work as usual:
|
|
36
25
|
|
|
37
26
|
```bash
|
|
38
|
-
npx ozmoz login #
|
|
39
|
-
npx ozmoz mcp #
|
|
27
|
+
npx ozmoz login # approve once in the browser
|
|
28
|
+
npx ozmoz mcp # writes AGENTS.md / CLAUDE.md / GEMINI.md and configures the MCP server
|
|
40
29
|
```
|
|
41
30
|
|
|
42
|
-
###
|
|
43
|
-
|
|
44
|
-
Install the package in your Vite project:
|
|
31
|
+
### Existing frontend project
|
|
45
32
|
|
|
46
33
|
```bash
|
|
47
34
|
npm install ozmoz
|
|
48
|
-
npx ozmoz init --keyApp=<
|
|
49
|
-
npx ozmoz sync
|
|
35
|
+
npx ozmoz init --keyApp=<KEY> --token=<TOKEN>
|
|
36
|
+
npx ozmoz sync # seals the contract and generates the IDE types
|
|
50
37
|
```
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<!-- STREAMING_CHUNK:Documenting execution modes and environment configuration... -->
|
|
39
|
+
Use `npx ozmoz sync --check-only` for an offline check.
|
|
55
40
|
|
|
56
|
-
##
|
|
41
|
+
## Modes
|
|
57
42
|
|
|
58
|
-
|
|
43
|
+
Set the mode in `.env`:
|
|
59
44
|
|
|
60
|
-
```
|
|
61
|
-
# Trial
|
|
62
|
-
#
|
|
45
|
+
```bash
|
|
46
|
+
# Trial mode: everything is simulated locally with demo data (oz.seed).
|
|
47
|
+
# The test code for authentication is always 0000.
|
|
63
48
|
OZ_PROTOTYPE=true
|
|
64
49
|
|
|
65
|
-
# Live mode:
|
|
50
|
+
# Live mode: the data contract is enforced on your real data.
|
|
66
51
|
OZ_PROTOTYPE=false
|
|
67
52
|
```
|
|
68
53
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<!-- STREAMING_CHUNK:Documenting CRUD operations and strict governance rules... -->
|
|
72
|
-
|
|
73
|
-
## SDK Usage & Strict Governance
|
|
74
|
-
|
|
75
|
-
Every write operation requires literal governance metadata `{ desc, role, guard }` to ensure contract validation.
|
|
54
|
+
## SDK usage
|
|
76
55
|
|
|
77
|
-
|
|
56
|
+
Every write operation carries governance metadata `{ desc, role, guard }`.
|
|
78
57
|
|
|
79
|
-
```
|
|
80
|
-
//
|
|
81
|
-
const res = await oz.add('orders', {
|
|
82
|
-
product: 'Desk Pro',
|
|
83
|
-
price: 299
|
|
84
|
-
}, {
|
|
58
|
+
```js
|
|
59
|
+
// Insert a document
|
|
60
|
+
const res = await oz.add('orders', { product: 'Desk Pro', price: 299 }, {
|
|
85
61
|
desc: 'Create customer order',
|
|
86
62
|
role: 'AUTHENTICATED',
|
|
87
63
|
guard: 'price > 0'
|
|
88
64
|
});
|
|
65
|
+
if (!res.success) console.error('Order creation failed:', res.error);
|
|
89
66
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} else {
|
|
93
|
-
console.log('Created document ID:', res.documentId);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 2. Update a document
|
|
97
|
-
const updateRes = await oz.update('orders', doc._id, {
|
|
98
|
-
price: 249
|
|
99
|
-
}, {
|
|
67
|
+
// Update a document
|
|
68
|
+
await oz.update('orders', doc._id, { price: 249 }, {
|
|
100
69
|
desc: 'Apply discount on order',
|
|
101
70
|
role: 'MANAGER'
|
|
102
71
|
});
|
|
103
72
|
|
|
104
|
-
//
|
|
105
|
-
|
|
73
|
+
// Delete a document
|
|
74
|
+
await oz.delete('orders', doc._id, {
|
|
106
75
|
desc: 'Cancel and delete order',
|
|
107
76
|
role: 'ADMIN'
|
|
108
77
|
});
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
<!-- STREAMING_CHUNK:Documenting read operations and authentication... -->
|
|
112
|
-
|
|
113
|
-
### Reading Data
|
|
114
78
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
// Read documents owned by the authenticated user
|
|
120
|
-
const userOrders = await oz.getDocsU('orders');
|
|
121
|
-
|
|
122
|
-
// Read a single document by its _id
|
|
79
|
+
// Read data
|
|
80
|
+
const orders = await oz.getDocsG('orders'); // all documents
|
|
81
|
+
const mine = await oz.getDocsU('orders'); // documents of the signed-in user
|
|
123
82
|
const order = await oz.getDoc('orders', 'order_123');
|
|
124
83
|
```
|
|
125
84
|
|
|
126
|
-
### Authentication (
|
|
127
|
-
|
|
128
|
-
```javascript
|
|
129
|
-
// Step 1: Send OTP code (In trial mode, code is always 0000)
|
|
130
|
-
const otpRes = await oz.sendotpsvro('user@example.com');
|
|
85
|
+
### Authentication (passwordless code)
|
|
131
86
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Check session status
|
|
139
|
-
const loggedIn = await oz.isAuthenticated();
|
|
87
|
+
```js
|
|
88
|
+
await oz.sendotpsvro('user@example.com'); // trial mode: code is 0000
|
|
89
|
+
const auth = await oz.verifyotpsvro('user@example.com', '0000');
|
|
90
|
+
const signedIn = await oz.isAuthenticated();
|
|
140
91
|
```
|
|
141
92
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<!-- STREAMING_CHUNK:Documenting defensive UI web components... -->
|
|
145
|
-
|
|
146
|
-
## Zero-Crash UI Components
|
|
147
|
-
|
|
148
|
-
Render data safely without risk of runtime null-pointer exceptions:
|
|
93
|
+
## Zero-crash components
|
|
149
94
|
|
|
150
95
|
```html
|
|
151
|
-
<!-- Text with fallback -->
|
|
152
96
|
<oz-text value={item.name} fallback="No name" />
|
|
153
|
-
|
|
154
|
-
<!-- Currency formatting with strict fallback -->
|
|
155
97
|
<oz-number value={item.price} currency="EUR" invalid-fallback="0" />
|
|
156
|
-
|
|
157
|
-
<!-- Boolean toggle representation -->
|
|
158
98
|
<oz-toggle value={item.isActive} label-true="Active" label-false="Inactive" />
|
|
159
|
-
|
|
160
|
-
<!-- Formatted date display -->
|
|
161
99
|
<oz-date value={item.createdAt} locale="en-US" invalid-fallback="Invalid date" />
|
|
162
|
-
|
|
163
|
-
<!-- Enum status validator -->
|
|
164
100
|
<oz-enum value={item.status} allowed="PENDING,CONFIRMED,DELIVERED" invalid-fallback="Unknown status" />
|
|
165
|
-
|
|
166
|
-
<!-- Relational reference resolution -->
|
|
167
101
|
<oz-reference table="users" id={item.userId} field="fullName" orphan-fallback="Deleted user" />
|
|
168
102
|
```
|
|
169
103
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
## CLI Reference
|
|
104
|
+
## CLI reference
|
|
173
105
|
|
|
174
106
|
| Command | Description |
|
|
175
|
-
|
|
176
|
-
| `npx ozmoz login` |
|
|
177
|
-
| `npx ozmoz mcp` | Configure AI agent
|
|
178
|
-
| `npx ozmoz
|
|
179
|
-
| `npx ozmoz
|
|
180
|
-
| `npx ozmoz sync
|
|
181
|
-
|
|
182
|
-
eof
|
|
183
|
-
|
|
184
|
-
### Résumé des éléments générés
|
|
107
|
+
|---|---|
|
|
108
|
+
| `npx ozmoz login` | Sign in from your workstation, through the browser. |
|
|
109
|
+
| `npx ozmoz mcp` | Configure your AI agent and register the OZmoz tools. |
|
|
110
|
+
| `npx ozmoz start <agent> "idea"` | Sign in, configure and launch your terminal agent. |
|
|
111
|
+
| `npx ozmoz init` | Initialize the local `.env` and project configuration. |
|
|
112
|
+
| `npx ozmoz sync` | Seal the data contract and generate the IDE types. |
|
|
113
|
+
| `npx ozmoz sync --check-only` | Offline check of the contract and types, no network request. |
|
|
185
114
|
|
|
186
|
-
|
|
187
|
-
2. **`README.md`** : Totalement unifié sous la marque **OZmoz**, avec des exemples d'écriture conformes aux règles d'intégrité (gouvernance `{ desc, role }` et vérification de `res.success`).
|
|
115
|
+
Run `npx ozmoz --help` for all commands.
|
|
188
116
|
|
|
117
|
+
## Links
|
|
189
118
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
```bash
|
|
193
|
-
npx ozmoz
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
OZmoz is the SDK and CLI behind the `oz.*` API and the `<oz-*>` tags.
|
|
197
|
-
|
|
198
|
-
## Quick start
|
|
199
|
-
|
|
200
|
-
Let an AI agent build for you (Claude Code, Gemini CLI, Codex CLI):
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
npx -y ozmoz start claude "your app idea"
|
|
204
|
-
```
|
|
119
|
+
- Website: https://ozmoz.io
|
|
120
|
+
- Support: open a ticket from https://ozmoz.io
|
|
205
121
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
```bash
|
|
209
|
-
npx ozmoz login # approve once in the browser
|
|
210
|
-
npx ozmoz mcp # writes AGENTS.md / CLAUDE.md / GEMINI.md and configures the MCP server
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## In an existing project
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
npm install ozmoz
|
|
217
|
-
npx ozmoz init --keyApp=<KEY> --token=<TOKEN>
|
|
218
|
-
npx ozmoz sync # seals the schema and generates the contract + IDE types
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Use `npx ozmoz sync --check-only` for an offline check.
|
|
222
|
-
|
|
223
|
-
## Modes
|
|
224
|
-
|
|
225
|
-
Set in `.env`:
|
|
122
|
+
---
|
|
226
123
|
|
|
227
|
-
|
|
228
|
-
- `OZ_PROTOTYPE=false`: live mode, the data contract is enforced.
|
|
124
|
+
MongoDB is a trademark of MongoDB, Inc. OZmoz is an independent product and is not affiliated with or endorsed by MongoDB, Inc.
|
|
229
125
|
|
|
230
|
-
|
|
126
|
+
Copyright OZmoz. All rights reserved.
|
package/dist/cli.cjs
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var i=require("fs"),a=require("path"),Y=require("https"),
|
|
2
|
+
var i=require("fs"),a=require("path"),Y=require("https"),se=require("http"),A=require("os"),{exec:ie,spawnSync:_}=require("child_process"),ae=[a.join(__dirname,"..","scripts","setup-lib.cjs"),a.join(__dirname,"..","..","scripts","setup-lib.cjs"),a.join(__dirname,"scripts","setup-lib.cjs")],ce=ae.find(i.existsSync)||a.join(__dirname,"..","scripts","setup-lib.cjs"),{runFullSetup:K,handleAuthAndEnv:q,touchTsConfig:le,attemptViteAutoImportPatch:de,injectUniversalRuntime:ue,writeBrowserConfig:me,patchAllowScripts:pe}=require(ce),fe=!!(process.stdout&&process.stdout.isTTY&&typeof process.stdout.hasColors=="function"&&process.stdout.hasColors(16777216)),he=fe?"\x1B[38;2;15;156;140m":"\x1B[36m",ge="\x1B[31m",H="\x1B[0m",X="https://api.ozmoz.io/cli/";function u(e){console.log(e.replace(/OZmoz|\[|\]|→/g,t=>`${he}${t}${H}`))}function y(e){console.error(`${ge}${e}${H}`)}var x="ozmoz",G=/<!--\s*(?:ozmoz|fleetbo|OZmoz):start[^>]*-->[\s\S]*?<!--\s*(?:ozmoz|fleetbo|OZmoz):end\s*-->/i,ye="<!-- OZmoz:end -->",Q="# OZMOZ DATA OS \u2014 DIRECTIVES SYST\xC8ME",Z={claude:"Claude Code",cursor:"Cursor",windsurf:"Windsurf",devin:"Devin",codex:"Codex CLI",gemini:"Gemini CLI",antigravity:"Antigravity CLI",copilot:"GitHub Copilot"},D={created:"created",updated:"updated",unchanged:"already up to date",appended:"OZmoz block appended (your own content was kept)",migrated:"old file replaced (backup: <file>.ozmoz-legacy.bak)",invalid:"skipped: the file is not valid JSON, fix or delete it first"};function Oe(){try{return JSON.parse(i.readFileSync(a.join(__dirname,"..","package.json"),"utf8")).version||"dev"}catch{return"dev"}}var ze=Buffer.from("QUl6YVN5QUM1Uk94STNibklPMUR5TmZsTWhGUnJ0blItNDVwNFJF","base64").toString("utf8");function ve(e){try{let t=JSON.parse(Buffer.from(e.split(".")[1],"base64url").toString("utf8"));return typeof t.exp=="number"&&t.exp*1e3<Date.now()+6e4}catch{return!1}}function ee(e,t){i.writeFileSync(e,t,{mode:384});try{i.chmodSync(e,384)}catch{}}async function Se(e,t){let r=await fetch(`https://securetoken.googleapis.com/v1/token?key=${ze}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=refresh_token&refresh_token=${encodeURIComponent(t)}`});if(!r.ok)return null;let n=await r.json();return n.id_token?(ee(e,JSON.stringify({devToken:n.id_token,refreshToken:n.refresh_token||t,updatedAt:new Date().toISOString()},null,2)),n.id_token):null}async function J(){if(process.env.FLEETBO_DEV_TOKEN)return process.env.FLEETBO_DEV_TOKEN;try{let e=a.join(A.homedir(),".fleetbo","credentials.json");if(i.existsSync(e)){let t=JSON.parse(i.readFileSync(e,"utf8"));if(t.devToken&&!ve(t.devToken))return t.devToken;if(t.refreshToken)try{let r=await Se(e,t.refreshToken);if(r)return r}catch{}}}catch{}return null}async function te(){let e=a.join(A.homedir(),".fleetbo","guidelines.json"),t=await J();t||y('[OZmoz] Not logged in: run "npx ozmoz login" first to load the full instructions. Using local cache if available.');try{u("[OZmoz] Fetching instructions from Cloud...");let r=await fetch(X+"getagentdirectives",{method:"POST",headers:Object.assign({"Content-Type":"application/json"},t?{Authorization:`Bearer ${t}`}:{}),body:JSON.stringify({data:{}})});if(!r.ok)y("[OZmoz] Cloud error HTTP "+r.status);else{let n=await r.json(),s=n.result?.data||n.result||n.data;if(s&&s.protocol&&s.directives)return u("[OZmoz] Instructions loaded successfully."),i.existsSync(a.dirname(e))||i.mkdirSync(a.dirname(e),{recursive:!0}),i.writeFileSync(e,JSON.stringify(s,null,2),"utf8"),s;y("[OZmoz] Empty Cloud response.")}}catch(r){y("[OZmoz] Network error: "+r.message)}if(i.existsSync(e)){u("[OZmoz] Using local cache (~/.fleetbo/guidelines.json)");try{return JSON.parse(i.readFileSync(e,"utf8"))}catch{}}return{protocol:"",directives:"",version:"1.0.0"}}function V(e,t){return"<!-- ozmoz:start v"+e+` -->
|
|
3
3
|
`+t.trim()+`
|
|
4
|
-
`+
|
|
5
|
-
`}function
|
|
4
|
+
`+ye+`
|
|
5
|
+
`}function we(e,t,r){let n=i.existsSync(e)?i.readFileSync(e,"utf8"):null,s,o;return n===null?(s="created",o=t):G.test(n)?(o=n.replace(G,()=>t.trimEnd()),s=o===n?"unchanged":"updated"):n.includes(Q)?(s="migrated",o=t):(s="appended",o=n.replace(/\s*$/,`
|
|
6
6
|
|
|
7
|
-
`)+t),!r&&s!=="unchanged"&&(s==="migrated"&&i.writeFileSync(e+".ozmoz-legacy.bak",
|
|
8
|
-
`,"utf8")),
|
|
9
|
-
`):[],p=f.find(w=>/^[\s|│┃]*ozmoz(?![\w@-])/.test(w)),m=f.some(w=>/^\s*(?:fleetbo|ozmoz):\s/.test(w));if(p&&p.includes("ozmoz@latest"))return{status:"unchanged",legacy:m};if(p&&!$(e,d).ok)return{status:"manual",detail:e+" "+d.join(" ")+" && "+c,legacy:m};let g=$(e,
|
|
10
|
-
`).slice(-2).join(" "),legacy:m}}function
|
|
7
|
+
`)+t),!r&&s!=="unchanged"&&(s==="migrated"&&i.writeFileSync(e+".ozmoz-legacy.bak",n,"utf8"),i.mkdirSync(a.dirname(e),{recursive:!0}),i.writeFileSync(e,o,"utf8")),s}function oe(){return[a.join(__dirname,"ozmoz-mcp.js"),a.join(__dirname,"..","bin","ozmoz-mcp.js"),a.join(__dirname,"bin","ozmoz-mcp.js"),a.join(__dirname,"..","src","bin","ozmoz-mcp.js")].find(t=>i.existsSync(t))||a.resolve(__dirname,"ozmoz-mcp.js")}function M(e,t,r){let n=Object.assign({command:process.execPath,args:[__filename,"mcp","--run"]},r||{}),s={};if(i.existsSync(e))try{let d=JSON.parse(i.readFileSync(e,"utf8"));if(!d||typeof d!="object"||Array.isArray(d))return"invalid";s=d}catch{return"invalid"}let o=s.mcpServers&&s.mcpServers[x];return o&&JSON.stringify(o)===JSON.stringify(n)?"unchanged":(s.mcpServers=s.mcpServers||{},s.mcpServers[x]=n,t||(i.mkdirSync(a.dirname(e),{recursive:!0}),i.writeFileSync(e,JSON.stringify(s,null,2)+`
|
|
8
|
+
`,"utf8")),o?"updated":"created")}function R(e){let t=process.platform==="win32"?"where":"which";return _(t,[e],{stdio:"ignore"}).status===0}function $(e,t,r){let n=_(e,t,{encoding:"utf8",timeout:r||3e4,shell:process.platform==="win32"});return{ok:n.status===0,out:(n.stdout||"")+(n.stderr||"")}}function Ee(e,t,r){let n=e==="claude"?["--scope",r||"user"]:[],s=oe(),o=["mcp","add"].concat(n,[x,"--",process.execPath,s]),d=["mcp","remove",x].concat(n),c=e+" "+o.join(" ");if(!R(e))return{status:"manual",detail:c};if(t)return{status:"planned",detail:c};let h=$(e,["mcp","list"],6e4),f=h.ok?h.out.split(`
|
|
9
|
+
`):[],p=f.find(w=>/^[\s|│┃]*ozmoz(?![\w@-])/.test(w)),m=f.some(w=>/^\s*(?:fleetbo|ozmoz):\s/.test(w));if(p&&p.includes("ozmoz@latest"))return{status:"unchanged",legacy:m};if(p&&!$(e,d).ok)return{status:"manual",detail:e+" "+d.join(" ")+" && "+c,legacy:m};let g=$(e,o);return g.ok?{status:p?"updated":"created",legacy:m}:{status:"failed",detail:c,message:g.out.trim().split(`
|
|
10
|
+
`).slice(-2).join(" "),legacy:m}}function be(e){let t={user:!1,local:!1,others:[]};try{let r=JSON.parse(i.readFileSync(a.join(A.homedir(),".claude.json"),"utf8")),n=c=>!!c&&typeof c=="object"&&/fleetbo|ozmoz/.test(String(c.command||"")+" "+JSON.stringify(c.args||[])),s=(c,h)=>{!c||typeof c!="object"||Object.keys(c).forEach(f=>{f===x?t[h]=!0:n(c[f])&&t.others.push({name:f,where:h})})};s(r.mcpServers,"user");let o=e;try{o=i.realpathSync(e)}catch{}let d=r.projects||{};Array.from(new Set([e,o])).forEach(c=>s((d[c]||{}).mcpServers,"local"))}catch{}return t}function Te(e){let t=be(e);t.user&&u(" \u26A0\uFE0F A global (user-scope) '"+x+"' entry from a previous version also exists: the tools appear in every project. To keep them in OZmoz projects only: claude mcp remove "+x+" --scope user"),t.local&&u(" \u26A0\uFE0F A local-scope '"+x+"' entry exists for this folder and takes precedence over .mcp.json. Remove it: claude mcp remove "+x+" --scope local"),t.others.forEach(r=>{u(" \u26A0\uFE0F An old MCP entry named '"+r.name+"' ("+r.where+" scope) points to OZmoz. Remove it: claude mcp remove "+r.name+" --scope "+r.where)})}function Ae(e){let t=A.homedir(),r=n=>i.existsSync(n);return{claude:R("claude")||r(a.join(t,".claude")),cursor:r(a.join(e,".cursor"))||r(a.join(t,".cursor"))||R("cursor"),windsurf:r(a.join(t,".codeium","windsurf"))||r(a.join(e,".windsurf")),devin:r(a.join(e,".devin")),codex:R("codex")||r(a.join(t,".codex")),gemini:R("gemini")||r(a.join(t,".gemini")),antigravity:R("agy")||r(a.join(t,".antigravity"))||r(a.join(e,".antigravity")),copilot:R("copilot")||r(a.join(t,".copilot"))}}var Ce={claude:{bin:"claude",launch:e=>[e]},gemini:{bin:"gemini",launch:e=>["-i",e]},codex:{bin:"codex",launch:e=>[e]},antigravity:{bin:"agy",launch:e=>["-i",e]}};function Ie(e){return String(e||"").replace(/[^\p{L}\p{N} '.,:\-_]/gu," ").replace(/\s+/g," ").trim().slice(0,4e3)}function ke(e,t,r){let n=Ie(t),s=n?`The user already provided their project description and technical choices: "${n}".
|
|
11
11
|
- Check if the framework (e.g. React, Vue) is already specified.
|
|
12
12
|
- If it is, DO NOT ask a generic "what tech do you want" question. Acknowledge their choice naturally (e.g. "J'ai bien not\xE9 que vous souhaitez utiliser React...") and only ask for the missing precision (TypeScript vs JavaScript) in a single friendly confirmation, or proceed directly if obvious.
|
|
13
|
-
- Once the user answers, do NOT run exploratory bash commands. IMMEDIATELY call the MCP tool 'oz_create_project' with the application name.`:"Ask me the starter questions from your OZmoz instructions before installing anything: the technology first, then a one-sentence project description, one question at a time.",
|
|
14
|
-
`).forEach(s=>{let
|
|
13
|
+
- Once the user answers, do NOT run exploratory bash commands. IMMEDIATELY call the MCP tool 'oz_create_project' with the application name.`:"Ask me the starter questions from your OZmoz instructions before installing anything: the technology first, then a one-sentence project description, one question at a time.",o=null,d=!0;try{let m=a.join(r,".env");if(i.existsSync(m)){let g=i.readFileSync(m,"utf8"),w=g.match(/^\s*OZ_DB_KEY\s*=\s*(.+?)\s*$/m);w&&(o=w[1].trim());let E=g.match(/^\s*OZ_PROTOTYPE\s*=\s*(.+?)\s*$/m);E&&(d=E[1].trim().toLowerCase()==="true")}}catch{}let c=i.existsSync(a.join(r,"svro.schema.json")),h=i.existsSync(a.join(r,"index.html"))||i.existsSync(a.join(r,"package.json")),f=d?"OZ_PROTOTYPE=true (mode essai / local sandbox)":"OZ_PROTOTYPE=false (mode r\xE9el / live database)",p=o||c?"This folder ALREADY contains an OZmoz project (OZ_DB_KEY="+o+", "+f+"). Do NOT call oz_create_project, and do NOT ask the starter questions. State this in one plain sentence to the user \u2014 then ask what I want to work on now.":h?'This folder does NOT have an OZmoz project yet, but a frontend project ALREADY exists here \u2014 do NOT run "npm create vite" or scaffold a new frontend. Skip the technology question and ask me only for a one-sentence project description, then call oz_create_project directly.':"This folder does NOT contain an OZmoz project yet (no .env, no svro.schema.json). "+s;return"Use OZmoz as the backend of my app. You are in the project folder ("+r+`). Your OZmoz instructions are already loaded: they are in AGENTS.md / GEMINI.md at the project root and in the OZmoz MCP server (prompt "oz_contract_rules", tool "oz_get_contract"). Never try to read files inside an mcp/ folder or in the agent's own configuration folders, and do not run bash commands or web searches to look for them. If you do not have them in context, call the MCP tool "oz_get_contract" once before answering. `+p}function xe(){let e=process.env;if(e.CLAUDECODE==="1"||e.GEMINI_CLI||e.CODEX_CLI||e.AI_AGENT||e.CURSOR_AGENT||e.ANTIGRAVITY_CLI)return!0;if(process.platform==="win32")return!1;try{let t=_("ps",["-A","-o","pid=,ppid=,args="],{encoding:"utf8"});if(t.status!==0)return!1;let r=new Map;t.stdout.split(`
|
|
14
|
+
`).forEach(s=>{let o=s.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);o&&r.set(Number(o[1]),{ppid:Number(o[2]),args:o[3]})});let n=process.ppid;for(let s=0;n>1&&s<20&&r.has(n);s++){let{ppid:o,args:d}=r.get(n);if(d.split(/\s+/).slice(0,2).some(h=>/(^|\/)(gemini|claude|codex|antigravity|agy)$/.test(h))||/@google\/gemini-cli|@anthropic-ai\/claude-code|@openai\/codex/.test(d))return!0;n=o}}catch{}return!1}function _e(e){let t=process.platform==="win32"?"where":"which";return _(t,[e],{stdio:"ignore"}).status===0}function Ne(e,t){let r=t.slice(1).filter(p=>!p.startsWith("--")),n=(r[0]||"").toLowerCase(),s=r.slice(1).join(" "),o=Ce[n];o||(y('Usage: npx ozmoz start <claude|gemini|codex> ["your app idea"]'),y('Cursor, Windsurf, Devin and GitHub Copilot have no terminal agent to launch: run "npx ozmoz mcp --agent=<cursor|windsurf|devin|copilot>" instead.'),process.exit(1));let d=!1;try{d=!!JSON.parse(i.readFileSync(a.join(A.homedir(),".fleetbo","credentials.json"),"utf8")).devToken}catch{}d||(u("[OZmoz] First use on this machine: sign in (your browser opens, approve once)."),_(process.execPath,[__filename,"login"],{stdio:"inherit",cwd:e,env:Object.assign({},process.env,{FLEETBO_START:"1"})}).status!==0&&(y('Sign-in did not complete. Run "npx ozmoz start '+n+'" again.'),process.exit(1))),_(process.execPath,[__filename,"mcp","--agent="+n],{stdio:"inherit",cwd:e,env:Object.assign({},process.env,{FLEETBO_START:"1"})}).status!==0&&(y("Could not configure "+o.bin+'. Run "npx ozmoz mcp --agent='+n+'" to see why.'),process.exit(1)),xe()&&(u(`
|
|
15
15
|
[OZmoz] OZmoz is set up in this folder (instruction file + MCP server).
|
|
16
|
-
`),process.exit(0)),
|
|
17
|
-
[OZmoz] Launching `+
|
|
18
|
-
`);let f=
|
|
19
|
-
[OZmoz] Configuring your AI agents`+(r?" (dry run: nothing is written)":"")+"...");let m=await
|
|
16
|
+
`),process.exit(0)),_e(o.bin)||(y('"'+o.bin+'" is not installed or not in your PATH. Install it, then run: npx ozmoz start '+n),process.exit(1));let h=ke(o.rules,s,e);u(`
|
|
17
|
+
[OZmoz] Launching `+o.bin+` in this folder...
|
|
18
|
+
`);let f=_(o.bin,o.launch(process.platform==="win32"?'"'+h+'"':h),{stdio:"inherit",cwd:e,shell:process.platform==="win32"});process.exit(f.status===null?1:f.status)}async function ne(e,t){let r=t.includes("--dry-run"),n=t.find(l=>l.startsWith("--agent=")),s=n&&n.slice(8).trim()?n.slice(8).split(",").map(l=>l.trim().toLowerCase()).filter(Boolean):null,o=null;if(s){o=s.includes("all")?Object.keys(Z):s;let l=o.filter(z=>!Z[z]);l.length&&(y("Unknown agent(s): "+l.join(", ")+". Valid values: "+Object.keys(Z).join(", ")+", all."),process.exit(1))}let d=t.find(l=>l.startsWith("--scope=")),c=d?d.slice(8).trim().toLowerCase():"project";["project","user","local"].includes(c)||(y("Unknown scope: "+c+". Valid values for Claude Code: project (default), user, local."),process.exit(1));let h=Ae(e),f=o||Object.keys(Z).filter(l=>h[l]),p=Oe();u(`
|
|
19
|
+
[OZmoz] Configuring your AI agents`+(r?" (dry run: nothing is written)":"")+"...");let m=await te(),g=m.version||p,w=`# OZMOZ DATA OS \u2014 DIRECTIVES AGENT
|
|
20
20
|
|
|
21
21
|
`+(m.directives||m.guidelines||"")+`
|
|
22
22
|
|
|
23
23
|
## PROTOCOLE DONN\xC9ES : QUI FAIT QUOI (AGENT FRONTEND \u2194 ALEX AI)
|
|
24
24
|
|
|
25
|
-
`+(m.protocol||""),E=V(g,w),
|
|
26
|
-
@AGENTS.md`)]],v=a.join(e,".cursorrules");if(i.existsSync(v)){let l=i.readFileSync(v,"utf8");(G.test(l)||l.includes(
|
|
25
|
+
`+(m.protocol||""),E=V(g,w),T=[["AGENTS.md",E],["GEMINI.md",E],["CLAUDE.md",V(g,`# OZMOZ DATA OS \u2014 DIRECTIVES AGENT
|
|
26
|
+
@AGENTS.md`)]],v=a.join(e,".cursorrules");if(i.existsSync(v)){let l=i.readFileSync(v,"utf8");(G.test(l)||l.includes(Q))&&T.push([".cursorrules",E])}for(let l of T)u("\u2192 "+l[0]+": "+D[we(a.join(e,l[0]),l[1],r)]);for(let l of f){let z=Z[l];if(l==="cursor")u("\u2192 "+z+" MCP (.cursor/mcp.json): "+D[M(a.join(e,".cursor","mcp.json"),r)]);else if(l==="windsurf")u("\u2192 "+z+" MCP (~/.codeium/windsurf/mcp_config.json): "+D[M(a.join(A.homedir(),".codeium","windsurf","mcp_config.json"),r)]);else if(l==="copilot")u("\u2192 "+z+" MCP (~/.copilot/mcp-config.json): "+D[M(a.join(A.homedir(),".copilot","mcp-config.json"),r,{type:"local",tools:["*"]})]);else if(l==="claude"&&c==="project"){let S=M(a.join(e,".mcp.json"),r);u("\u2192 "+z+" MCP (.mcp.json, project scope): "+D[S]),(S==="created"||S==="updated")&&u(" Claude Code asks you to approve a project server the first time you launch it. Commit .mcp.json to share it with your team (it contains no token)."),Te(e)}else if(l==="gemini"){let S=[a.join(A.homedir(),".gemini","settings.json"),a.join(e,".gemini","settings.json"),a.join(e,".mcp.json")],b="unchanged";S.forEach(F=>{let P=M(F,r);(P==="created"||P==="updated")&&(b=P)}),u("\u2192 "+z+" MCP (settings.json & .mcp.json): "+D[b])}else if(l==="claude"||l==="codex"||l==="antigravity"){let S=l==="antigravity"?"agy":l,b=Ee(S,r,c);b.status==="manual"?u("\u2192 "+z+": '"+S+"' not found or not scriptable. Run manually: "+b.detail):b.status==="planned"?u("\u2192 "+z+" MCP: would run: "+b.detail):b.status==="failed"?y("\u2192 "+z+" MCP: registration failed ("+b.message+"). Run manually: "+b.detail):u("\u2192 "+z+" MCP"+(l==="claude"?" ("+c+" scope)":"")+": "+D[b.status]),b.legacy&&u(" \u26A0\uFE0F An old MCP entry named 'fleetbo' exists. Remove it: "+S+" mcp remove fleetbo")}else l==="devin"&&u("\u2192 "+z+": reads AGENTS.md. Devin runs in its own environment: add the OZmoz MCP server from Devin's settings (not automated).")}!o&&f.length===0&&u("\u2192 No agent detected: instruction files were written; re-run with --agent=<name> to register the MCP server.");let I=!!(B(e).FLEETBO_DEV_TOKEN||process.env.FLEETBO_DEV_TOKEN),O=a.join(A.homedir(),".fleetbo","credentials.json");if(i.existsSync(O))try{I=I||!!JSON.parse(i.readFileSync(O,"utf8")).devToken}catch{}I||y('\u26A0\uFE0F No developer token found. Run "npx ozmoz login" once.'),process.env.FLEETBO_START||u(`
|
|
27
27
|
[OZmoz] Done.
|
|
28
|
-
`)}function
|
|
28
|
+
`)}function Pe(e){let t=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";ie(`${t} "${e}"`)}function je(e){return new Promise((t,r)=>{let n=se.createServer((s,o)=>{if(o.setHeader("Access-Control-Allow-Origin","*"),o.setHeader("Access-Control-Allow-Methods","GET, OPTIONS"),o.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),o.setHeader("Access-Control-Allow-Private-Network","true"),s.method==="OPTIONS"){o.writeHead(204),o.end();return}let d=new URL(s.url,"http://127.0.0.1");if(d.pathname==="/callback"){let c=d.searchParams.get("token"),h=d.searchParams.get("refresh");if(!c){o.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),o.end("<h3>Error: Missing token.</h3>");return}let f=a.join(A.homedir(),".fleetbo");i.existsSync(f)||i.mkdirSync(f,{recursive:!0,mode:448}),ee(a.join(f,"credentials.json"),JSON.stringify({devToken:c,refreshToken:h||null,updatedAt:new Date().toISOString()},null,2));let p=a.join(e,".env");if(i.existsSync(p)){let m=i.readFileSync(p,"utf8");m.includes("FLEETBO_DEV_TOKEN=")?m=m.replace(/FLEETBO_DEV_TOKEN=.*/g,`FLEETBO_DEV_TOKEN=${c}`):m+=`
|
|
29
29
|
FLEETBO_DEV_TOKEN=${c}
|
|
30
|
-
`,i.writeFileSync(p,m,"utf8")}
|
|
30
|
+
`,i.writeFileSync(p,m,"utf8")}o.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),o.end(`
|
|
31
31
|
<!DOCTYPE html>
|
|
32
32
|
<html>
|
|
33
33
|
<head>
|
|
@@ -48,25 +48,25 @@ FLEETBO_DEV_TOKEN=${c}
|
|
|
48
48
|
`),u(`
|
|
49
49
|
[OZmoz] Developer token received and stored successfully!`),!process.env.FLEETBO_START&&!["AGENTS.md","CLAUDE.md","GEMINI.md"].some(m=>i.existsSync(a.join(e,m)))&&(u(`
|
|
50
50
|
Next step to connect your AI agents:`),u(` npx ozmoz mcp (Configure Claude Code, Cursor, Windsurf, Devin, Codex CLI, Gemini CLI, Antigravity CLI, GitHub Copilot)
|
|
51
|
-
`)),typeof
|
|
52
|
-
[OZmoz] Waiting for authentication...`),u(`If your browser does not open automatically, visit: ${
|
|
53
|
-
`).forEach(s=>{let[
|
|
51
|
+
`)),typeof n.closeAllConnections=="function"&&n.closeAllConnections(),n.close(()=>t()),setTimeout(()=>t(),300)}});n.listen(0,"127.0.0.1",()=>{let o=`https://ozmoz.io/cli-auth?port=${n.address().port}`;u(`
|
|
52
|
+
[OZmoz] Waiting for authentication...`),u(`If your browser does not open automatically, visit: ${o}`),Pe(o);let d=Number(process.env.FLEETBO_LOGIN_TIMEOUT_MS)||12e4;setTimeout(()=>{try{typeof n.closeAllConnections=="function"&&n.closeAllConnections()}catch{}n.close(),r(new Error(`Login timed out after ${Math.round(d/1e3)}s: browser approval not received. Run "npx ozmoz login" once more and approve in the browser.`))},d).unref()}),n.on("error",s=>r(s))})}async function De(e,t,r){let n=await J();return new Promise((s,o)=>{let d=JSON.stringify({enterpriseID:e,schema:t,workflow:r}),c={hostname:"api.ozmoz.io",path:"/sync-schema",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(d),...n?{Authorization:`Bearer ${n}`}:{}}},h=Y.request(c,f=>{let p="";f.on("data",m=>{p+=m}),f.on("end",()=>{f.statusCode>=200&&f.statusCode<300?s(JSON.parse(p)):f.statusCode===401?o(new Error('Please sign in first: run "npx ozmoz login".')):f.statusCode===403?o(new Error("Access denied: your account does not have access to this project.")):o(new Error(`OZmoz sync failed with HTTP ${f.statusCode}`))})});h.on("error",f=>o(f)),h.write(d),h.end()})}function B(e){let t=a.join(e,".env");if(!i.existsSync(t))return{};let r=i.readFileSync(t,"utf8"),n={};return r.split(`
|
|
53
|
+
`).forEach(s=>{let[o,...d]=s.split("=");o&&d.length>0&&(n[o.trim()]=d.join("=").trim())}),n}async function W(e,t){let r=await J();return new Promise((n,s)=>{let o=JSON.stringify({projectId:e,moduleName:t}),d={hostname:"api.ozmoz.io",path:"/cli/getmodulecache",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(o),...r?{Authorization:`Bearer ${r}`}:{}}},c=Y.request(d,h=>{let f="";h.on("data",p=>{f+=p}),h.on("end",()=>{if(h.statusCode>=200&&h.statusCode<300)try{let p=JSON.parse(f);if(p.success&&p.found&&p.module){let m=p.module.code||p.module.mockCode;n(typeof m=="string"?JSON.parse(m):m)}else n(null)}catch{s(new Error(`Invalid JSON response for ${t}.`))}else h.statusCode===401||h.statusCode===403?s(new Error('Access denied. Run "npx ozmoz login" with an account that has access to this project.')):s(new Error(`Server error ${h.statusCode} while fetching ${t}`))})});c.on("error",h=>s(h)),c.write(o),c.end()})}var C=process.argv.slice(2),k=C[0],Re=["id","_id","userId","author","dateCreated","createdAt","_schema_version"],re=e=>Re.includes(e)||/^_/.test(e)||/^(created|updated|modified|deleted)(At|By)$/i.test(e);function U(e){return!e||typeof e!="object"?null:e.fields&&typeof e.fields=="object"?e.fields:e.schema&&typeof e.schema=="object"?e.schema:null}function Fe(e){let t=[];if(!e||typeof e!="object"||!e.collections)return{schema:e,changed:!1,notes:t};let r=JSON.parse(JSON.stringify(e));return Object.entries(r.collections).forEach(([n,s])=>{let o=U(s);o&&Object.keys(o).forEach(d=>{re(d)&&o[d]&&typeof o[d]=="object"&&o[d].required===!0&&(o[d].required=!1,t.push(n+"."+d))})}),{schema:r,changed:t.length>0,notes:t}}function Le(e){let t=String(typeof e=="string"?e:e&&e.type||"").toLowerCase(),r=e&&(e.enum||e.values||e.options);return Array.isArray(r)&&r.length?JSON.stringify(r[0]):t==="number"||t==="integer"||t==="float"?"0":t==="boolean"||t==="toggle"?"false":t==="date"||t==="datetime"||t==="timestamp"?"new Date().toISOString()":t==="reference"?"'<id du document li\xE9>'":t==="media"?"null /* image/fichier : oz.addWithMedia */":t==="array"?"[]":"'\u2026'"}function Ze(e){let r=Object.entries(e&&e.collections||{}),n=`## \u{1F4E5} CE QUE LE CODE DOIT ENVOYER (MOD\xC8LES D'\xC9CRITURE)
|
|
54
54
|
|
|
55
|
-
`;
|
|
56
|
-
`,
|
|
57
|
-
`:(s.forEach(([
|
|
55
|
+
`;n+=`### \u{1F194} Identifiants
|
|
56
|
+
`,n+="* OZmoz attribue lui-m\xEAme `_id` (cha\xEEne) \xE0 chaque document. Utilise `doc._id` partout ; \xE0 la lecture, `doc.id` existe comme alias de `_id` (m\xEAme valeur), en prototype comme en production, mais la r\xE9f\xE9rence est `_id`.\n",n+="* N'envoie **jamais** `id` ni `_id` dans `oz.add` : `oz.add` renvoie `{ success, documentId }` (`documentId` = `_id`).\n",n+="* Pour lire, modifier ou supprimer un document : `oz.getDoc(table, doc._id)`, `oz.update(table, doc._id, {\u2026})`, `oz.delete(table, doc._id)`. Pour relier deux collections, stocke le `_id` du document li\xE9 dans un champ (ex. `playlistId`).\n",n+="* `userId`, `createdAt` et les autres champs de tra\xE7abilit\xE9 sont renseign\xE9s par le moteur : ne les envoie pas.\n\n";let s=r.filter(([,o])=>U(o)&&Object.keys(U(o)).length>0);return s.length===0?n+`_Aucune collection scell\xE9e pour l'instant : les mod\xE8les appara\xEEtront apr\xE8s la mod\xE9lisation d'Alex._
|
|
57
|
+
`:(s.forEach(([o,d])=>{let c=U(d),h=Object.entries(c).filter(([m])=>!re(m));n+="### `"+o+`\`
|
|
58
58
|
|
|
59
59
|
| Champ | Type | Obligatoire | Valeurs / remarques |
|
|
60
60
|
|---|---|---|---|
|
|
61
|
-
`,h.forEach(([m,g])=>{let w=typeof g=="string"?g:g&&g.type||"any",E=g&&(g.enum||g.values||g.options),
|
|
62
|
-
`});let f=h.filter(([,m])=>m&&m.required===!0),p=h.filter(([,m])=>!(m&&m.required===!0));
|
|
63
|
-
`,f.forEach(([m,g])=>{
|
|
64
|
-
`}),p.length&&(
|
|
65
|
-
`),
|
|
66
|
-
`,
|
|
67
|
-
`,
|
|
68
|
-
`,
|
|
69
|
-
`,
|
|
61
|
+
`,h.forEach(([m,g])=>{let w=typeof g=="string"?g:g&&g.type||"any",E=g&&(g.enum||g.values||g.options),T=Array.isArray(E)&&E.length?"parmi : "+E.map(v=>"`"+v+"`").join(", "):"";n+="| `"+m+"` | "+w+" | "+(g&&g.required===!0?"\u2705 oui":"non")+" | "+T+` |
|
|
62
|
+
`});let f=h.filter(([,m])=>m&&m.required===!0),p=h.filter(([,m])=>!(m&&m.required===!0));n+="\nInsertion valide :\n```js\nawait oz.add('"+o+`', {
|
|
63
|
+
`,f.forEach(([m,g])=>{n+=" "+m+": "+Le(g)+`,
|
|
64
|
+
`}),p.length&&(n+=" // optionnels : "+p.map(([m])=>m).join(", ")+`
|
|
65
|
+
`),n+=`}, {
|
|
66
|
+
`,n+=" desc: 'Cr\xE9ation d\\'un \xE9l\xE9ment dans "+o+`',
|
|
67
|
+
`,n+=` role: 'AUTHENTICATED' // ou 'ANONYMOUS', 'ADMIN'
|
|
68
|
+
`,n+=` // guard: 'condition sur les donn\xE9es' (optionnel)
|
|
69
|
+
`,n+="});\n```\n\n"}),n)}function Me(e,t,r=""){return`# THE OZMOZ DATA CONTRACT (SVRO DATA CONTRACT)
|
|
70
70
|
|
|
71
71
|
### \u{1F6A8} MANDATORY CODE RULES (READ BEFORE WRITING ANY VIEW)
|
|
72
72
|
1. **Identifiers:** the database generates \`_id\` (string) for each document. Use \`doc._id\` for \`key={doc._id}\`, for \`oz.getDoc/update/delete\`, and to link two collections (store the linked document's \`_id\` in a field, e.g., \`playlistId\`). \`doc.id\` exists on read as an alias (same value), but the canonical reference is \`_id\`.
|
|
@@ -612,7 +612,7 @@ ${e?.context||t?.context||"No written specifications provided."}
|
|
|
612
612
|
|
|
613
613
|
---
|
|
614
614
|
|
|
615
|
-
${
|
|
615
|
+
${Ze(e)}
|
|
616
616
|
|
|
617
617
|
---
|
|
618
618
|
|
|
@@ -627,29 +627,29 @@ ${JSON.stringify({...e||{},context:void 0},null,2)}
|
|
|
627
627
|
\`\`\`json
|
|
628
628
|
${JSON.stringify(t||{},null,2)}
|
|
629
629
|
\`\`\`
|
|
630
|
-
`}function
|
|
630
|
+
`}function Ue(e){return String(e).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]/g,"_").replace(/^_+|_+$/g,"").slice(0,30)||"app"}async function Be(e,t){let r=B(e),n=i.existsSync(a.join(e,"svro.schema.json"));(r.OZ_DB_KEY||n)&&(y("Cannot create project: an OZmoz project already exists in this folder."),u('\u2192 Use "npx ozmoz sync" or evolve the existing contract instead.'),process.exit(1));let s=(t||"").trim();s||(y('Project name is required. Usage: npx ozmoz create "<name>"'),process.exit(1));let o=a.join(e,".ozmoz.lock");if(i.existsSync(o)){let c=!1;try{let h=Number(i.readFileSync(o,"utf8"));Date.now()-h>6e4&&(c=!0)}catch{c=!0}c||(y("A project creation is already running in this folder."),process.exit(1)),i.unlinkSync(o)}i.writeFileSync(o,String(Date.now()));let d=()=>{if(i.existsSync(o))try{i.unlinkSync(o)}catch{}};try{let c=process.env.FLEETBO_DEV_TOKEN;if(!c){let E=a.join(A.homedir(),".fleetbo","credentials.json");if(i.existsSync(E))try{c=JSON.parse(i.readFileSync(E,"utf8")).devToken}catch{}}c||(d(),y('Missing developer token. Please run "npx ozmoz login" first.'),process.exit(1)),u(`[OZmoz] Creating project "${s}" on Cloud...`);let h=Ue(s),f=[h,`${h.slice(0,24)}_${Math.random().toString(36).slice(2,6)}`,`${h.slice(0,24)}_${Math.random().toString(36).slice(2,6)}`],p=null,m=null;for(let E of f)try{let T=await fetch(X+"createprojectlight",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${c}`},body:JSON.stringify({data:{name:s,keyApp:E}})}),v=await T.json();if(T.ok&&!v.error){p={keyApp:E,data:v.result?.data||v.data||{}};break}else m=v.error?.message||`HTTP ${T.status}`}catch(T){m=T.message}p||(d(),y(`Failed to create project on Cloud: ${m||"Unknown error"}`),process.exit(1));let g=p.data.bootstrapToken;await q(e,[`--keyApp=${p.keyApp}`,`--token=${g}`]);let w=a.join(e,"package.json");K(e,w),await ne(e,[]),u(`
|
|
631
631
|
[OZmoz] Project "${s}" (${p.keyApp}) created and configured successfully.`),u(`AI AGENTS: do NOT stop here and do NOT ask the user to restart. Continue NOW in the same turn: scaffold the frontend if missing, run "npx ozmoz sync", read ozmoz_contract.md, write ALL views in trial mode (OZ_PROTOTYPE=true), then start the dev server and ask the user to validate.
|
|
632
|
-
`)}finally{if(i.existsSync(
|
|
632
|
+
`)}finally{if(i.existsSync(o))try{i.unlinkSync(o)}catch{}}}async function $e(e){let t=process.env.FLEETBO_DEV_TOKEN;if(!t){let O=a.join(A.homedir(),".fleetbo","credentials.json");if(i.existsSync(O))try{t=JSON.parse(i.readFileSync(O,"utf8")).devToken}catch{}}let n=B(e).OZ_KEY_APP;(!n||!t)&&(y("Cannot update: project uninitialized or dev token missing."),process.exit(1));let s=i.existsSync(a.join(e,"src"))?a.join(e,"src"):e,o=new Set([".jsx",".tsx",".vue",".svelte",".js",".ts",".html"]),d=[],c=0;function h(O){let l=[];try{l=i.readdirSync(O,{withFileTypes:!0})}catch{return}for(let z of l){if(/^(node_modules|\.git|dist|build|\.next|\.svelte-kit)$/.test(z.name))continue;let S=a.join(O,z.name);if(z.isDirectory())h(S);else if(o.has(a.extname(z.name))&&!z.name.endsWith(".d.ts"))try{let b=i.statSync(S);if(b.size>3e5)continue;let F=i.readFileSync(S,"utf8");/oz\.|<oz-/i.test(F)&&(c+=b.size,d.push({name:a.relative(e,S).split(a.sep).join("/"),content:F}))}catch{}}}let f=C.find(O=>O.startsWith("--files=")),p=f?f.slice(8).split(",").map(O=>O.trim()).filter(Boolean):null;if(p&&p.length>0)for(let O of p){let l=a.resolve(e,O);if(i.existsSync(l)){let z=i.readFileSync(l,"utf8");c+=i.statSync(l).size,d.push({name:a.relative(e,l).split(a.sep).join("/"),content:z})}}else h(s);d.length===0&&(y("Cannot update: no components using oz.* found."),process.exit(1)),c>8*1024*1024&&(y("Payload too large: total UI source files exceed 8 MB limit."),process.exit(1));let m=C.find(O=>O.startsWith("--rules=")),g=m?m.slice(8).trim():"",w=C.find(O=>O.startsWith("--reasoning=")),E=w?w.slice(12).trim().toLowerCase():void 0;E!=="standard"&&E!=="high"&&(y("Missing or invalid --reasoning=<standard|high>."),y("If you are an AI agent: ask the user which analysis depth they want (numbered [1]/[2] choice, recommended option first) before running this command, then pass --reasoning=standard or --reasoning=high accordingly. Never pick it yourself."),process.exit(1)),u(`[OZmoz] Sending ${d.length} component(s) to Cloud Engine for modeling...`);try{let O,l,z;for(let S=1;S<=3&&(O=await fetch("https://updateprojectlight-jqycakhlxa-uc.a.run.app",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({data:{keyApp:n,name:n,newViewFiles:d,oz_rules:g,reasoning:E}})}),l=await O.json().catch(()=>({})),z=l.result?.data||l.data||l,!(O.status<500||S===3));S++)u("\u2192 The modeling service is busy (attempt "+S+"/3). Retrying in 20s..."),await new Promise(b=>setTimeout(b,2e4));if(!O.ok||l.error||z.success===!1){let S=l.error?.message||z.warnings?.join(", ")||l.message||"Server error";y(`Update failed: ${S}`),process.exit(1)}u("\u2192 Cloud modeling response: "+JSON.stringify(l).slice(0,800))}catch(O){y(`Network error: ${O.message}`),process.exit(1)}u("\u2192 Structure modeled. Sealing into OZmoz SVRO Kernel...");let T=_(process.execPath,[__filename,"sync"],{stdio:"inherit",cwd:e}),v=Date.now();for(let O=1;O<=5&&T.status===0;O++){let l=!1;try{let z=JSON.parse(i.readFileSync(a.join(e,"svro.schema.json"),"utf8")),S=JSON.parse(i.readFileSync(a.join(e,"svro.workflow.json"),"utf8"));l=Object.keys(z.collections||{}).length>0&&(S.sequences||[]).length>0}catch{}if(l)break;u("\u2192 Contract still empty, waiting for the cloud (attempt "+O+"/5, "+Math.round((Date.now()-v)/1e3)+"s elapsed)..."),await new Promise(z=>setTimeout(z,6e3)),T=_(process.execPath,[__filename,"sync"],{stdio:"inherit",cwd:e})}let N=!1,I=!1;try{let O=JSON.parse(i.readFileSync(a.join(e,"svro.schema.json"),"utf8"));N=Object.keys(O.collections||{}).length>0,I=(JSON.parse(i.readFileSync(a.join(e,"svro.workflow.json"),"utf8")).sequences||[]).length>0}catch{}T.status===0&&N&&I?u(`
|
|
633
633
|
[OZmoz] Contract locked into SVRO Kernel.
|
|
634
|
-
`):
|
|
634
|
+
`):T.status===0&&N&&!I?(y(`
|
|
635
635
|
[OZmoz] Contract incomplete: the workflow is empty. Rerun the update before writing any code.
|
|
636
|
-
`),process.exit(1)):
|
|
636
|
+
`),process.exit(1)):T.status===0&&!N?(y(`
|
|
637
637
|
[OZmoz] Sync completed with 0 collections. Rerun with --reasoning=high.
|
|
638
638
|
`),process.exit(1)):(y(`
|
|
639
639
|
[OZmoz] Sync failed. Run "npx ozmoz sync".
|
|
640
|
-
`),process.exit(1))}async function
|
|
640
|
+
`),process.exit(1))}async function Ge(){let e=process.cwd(),t=a.join(e,"package.json");if(k==="init"||k==="doctor")u(`
|
|
641
641
|
[OZmoz] Initializing and configuring OZmoz...`),await q(e,C),K(e,t),u(`
|
|
642
642
|
[OZmoz] Configuration completed successfully!
|
|
643
643
|
`),u(`
|
|
644
644
|
Next step:`),u(` npx ozmoz sync (Sync cloud schema & generate IDE types)
|
|
645
|
-
`);else if(k==="sync"){let r=C.includes("--dry-run"),
|
|
646
|
-
`),u("\u2192 Local svro.schema.json updated.")),m=await W(f,"svro.workflow.json"),m&&(i.writeFileSync(
|
|
647
|
-
`),u("\u2192 Local svro.workflow.json updated."))}catch(v){y(`\u26A0\uFE0F Cloud fetch bypassed: ${v.message}. Using local file fallback.`)}else y("\u26A0\uFE0F OZ_ENTERPRISE_ID missing in .env. Skipping cloud fetch.");let g=p;if(!g&&i.existsSync(s))try{g=JSON.parse(i.readFileSync(s,"utf8"))}catch{}g||(g={context:"Application en cours de mod\xE9lisation initiale.",collections:{}},i.writeFileSync(s,JSON.stringify(g,null,2)+`
|
|
648
|
-
`,"utf8"),u("\u2192 Initial blank svro.schema.json created."));let w=m;if(!w&&i.existsSync(
|
|
649
|
-
`,"utf8"),u("\u2192 Initial blank svro.workflow.json created."));{let v=
|
|
650
|
-
`,"utf8"),u("\u2192 Managed by system, no longer required from your code: "+v.notes.join(", ")+"."))}let{protocol:E}=await
|
|
645
|
+
`);else if(k==="sync"){let r=C.includes("--dry-run"),n=C.includes("--check-only");u(`[OZmoz] Syncing schema...${r?" (dry-run)":""}${n?" (check-only, no network)":""}`),r||enableFacebookLoginIfUsed(e),r||me(e);let s=a.join(e,"svro.schema.json"),o=a.join(e,"svro.workflow.json"),d=a.join(e,"ozmoz_contract.md"),c=a.join(e,"src","ozmoz.d.ts"),f=B(e).OZ_ENTERPRISE_ID,p=null,m=null;if(n)u("\u2192 Mode check-only : Local contract used, no cloud fetch..");else if(f)try{u(" \u2192 Fetching latest contracts from OZmoz..."),p=await W(f,"svro.schema.json"),p&&(i.writeFileSync(s,JSON.stringify(p,null,2)+`
|
|
646
|
+
`),u(" \u2192 Local svro.schema.json updated.")),m=await W(f,"svro.workflow.json"),m&&(i.writeFileSync(o,JSON.stringify(m,null,2)+`
|
|
647
|
+
`),u(" \u2192 Local svro.workflow.json updated."))}catch(v){y(`\u26A0\uFE0F Cloud fetch bypassed: ${v.message}. Using local file fallback.`)}else y("\u26A0\uFE0F OZ_ENTERPRISE_ID missing in .env. Skipping cloud fetch.");let g=p;if(!g&&i.existsSync(s))try{g=JSON.parse(i.readFileSync(s,"utf8"))}catch{}g||(g={context:"Application en cours de mod\xE9lisation initiale.",collections:{}},i.writeFileSync(s,JSON.stringify(g,null,2)+`
|
|
648
|
+
`,"utf8"),u("\u2192 Initial blank svro.schema.json created."));let w=m;if(!w&&i.existsSync(o))try{w=JSON.parse(i.readFileSync(o,"utf8"))}catch{}w||(w={actors:{},sequences:[]},i.writeFileSync(o,JSON.stringify(w,null,2)+`
|
|
649
|
+
`,"utf8"),u("\u2192 Initial blank svro.workflow.json created."));{let v=Fe(g);v.changed&&(g=v.schema,i.writeFileSync(s,JSON.stringify(g,null,2)+`
|
|
650
|
+
`,"utf8"),u("\u2192 Managed by system, no longer required from your code: "+v.notes.join(", ")+"."))}let{protocol:E}=await te(),T=Me(g,w,E);i.writeFileSync(d,T,"utf8"),u(" \u2192 Contract markdown successfully generated at project root.");try{let v=Object.keys(g.collections||{});if(f&&!n&&Object.keys(g.collections||{}).length>0)try{await De(f,g,w),u(" \u2192 Schema successfully sealed into OZmoz Kernel.")}catch(l){y(`\u26A0\uFE0F Kernel Shield sync warning: ${l.message}`)}let N=g.collections||{},I=Object.entries(N).map(([l,z])=>{let S=z.fields||z.schema||{},b=Object.entries(S).map(([F,P])=>{let L="any",j=String(typeof P=="string"?P:P.type||"").toLowerCase();return j==="string"||j==="text"||j==="media"?L="string":j==="number"?L="number":j==="boolean"||j==="toggle"?L="boolean":j==="array"&&(L="any[]"),` ${F}?:${L};`}).join(`
|
|
651
651
|
`);return` ${l}: {
|
|
652
|
-
${
|
|
652
|
+
${b}
|
|
653
653
|
};`}).join(`
|
|
654
654
|
`),O=v.length>0?v.map(l=>`'${l}'`).join(" | "):"string";if(i.existsSync(a.dirname(c))){let l=`/* eslint-disable */
|
|
655
655
|
/* prettier-ignore */
|
|
@@ -799,7 +799,7 @@ declare module 'svelte/elements' {
|
|
|
799
799
|
'oz-reference': OzProp;
|
|
800
800
|
}
|
|
801
801
|
}
|
|
802
|
-
`;i.writeFileSync(c,l)}
|
|
802
|
+
`;i.writeFileSync(c,l)}le(e),de(e,t),ue(e),pe(e),u(`[OZmoz] Schema synced successfully (${v.length} collection(s)).`),r&&u(" \u2192 [Dry-run] Schema is valid.")}catch(v){y(`Error during synchronization: ${v.message}`),process.exit(1)}}else if(k==="create"){let r=C.slice(1).filter(n=>!n.startsWith("--")).join(" ");await Be(e,r),process.exit(0)}else if(k==="update")await $e(e),process.exit(0);else if(k==="mcp"){let r=C.includes("--run"),n=!1;if(!r&&!process.stdin.isTTY&&(r=await new Promise(s=>{let o=process.stdin,d=null,c=p=>{clearTimeout(d),o.removeListener("data",h),o.removeListener("end",f),o.removeListener("error",f),s(p)},h=p=>{o.pause(),o.unshift(p),n=!0,c(!0)},f=()=>c(!1);d=setTimeout(()=>{o.pause(),c(!1)},1500),o.on("data",h),o.once("end",f),o.once("error",f)})),r){try{await import(`file://${oe()}`),n&&process.stdin.resume()}catch(s){y(`Failed to start MCP server: ${s.message}`),process.exit(1)}return}await ne(e,C),process.exit(0)}else k==="login"?(await je(e),process.exit(0)):k==="start"?Ne(e,C):["--help","-h","help"].includes(k)?(u(`OZmoz CLI
|
|
803
803
|
|
|
804
804
|
Commands:
|
|
805
805
|
login Authenticate this machine (opens the browser: approve once, the command exits by itself)
|
|
@@ -815,4 +815,4 @@ Commands:
|
|
|
815
815
|
|
|
816
816
|
AI agents: to create a project, call the MCP tool "oz_create_project".
|
|
817
817
|
To evolve schema/workflow from UI files, call the MCP tool "oz_update_project", then run "sync".
|
|
818
|
-
`),process.exit(0)):(y('Unknown command. Run "npx ozmoz --help".'),process.exit(1))}
|
|
818
|
+
`),process.exit(0)):(y('Unknown command. Run "npx ozmoz --help".'),process.exit(1))}Ge().catch(e=>{y(`OZmoz CLI Error: ${e.message}`),process.exit(1)});
|