ozmoz 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.cjs +37 -37
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- var l=require("fs"),c=require("path"),ne=require("https"),pe=require("http"),A=require("os"),{exec:me,spawnSync:_}=require("child_process"),he=[c.join(__dirname,"..","scripts","setup-lib.cjs"),c.join(__dirname,"..","..","scripts","setup-lib.cjs"),c.join(__dirname,"scripts","setup-lib.cjs")],fe=he.find(l.existsSync)||c.join(__dirname,"..","scripts","setup-lib.cjs"),{runFullSetup:oe,handleAuthAndEnv:se,touchTsConfig:ge,attemptViteAutoImportPatch:ye,injectUniversalRuntime:ve,writeBrowserConfig:ze,patchAllowScripts:Ee}=require(fe),Oe="\x1B[34m",we="\x1B[31m",re="\x1B[0m";function m(e){console.log(`${Oe}${e}${re}`)}function z(e){console.error(`${we}${e}${re}`)}var ie=`
2
+ var l=require("fs"),c=require("path"),te=require("https"),pe=require("http"),A=require("os"),{exec:me,spawnSync:_}=require("child_process"),he=[c.join(__dirname,"..","scripts","setup-lib.cjs"),c.join(__dirname,"..","..","scripts","setup-lib.cjs"),c.join(__dirname,"scripts","setup-lib.cjs")],fe=he.find(l.existsSync)||c.join(__dirname,"..","scripts","setup-lib.cjs"),{runFullSetup:ne,handleAuthAndEnv:oe,touchTsConfig:ge,attemptViteAutoImportPatch:ye,injectUniversalRuntime:ve,writeBrowserConfig:ze,patchAllowScripts:Oe}=require(fe),we="\x1B[34m",Ee="\x1B[31m",se="\x1B[0m";function m(e){console.log(`${we}${e}${se}`)}function z(e){console.error(`${Ee}${e}${se}`)}var re=`
3
3
  1. YOUR FREEDOM: the entire frontend belongs to you (rendering, state, styles, navigation, components, UI logic). Only one exception: the technology (the framework), which the user chooses among supported options; you ask them before installing anything. ozmoZ only handles data, authentication, and storage. A change that does not touch data requires nobody's approval.
4
4
 
5
5
  2. YOUR USER is not a developer. In your messages to the user (not in code), always speak in the user's language (detect it from their prompt, e.g. French if they write in French) in a plain, jargon-free manner: never say prototype, true, false, .env, contract, schema, sync, Edge, endpoint, collection, nor oz_update_project. Instead, say (adapt to their language):
@@ -46,7 +46,7 @@ var l=require("fs"),c=require("path"),ne=require("https"),pe=require("http"),A=r
46
46
 
47
47
  8. NEVER: modify OZ_PROTOTYPE on your own initiative (only with explicit user consent, after explaining the difference between trial mode and live mode in one sentence); design or edit svro.schema.json and svro.workflow.json (Alex's authority); write a backend, API route, or ORM; invent oz.* methods or <oz-*> tags.
48
48
 
49
- 9. WRITE GOVERNANCE (MANDATORY): every oz.add / addWithId / addWithUserId / addWithMedia / update / delete call MUST carry a literal 3rd argument {desc, guard, role} (4th for update) \u2014 never optional, never omitted, even in trial mode (this parameter is never sent over the network; it only informs Alex). "desc": one sentence explaining the exact business intent of this write operation. "guard": a simple boolean condition on the "data" fields you send (e.g., "price > 0 && name.length > 2") \u2014 omit if no rule is required. "role": required access level for this action ("ANONYMOUS", "AUTHENTICATED", or an explicit business role like "MANAGER" if defined). Never invent a "guard" you cannot justify by actual screen logic; in doubt, omit "guard" rather than writing an arbitrary one. This applies to reads only if relevant (never mandatory for getDocsG/getDocsU/getDoc/join).`,k="ozmoz",q=/<!-- (?:ozmoz|fleetbo):start[^>]*-->[\s\S]*?<!-- (?:ozmoz|fleetbo):end -->/,Te="<!-- ozmoZ:end -->",ae="# 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"},Se=`D1. EVERY request to create a NEW application MUST trigger the 'oz_create_project' MCP tool. However, if the project's '.env' already contains OZ_DB_KEY (or if 'svro.schema.json' exists), the project ALREADY EXISTS: NEVER call 'oz_create_project'; use 'oz_update_project' instead ('keyApp' is the value of OZ_KEY_APP; pass it explicitly if the session was not launched from the project folder).
49
+ 9. WRITE GOVERNANCE (MANDATORY): every oz.add / addWithId / addWithUserId / addWithMedia / update / delete call MUST carry a literal 3rd argument {desc, guard, role} (4th for update) \u2014 never optional, never omitted, even in trial mode (this parameter is never sent over the network; it only informs Alex). "desc": one sentence explaining the exact business intent of this write operation. "guard": a simple boolean condition on the "data" fields you send (e.g., "price > 0 && name.length > 2") \u2014 omit if no rule is required. "role": required access level for this action ("ANONYMOUS", "AUTHENTICATED", or an explicit business role like "MANAGER" if defined). Never invent a "guard" you cannot justify by actual screen logic; in doubt, omit "guard" rather than writing an arbitrary one. This applies to reads only if relevant (never mandatory for getDocsG/getDocsU/getDoc/join).`,k="ozmoz",q=/<!-- (?:ozmoz|fleetbo):start[^>]*-->[\s\S]*?<!-- (?:ozmoz|fleetbo):end -->/,Te="<!-- ozmoZ:end -->",ie="# OZMOZ DATA OS \u2014 DIRECTIVES SYST\xC8ME",$={claude:"Claude Code",cursor:"Cursor",windsurf:"Windsurf",devin:"Devin",codex:"Codex CLI",gemini:"Gemini CLI",antigravity:"Antigravity CLI"},Se=`D1. EVERY request to create a NEW application MUST trigger the 'oz_create_project' MCP tool. However, if the project's '.env' already contains OZ_DB_KEY (or if 'svro.schema.json' exists), the project ALREADY EXISTS: NEVER call 'oz_create_project'; use 'oz_update_project' instead ('keyApp' is the value of OZ_KEY_APP; pass it explicitly if the session was not launched from the project folder).
50
50
 
51
51
  D2. The MCP server is already pre-authenticated: do not inspect the shell ('env', 'grep'), do not traverse the filesystem outside the project root, and do not trigger a login (reading the project's '.env' is allowed). Only exception: if a tool returns "FLEETBO_DEV_TOKEN missing", run 'npx ozmoz login' ONCE, wait for it to complete (the user approves in the browser: simply notify them), then call the tool again.
52
52
 
@@ -90,32 +90,32 @@ D15 (CONTRACT FIRST & STRICT LAST-RESORT ESCALATION):
90
90
  - ZERO EXPLORATION: Never execute exploratory shell scripts ('find', 'grep', 'node -e') or web searches.
91
91
  - LAST-RESORT ESCALATION ONLY: ONLY if you have exhausted all contract checks and code fixes, and the issue is proven to be a persistent platform or SDK bug with NO possible solution on your end, stop cleanly and tell the user:
92
92
  \xAB Je ne parviens pas \xE0 r\xE9soudre ce blocage malgr\xE9 la v\xE9rification du contrat et du code. En dernier recours, vous pouvez ouvrir un ticket sur https://ozmoz.io \xBB.
93
-
93
+
94
94
  `,P={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 be(){try{return JSON.parse(l.readFileSync(c.join(__dirname,"..","package.json"),"utf8")).version||"dev"}catch{return"dev"}}function xe(){return`# OZMOZ DATA OS \u2014 DIRECTIVES AGENT
95
95
 
96
96
  `+Se+`
97
97
 
98
98
  ## PROTOCOLE DONN\xC9ES : QUI FAIT QUOI (AGENT FRONTEND \u2194 ALEX AI)
99
99
 
100
- `+ie}function X(e,o){return"<!-- ozmoz:start v"+e+` -->
100
+ `+re}function K(e,o){return"<!-- ozmoz:start v"+e+` -->
101
101
  `+o.trim()+`
102
102
  `+Te+`
103
- `}function Ae(e,o,s){let n=l.existsSync(e)?l.readFileSync(e,"utf8"):null,a,t;return n===null?(a="created",t=o):q.test(n)?(t=n.replace(q,()=>o.trimEnd()),a=t===n?"unchanged":"updated"):n.includes(ae)?(a="migrated",t=o):(a="appended",t=n.replace(/\s*$/,`
103
+ `}function Ae(e,o,s){let n=l.existsSync(e)?l.readFileSync(e,"utf8"):null,a,t;return n===null?(a="created",t=o):q.test(n)?(t=n.replace(q,()=>o.trimEnd()),a=t===n?"unchanged":"updated"):n.includes(ie)?(a="migrated",t=o):(a="appended",t=n.replace(/\s*$/,`
104
104
 
105
- `)+o),!s&&a!=="unchanged"&&(a==="migrated"&&l.writeFileSync(e+".ozmoz-legacy.bak",n,"utf8"),l.mkdirSync(c.dirname(e),{recursive:!0}),l.writeFileSync(e,t,"utf8")),a}function H(){return[c.join(__dirname,"ozmoz-mcp.js"),c.join(__dirname,"..","bin","ozmoz-mcp.js"),c.join(__dirname,"bin","ozmoz-mcp.js"),c.join(__dirname,"..","src","bin","ozmoz-mcp.js")].find(o=>l.existsSync(o))||c.resolve(__dirname,"ozmoz-mcp.js")}function $(e,o){let s=H(),n={command:process.execPath,args:[s]},a={};if(l.existsSync(e))try{let r=JSON.parse(l.readFileSync(e,"utf8"));if(!r||typeof r!="object"||Array.isArray(r))return"invalid";a=r}catch{return"invalid"}let t=a.mcpServers&&a.mcpServers[k];return t&&JSON.stringify(t)===JSON.stringify(n)?"unchanged":(a.mcpServers=a.mcpServers||{},a.mcpServers[k]=n,o||(l.mkdirSync(c.dirname(e),{recursive:!0}),l.writeFileSync(e,JSON.stringify(a,null,2)+`
106
- `,"utf8")),t?"updated":"created")}function ke(e){if(!l.existsSync(e))return"absent";let o;try{o=JSON.parse(l.readFileSync(e,"utf8"))}catch{return"invalid"}return!o||typeof o!="object"||!o.mcpServers||!o.mcpServers[k]?"absent":(delete o.mcpServers[k],l.writeFileSync(e,JSON.stringify(o,null,2)+`
107
- `,"utf8"),"removed")}function R(e){let o=process.platform==="win32"?"where":"which";return _(o,[e],{stdio:"ignore"}).status===0}function Y(e,o,s){let n=_(e,o,{encoding:"utf8",timeout:s||3e4,shell:process.platform==="win32"});return{ok:n.status===0,out:(n.stdout||"")+(n.stderr||"")}}function je(e,o,s){let n=e==="claude"?["--scope",s||"user"]:[],a=H(),t=["mcp","add"].concat(n,[k,"--",process.execPath,a]),r=["mcp","remove",k].concat(n),i=e+" "+t.join(" ");if(!R(e))return{status:"manual",detail:i};if(o)return{status:"planned",detail:i};let d=Y(e,["mcp","list"],6e4),u=d.ok?d.out.split(`
105
+ `)+o),!s&&a!=="unchanged"&&(a==="migrated"&&l.writeFileSync(e+".ozmoz-legacy.bak",n,"utf8"),l.mkdirSync(c.dirname(e),{recursive:!0}),l.writeFileSync(e,t,"utf8")),a}function ae(){return[c.join(__dirname,"ozmoz-mcp.js"),c.join(__dirname,"..","bin","ozmoz-mcp.js"),c.join(__dirname,"bin","ozmoz-mcp.js"),c.join(__dirname,"..","src","bin","ozmoz-mcp.js")].find(o=>l.existsSync(o))||c.resolve(__dirname,"ozmoz-mcp.js")}function R(e,o){let s={command:process.execPath,args:[__filename,"mcp","--run"]},n={};if(l.existsSync(e))try{let t=JSON.parse(l.readFileSync(e,"utf8"));if(!t||typeof t!="object"||Array.isArray(t))return"invalid";n=t}catch{return"invalid"}let a=n.mcpServers&&n.mcpServers[k];return a&&JSON.stringify(a)===JSON.stringify(s)?"unchanged":(n.mcpServers=n.mcpServers||{},n.mcpServers[k]=s,o||(l.mkdirSync(c.dirname(e),{recursive:!0}),l.writeFileSync(e,JSON.stringify(n,null,2)+`
106
+ `,"utf8")),a?"updated":"created")}function ke(e){if(!l.existsSync(e))return"absent";let o;try{o=JSON.parse(l.readFileSync(e,"utf8"))}catch{return"invalid"}return!o||typeof o!="object"||!o.mcpServers||!o.mcpServers[k]?"absent":(delete o.mcpServers[k],l.writeFileSync(e,JSON.stringify(o,null,2)+`
107
+ `,"utf8"),"removed")}function L(e){let o=process.platform==="win32"?"where":"which";return _(o,[e],{stdio:"ignore"}).status===0}function Y(e,o,s){let n=_(e,o,{encoding:"utf8",timeout:s||3e4,shell:process.platform==="win32"});return{ok:n.status===0,out:(n.stdout||"")+(n.stderr||"")}}function je(e,o,s){let n=e==="claude"?["--scope",s||"user"]:[],a=ae(),t=["mcp","add"].concat(n,[k,"--",process.execPath,a]),r=["mcp","remove",k].concat(n),i=e+" "+t.join(" ");if(!L(e))return{status:"manual",detail:i};if(o)return{status:"planned",detail:i};let d=Y(e,["mcp","list"],6e4),u=d.ok?d.out.split(`
108
108
  `):[],p=u.find(T=>/^[\s|│┃]*ozmoz(?![\w@-])/.test(T)),f=u.some(T=>/^\s*(?:fleetbo|fleetbo-svro):\s/.test(T));if(p&&p.includes("ozmoz@latest"))return{status:"unchanged",legacy:f};if(p&&!Y(e,r).ok)return{status:"manual",detail:e+" "+r.join(" ")+" && "+i,legacy:f};let v=Y(e,t);return v.ok?{status:p?"updated":"created",legacy:f}:{status:"failed",detail:i,message:v.out.trim().split(`
109
- `).slice(-2).join(" "),legacy:f}}function Ie(e){let o={user:!1,local:!1,others:[]};try{let s=JSON.parse(l.readFileSync(c.join(A.homedir(),".claude.json"),"utf8")),n=i=>!!i&&typeof i=="object"&&/fleetbo-svro|ozmoz/.test(String(i.command||"")+" "+JSON.stringify(i.args||[])),a=(i,d)=>{!i||typeof i!="object"||Object.keys(i).forEach(u=>{u===k?o[d]=!0:n(i[u])&&o.others.push({name:u,where:d})})};a(s.mcpServers,"user");let t=e;try{t=l.realpathSync(e)}catch{}let r=s.projects||{};Array.from(new Set([e,t])).forEach(i=>a((r[i]||{}).mcpServers,"local"))}catch{}return o}function _e(e){let o=Ie(e);o.user&&m(" \u26A0\uFE0F A global (user-scope) '"+k+"' entry from a previous version also exists: the tools appear in every project. To keep them in ozmoZ projects only: claude mcp remove "+k+" --scope user"),o.local&&m(" \u26A0\uFE0F A local-scope '"+k+"' entry exists for this folder and takes precedence over .mcp.json. Remove it: claude mcp remove "+k+" --scope local"),o.others.forEach(s=>{m(" \u26A0\uFE0F An old MCP entry named '"+s.name+"' ("+s.where+" scope) points to ozmoZ/Fleetbo. Remove it: claude mcp remove "+s.name+" --scope "+s.where)})}function Ce(e){let o=A.homedir(),s=n=>l.existsSync(n);return{claude:R("claude")||s(c.join(o,".claude")),cursor:s(c.join(e,".cursor"))||s(c.join(o,".cursor"))||R("cursor"),windsurf:s(c.join(o,".codeium","windsurf"))||s(c.join(e,".windsurf")),devin:s(c.join(e,".devin")),codex:R("codex")||s(c.join(o,".codex")),gemini:R("gemini")||s(c.join(o,".gemini")),antigravity:R("agy")||s(c.join(o,".antigravity"))||s(c.join(e,".antigravity"))}}var Ne={claude:{bin:"claude",rules:"CLAUDE.md",launch:e=>[e]},gemini:{bin:"gemini",rules:"GEMINI.md",launch:e=>["-i",e]},codex:{bin:"codex",rules:"AGENTS.md",launch:e=>[e]},antigravity:{bin:"agy",rules:"AGENTS.md",launch:e=>["-i",e]}};function Pe(e){return String(e||"").replace(/[^\p{L}\p{N} '.,:\-_]/gu," ").replace(/\s+/g," ").trim().slice(0,4e3)}function De(e,o,s){let n=Pe(o),a=n?"The project description is already given: "+n+". Ask me only the technology question, then build it: ask me another question only if essential information is missing.":"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.",t=null;try{let u=c.join(s,".env");if(l.existsSync(u)){let p=l.readFileSync(u,"utf8").match(/^\s*OZ_DB_KEY\s*=\s*(.+?)\s*$/m);p&&(t=p[1].trim())}}catch{}let r=l.existsSync(c.join(s,"svro.schema.json")),i=l.existsSync(c.join(s,"index.html"))||l.existsSync(c.join(s,"package.json")),d=t||r?"This folder ALREADY contains an ozmoZ project"+(t?" (OZ_DB_KEY="+t+")":"")+' \u2014 do NOT read .env or svro.schema.json yourself, do NOT call oz_create_project, and do NOT ask the starter questions (technology/description). Before doing anything else, state this in one plain sentence to the user (e.g. "Ce projet existe d\xE9j\xE0, je le reprends.") so they can correct you immediately if this is wrong \u2014 then ask what I want to work on now.':i?'This folder does NOT have an ozmoZ project yet, but a frontend project ALREADY exists here (index.html or package.json found) \u2014 do NOT run "npm create vite" or scaffold a new frontend, you would overwrite it. Do not spend a tool call checking which framework it uses. Skip the technology question (detect it from the existing files if needed) and ask me only for a one-sentence project description, then call oz_create_project directly on this existing project.':"This folder does NOT contain an ozmoZ project yet (no .env, no svro.schema.json) \u2014 do not spend a tool call checking, proceed directly to the starter questions below. "+a;return"Use ozmoZ as the backend of my app. Read and follow the directives in ./"+e+" at the project root (do not search the filesystem). "+d}function Re(){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 o=_("ps",["-A","-o","pid=,ppid=,args="],{encoding:"utf8"});if(o.status!==0)return!1;let s=new Map;o.stdout.split(`
109
+ `).slice(-2).join(" "),legacy:f}}function Ie(e){let o={user:!1,local:!1,others:[]};try{let s=JSON.parse(l.readFileSync(c.join(A.homedir(),".claude.json"),"utf8")),n=i=>!!i&&typeof i=="object"&&/fleetbo-svro|ozmoz/.test(String(i.command||"")+" "+JSON.stringify(i.args||[])),a=(i,d)=>{!i||typeof i!="object"||Object.keys(i).forEach(u=>{u===k?o[d]=!0:n(i[u])&&o.others.push({name:u,where:d})})};a(s.mcpServers,"user");let t=e;try{t=l.realpathSync(e)}catch{}let r=s.projects||{};Array.from(new Set([e,t])).forEach(i=>a((r[i]||{}).mcpServers,"local"))}catch{}return o}function _e(e){let o=Ie(e);o.user&&m(" \u26A0\uFE0F A global (user-scope) '"+k+"' entry from a previous version also exists: the tools appear in every project. To keep them in ozmoZ projects only: claude mcp remove "+k+" --scope user"),o.local&&m(" \u26A0\uFE0F A local-scope '"+k+"' entry exists for this folder and takes precedence over .mcp.json. Remove it: claude mcp remove "+k+" --scope local"),o.others.forEach(s=>{m(" \u26A0\uFE0F An old MCP entry named '"+s.name+"' ("+s.where+" scope) points to ozmoZ/Fleetbo. Remove it: claude mcp remove "+s.name+" --scope "+s.where)})}function Ce(e){let o=A.homedir(),s=n=>l.existsSync(n);return{claude:L("claude")||s(c.join(o,".claude")),cursor:s(c.join(e,".cursor"))||s(c.join(o,".cursor"))||L("cursor"),windsurf:s(c.join(o,".codeium","windsurf"))||s(c.join(e,".windsurf")),devin:s(c.join(e,".devin")),codex:L("codex")||s(c.join(o,".codex")),gemini:L("gemini")||s(c.join(o,".gemini")),antigravity:L("agy")||s(c.join(o,".antigravity"))||s(c.join(e,".antigravity"))}}var Ne={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 Pe(e){return String(e||"").replace(/[^\p{L}\p{N} '.,:\-_]/gu," ").replace(/\s+/g," ").trim().slice(0,4e3)}function De(e,o,s){let n=Pe(o),a=n?"The project description is already given: "+n+". Ask me only the technology question, then build it: ask me another question only if essential information is missing.":"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.",t=null;try{let u=c.join(s,".env");if(l.existsSync(u)){let p=l.readFileSync(u,"utf8").match(/^\s*OZ_DB_KEY\s*=\s*(.+?)\s*$/m);p&&(t=p[1].trim())}}catch{}let r=l.existsSync(c.join(s,"svro.schema.json")),i=l.existsSync(c.join(s,"index.html"))||l.existsSync(c.join(s,"package.json")),d=t||r?"This folder ALREADY contains an ozmoZ project"+(t?" (OZ_DB_KEY="+t+")":"")+' \u2014 do NOT read .env or svro.schema.json yourself, do NOT call oz_create_project, and do NOT ask the starter questions (technology/description). Before doing anything else, state this in one plain sentence to the user (e.g. "Ce projet existe d\xE9j\xE0, je le reprends.") so they can correct you immediately if this is wrong \u2014 then ask what I want to work on now.':i?'This folder does NOT have an ozmoZ project yet, but a frontend project ALREADY exists here (index.html or package.json found) \u2014 do NOT run "npm create vite" or scaffold a new frontend, you would overwrite it. Do not spend a tool call checking which framework it uses. Skip the technology question (detect it from the existing files if needed) and ask me only for a one-sentence project description, then call oz_create_project directly on this existing project.':"This folder does NOT contain an ozmoZ project yet (no .env, no svro.schema.json) \u2014 do not spend a tool call checking, proceed directly to the starter questions below. "+a;return"Use ozmoZ as the backend of my app. You are in the project folder ("+s+"). Follow your ozmoZ local instructions (do NOT run bash commands or web searches to inspect instruction files, they are already active). "+d}function Re(){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 o=_("ps",["-A","-o","pid=,ppid=,args="],{encoding:"utf8"});if(o.status!==0)return!1;let s=new Map;o.stdout.split(`
110
110
  `).forEach(a=>{let t=a.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);t&&s.set(Number(t[1]),{ppid:Number(t[2]),args:t[3]})});let n=process.ppid;for(let a=0;n>1&&a<20&&s.has(n);a++){let{ppid:t,args:r}=s.get(n);if(r.split(/\s+/).slice(0,2).some(d=>/(^|\/)(gemini|claude|codex|antigravity|agy)$/.test(d))||/@google\/gemini-cli|@anthropic-ai\/claude-code|@openai\/codex/.test(r))return!0;n=t}}catch{}return!1}function Le(e){let o=process.platform==="win32"?"where":"which";return _(o,[e],{stdio:"ignore"}).status===0}function Fe(e,o){let s=o.slice(1).filter(p=>!p.startsWith("--")),n=(s[0]||"").toLowerCase(),a=s.slice(1).join(" "),t=Ne[n];t||(z('Usage: npx ozmoz start <claude|gemini|codex> ["your app idea"]'),z('Cursor, Windsurf and Devin have no terminal agent to launch: run "npx ozmoz mcp --agent=<cursor|windsurf|devin>" instead.'),process.exit(1));let r=!1;try{r=!!JSON.parse(l.readFileSync(c.join(A.homedir(),".fleetbo","credentials.json"),"utf8")).devToken}catch{}r||(m("[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&&(z('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&&(z("Could not configure "+t.bin+'. Run "npx ozmoz mcp --agent='+n+'" to see why.'),process.exit(1)),Re()&&(m(`
111
111
  [ozmoZ] ozmoZ is set up in this folder (instruction file + MCP server).`),m("[ozmoZ] You are already inside an AI agent's shell, so no second agent is launched."),m('[ozmoZ] NEXT: tell the user, in plain words, to quit this agent and start it again in this folder (or to run "npx -y ozmoz@latest start '+n+`" in a normal terminal), then to paste the message again. Do not run any other command.
112
112
  `),process.exit(0)),Le(t.bin)||(z('"'+t.bin+'" is not installed or not in your PATH. Install it, then run: npx ozmoz start '+n),process.exit(1));let d=De(t.rules,a,e);m(`
113
113
  [ozmoZ] Launching `+t.bin+` in this folder...
114
- `);let u=_(t.bin,t.launch(process.platform==="win32"?'"'+d+'"':d),{stdio:"inherit",cwd:e,shell:process.platform==="win32"});process.exit(u.status===null?1:u.status)}async function ce(e,o){let s=o.includes("--dry-run"),n=o.find(y=>y.startsWith("--agent=")),a=n&&n.slice(8).trim()?n.slice(8).split(",").map(y=>y.trim().toLowerCase()).filter(Boolean):null,t=null;if(a){t=a.includes("all")?Object.keys(Z):a;let y=t.filter(S=>!Z[S]);y.length&&(z("Unknown agent(s): "+y.join(", ")+". Valid values: "+Object.keys(Z).join(", ")+", all."),process.exit(1))}let r=o.find(y=>y.startsWith("--scope=")),i=r?r.slice(8).trim().toLowerCase():"project";["project","user","local"].includes(i)||(z("Unknown scope: "+i+". Valid values for Claude Code: project (default), user, local."),process.exit(1));let d=Ce(e),u=t||Object.keys(Z).filter(y=>d[y]),p=be();m(`
115
- [ozmoZ] Configuring your AI agents`+(s?" (dry run: nothing is written)":"")+"...");let f=X(p,xe()),v=[];(!t||t.some(y=>y!=="gemini"))&&v.push(["AGENTS.md",f]),(!t||t.includes("claude"))&&v.push(["CLAUDE.md",X(p,`# OZMOZ DATA OS \u2014 DIRECTIVES AGENT
116
- @AGENTS.md`)]),(!t||t.includes("gemini"))&&v.push(["GEMINI.md",f]);let T=c.join(e,".cursorrules");if(l.existsSync(T)){let y=l.readFileSync(T,"utf8");(q.test(y)||y.includes(ae))&&v.push([".cursorrules",f])}for(let y of v)m("\u2192 "+y[0]+": "+P[Ae(c.join(e,y[0]),y[1],s)]);for(let y of u){let S=Z[y];if(y==="cursor")m("\u2192 "+S+" MCP (.cursor/mcp.json): "+P[$(c.join(e,".cursor","mcp.json"),s)]);else if(y==="windsurf")m("\u2192 "+S+" MCP (~/.codeium/windsurf/mcp_config.json): "+P[$(c.join(A.homedir(),".codeium","windsurf","mcp_config.json"),s)]);else if(y==="antigravity"){if(m("\u2192 "+S+" MCP (.agents/mcp_config.json): "+P[$(c.join(e,".agents","mcp_config.json"),s)]),!s){let E=[];!u.includes("claude")&&!u.includes("gemini")&&E.push(c.join(e,".mcp.json")),u.includes("gemini")||(E.push(c.join(e,".gemini","settings.json")),E.push(c.join(A.homedir(),".gemini","settings.json"))),E.push(c.join(e,".antigravity","settings.json")),E.push(c.join(A.homedir(),".antigravity","settings.json")),E.push(c.join(e,".antigravity","mcp.json")),E.forEach(b=>{try{ke(b)==="removed"&&m("\u2192 Removed stale ozmoz entry: "+(c.relative(e,b)||b))}catch{}})}}else if(y==="gemini"){let E=[c.join(A.homedir(),".gemini","settings.json"),c.join(e,".gemini","settings.json"),c.join(e,".mcp.json")],b="unchanged";E.forEach(x=>{let w=$(x,s);(w==="created"||w==="updated")&&(b=w)}),m("\u2192 "+S+" MCP (settings.json & .mcp.json): "+P[b])}else if(y==="claude"&&i==="project"){let E=$(c.join(e,".mcp.json"),s);m("\u2192 "+S+" MCP (.mcp.json, project scope): "+P[E]),(E==="created"||E==="updated")&&m(" 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)."),_e(e)}else if(y==="claude"||y==="codex"){let E=je(y,s,i);E.status==="manual"?m("\u2192 "+S+": '"+y+"' not found or not scriptable. Run manually: "+E.detail):E.status==="planned"?m("\u2192 "+S+" MCP: would run: "+E.detail):E.status==="failed"?z("\u2192 "+S+" MCP: registration failed ("+E.message+"). Run manually: "+E.detail):m("\u2192 "+S+" MCP"+(y==="claude"?" ("+i+" scope)":" (~/.codex/config.toml, global)")+": "+P[E.status]),E.legacy&&m(" \u26A0\uFE0F An old MCP entry named 'fleetbo' exists. Remove it: "+y+" mcp remove fleetbo")}else y==="devin"&&m("\u2192 "+S+": reads AGENTS.md. Devin runs in its own environment: add the Fleetbo MCP server from Devin's settings (not automated).")}!t&&u.length===0&&m("\u2192 No agent detected: instruction files were written; re-run with --agent=<name> to register the MCP server.");let g=!!(V(e).FLEETBO_DEV_TOKEN||process.env.FLEETBO_DEV_TOKEN),O=c.join(A.homedir(),".fleetbo","credentials.json");if(l.existsSync(O))try{g=g||!!JSON.parse(l.readFileSync(O,"utf8")).devToken}catch{}g||z('\u26A0\uFE0F No developer token found. Run "npx ozmoz login" once.'),process.env.FLEETBO_START||m(`
114
+ `);let u=_(t.bin,t.launch(process.platform==="win32"?'"'+d+'"':d),{stdio:"inherit",cwd:e,shell:process.platform==="win32"});process.exit(u.status===null?1:u.status)}async function ce(e,o){let s=o.includes("--dry-run"),n=o.find(y=>y.startsWith("--agent=")),a=n&&n.slice(8).trim()?n.slice(8).split(",").map(y=>y.trim().toLowerCase()).filter(Boolean):null,t=null;if(a){t=a.includes("all")?Object.keys($):a;let y=t.filter(S=>!$[S]);y.length&&(z("Unknown agent(s): "+y.join(", ")+". Valid values: "+Object.keys($).join(", ")+", all."),process.exit(1))}let r=o.find(y=>y.startsWith("--scope=")),i=r?r.slice(8).trim().toLowerCase():"project";["project","user","local"].includes(i)||(z("Unknown scope: "+i+". Valid values for Claude Code: project (default), user, local."),process.exit(1));let d=Ce(e),u=t||Object.keys($).filter(y=>d[y]),p=be();m(`
115
+ [ozmoZ] Configuring your AI agents`+(s?" (dry run: nothing is written)":"")+"...");let f=K(p,xe()),v=[["AGENTS.md",f],["GEMINI.md",f],["CLAUDE.md",K(p,`# OZMOZ DATA OS \u2014 DIRECTIVES AGENT
116
+ @AGENTS.md`)]],T=c.join(e,".cursorrules");if(l.existsSync(T)){let y=l.readFileSync(T,"utf8");(q.test(y)||y.includes(ie))&&v.push([".cursorrules",f])}for(let y of v)m("\u2192 "+y[0]+": "+P[Ae(c.join(e,y[0]),y[1],s)]);for(let y of u){let S=$[y];if(y==="cursor")m("\u2192 "+S+" MCP (.cursor/mcp.json): "+P[R(c.join(e,".cursor","mcp.json"),s)]);else if(y==="windsurf")m("\u2192 "+S+" MCP (~/.codeium/windsurf/mcp_config.json): "+P[R(c.join(A.homedir(),".codeium","windsurf","mcp_config.json"),s)]);else if(y==="antigravity"){if(m("\u2192 "+S+" MCP (.agents/mcp_config.json): "+P[R(c.join(e,".agents","mcp_config.json"),s)]),R(c.join(e,".mcp.json"),s),!s){let O=[];u.includes("gemini")||(O.push(c.join(e,".gemini","settings.json")),O.push(c.join(A.homedir(),".gemini","settings.json"))),O.push(c.join(e,".antigravity","settings.json")),O.push(c.join(A.homedir(),".antigravity","settings.json")),O.push(c.join(e,".antigravity","mcp.json")),O.forEach(b=>{try{ke(b)==="removed"&&m("\u2192 Removed stale ozmoz entry: "+(c.relative(e,b)||b))}catch{}})}}else if(y==="gemini"){let O=[c.join(A.homedir(),".gemini","settings.json"),c.join(e,".gemini","settings.json"),c.join(e,".mcp.json")],b="unchanged";O.forEach(x=>{let E=R(x,s);(E==="created"||E==="updated")&&(b=E)}),m("\u2192 "+S+" MCP (settings.json & .mcp.json): "+P[b])}else if(y==="claude"&&i==="project"){let O=R(c.join(e,".mcp.json"),s);m("\u2192 "+S+" MCP (.mcp.json, project scope): "+P[O]),(O==="created"||O==="updated")&&m(" 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)."),_e(e)}else if(y==="claude"||y==="codex"){let O=je(y,s,i);O.status==="manual"?m("\u2192 "+S+": '"+y+"' not found or not scriptable. Run manually: "+O.detail):O.status==="planned"?m("\u2192 "+S+" MCP: would run: "+O.detail):O.status==="failed"?z("\u2192 "+S+" MCP: registration failed ("+O.message+"). Run manually: "+O.detail):m("\u2192 "+S+" MCP"+(y==="claude"?" ("+i+" scope)":" (~/.codex/config.toml, global)")+": "+P[O.status]),O.legacy&&m(" \u26A0\uFE0F An old MCP entry named 'fleetbo' exists. Remove it: "+y+" mcp remove fleetbo")}else y==="devin"&&m("\u2192 "+S+": reads AGENTS.md. Devin runs in its own environment: add the Fleetbo MCP server from Devin's settings (not automated).")}!t&&u.length===0&&m("\u2192 No agent detected: instruction files were written; re-run with --agent=<name> to register the MCP server.");let g=!!(V(e).FLEETBO_DEV_TOKEN||process.env.FLEETBO_DEV_TOKEN),w=c.join(A.homedir(),".fleetbo","credentials.json");if(l.existsSync(w))try{g=g||!!JSON.parse(l.readFileSync(w,"utf8")).devToken}catch{}g||z('\u26A0\uFE0F No developer token found. Run "npx ozmoz login" once.'),process.env.FLEETBO_START||m(`
117
117
  [ozmoZ] Done. Restart your agent so it reloads the tools and instruction files.
118
- `)}function Ue(e){let o=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";me(`${o} "${e}"`)}function Me(e){return new Promise((o,s)=>{let n=pe.createServer((a,t)=>{if(t.setHeader("Access-Control-Allow-Origin","*"),t.setHeader("Access-Control-Allow-Methods","GET, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),t.setHeader("Access-Control-Allow-Private-Network","true"),a.method==="OPTIONS"){t.writeHead(204),t.end();return}let r=new URL(a.url,"http://127.0.0.1");if(r.pathname==="/callback"){let i=r.searchParams.get("token"),d=r.searchParams.get("refresh");if(!i){t.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),t.end("<h3>Error: Missing token.</h3>");return}let u=c.join(A.homedir(),".fleetbo");l.existsSync(u)||l.mkdirSync(u,{recursive:!0}),l.writeFileSync(c.join(u,"credentials.json"),JSON.stringify({devToken:i,refreshToken:d||null,updatedAt:new Date().toISOString()},null,2));let p=c.join(e,".env");if(l.existsSync(p)){let f=l.readFileSync(p,"utf8");f.includes("FLEETBO_DEV_TOKEN=")?f=f.replace(/FLEETBO_DEV_TOKEN=.*/g,`FLEETBO_DEV_TOKEN=${i}`):f+=`
118
+ `)}function Ue(e){let o=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";me(`${o} "${e}"`)}function Ze(e){return new Promise((o,s)=>{let n=pe.createServer((a,t)=>{if(t.setHeader("Access-Control-Allow-Origin","*"),t.setHeader("Access-Control-Allow-Methods","GET, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),t.setHeader("Access-Control-Allow-Private-Network","true"),a.method==="OPTIONS"){t.writeHead(204),t.end();return}let r=new URL(a.url,"http://127.0.0.1");if(r.pathname==="/callback"){let i=r.searchParams.get("token"),d=r.searchParams.get("refresh");if(!i){t.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),t.end("<h3>Error: Missing token.</h3>");return}let u=c.join(A.homedir(),".fleetbo");l.existsSync(u)||l.mkdirSync(u,{recursive:!0}),l.writeFileSync(c.join(u,"credentials.json"),JSON.stringify({devToken:i,refreshToken:d||null,updatedAt:new Date().toISOString()},null,2));let p=c.join(e,".env");if(l.existsSync(p)){let f=l.readFileSync(p,"utf8");f.includes("FLEETBO_DEV_TOKEN=")?f=f.replace(/FLEETBO_DEV_TOKEN=.*/g,`FLEETBO_DEV_TOKEN=${i}`):f+=`
119
119
  FLEETBO_DEV_TOKEN=${i}
120
120
  `,l.writeFileSync(p,f,"utf8")}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),t.end(`
121
121
  <!DOCTYPE html>
@@ -139,8 +139,8 @@ FLEETBO_DEV_TOKEN=${i}
139
139
  [ozmoZ] Developer token received and stored successfully!`),!process.env.FLEETBO_START&&!["AGENTS.md","CLAUDE.md","GEMINI.md"].some(f=>l.existsSync(c.join(e,f)))&&(m(`
140
140
  Next step to connect your AI agents:`),m(` npx ozmoz mcp (Configure Claude Code, Cursor, Windsurf, Devin, Codex CLI, Gemini CLI)
141
141
  `)),typeof n.closeAllConnections=="function"&&n.closeAllConnections(),n.close(()=>o()),setTimeout(()=>o(),300)}});n.listen(0,"127.0.0.1",()=>{let t=`https://ozmoz.io/cli-auth?port=${n.address().port}`;m(`
142
- [ozmoZ] Waiting for authentication...`),m(`If your browser does not open automatically, visit: ${t}`),Ue(t);let r=Number(process.env.FLEETBO_LOGIN_TIMEOUT_MS)||12e4;setTimeout(()=>{try{typeof n.closeAllConnections=="function"&&n.closeAllConnections()}catch{}n.close(),s(new Error(`Login timed out after ${Math.round(r/1e3)}s: browser approval not received. Run "npx ozmoz login" once more and approve in the browser.`))},r).unref()}),n.on("error",a=>s(a))})}function Ze(e,o){return new Promise((s,n)=>{let a=JSON.stringify({enterpriseID:e,schema:o}),t={hostname:"fleetbo-gatekeeper.fleetbo.workers.dev",path:"/sync-schema",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a)}},r=ne.request(t,i=>{let d="";i.on("data",u=>{d+=u}),i.on("end",()=>{i.statusCode>=200&&i.statusCode<300?s(JSON.parse(d)):n(new Error(`Edge sync failed with HTTP ${i.statusCode}`))})});r.on("error",i=>n(i)),r.write(a),r.end()})}function V(e){let o=c.join(e,".env");if(!l.existsSync(o))return{};let s=l.readFileSync(o,"utf8"),n={};return s.split(`
143
- `).forEach(a=>{let[t,...r]=a.split("=");t&&r.length>0&&(n[t.trim()]=r.join("=").trim())}),n}function Q(e,o){return new Promise((s,n)=>{let a=JSON.stringify({projectId:e,moduleName:o}),t={hostname:"getmodulecache-jqycakhlxa-uc.a.run.app",path:"/",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a)}},r=ne.request(t,i=>{let d="";i.on("data",u=>{d+=u}),i.on("end",()=>{if(i.statusCode>=200&&i.statusCode<300)try{let u=JSON.parse(d);if(u.success&&u.found&&u.module){let p=u.module.code||u.module.mockCode;s(typeof p=="string"?JSON.parse(p):p)}else s(null)}catch{n(new Error(`Invalid JSON response for ${o}.`))}else n(new Error(`Server error ${i.statusCode} while fetching ${o}`))})});r.on("error",i=>n(i)),r.write(a),r.end()})}var I=process.argv.slice(2),j=I[0],$e=["id","_id","userId","author","dateCreated","createdAt","_schema_version"],le=e=>$e.includes(e)||/^_/.test(e)||/^(created|updated|modified|deleted)(At|By)$/i.test(e);function W(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 We(e){let o=[];if(!e||typeof e!="object"||!e.collections)return{schema:e,changed:!1,notes:o};let s=JSON.parse(JSON.stringify(e));return Object.entries(s.collections).forEach(([n,a])=>{let t=W(a);t&&Object.keys(t).forEach(r=>{le(r)&&t[r]&&typeof t[r]=="object"&&t[r].required===!0&&(t[r].required=!1,o.push(n+"."+r))})}),{schema:s,changed:o.length>0,notes:o}}function Ge(e){let o=String(typeof e=="string"?e:e&&e.type||"").toLowerCase(),s=e&&(e.enum||e.values||e.options);return Array.isArray(s)&&s.length?JSON.stringify(s[0]):o==="number"||o==="integer"||o==="float"?"0":o==="boolean"||o==="toggle"?"false":o==="date"||o==="datetime"||o==="timestamp"?"new Date().toISOString()":o==="reference"?"'<id du document li\xE9>'":o==="media"?"null /* image/fichier : oz.addWithMedia */":o==="array"?"[]":"'\u2026'"}function Be(e){let s=Object.entries(e&&e.collections||{}),n=`## \u{1F4E5} CE QUE LE CODE DOIT ENVOYER (MOD\xC8LES D'\xC9CRITURE)
142
+ [ozmoZ] Waiting for authentication...`),m(`If your browser does not open automatically, visit: ${t}`),Ue(t);let r=Number(process.env.FLEETBO_LOGIN_TIMEOUT_MS)||12e4;setTimeout(()=>{try{typeof n.closeAllConnections=="function"&&n.closeAllConnections()}catch{}n.close(),s(new Error(`Login timed out after ${Math.round(r/1e3)}s: browser approval not received. Run "npx ozmoz login" once more and approve in the browser.`))},r).unref()}),n.on("error",a=>s(a))})}function Me(e,o){return new Promise((s,n)=>{let a=JSON.stringify({enterpriseID:e,schema:o}),t={hostname:"fleetbo-gatekeeper.fleetbo.workers.dev",path:"/sync-schema",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a)}},r=te.request(t,i=>{let d="";i.on("data",u=>{d+=u}),i.on("end",()=>{i.statusCode>=200&&i.statusCode<300?s(JSON.parse(d)):n(new Error(`Edge sync failed with HTTP ${i.statusCode}`))})});r.on("error",i=>n(i)),r.write(a),r.end()})}function V(e){let o=c.join(e,".env");if(!l.existsSync(o))return{};let s=l.readFileSync(o,"utf8"),n={};return s.split(`
143
+ `).forEach(a=>{let[t,...r]=a.split("=");t&&r.length>0&&(n[t.trim()]=r.join("=").trim())}),n}function X(e,o){return new Promise((s,n)=>{let a=JSON.stringify({projectId:e,moduleName:o}),t={hostname:"getmodulecache-jqycakhlxa-uc.a.run.app",path:"/",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a)}},r=te.request(t,i=>{let d="";i.on("data",u=>{d+=u}),i.on("end",()=>{if(i.statusCode>=200&&i.statusCode<300)try{let u=JSON.parse(d);if(u.success&&u.found&&u.module){let p=u.module.code||u.module.mockCode;s(typeof p=="string"?JSON.parse(p):p)}else s(null)}catch{n(new Error(`Invalid JSON response for ${o}.`))}else n(new Error(`Server error ${i.statusCode} while fetching ${o}`))})});r.on("error",i=>n(i)),r.write(a),r.end()})}var I=process.argv.slice(2),j=I[0],$e=["id","_id","userId","author","dateCreated","createdAt","_schema_version"],le=e=>$e.includes(e)||/^_/.test(e)||/^(created|updated|modified|deleted)(At|By)$/i.test(e);function W(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 We(e){let o=[];if(!e||typeof e!="object"||!e.collections)return{schema:e,changed:!1,notes:o};let s=JSON.parse(JSON.stringify(e));return Object.entries(s.collections).forEach(([n,a])=>{let t=W(a);t&&Object.keys(t).forEach(r=>{le(r)&&t[r]&&typeof t[r]=="object"&&t[r].required===!0&&(t[r].required=!1,o.push(n+"."+r))})}),{schema:s,changed:o.length>0,notes:o}}function Be(e){let o=String(typeof e=="string"?e:e&&e.type||"").toLowerCase(),s=e&&(e.enum||e.values||e.options);return Array.isArray(s)&&s.length?JSON.stringify(s[0]):o==="number"||o==="integer"||o==="float"?"0":o==="boolean"||o==="toggle"?"false":o==="date"||o==="datetime"||o==="timestamp"?"new Date().toISOString()":o==="reference"?"'<id du document li\xE9>'":o==="media"?"null /* image/fichier : oz.addWithMedia */":o==="array"?"[]":"'\u2026'"}function Ge(e){let s=Object.entries(e&&e.collections||{}),n=`## \u{1F4E5} CE QUE LE CODE DOIT ENVOYER (MOD\xC8LES D'\xC9CRITURE)
144
144
 
145
145
  `;n+=`### \u{1F194} Identifiants
146
146
  `,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 a=s.filter(([,t])=>W(t)&&Object.keys(W(t)).length>0);return a.length===0?n+`_Aucune collection scell\xE9e pour l'instant : les mod\xE8les appara\xEEtront apr\xE8s la mod\xE9lisation d'Alex._
@@ -148,21 +148,21 @@ Next step to connect your AI agents:`),m(` npx ozmoz mcp (Configure Claude C
148
148
 
149
149
  | Champ | Type | Obligatoire | Valeurs / remarques |
150
150
  |---|---|---|---|
151
- `,d.forEach(([f,v])=>{let T=typeof v=="string"?v:v&&v.type||"any",h=v&&(v.enum||v.values||v.options),g=Array.isArray(h)&&h.length?"parmi : "+h.map(O=>"`"+O+"`").join(", "):"";n+="| `"+f+"` | "+T+" | "+(v&&v.required===!0?"\u2705 oui":"non")+" | "+g+` |
151
+ `,d.forEach(([f,v])=>{let T=typeof v=="string"?v:v&&v.type||"any",h=v&&(v.enum||v.values||v.options),g=Array.isArray(h)&&h.length?"parmi : "+h.map(w=>"`"+w+"`").join(", "):"";n+="| `"+f+"` | "+T+" | "+(v&&v.required===!0?"\u2705 oui":"non")+" | "+g+` |
152
152
  `});let u=d.filter(([,f])=>f&&f.required===!0),p=d.filter(([,f])=>!(f&&f.required===!0));n+="\nInsertion valide :\n```js\nawait oz.add('"+t+`', {
153
- `,u.forEach(([f,v])=>{n+=" "+f+": "+Ge(v)+`,
153
+ `,u.forEach(([f,v])=>{n+=" "+f+": "+Be(v)+`,
154
154
  `}),p.length&&(n+=" // optionnels : "+p.map(([f])=>f).join(", ")+`
155
155
  `),n+=`}, {
156
156
  `,n+=" desc: 'Cr\xE9ation d\\'un \xE9l\xE9ment dans "+t+`',
157
157
  `,n+=` role: 'AUTHENTICATED' // ou 'ANONYMOUS', 'ADMIN'
158
158
  `,n+=` // guard: 'condition sur les donn\xE9es' (optionnel)
159
- `,n+="});\n```\n\n"}),n)}function J(e){return e.replace(/("(\\.|[^"\\\r\n])*"|'(\\.|[^'\\\r\n])*'|`(\\.|[^`\\])*`)|(\/\*[\s\S]*?\*\/|\/\/[^\r\n]*)/g,(o,s)=>s||"")}function de(e,o){let s=e[o];for(let n=o+1;n<e.length;n++){if(e[n]==="\\"){n++;continue}if(s!=="`"&&(e[n]===`
160
- `||e[n]==="\r"))return n;if(s==="`"&&e[n]==="$"&&e[n+1]==="{"){n=L(e,n+1)-1;continue}if(e[n]===s)return n+1}return e.length}function L(e,o){let s=e[o],n=s==="("?")":s==="["?"]":"}",a=0;for(let t=o;t<e.length;t++){let r=e[t];if(r==='"'||r==="'"||r==="`"){t=de(e,t)-1;continue}if(r==="/"&&e[t+1]==="/"){let i=e.indexOf(`
159
+ `,n+="});\n```\n\n"}),n)}function H(e){return e.replace(/("(\\.|[^"\\\r\n])*"|'(\\.|[^'\\\r\n])*'|`(\\.|[^`\\])*`)|(\/\*[\s\S]*?\*\/|\/\/[^\r\n]*)/g,(o,s)=>s||"")}function de(e,o){let s=e[o];for(let n=o+1;n<e.length;n++){if(e[n]==="\\"){n++;continue}if(s!=="`"&&(e[n]===`
160
+ `||e[n]==="\r"))return n;if(s==="`"&&e[n]==="$"&&e[n+1]==="{"){n=F(e,n+1)-1;continue}if(e[n]===s)return n+1}return e.length}function F(e,o){let s=e[o],n=s==="("?")":s==="["?"]":"}",a=0;for(let t=o;t<e.length;t++){let r=e[t];if(r==='"'||r==="'"||r==="`"){t=de(e,t)-1;continue}if(r==="/"&&e[t+1]==="/"){let i=e.indexOf(`
161
161
  `,t);if(i<0)return e.length;t=i;continue}if(r==="/"&&e[t+1]==="*"){let i=e.indexOf("*/",t+2);if(i<0)return e.length;t=i+1;continue}if(r==="("||r==="["||r==="{")a++;else if((r===")"||r==="]"||r==="}")&&(a--,a===0&&r===n||a<0))return t+1}return e.length}function ue(e,o,s){let n=[],a=o,t=o;for(;t<s;){let r=e[t];if(r==='"'||r==="'"||r==="`"){t=de(e,t);continue}if(r==="/"&&e[t+1]==="/"){let i=e.indexOf(`
162
- `,t);t=i<0?s:i;continue}if(r==="/"&&e[t+1]==="*"){let i=e.indexOf("*/",t+2);t=i<0?s:i+2;continue}if(r==="("||r==="["||r==="{"){t=L(e,t);continue}r===","&&(n.push(e.slice(a,t)),a=t+1),t++}return e.slice(a,s).trim()!==""&&n.push(e.slice(a,s)),n}function Ve(e){let o=e.trim(),s=new Set,n=!1,a=!1;return ue(o,1,o.length-1).forEach(t=>{let r=t.replace(/^(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)+/,"").trim();if(!r)return;if(r.startsWith("...")){n=!0;return}if(r.startsWith("[")){a=!0;return}let i=r.match(/^(['"])(.*?)\1\s*:/);if(i){s.add(i[2]);return}i=r.match(/^([A-Za-z_$][\w$]*)\s*(:|\(|$)/),i&&s.add(i[1])}),{keys:s,hasSpread:n,hasComputed:a}}function Ye(e,o,s){let n=o.trim();if(n.startsWith("{"))return n;if(!/^[A-Za-z_$][\w$]*$/.test(n))return null;let a=new RegExp("(?:const|let|var)\\s+"+n.replace(/\$/g,"\\$")+"\\s*=\\s*\\{","g"),t=-1,r;for(;(r=a.exec(e))&&r.index<s;)t=r.index+r[0].length-1;return t<0?null:e.slice(t,L(e,t))}function G(e,o){return e.slice(0,o).split(`
163
- `).length}function K(e){let o=[],s=new Set(["node_modules","dist","build",".next",".git",".svelte-kit",".nuxt","coverage"]),n=(a,t)=>{if(t>6||o.length>400)return;let r=[];try{r=l.readdirSync(a,{withFileTypes:!0})}catch{return}r.forEach(i=>{if(s.has(i.name))return;let d=c.join(a,i.name);if(i.isDirectory())n(d,t+1);else if(/\.(jsx?|tsx?|mjs|vue|svelte)$/.test(i.name)&&!/\.d\.ts$/.test(i.name))try{l.statSync(d).size<5e5&&o.push(d)}catch{}})};return["src","app","pages","components"].forEach(a=>n(c.join(e,a),0)),o}function qe(e,o){let s=[],n=0,a=o&&o.collections||{};if(Object.keys(a).length===0)return{issues:s,checked:n,skipped:!0};let t=/\boz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update)\s*\(/g,r=/\boz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update|delete|getDocsG|getDocsU|getDoc|join)\s*\(\s*(['"`])([^'"`$\\]+)\2/g;return K(e).forEach(i=>{let d="";try{d=l.readFileSync(i,"utf8")}catch{return}if(!/oz\./.test(d)&&!/<oz-/i.test(d))return;d=J(d);let u=c.relative(e,i).split(c.sep).join("/"),p;for(r.lastIndex=0;p=r.exec(d);)Object.prototype.hasOwnProperty.call(a,p[3])||s.push({where:u+":"+G(d,p.index),msg:"oz."+p[1]+"('"+p[3]+"') : the collection '"+p[3]+"' is not in the contract"});for(t.lastIndex=0;p=t.exec(d);){let f=p.index+p[0].length-1,v=L(d,f)-1,T=ue(d,f+1,v),h=p[1],O=(T[0]||"").trim().match(/^(['"`])([^'"`$\\]+)\1$/),y=h==="update"?T[2]:T[1];if(!O||!y)continue;let S=W(a[O[2]]);if(!S)continue;let E=Ye(d,y,p.index);if(!E)continue;let{keys:b,hasSpread:x,hasComputed:w}=Ve(E);n++;let F=u+":"+G(d,p.index),B=[...b].filter(D=>!Object.prototype.hasOwnProperty.call(S,D));if(B.length&&s.push({where:F,msg:"oz."+h+"('"+O[2]+"') sends field(s) not in the contract: "+B.join(", ")}),h!=="update"&&!x&&!w){let D=Object.entries(S).filter(([U,C])=>C&&typeof C=="object"&&C.required===!0&&!b.has(U)).map(([U])=>U);D.length&&s.push({where:F,msg:"oz."+h+"('"+O[2]+"') is missing required field(s): "+D.join(", ")})}}}),{issues:s,checked:n,skipped:!1}}function He(e,o){let s=qe(e,o);if(!s.skipped){if(s.issues.length===0){s.checked>0&&m("\u2192 Contract check: "+s.checked+" write call(s) match the contract.");return}m(`
162
+ `,t);t=i<0?s:i;continue}if(r==="/"&&e[t+1]==="*"){let i=e.indexOf("*/",t+2);t=i<0?s:i+2;continue}if(r==="("||r==="["||r==="{"){t=F(e,t);continue}r===","&&(n.push(e.slice(a,t)),a=t+1),t++}return e.slice(a,s).trim()!==""&&n.push(e.slice(a,s)),n}function Ve(e){let o=e.trim(),s=new Set,n=!1,a=!1;return ue(o,1,o.length-1).forEach(t=>{let r=t.replace(/^(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)+/,"").trim();if(!r)return;if(r.startsWith("...")){n=!0;return}if(r.startsWith("[")){a=!0;return}let i=r.match(/^(['"])(.*?)\1\s*:/);if(i){s.add(i[2]);return}i=r.match(/^([A-Za-z_$][\w$]*)\s*(:|\(|$)/),i&&s.add(i[1])}),{keys:s,hasSpread:n,hasComputed:a}}function Ye(e,o,s){let n=o.trim();if(n.startsWith("{"))return n;if(!/^[A-Za-z_$][\w$]*$/.test(n))return null;let a=new RegExp("(?:const|let|var)\\s+"+n.replace(/\$/g,"\\$")+"\\s*=\\s*\\{","g"),t=-1,r;for(;(r=a.exec(e))&&r.index<s;)t=r.index+r[0].length-1;return t<0?null:e.slice(t,F(e,t))}function B(e,o){return e.slice(0,o).split(`
163
+ `).length}function J(e){let o=[],s=new Set(["node_modules","dist","build",".next",".git",".svelte-kit",".nuxt","coverage"]),n=(a,t)=>{if(t>6||o.length>400)return;let r=[];try{r=l.readdirSync(a,{withFileTypes:!0})}catch{return}r.forEach(i=>{if(s.has(i.name))return;let d=c.join(a,i.name);if(i.isDirectory())n(d,t+1);else if(/\.(jsx?|tsx?|mjs|vue|svelte)$/.test(i.name)&&!/\.d\.ts$/.test(i.name))try{l.statSync(d).size<5e5&&o.push(d)}catch{}})};return["src","app","pages","components"].forEach(a=>n(c.join(e,a),0)),o}function qe(e,o){let s=[],n=0,a=o&&o.collections||{};if(Object.keys(a).length===0)return{issues:s,checked:n,skipped:!0};let t=/\boz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update)\s*\(/g,r=/\boz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update|delete|getDocsG|getDocsU|getDoc|join)\s*\(\s*(['"`])([^'"`$\\]+)\2/g;return J(e).forEach(i=>{let d="";try{d=l.readFileSync(i,"utf8")}catch{return}if(!/oz\./.test(d)&&!/<oz-/i.test(d))return;d=H(d);let u=c.relative(e,i).split(c.sep).join("/"),p;for(r.lastIndex=0;p=r.exec(d);)Object.prototype.hasOwnProperty.call(a,p[3])||s.push({where:u+":"+B(d,p.index),msg:"oz."+p[1]+"('"+p[3]+"') : the collection '"+p[3]+"' is not in the contract"});for(t.lastIndex=0;p=t.exec(d);){let f=p.index+p[0].length-1,v=F(d,f)-1,T=ue(d,f+1,v),h=p[1],w=(T[0]||"").trim().match(/^(['"`])([^'"`$\\]+)\1$/),y=h==="update"?T[2]:T[1];if(!w||!y)continue;let S=W(a[w[2]]);if(!S)continue;let O=Ye(d,y,p.index);if(!O)continue;let{keys:b,hasSpread:x,hasComputed:E}=Ve(O);n++;let U=u+":"+B(d,p.index),G=[...b].filter(D=>!Object.prototype.hasOwnProperty.call(S,D));if(G.length&&s.push({where:U,msg:"oz."+h+"('"+w[2]+"') sends field(s) not in the contract: "+G.join(", ")}),h!=="update"&&!x&&!E){let D=Object.entries(S).filter(([Z,C])=>C&&typeof C=="object"&&C.required===!0&&!b.has(Z)).map(([Z])=>Z);D.length&&s.push({where:U,msg:"oz."+h+"('"+w[2]+"') is missing required field(s): "+D.join(", ")})}}}),{issues:s,checked:n,skipped:!1}}function He(e,o){let s=qe(e,o);if(!s.skipped){if(s.issues.length===0){s.checked>0&&m("\u2192 Contract check: "+s.checked+" write call(s) match the contract.");return}m(`
164
164
  \u26A0\uFE0F Contract check: `+s.issues.length+" problem(s) that production (OZ_PROTOTYPE=false) would reject with HTTP 422:"),s.issues.slice(0,20).forEach(n=>m(" "+n.where+" "+n.msg)),m(` \u2192 Fix each one in your code now, or evolve the contract with oz_update_project, BEFORE switching to live mode. Details: ozmoz_contract.md, section "CE QUE LE CODE DOIT ENVOYER".
165
- `)}}var ee=/(?:const|let|var)\s+(\w+)\s*=\s*await\s+oz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update|delete|sendotpsvro|verifyotpsvro)\s*\(/g,te=/(?<!=\s*)\bawait\s+oz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update|delete|sendotpsvro|verifyotpsvro)\s*\(/g;function Je(e){let o=[];return K(e).forEach(s=>{let n="";try{n=l.readFileSync(s,"utf8")}catch{return}if(!/oz\./.test(n))return;let a=J(n),t=c.relative(e,s).split(c.sep).join("/");ee.lastIndex=0;let r;for(;r=ee.exec(a);){let i=r[1],d=a.slice(r.index);new RegExp("\\b"+i+"\\s*(?:\\?\\.|\\.)\\s*(success|error)\\b").test(d)||o.push({where:t+":"+G(a,r.index),msg:`oz.${r[2]}(): return value ('${i}') never checked with .success/.error (rule 4).`})}for(te.lastIndex=0;r=te.exec(a);)o.push({where:t+":"+G(a,r.index),msg:`oz.${r[1]}(): called without assigning return value to check .success/.error (rule 4).`})}),o}function Ke(e){let o=[],s=/\bresource\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g,n;for(;n=s.exec(String(e));)o.push(n[1]);return Array.from(new Set(o))}function Xe(e){let o=[],s=c.join(e,"svro.schema.json"),n=c.join(e,"svro.workflow.json");if(!l.existsSync(s)||!l.existsSync(n))return o;let a,t;try{a=JSON.parse(l.readFileSync(s,"utf8")),t=JSON.parse(l.readFileSync(n,"utf8"))}catch{return o}return Array.isArray(t.sequences)&&t.sequences.forEach(r=>{let i=a.collections?.[r.collection]?.fields||null;if(!i){o.push({where:r.id||"?",msg:`S\xE9quence sur la collection '${r.collection}', absente de svro.schema.json.`});return}(r.guards||[]).forEach(d=>{Ke(d).forEach(u=>{u in i||o.push({where:r.id||r.collection,msg:`Guard "${d}" r\xE9f\xE9rence '${u}', absent des champs d\xE9clar\xE9s de '${r.collection}' \u2014 l'Edge rejettera toute \xE9criture contenant ce champ (HTTP 422).`})})})}),o}var Qe=["add","addWithId","addWithUserId","addWithMedia","update","delete"];function et(e){let o=[],s=new RegExp("oz\\.("+Qe.join("|")+`)\\s*\\(\\s*['"][A-Za-z0-9_]+['"]`,"g");return K(e).forEach(n=>{let a="";try{a=l.readFileSync(n,"utf8")}catch{return}if(!/oz\./.test(a))return;let t=J(a),r=c.relative(e,n).split(c.sep).join("/");s.lastIndex=0;let i;for(;i=s.exec(t);){let d=i[1],u=t.indexOf("(",i.index);if(u===-1)continue;let p=L(t,u)-1;if(p<=u)continue;let f=t.slice(u+1,p),v=/\bdesc\s*:/.test(f),T=/\brole\s*:/.test(f);if(!v||!T){let h=[];v||h.push("desc"),T||h.push("role"),o.push({where:r+":"+G(t,i.index),msg:`oz.${d}() missing mandatory governance metadata: ${h.join(", ")} (desc and role are required, guard is optional).`})}}}),o}function tt(e,o){let s=e?.context||o?.context||"No written specifications provided.";return`# THE OZMOZ DATA CONTRACT (SVRO DATA CONTRACT)
165
+ `)}}var Q=/(?:const|let|var)\s+(\w+)\s*=\s*await\s+oz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update|delete|sendotpsvro|verifyotpsvro)\s*\(/g,ee=/(?<!=\s*)\bawait\s+oz\s*\.\s*(add|addWithId|addWithUserId|addWithMedia|update|delete|sendotpsvro|verifyotpsvro)\s*\(/g;function Je(e){let o=[];return J(e).forEach(s=>{let n="";try{n=l.readFileSync(s,"utf8")}catch{return}if(!/oz\./.test(n))return;let a=H(n),t=c.relative(e,s).split(c.sep).join("/");Q.lastIndex=0;let r;for(;r=Q.exec(a);){let i=r[1],d=a.slice(r.index);new RegExp("\\b"+i+"\\s*(?:\\?\\.|\\.)\\s*(success|error)\\b").test(d)||o.push({where:t+":"+B(a,r.index),msg:`oz.${r[2]}(): return value ('${i}') never checked with .success/.error (rule 4).`})}for(ee.lastIndex=0;r=ee.exec(a);)o.push({where:t+":"+B(a,r.index),msg:`oz.${r[1]}(): called without assigning return value to check .success/.error (rule 4).`})}),o}function Ke(e){let o=[],s=/\bresource\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g,n;for(;n=s.exec(String(e));)o.push(n[1]);return Array.from(new Set(o))}function Xe(e){let o=[],s=c.join(e,"svro.schema.json"),n=c.join(e,"svro.workflow.json");if(!l.existsSync(s)||!l.existsSync(n))return o;let a,t;try{a=JSON.parse(l.readFileSync(s,"utf8")),t=JSON.parse(l.readFileSync(n,"utf8"))}catch{return o}return Array.isArray(t.sequences)&&t.sequences.forEach(r=>{let i=a.collections?.[r.collection]?.fields||null;if(!i){o.push({where:r.id||"?",msg:`S\xE9quence sur la collection '${r.collection}', absente de svro.schema.json.`});return}(r.guards||[]).forEach(d=>{Ke(d).forEach(u=>{u in i||o.push({where:r.id||r.collection,msg:`Guard "${d}" r\xE9f\xE9rence '${u}', absent des champs d\xE9clar\xE9s de '${r.collection}' \u2014 l'Edge rejettera toute \xE9criture contenant ce champ (HTTP 422).`})})})}),o}var Qe=["add","addWithId","addWithUserId","addWithMedia","update","delete"];function et(e){let o=[],s=new RegExp("oz\\.("+Qe.join("|")+`)\\s*\\(\\s*['"][A-Za-z0-9_]+['"]`,"g");return J(e).forEach(n=>{let a="";try{a=l.readFileSync(n,"utf8")}catch{return}if(!/oz\./.test(a))return;let t=H(a),r=c.relative(e,n).split(c.sep).join("/");s.lastIndex=0;let i;for(;i=s.exec(t);){let d=i[1],u=t.indexOf("(",i.index);if(u===-1)continue;let p=F(t,u)-1;if(p<=u)continue;let f=t.slice(u+1,p),v=/\bdesc\s*:/.test(f),T=/\brole\s*:/.test(f);if(!v||!T){let h=[];v||h.push("desc"),T||h.push("role"),o.push({where:r+":"+B(t,i.index),msg:`oz.${d}() missing mandatory governance metadata: ${h.join(", ")} (desc and role are required, guard is optional).`})}}}),o}function tt(e,o){let s=e?.context||o?.context||"No written specifications provided.";return`# THE OZMOZ DATA CONTRACT (SVRO DATA CONTRACT)
166
166
 
167
167
  ### \u{1F6A8} MANDATORY CODE RULES (READ BEFORE WRITING ANY VIEW)
168
168
  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\`.
@@ -201,7 +201,7 @@ This document constitutes the **Official Specifications and Bilateral Integrity
201
201
  ---
202
202
 
203
203
  ### \u{1F9ED} DATA PROTOCOL: WHO DOES WHAT (FRONTEND AGENT \u2194 ALEX AI)
204
- ${ie}
204
+ ${re}
205
205
 
206
206
  ---
207
207
 
@@ -287,7 +287,7 @@ ${s}
287
287
 
288
288
  ---
289
289
 
290
- ${Be(e)}
290
+ ${Ge(e)}
291
291
 
292
292
  ---
293
293
 
@@ -302,27 +302,27 @@ ${JSON.stringify(e||{},null,2)}
302
302
  \`\`\`json
303
303
  ${JSON.stringify(o||{},null,2)}
304
304
  \`\`\`
305
- `}function nt(e){return String(e).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]/g,"_").replace(/^_+|_+$/g,"").slice(0,30)||"app"}async function ot(e,o){let s=V(e),n=l.existsSync(c.join(e,"svro.schema.json"));(s.OZ_DB_KEY||n)&&(z("Cannot create project: an ozmoZ project already exists in this folder."),m('\u2192 Use "npx ozmoz sync" or evolve the existing contract instead.'),process.exit(1));let a=(o||"").trim();a||(z('Project name is required. Usage: npx ozmoz create "<name>"'),process.exit(1));let t=c.join(e,".ozmoz.lock");if(l.existsSync(t)){let i=!1;try{let d=Number(l.readFileSync(t,"utf8"));Date.now()-d>6e4&&(i=!0)}catch{i=!0}i||(z("A project creation is already running in this folder."),process.exit(1)),l.unlinkSync(t)}l.writeFileSync(t,String(Date.now()));let r=()=>{if(l.existsSync(t))try{l.unlinkSync(t)}catch{}};try{let i=process.env.FLEETBO_DEV_TOKEN;if(!i){let h=c.join(A.homedir(),".fleetbo","credentials.json");if(l.existsSync(h))try{i=JSON.parse(l.readFileSync(h,"utf8")).devToken}catch{}}i||(r(),z('Missing developer token. Please run "npx ozmoz login" first.'),process.exit(1)),m(`[ozmoZ] Creating project "${a}" on Cloud...`);let d=nt(a),u=[d,`${d.slice(0,24)}_${Math.random().toString(36).slice(2,6)}`,`${d.slice(0,24)}_${Math.random().toString(36).slice(2,6)}`],p=null,f=null;for(let h of u)try{let g=await fetch("https://createprojectlight-jqycakhlxa-uc.a.run.app",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({data:{name:a,keyApp:h}})}),O=await g.json();if(g.ok&&!O.error){p={keyApp:h,data:O.result?.data||O.data||{}};break}else f=O.error?.message||`HTTP ${g.status}`}catch(g){f=g.message}p||(r(),z(`Failed to create project on Cloud: ${f||"Unknown error"}`),process.exit(1));let v=p.data.bootstrapToken;await se(e,[`--keyApp=${p.keyApp}`,`--token=${v}`]);let T=c.join(e,"package.json");oe(e,T),await ce(e,[]),m(`
305
+ `}function nt(e){return String(e).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]/g,"_").replace(/^_+|_+$/g,"").slice(0,30)||"app"}async function ot(e,o){let s=V(e),n=l.existsSync(c.join(e,"svro.schema.json"));(s.OZ_DB_KEY||n)&&(z("Cannot create project: an ozmoZ project already exists in this folder."),m('\u2192 Use "npx ozmoz sync" or evolve the existing contract instead.'),process.exit(1));let a=(o||"").trim();a||(z('Project name is required. Usage: npx ozmoz create "<name>"'),process.exit(1));let t=c.join(e,".ozmoz.lock");if(l.existsSync(t)){let i=!1;try{let d=Number(l.readFileSync(t,"utf8"));Date.now()-d>6e4&&(i=!0)}catch{i=!0}i||(z("A project creation is already running in this folder."),process.exit(1)),l.unlinkSync(t)}l.writeFileSync(t,String(Date.now()));let r=()=>{if(l.existsSync(t))try{l.unlinkSync(t)}catch{}};try{let i=process.env.FLEETBO_DEV_TOKEN;if(!i){let h=c.join(A.homedir(),".fleetbo","credentials.json");if(l.existsSync(h))try{i=JSON.parse(l.readFileSync(h,"utf8")).devToken}catch{}}i||(r(),z('Missing developer token. Please run "npx ozmoz login" first.'),process.exit(1)),m(`[ozmoZ] Creating project "${a}" on Cloud...`);let d=nt(a),u=[d,`${d.slice(0,24)}_${Math.random().toString(36).slice(2,6)}`,`${d.slice(0,24)}_${Math.random().toString(36).slice(2,6)}`],p=null,f=null;for(let h of u)try{let g=await fetch("https://createprojectlight-jqycakhlxa-uc.a.run.app",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({data:{name:a,keyApp:h}})}),w=await g.json();if(g.ok&&!w.error){p={keyApp:h,data:w.result?.data||w.data||{}};break}else f=w.error?.message||`HTTP ${g.status}`}catch(g){f=g.message}p||(r(),z(`Failed to create project on Cloud: ${f||"Unknown error"}`),process.exit(1));let v=p.data.bootstrapToken;await oe(e,[`--keyApp=${p.keyApp}`,`--token=${v}`]);let T=c.join(e,"package.json");ne(e,T),await ce(e,[]),m(`
306
306
  [ozmoZ] Project "${a}" (${p.keyApp}) created and configured successfully.`),m(`Next step: scaffold the frontend if needed, then run "npx ozmoz sync".
307
- `)}finally{if(l.existsSync(t))try{l.unlinkSync(t)}catch{}}}function st(e){if(!e||typeof e!="string")return e;let o=new Set(["auth","resource","true","false","null","undefined","in","typeof","instanceof","new","return","Math","Number","String","Boolean","Date"]);return e.replace(/(?<![\w.$])([a-zA-Z_$][a-zA-Z0-9_$]*)\b(?!\s*\()/g,(s,n,a,t)=>a>0&&t[a-1]==="."||o.has(n)||!isNaN(n)?s:`resource.${n}`)}async function rt(e){let o=process.env.FLEETBO_DEV_TOKEN;if(!o){let h=c.join(A.homedir(),".fleetbo","credentials.json");if(l.existsSync(h))try{o=JSON.parse(l.readFileSync(h,"utf8")).devToken}catch{}}let n=V(e).OZ_KEY_APP;(!n||!o)&&(z("Cannot update: project not initialized or developer token missing."),process.exit(1));let a=l.existsSync(c.join(e,"src"))?c.join(e,"src"):e,t=[],r=new Set([".jsx",".tsx",".vue",".svelte",".js",".ts",".html"]);function i(h){let g=[];try{g=l.readdirSync(h,{withFileTypes:!0})}catch{return}for(let O of g){if(/^(node_modules|\.git|dist|build)$/.test(O.name))continue;let y=c.join(h,O.name);if(O.isDirectory())i(y);else if(r.has(c.extname(O.name))&&!O.name.endsWith(".d.ts")){let S=l.readFileSync(y,"utf8");/oz\.|<oz-/i.test(S)&&t.push({name:c.basename(y),content:S})}}}i(a),t.length===0&&(z("Cannot update: no files calling oz.* found."),process.exit(1));let d=[],u=/oz\.(add|addWithId|addWithUserId|addWithMedia|update|delete)\s*\(\s*['"]([A-Za-z0-9_]+)['"]/g;t.forEach(h=>{u.lastIndex=0;let g;for(;g=u.exec(h.content);){let O=h.content.indexOf("(",g.index);if(O===-1)continue;let y=L(h.content,O)-1;if(y<=O)continue;let S=h.content.slice(O+1,y),E=w=>(S.match(new RegExp(w+"\\s*:\\s*(['\"`])((?:(?!\\1)[\\s\\S])*?)\\1"))||[])[2]||null,b=S.matchAll(/([a-zA-Z0-9_]+)\s*:/g),x=0;for(let w of b)["desc","guard","role"].includes(w[1])||x++;d.push({operation:g[1],table:g[2],fieldCount:x,desc:E("desc"),guard:st(E("guard")),role:E("role"),source:h.name})}});let p=d.filter(h=>!h.desc||!h.role);p.length>0&&(z(`Governance check failed: missing mandatory metadata { desc, role } on ${p.length} write operation(s).`),p.forEach(h=>{let g=[];h.desc||g.push("desc"),h.role||g.push("role"),z(`\u2192 oz.${h.operation}('${h.table}') in ${h.source} [manquant : ${g.join(", ")}]`)}),process.exit(1));let f=new Map;d.forEach(h=>{let g=`${h.table}:${h.operation}`;(!f.has(g)||h.fieldCount>f.get(g).fieldCount)&&f.set(g,h)});let v=Array.from(f.values()).map(({fieldCount:h,...g})=>g);m(`[ozmoZ] Sending ${t.length} component(s) to Alex AI...`);try{let h=await fetch("https://updateprojectlight-jqycakhlxa-uc.a.run.app",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({data:{keyApp:n,name:n,newViewFiles:t,explicitGovernance:v}})}),g=await h.json();(!h.ok||g.error)&&(z(`Update failed: ${g.error?.message||"Unknown API error"}`),process.exit(1))}catch(h){z(`Network error contacting Cloud: ${h.message}`),process.exit(1)}m("\u2192 Structure modeled. Sealing contract on Edge..."),_(process.execPath,[__filename,"sync"],{stdio:"inherit",cwd:e}).status===0?m(`
307
+ `)}finally{if(l.existsSync(t))try{l.unlinkSync(t)}catch{}}}function st(e){if(!e||typeof e!="string")return e;let o=new Set(["auth","resource","true","false","null","undefined","in","typeof","instanceof","new","return","Math","Number","String","Boolean","Date"]);return e.replace(/(?<![\w.$])([a-zA-Z_$][a-zA-Z0-9_$]*)\b(?!\s*\()/g,(s,n,a,t)=>a>0&&t[a-1]==="."||o.has(n)||!isNaN(n)?s:`resource.${n}`)}async function rt(e){let o=process.env.FLEETBO_DEV_TOKEN;if(!o){let h=c.join(A.homedir(),".fleetbo","credentials.json");if(l.existsSync(h))try{o=JSON.parse(l.readFileSync(h,"utf8")).devToken}catch{}}let n=V(e).OZ_KEY_APP;(!n||!o)&&(z("Cannot update: project not initialized or developer token missing."),process.exit(1));let a=l.existsSync(c.join(e,"src"))?c.join(e,"src"):e,t=[],r=new Set([".jsx",".tsx",".vue",".svelte",".js",".ts",".html"]);function i(h){let g=[];try{g=l.readdirSync(h,{withFileTypes:!0})}catch{return}for(let w of g){if(/^(node_modules|\.git|dist|build)$/.test(w.name))continue;let y=c.join(h,w.name);if(w.isDirectory())i(y);else if(r.has(c.extname(w.name))&&!w.name.endsWith(".d.ts")){let S=l.readFileSync(y,"utf8");/oz\.|<oz-/i.test(S)&&t.push({name:c.basename(y),content:S})}}}i(a),t.length===0&&(z("Cannot update: no files calling oz.* found."),process.exit(1));let d=[],u=/oz\.(add|addWithId|addWithUserId|addWithMedia|update|delete)\s*\(\s*['"]([A-Za-z0-9_]+)['"]/g;t.forEach(h=>{u.lastIndex=0;let g;for(;g=u.exec(h.content);){let w=h.content.indexOf("(",g.index);if(w===-1)continue;let y=F(h.content,w)-1;if(y<=w)continue;let S=h.content.slice(w+1,y),O=E=>(S.match(new RegExp(E+"\\s*:\\s*(['\"`])((?:(?!\\1)[\\s\\S])*?)\\1"))||[])[2]||null,b=S.matchAll(/([a-zA-Z0-9_]+)\s*:/g),x=0;for(let E of b)["desc","guard","role"].includes(E[1])||x++;d.push({operation:g[1],table:g[2],fieldCount:x,desc:O("desc"),guard:st(O("guard")),role:O("role"),source:h.name})}});let p=d.filter(h=>!h.desc||!h.role);p.length>0&&(z(`Governance check failed: missing mandatory metadata { desc, role } on ${p.length} write operation(s).`),p.forEach(h=>{let g=[];h.desc||g.push("desc"),h.role||g.push("role"),z(`\u2192 oz.${h.operation}('${h.table}') in ${h.source} [manquant : ${g.join(", ")}]`)}),process.exit(1));let f=new Map;d.forEach(h=>{let g=`${h.table}:${h.operation}`;(!f.has(g)||h.fieldCount>f.get(g).fieldCount)&&f.set(g,h)});let v=Array.from(f.values()).map(({fieldCount:h,...g})=>g);m(`[ozmoZ] Sending ${t.length} component(s) to Alex AI...`);try{let h=await fetch("https://updateprojectlight-jqycakhlxa-uc.a.run.app",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({data:{keyApp:n,name:n,newViewFiles:t,explicitGovernance:v}})}),g=await h.json();(!h.ok||g.error)&&(z(`Update failed: ${g.error?.message||"Unknown API error"}`),process.exit(1))}catch(h){z(`Network error contacting Cloud: ${h.message}`),process.exit(1)}m("\u2192 Structure modeled. Sealing contract on Edge..."),_(process.execPath,[__filename,"sync"],{stdio:"inherit",cwd:e}).status===0?m(`
308
308
  [ozmoZ] Contract successfully updated and sealed at Edge.
309
309
  `):(z(`
310
310
  [ozmoZ] Edge sync failed. Run "npx ozmoz sync" manually.
311
311
  `),process.exit(1))}async function it(){let e=process.cwd(),o=c.join(e,"package.json");if(j==="init"||j==="doctor")m(`
312
- [ozmoZ] Initializing and configuring ozmoZ...`),await se(e,I),oe(e,o),m(`
312
+ [ozmoZ] Initializing and configuring ozmoZ...`),await oe(e,I),ne(e,o),m(`
313
313
  [ozmoZ] Configuration completed successfully!
314
314
  `),m(`
315
315
  Next step:`),m(` npx ozmoz sync (Sync cloud schema & generate IDE types)
316
- `);else if(j==="sync"){let s=I.includes("--dry-run"),n=I.includes("--check-only");m(`[ozmoZ] Syncing schema...${s?" (dry-run)":""}${n?" (check-only, no network)":""}`),s||ze(e);let a=c.join(e,"svro.schema.json"),t=c.join(e,"svro.workflow.json"),r=c.join(e,"ozmoz_contract.md"),i=c.join(e,"src","ozmoz.d.ts"),u=V(e).OZ_ENTERPRISE_ID,p=null,f=null;if(n)m("\u2192 Mode check-only : Local contract used, no cloud fetch..");else if(u)try{m("\u2192 Fetching latest contracts from Cloud..."),p=await Q(u,"svro.schema.json"),p&&(l.writeFileSync(a,JSON.stringify(p,null,2)+`
317
- `),m("\u2192 Local svro.schema.json updated.")),f=await Q(u,"svro.workflow.json"),f&&(l.writeFileSync(t,JSON.stringify(f,null,2)+`
316
+ `);else if(j==="sync"){let s=I.includes("--dry-run"),n=I.includes("--check-only");m(`[ozmoZ] Syncing schema...${s?" (dry-run)":""}${n?" (check-only, no network)":""}`),s||ze(e);let a=c.join(e,"svro.schema.json"),t=c.join(e,"svro.workflow.json"),r=c.join(e,"ozmoz_contract.md"),i=c.join(e,"src","ozmoz.d.ts"),u=V(e).OZ_ENTERPRISE_ID,p=null,f=null;if(n)m("\u2192 Mode check-only : Local contract used, no cloud fetch..");else if(u)try{m("\u2192 Fetching latest contracts from Cloud..."),p=await X(u,"svro.schema.json"),p&&(l.writeFileSync(a,JSON.stringify(p,null,2)+`
317
+ `),m("\u2192 Local svro.schema.json updated.")),f=await X(u,"svro.workflow.json"),f&&(l.writeFileSync(t,JSON.stringify(f,null,2)+`
318
318
  `),m("\u2192 Local svro.workflow.json updated."))}catch(g){z(`\u26A0\uFE0F Cloud fetch bypassed: ${g.message}. Using local file fallback.`)}else z("\u26A0\uFE0F OZ_ENTERPRISE_ID missing in .env. Skipping cloud fetch.");let v=p;if(!v&&l.existsSync(a))try{v=JSON.parse(l.readFileSync(a,"utf8"))}catch{}v||(v={context:"Application en cours de mod\xE9lisation initiale.",collections:{}},l.writeFileSync(a,JSON.stringify(v,null,2)+`
319
319
  `,"utf8"),m("\u2192 Initial blank svro.schema.json created."));let T=f;if(!T&&l.existsSync(t))try{T=JSON.parse(l.readFileSync(t,"utf8"))}catch{}T||(T={actors:{},sequences:[]},l.writeFileSync(t,JSON.stringify(T,null,2)+`
320
320
  `,"utf8"),m("\u2192 Initial blank svro.workflow.json created."));{let g=We(v);g.changed&&(v=g.schema,l.writeFileSync(a,JSON.stringify(v,null,2)+`
321
- `,"utf8"),m("\u2192 Managed by system, no longer required from your code: "+g.notes.join(", ")+"."))}let h=tt(v,T);l.writeFileSync(r,h,"utf8"),m("\u2192 Contract markdown successfully generated at project root.");try{let g=Object.keys(v.collections||{});if(u&&!n)try{await Ze(u,v),m("\u2192 Schema successfully sealed at Global Edge (O(1)).")}catch(w){z(`\u26A0\uFE0F Edge Shield sync warning: ${w.message}`)}let O=v.collections||{},y=Object.entries(O).map(([w,F])=>{let B=F.fields||F.schema||{},D=Object.entries(B).map(([U,C])=>{let M="any",N=String(typeof C=="string"?C:C.type||"").toLowerCase();return N==="string"||N==="text"||N==="media"?M="string":N==="number"?M="number":N==="boolean"||N==="toggle"?M="boolean":N==="array"&&(M="any[]"),` ${U}?:${M};`}).join(`
322
- `);return` ${w}: {
321
+ `,"utf8"),m("\u2192 Managed by system, no longer required from your code: "+g.notes.join(", ")+"."))}let h=tt(v,T);l.writeFileSync(r,h,"utf8"),m("\u2192 Contract markdown successfully generated at project root.");try{let g=Object.keys(v.collections||{});if(u&&!n)try{await Me(u,v),m("\u2192 Schema successfully sealed at Global Edge (O(1)).")}catch(E){z(`\u26A0\uFE0F Edge Shield sync warning: ${E.message}`)}let w=v.collections||{},y=Object.entries(w).map(([E,U])=>{let G=U.fields||U.schema||{},D=Object.entries(G).map(([Z,C])=>{let M="any",N=String(typeof C=="string"?C:C.type||"").toLowerCase();return N==="string"||N==="text"||N==="media"?M="string":N==="number"?M="number":N==="boolean"||N==="toggle"?M="boolean":N==="array"&&(M="any[]"),` ${Z}?:${M};`}).join(`
322
+ `);return` ${E}: {
323
323
  ${D}
324
324
  };`}).join(`
325
- `),S=g.length>0?g.map(w=>`'${w}'`).join(" | "):"string";if(l.existsSync(c.dirname(i))){let w=`/* eslint-disable */
325
+ `),S=g.length>0?g.map(E=>`'${E}'`).join(" | "):"string";if(l.existsSync(c.dirname(i))){let E=`/* eslint-disable */
326
326
  /* prettier-ignore */
327
327
  // @ts-nocheck
328
328
  // Generated by ozmoZ \u2014 Updated via sync
@@ -458,13 +458,13 @@ declare module 'svelte/elements' {
458
458
  'oz-reference': OzProp;
459
459
  }
460
460
  }
461
- `;l.writeFileSync(i,w)}ge(e),ye(e,o),ve(e),Ee(e),m(`[ozmoZ] Schema synced successfully (${g.length} collection(s)).`);try{He(e,v)}catch{}let E=Je(e);E.length>0&&(z(`
462
- \u{1F6AB} Sync blocked: ${E.length} write call(s) do not check results (rule 4):`),E.slice(0,20).forEach(w=>z(" "+w.where+" "+w.msg)),z(` \u2192 Inspect res.success and handle res.error on each call before syncing.
461
+ `;l.writeFileSync(i,E)}ge(e),ye(e,o),ve(e),Oe(e),m(`[ozmoZ] Schema synced successfully (${g.length} collection(s)).`);try{He(e,v)}catch{}let O=Je(e);O.length>0&&(z(`
462
+ \u{1F6AB} Sync blocked: ${O.length} write call(s) do not check results (rule 4):`),O.slice(0,20).forEach(E=>z(" "+E.where+" "+E.msg)),z(` \u2192 Inspect res.success and handle res.error on each call before syncing.
463
463
  `),process.exit(1));let b=Xe(e);b.length>0&&(z(`
464
- \u{1F6AB} Sync blocked: ${b.length} workflow guard(s) reference unsealed fields:`),b.slice(0,20).forEach(w=>z(" ["+w.where+"] "+w.msg)),z(` \u2192 Update svro.schema.json or adjust workflow guards before continuing.
464
+ \u{1F6AB} Sync blocked: ${b.length} workflow guard(s) reference unsealed fields:`),b.slice(0,20).forEach(E=>z(" ["+E.where+"] "+E.msg)),z(` \u2192 Update svro.schema.json or adjust workflow guards before continuing.
465
465
  `),process.exit(1));let x=et(e);x.length>0&&(z(`
466
- \u{1F6AB} Sync blocked: ${x.length} write call(s) missing mandatory governance { desc, role }:`),x.slice(0,20).forEach(w=>z(" "+w.where+" "+w.msg)),z(` \u2192 Add literal { desc, role } to each write call before syncing (optional only on reads).
467
- `),process.exit(1)),s&&m("\u2192 [Dry-run] Schema is valid.")}catch(g){z(`Error during synchronization: ${g.message}`),process.exit(1)}}else if(j==="create"){let s=I.slice(1).filter(n=>!n.startsWith("--")).join(" ");await ot(e,s),process.exit(0)}else if(j==="update")await rt(e),process.exit(0);else if(j==="mcp"){let s=I.includes("--run"),n=!1;if(!s&&!process.stdin.isTTY&&(s=await new Promise(a=>{let t=process.stdin,r=null,i=p=>{clearTimeout(r),t.removeListener("data",d),t.removeListener("end",u),t.removeListener("error",u),a(p)},d=p=>{t.pause(),t.unshift(p),n=!0,i(!0)},u=()=>i(!1);r=setTimeout(()=>{t.pause(),i(!1)},1500),t.on("data",d),t.once("end",u),t.once("error",u)})),s){try{await import(`file://${H()}`),n&&process.stdin.resume()}catch(a){z(`Failed to start MCP server: ${a.message}`),process.exit(1)}return}await ce(e,I),process.exit(0)}else j==="login"?(await Me(e),process.exit(0)):j==="start"?Fe(e,I):["--help","-h","help"].includes(j)?(m(`ozmoZ CLI
466
+ \u{1F6AB} Sync blocked: ${x.length} write call(s) missing mandatory governance { desc, role }:`),x.slice(0,20).forEach(E=>z(" "+E.where+" "+E.msg)),z(` \u2192 Add literal { desc, role } to each write call before syncing (optional only on reads).
467
+ `),process.exit(1)),s&&m("\u2192 [Dry-run] Schema is valid.")}catch(g){z(`Error during synchronization: ${g.message}`),process.exit(1)}}else if(j==="create"){let s=I.slice(1).filter(n=>!n.startsWith("--")).join(" ");await ot(e,s),process.exit(0)}else if(j==="update")await rt(e),process.exit(0);else if(j==="mcp"){let s=I.includes("--run"),n=!1;if(!s&&!process.stdin.isTTY&&(s=await new Promise(a=>{let t=process.stdin,r=null,i=p=>{clearTimeout(r),t.removeListener("data",d),t.removeListener("end",u),t.removeListener("error",u),a(p)},d=p=>{t.pause(),t.unshift(p),n=!0,i(!0)},u=()=>i(!1);r=setTimeout(()=>{t.pause(),i(!1)},1500),t.on("data",d),t.once("end",u),t.once("error",u)})),s){try{await import(`file://${ae()}`),n&&process.stdin.resume()}catch(a){z(`Failed to start MCP server: ${a.message}`),process.exit(1)}return}await ce(e,I),process.exit(0)}else j==="login"?(await Ze(e),process.exit(0)):j==="start"?Fe(e,I):["--help","-h","help"].includes(j)?(m(`ozmoZ CLI
468
468
 
469
469
  Commands:
470
470
  login Authenticate this machine (opens the browser: approve once, the command exits by itself)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ozmoz",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "SDK and CLI for the oz.* data API and the <oz-*> web components.",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": [