@microi.net/cli 4.7.3 → 4.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -560,7 +560,7 @@ if (V8.ThisValue && typeof V8.ThisValue === 'object') {
560
560
  `,"| # | Title | Number | Description | Updated |","|---|-------|--------|-------------|---------|"];return c.forEach((u,p)=>{l.push(`| ${p+1} | ${u.Title||""} | ${u.Number||""} | ${u.Desc||""} | ${u.UpdateTime||""} |`)}),{content:[{type:"text",text:l.join(`
561
561
  `)}]}}catch(s){return{content:[{type:"text",text:`Error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}}),i.tool("microi_get_page",`Get page engine detail including full JSON configuration for OsClient "${r}". The JsonObj field contains the complete page structure with formData, wrapperList, and widgetList.`,{pageId:f.string().describe("The page Id to retrieve")},async({pageId:o})=>{try{let s=await t.getPageEngineDetail(o);if(s.Code!==1)return{content:[{type:"text",text:`Error: ${s.Msg}`}],isError:!0};let c=s.Data;return{content:[{type:"text",text:[`## Page: ${c?.Title||o}`,c?.Number?`- **Number**: ${c.Number}`:"",c?.Desc?`- **Description**: ${c.Desc}`:"","","### JSON Configuration","```json",typeof c?.JsonObj=="string"?c.JsonObj:JSON.stringify(c?.JsonObj,null,2),"```"].filter(Boolean).join(`
562
562
  `)}]}}catch(s){return{content:[{type:"text",text:`Error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}}),i.tool("microi_save_page",`Create or update a page engine page for OsClient "${r}". Accepts raw JsonObj, {JsonObj}, {JsonStr}, a mic_page row, or {formData:{JsonObj}} and normalizes it to the canonical JsonObj saved in mic_page.JsonObj. Pass pageId to update an existing page, or omit to create a new one.`,{pageId:f.string().optional().describe("Page Id to update. Omit to create a new page."),title:f.string().describe('Page title (e.g. "\u9500\u552E\u4EEA\u8868\u76D8", "\u6570\u636E\u6982\u89C8")'),number:f.string().optional().describe("Page number/code (auto-generated if omitted)"),desc:f.string().optional().describe("Page description"),jsonStr:f.string().optional().describe("Page Engine JsonObj string. Prefer json for object input."),json:f.unknown().optional().describe("Page Engine JSON object/string in any common AI output shape."),routePath:f.string().optional().describe("Optional route path saved to mic_page.RoutePath."),componentPath:f.string().optional().describe("Optional component path saved to mic_page.ComponentPath.")},async({pageId:o,title:s,number:c,desc:l,jsonStr:u,json:p,routePath:d,componentPath:h})=>{try{let m=dn(p??u);if(!m.ok||!m.json)return{content:[{type:"text",text:JSON.stringify(m,null,2)}],isError:!0};let y=await t.savePageEngine({PageId:o,Title:s,Number:c,Desc:l,JsonStr:m.json,RoutePath:d,ComponentPath:h});if(y.Code!==1)return{content:[{type:"text",text:`Error: ${y.Msg}`}],isError:!0};let g=y.Data;return{content:[{type:"text",text:`\u2705 ${g?.Message||"Page saved successfully."}
563
- - PageId: ${g?.PageId}`}]}}catch(m){return{content:[{type:"text",text:`Error: ${m instanceof Error?m.message:String(m)}`}],isError:!0}}}),i.tool("microi_list_applications",`List every online AI application for OsClient "${r}" across Web, UniApp and MicroService, including each app's complete source-file manifest by default. AI agents should call this at the beginning of application/page work so they understand existing apps before creating duplicates. For complex custom dialogs/pages, prefer extending an existing MicroService or creating one with microi_create_microservice + microi_sync_microservice_source; do not embed large HTML in V8.ConfirmTips.`,{appType:f.enum(["Web","UniApp","MicroService"]).optional().describe("Optional exact application type filter."),keyword:f.string().optional().describe("Optional case-insensitive search across name, AppKey, type and description."),includeFiles:f.boolean().optional().default(!0).describe("Include the complete file manifest for every app. Defaults to true.")},async({appType:o,keyword:s,includeFiles:c})=>{try{let l=await t.listApplications({AppType:o,Keyword:s,IncludeFiles:c!==!1});return l.Code!==1?{content:[{type:"text",text:`Error: ${l.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(l.Data,null,2)}]}}catch(l){return{content:[{type:"text",text:`Error: ${l instanceof Error?l.message:String(l)}`}],isError:!0}}}),i.tool("microi_get_application_context",`Get one Web, UniApp or MicroService application by Id/AppKey for OsClient "${r}". It returns metadata and the full file manifest by default without embedding source bodies; set includeContents=true only when the complete source is actually needed, or use microi_get_application_file for one exact file. MicroService responses also include sys_microiservice runtime/pages.`,{appIdOrKey:f.string().describe("sys_microistore.Id or AppKey."),includeContents:f.boolean().optional().default(!1).describe("Read private HDFS source contents. Defaults to false; prefer microi_get_application_file for targeted reads."),maxFileBytes:f.number().int().positive().optional().describe("Maximum bytes read per source file. Default 2MB."),maxTotalBytes:f.number().int().positive().optional().describe("Maximum total bytes read for this app. Default 50MB.")},async({appIdOrKey:o,includeContents:s,maxFileBytes:c,maxTotalBytes:l})=>{try{let u=await t.getApplicationContext({AppIdOrKey:o,IncludeContents:s===!0,...c?{MaxFileBytes:c}:{},...l?{MaxTotalBytes:l}:{}});if(u.Code!==1)return{content:[{type:"text",text:`Error: ${u.Msg}`}],isError:!0};let p=u.Data,h=(Array.isArray(p?.Files)?p.Files:[]).filter(y=>typeof y?.ContentReadError=="string"&&y.ContentReadError).length,m={...p,McpReadSummary:{RequestedContents:s===!0,ContentsComplete:s!==!0||h===0,ContentErrorCount:h}};return{content:[{type:"text",text:JSON.stringify(m,null,2)}]}}catch(u){return{content:[{type:"text",text:`Error: ${u instanceof Error?u.message:String(u)}`}],isError:!0}}}),i.tool("microi_get_application_file",`Read one exact source file from a Web, UniApp or MicroService online AI application for OsClient "${r}". Text code is returned as UTF-8 Content; binary files are returned as FileByteBase64.`,{appIdOrKey:f.string().describe("sys_microistore.Id or AppKey."),filePath:f.string().describe("Exact relative source path from the application file manifest."),maxFileBytes:f.number().int().positive().optional().describe("Maximum bytes read. Default 10MB.")},async({appIdOrKey:o,filePath:s,maxFileBytes:c})=>{try{let l=await t.getApplicationFile({AppIdOrKey:o,FilePath:s,IncludeContents:!0,...c?{MaxFileBytes:c}:{}});return l.Code!==1?{content:[{type:"text",text:`Error: ${l.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(l.Data,null,2)}]}}catch(l){return{content:[{type:"text",text:`Error: ${l instanceof Error?l.message:String(l)}`}],isError:!0}}}),i.tool("microi_scaffold_vue_microservice",`Create a safe, maintainable MicroService scaffold for OsClient "${r}" using the stable Vue 3.5.40 + Vite 7.3.6 + TypeScript 5.9.3 baseline, @vitejs/plugin-vue 6.0.8, vue-tsc 3.3.9, Composition API and <script setup lang="ts">. The minimal template keeps Microi SDK and host/internal-route integration but does not force Pinia or Vue Router; add either only when application complexity needs it. It writes .microi-micro-app.json, microi.routes.json and exactly one Vue component per declared route inside the local tenant AI\u5E94\u7528 directory. It only accepts a real absolute directory whose basename is AI\u5E94\u7528, never overwrites a different existing project, and performs a dry run until confirmExecution exactly equals appKey. After scaffolding, run npm install and npm run build, then use microi_create_microservice, microi_sync_microservice_source and microi_publish_application_directory_stream (or the legacy publisher for a tiny compatibility payload).`,{appKey:f.string().regex(/^[a-z0-9](?:[a-z0-9_-]{0,62}[a-z0-9])?$/u).describe("Stable lowercase application key and local directory name."),name:f.string().min(1).max(120).describe("Human-readable MicroService name."),description:f.string().optional().describe("Optional application description."),aiApplicationsDirectory:f.string().optional().describe("Absolute tenant AI\u5E94\u7528 directory. Defaults to MICROI_AI_APPLICATIONS_DIR injected by Microi.VSCode."),buildVersion:f.string().regex(/^v\d+\.\d+\.\d+$/u).optional().default("v0.1.0").describe("Initial semantic build version. Default v0.1.0."),routes:f.array(f.object({path:f.string().describe("Internal route path such as /context-test."),name:f.string().describe("Stable route key such as context-test."),title:f.string().describe("Page/menu title."),description:f.string().optional().describe("Optional visible page explanation."),isHome:f.boolean().optional().describe("Mark one route as the default page. The first route is used when omitted.")})).min(1).max(50).describe("Vue pages and MicroService internal routes. One .vue file is generated per item."),confirmExecution:f.string().optional().describe("Required for filesystem writes and must exactly equal appKey. Omit for a local preflight only.")},async({appKey:o,name:s,description:c,aiApplicationsDirectory:l,buildVersion:u,routes:p,confirmExecution:d})=>{try{let h=String(l||process.env.MICROI_AI_APPLICATIONS_DIR||"").trim();if(!h)return{content:[{type:"text",text:"Error: \u7F3A\u5C11 AI \u5E94\u7528\u76EE\u5F55\u3002\u8BF7\u7531 Microi.VSCode \u6CE8\u5165 MICROI_AI_APPLICATIONS_DIR\uFF0C\u6216\u663E\u5F0F\u4F20\u5165 aiApplicationsDirectory\u3002"}],isError:!0};if(d&&d!==o)return{content:[{type:"text",text:`Error: confirmExecution \u5FC5\u987B\u7CBE\u786E\u7B49\u4E8E ${o}`}],isError:!0};let m={aiApplicationsDirectory:h,appKey:o,name:s,description:c,apiBaseUrl:e.apiBaseUrl,osClient:r,buildVersion:u,routes:p,sdkSource:FP(process.env.MICROI_WORKSPACE_ROOT)},y=Qv(m);if(d!==o)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,confirmationRequired:o,targetDirectory:y.targetDirectory,appKey:y.appKey,buildVersion:y.buildVersion,routeCount:y.routes.length,routes:y.routes.map(x=>({path:x.path,name:x.name,title:x.title,sourceFile:x.sourceFile,isHome:x.isHome})),fileCount:y.files.length,files:y.files.map(x=>({Path:x.relativePath,Size:x.size,Sha256:x.sha256}))},null,2)}]};let g=jP(m);return{content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(h){return{content:[{type:"text",text:`Error: ${h instanceof Error?h.message:String(h)}`}],isError:!0}}}),i.tool("microi_get_microservice",`Get one Microi microservice / micro-app by MsKey for OsClient "${r}". Use this before publishing to inspect current BuildVersion, EntryPath and asset manifest.`,{msKey:f.string().describe("Microservice key, stored in sys_microiservice.MsKey")},async({msKey:o})=>{try{let s=await t.getMicroService(o);return s.Code!==1?{content:[{type:"text",text:`Error: ${s.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(s.Data,null,2)}]}}catch(s){return{content:[{type:"text",text:`Error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}}),i.tool("microi_create_microservice",`Create or update sys_microiservice metadata for OsClient "${r}". This only writes metadata. For generated app source/dist files, use microi_publish_microservice.`,{microService:mr.describe("Microservice metadata. Required: MsKey and MsName/Name. Optional: MsType, Runtime, StorageMode, SourceDirName, EntryPath, BuildVersion."),confirmExecution:f.string().optional().describe("Required for real writes. Pass any non-empty confirmation string after reviewing the payload.")},async({microService:o,confirmExecution:s})=>{if(!s)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,microService:o},null,2)}]};try{let c=await t.createMicroService(o);return c.Code!==1?{content:[{type:"text",text:`Error: ${c.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(c.Data,null,2)}]}}catch(c){return{content:[{type:"text",text:`Error: ${c instanceof Error?c.message:String(c)}`}],isError:!0}}}),i.tool("microi_sync_microservice_source",`Sync local microservice source files into the online AI Application for OsClient "${r}". The app is created/upserted as AppType=MicroService; source files are private and remain separate from published assets.`,{microService:mr.describe("Microservice metadata. Required: MsKey and MsName/Name. Optional: Description and SourceDirName."),sourceFiles:f.array(mr).describe("Source files. Each item needs Path/FilePath and FileByteBase64/ContentBase64. Optional: Size and Sha256."),replace:f.boolean().optional().describe("When true, remove stale online source metadata not present in this manifest."),confirmExecution:f.string().optional().describe("Required for real writes. Pass any non-empty confirmation string after reviewing the payload.")},async({microService:o,sourceFiles:s,replace:c,confirmExecution:l})=>{if(!l)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,microService:o,sourceFileCount:s.length,replace:c===!0},null,2)}]};try{let u=await t.syncMicroServiceSource({microService:o,sourceFiles:s,Replace:c===!0});return u.Code!==1?{content:[{type:"text",text:`Error: ${u.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(u.Data,null,2)}]}}catch(u){return{content:[{type:"text",text:`Error: ${u instanceof Error?u.message:String(u)}`}],isError:!0}}}),i.tool("microi_upload_application_asset_stream",`Stream one local built asset directly to the immutable HDFS version directory for OsClient "${r}". The file is never encoded as Base64 and never enters Jint. This is a low-level resumable primitive; normally use microi_publish_application_directory_stream to upload and atomically promote a complete directory.`,{appIdOrKey:f.string().min(1).describe("Existing sys_microistore Id or AppKey."),versionNo:f.string().regex(/^v?\d+\.\d+\.\d+$/u).describe("Immutable semantic version, e.g. v1.2.3."),relativePath:f.string().min(1).describe("POSIX-style path inside the compiled output, e.g. assets/index-abcd.js."),localFilePath:f.string().min(1).describe("Absolute or workspace-relative path of one local ordinary file."),sha256:f.string().regex(/^[a-fA-F0-9]{64}$/u).optional().describe("Optional expected SHA-256. MCP computes and verifies it when omitted."),timeoutMs:f.number().int().min(1e3).max(2*60*6e4).optional().describe("Per-file upload timeout. Default 30 minutes; maximum 2 hours."),confirmExecution:f.string().optional().describe("Required for the real upload and must exactly equal appIdOrKey.")},async({appIdOrKey:o,versionNo:s,relativePath:c,localFilePath:l,sha256:u,timeoutMs:p,confirmExecution:d})=>{try{let h=rd(c),m=Ye.default.resolve(l),y=xt.default.lstatSync(m);if(!y.isFile()||y.isSymbolicLink())throw new Error(`\u672C\u5730\u8D44\u4EA7\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\u4E14\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${m}`);let g=await r$(m);if(u&&u.toLowerCase()!==g)throw new Error("\u672C\u5730\u6587\u4EF6 SHA-256 \u4E0E\u4F20\u5165\u503C\u4E0D\u4E00\u81F4");let x=`mcp-low-${na([o,It(s),h,g]).slice(0,48)}`,v=n$({deliveryBatchId:x,appIdOrKey:o,versionNo:s,relativePath:h,sha256:g}),b={appIdOrKey:o,versionNo:s,relativePath:h,size:y.size,sha256:g,requestId:v};if(d!==o)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,confirmationRequired:o,...b},null,2)}]};let w=await t.uploadApplicationAssetStream({AppIdOrKey:o,VersionNo:s,RelativePath:h,ExpectedSha256:g,RequestId:v,FilePath:m,TimeoutMs:p});if(w.Code!==1)return{content:[{type:"text",text:`Error: ${w.Msg}`}],isError:!0};let E=a$(w,{requestId:v,versionNo:s,relativePath:h,sha256:g,size:y.size});return{content:[{type:"text",text:JSON.stringify(E,null,2)}]}}catch(h){return{content:[{type:"text",text:`Error: ${h instanceof Error?h.message:String(h)}`}],isError:!0}}}),i.tool("microi_publish_application_directory_stream",`Publish a real Web, UniApp or MicroService build directory for OsClient "${r}" using bounded multipart file streams and deterministic RequestId evidence. publishMode=stage uploads immutable version assets only; finalize rebuilds the same local manifest and promotes it without uploading; stage-and-finalize keeps the original one-call flow. Each file is capped at 128 MiB and the complete manifest at 1 GiB before upload. Paths, hashes, sizes, request ids, version preconditions and final manifest evidence are verified strictly. Old API nodes without RequestId support fail closed.`,{appIdOrKey:f.string().min(1).describe("Existing sys_microistore Id or AppKey."),versionNo:f.string().regex(/^v?\d+\.\d+\.\d+$/u).describe("Immutable semantic version, e.g. v1.2.3."),directory:f.string().min(1).describe("Local compiled output directory such as dist or unpackage/dist/build/h5."),entryPath:f.string().optional().default("index.html").describe("Entry file relative to directory. Default index.html."),routes:f.array(mr).optional().default([]).describe("Canonical v3 route snapshot input; missing routes are frozen explicitly as []."),routeSnapshotJson:f.string().max(1024*1024).optional().describe("Protocol v3 requires the exact recursive-key-sorted, array-order-preserving UTF-8 canonical JSON for routes."),routeSnapshotHash:f.string().regex(/^[a-f0-9]{64}$/u).optional().describe("Protocol v3 requires the lowercase SHA-256 of RouteSnapshotJson."),changeSummary:f.string().optional().describe("Version change summary stored in mci_ai_app_version."),sourceManifestHash:f.string().regex(/^[a-fA-F0-9]{64}$/u).optional().describe("Optional source-manifest SHA-256 returned by microi_sync_microservice_source, tying source and runtime to one delivery."),runtimeManifestHash:f.string().regex(/^[a-f0-9]{64}$/u).optional().describe("Protocol v3 requires the exact lowercase runtime-manifest SHA-256 computed during the local dry run."),deliveryBatchId:f.string().min(8).max(50).optional().describe("Optional stable delivery batch id (8-50 characters, matching the API and LastBuildTaskId). When omitted MCP derives a deterministic id from app/version/manifest so retries remain stable."),publishMode:f.enum(["stage","finalize","stage-and-finalize"]).optional().default("stage-and-finalize").describe("stage uploads immutable assets only; finalize submits the local manifest only; stage-and-finalize preserves the original one-call behavior."),protocolVersion:f.literal(3).optional().describe("Explicit protocol v3 gate. When set, publishMode must be explicit stage or finalize and every v3 precondition is required."),expectedGateEpoch:f.string().regex(/^(0|[1-9]\d*)$/u).optional().describe("Protocol v3 gate epoch as a canonical decimal bigint string."),requestId:f.string().regex(/^[A-Za-z0-9._:-]{8,100}$/u).optional().describe("Protocol v3 stable release RequestId; stage/finalize and all Pending replays must reuse it exactly."),requestFingerprint:f.string().regex(/^[a-f0-9]{64}$/u).optional().describe("Protocol v3 immutable release fingerprint."),expectedCurrentVersion:f.number().int().min(0).optional().describe("Required for finalize/stage-and-finalize and omitted only during stage. Must equal the application CurrentVersion read before staging."),expectedAppVersion:f.string().nullable().optional().describe("Application AppVersion baseline. Protocol v3 requires explicit string or null for both stage and finalize."),expectedPublishFence:f.string().regex(/^(0|[1-9]\d*)$/u).optional().describe("Protocol v3 sys_microistore.PublishFence canonical decimal bigint string."),expectedPublishRowVersion:f.string().regex(/^(0|[1-9]\d*)$/u).optional().describe("Protocol v3 sys_microistore.PublishRowVersion canonical decimal bigint string."),expectedVersionRowVersion:f.string().regex(/^(0|[1-9]\d*)$/u).nullable().optional().describe("Protocol v3 target mci_ai_app_version.RowVersion; explicit null means the row must not exist."),expectedActivePublishVersionId:f.string().min(1).nullable().optional().describe("Protocol v3 active version id baseline; explicit null means empty."),expectedCommittedPublishVersionId:f.string().min(1).nullable().optional().describe("Protocol v3 committed version id baseline; explicit null means empty."),includeSourceMaps:f.boolean().optional().default(!1).describe("Publish *.map source maps. Defaults to false to avoid source disclosure."),maxFiles:f.number().int().min(1).max(2e4).optional().describe("Safety cap checked before upload. Default and hard maximum 20,000."),maxTotalMegabytes:f.number().positive().max(1024).optional().default(1024).describe("Safety cap checked before upload. Default and hard maximum 1 GiB (1024 MiB)."),timeoutMsPerFile:f.number().int().min(1e3).max(2*60*6e4).optional().describe("Per-file upload timeout. Default 30 minutes."),allowLegacyFallback:f.boolean().optional().default(!1).describe("Deprecated compatibility flag. RequestId-capable two-phase publishing fails closed on old API nodes instead of publishing through the legacy payload."),confirmExecution:f.string().optional().describe("Required for real publishing and must exactly equal appIdOrKey. Omit for a local preflight manifest only.")},async({appIdOrKey:o,versionNo:s,directory:c,entryPath:l,routes:u,routeSnapshotJson:p,routeSnapshotHash:d,changeSummary:h,sourceManifestHash:m,runtimeManifestHash:y,deliveryBatchId:g,publishMode:x,protocolVersion:v,expectedGateEpoch:b,requestId:w,requestFingerprint:E,expectedCurrentVersion:T,expectedAppVersion:k,expectedPublishFence:$,expectedPublishRowVersion:N,expectedVersionRowVersion:P,expectedActivePublishVersionId:F,expectedCommittedPublishVersionId:oe,includeSourceMaps:ye,maxFiles:Oe,maxTotalMegabytes:Ve,timeoutMsPerFile:Ze,allowLegacyFallback:Rt,confirmExecution:Ae})=>EW(t,{appIdOrKey:o,versionNo:s,directory:c,entryPath:l,routes:u,routeSnapshotJson:p,routeSnapshotHash:d,changeSummary:h,sourceManifestHash:m,runtimeManifestHash:y,deliveryBatchId:g,publishMode:x,protocolVersion:v,expectedGateEpoch:b,requestId:w,requestFingerprint:E,expectedCurrentVersion:T,expectedAppVersion:k,expectedPublishFence:$,expectedPublishRowVersion:N,expectedVersionRowVersion:P,expectedActivePublishVersionId:F,expectedCommittedPublishVersionId:oe,includeSourceMaps:ye,maxFiles:Oe,maxTotalMegabytes:Ve,timeoutMsPerFile:Ze,allowLegacyFallback:Rt,confirmExecution:Ae})),i.tool("microi_publish_microservice","Legacy small-payload publisher for generated microservice / micro-app files in Base64. For real compiled directories or large assets, use microi_publish_application_directory_stream so bytes stream directly to HDFS and never enter JSON/Jint.",{microService:mr.describe("Microservice metadata. Required: MsKey and MsName/Name. Optional: BuildVersion, EntryPath, SourceDirName."),assets:f.array(mr).describe("Built asset files. Each item needs Path/RelativePath/FileName and FileByteBase64/ContentBase64. Mark the main HTML/JS entry with IsEntry=true or Entry=true."),routes:f.array(mr).optional().describe("Optional route/page records for sys_microiservice_page. Fields: PageKey, PageName, PageTitle, RoutePath, EntryPath, SourceDirName, SourceFile, RouteMetaJson, Sort, IsHome."),sourceManifestHash:f.string().regex(/^[a-fA-F0-9]{64}$/u).optional().describe("Optional source-manifest SHA-256 returned by source sync."),deliveryBatchId:f.string().min(8).max(50).optional().describe("Optional stable delivery batch id (8-50 characters, matching the API and LastBuildTaskId). MCP generates one when omitted."),confirmExecution:f.string().optional().describe("Required for real writes. Pass any non-empty confirmation string after reviewing the payload.")},async({microService:o,assets:s,routes:c,sourceManifestHash:l,deliveryBatchId:u,confirmExecution:p})=>{if(!p)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,microService:o,assetCount:s.length,routes:c||[]},null,2)}]};try{let d=await t.publishMicroService({microService:o,assets:s,routes:c||[],DeliveryBatchId:u||Qe.default.randomUUID(),SourceManifestHash:l||""});if(d.Code!==1)return{content:[{type:"text",text:`Error: ${d.Msg}`}],isError:!0};let h=String(o.MsKey||o.MicroServiceKey||o.AppKey||"").trim(),m=h?await t.probeMicroAppEntry(h):{ok:!1,url:"",error:"Cannot probe runtime because MsKey is missing"};for(let g of[250,750,1500]){if(m.ok||!h)break;await new Promise(x=>setTimeout(x,g)),m=await t.probeMicroAppEntry(h)}let y={...d.Data&&typeof d.Data=="object"?d.Data:{},RuntimeProbe:m,PublishedButUnavailable:!m.ok,...m.ok?{}:{Warning:"\u53D1\u5E03\u5143\u6570\u636E\u548C\u8D44\u4EA7\u5199\u5165\u5DF2\u5B8C\u6210\uFF0C\u4F46\u7A33\u5B9A\u5165\u53E3\u4E0D\u53EF\u7528\u3002\u8BF7\u68C0\u67E5 API \u8282\u70B9\u5230\u79DF\u6237 HDFS/MinIO \u516C\u6709\u6876\u7684\u670D\u52A1\u7AEF\u8BFB\u53D6\u94FE\u8DEF\uFF1B\u4E0D\u8981\u65E0\u5224\u65AD\u91CD\u590D\u53D1\u5E03\u3002"}};return{content:[{type:"text",text:JSON.stringify(y,null,2)}],...m.ok?{}:{isError:!0}}}catch(d){return{content:[{type:"text",text:`Error: ${d instanceof Error?d.message:String(d)}`}],isError:!0}}}),zP(i,t,e),NP(i,t,e),DP(i,t,e),a.flush(e.codexMode?["microi_codex"]:void 0),i}function FW(t){let e=t.trim();if(!e||!/^[A-Za-z0-9+/]+={0,2}$/.test(e)||e.length%4!==0)return"";let r=Buffer.from(e,"base64");return r.toString("base64").replace(/=+$/,"")!==e.replace(/=+$/,"")?"":r.toString("utf8")}function s$(t){let e=t.MICROI_LABEL_BASE64||"";if(e){let r=FW(e);if(r)return r}return t.MICROI_LABEL||""}function c$(t,e,r,n="",i=""){try{let a=JSON.parse(ad.default.readFileSync(t,"utf-8"));return Zu(e,r,n,i).map(o=>a[o]).find(Boolean)}catch{return}}function jW(t){let e=t.body,r=String(t.headers["content-type"]||"");if(typeof e=="string"){/^application\/json\b/i.test(r)||(t.headers["content-type"]="application/json");try{return JSON.parse(e)}catch{return e}}if(e&&typeof e=="object")return/^application\/json\b/i.test(r)||(t.headers["content-type"]="application/json"),e}async function LW(){let t={apiBaseUrl:(process.env.MICROI_API_URL||"").replace(/\/+$/,""),username:process.env.MICROI_USERNAME||"",password:process.env.MICROI_PASSWORD||"",osClient:process.env.MICROI_OS_CLIENT||"",osClientType:process.env.MICROI_OS_CLIENT_TYPE||"",osClientNetwork:process.env.MICROI_OS_CLIENT_NETWORK||"",rsaPublicKey:process.env.MICROI_RSA_PUBLIC_KEY||void 0,token:process.env.MICROI_TOKEN||void 0};/^https:\/\/(localhost|127\.0\.0\.1)(:\d+)?/i.test(t.apiBaseUrl)&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0",console.error("[microi-mcp] Detected localhost HTTPS, disabled TLS certificate verification"));let e=process.env.MICROI_TOKEN_FILE;if(e){t.tokenFilePath=e,t.authRecoveryRequestDir=process.env.MICROI_AUTH_RECOVERY_DIR||void 0;let n=c$(e,t.apiBaseUrl,t.osClient||"",t.osClientType||"",t.osClientNetwork||"");n&&(t.token=n)}if((process.env.MCP_TRANSPORT||"stdio")==="sse")t.apiBaseUrl||(console.error("Missing required: MICROI_API_URL (Microi backend API URL)"),process.exit(1)),await VW(parseInt(process.env.MCP_PORT||"3000",10),t);else{t.apiBaseUrl||(console.error("Missing required: MICROI_API_URL"),process.exit(1)),!t.token&&(!t.username||!t.password)&&(console.error("Missing required environment variables:"),console.error(" MICROI_API_URL - Microi backend API URL (e.g. https://api.example.com)"),console.error(" MICROI_TOKEN_FILE - Token file path (preferred, auto-managed by VS Code extension)"),console.error(" MICROI_AUTH_RECOVERY_DIR - Optional credential-free VS Code recovery request directory"),console.error(" MICROI_TOKEN - JWT token (fallback)"),console.error(" MICROI_USERNAME - Login username (fallback if no token)"),console.error(" MICROI_PASSWORD - Login password (fallback if no token)"),console.error("Optional:"),console.error(" MICROI_OS_CLIENT - OsClient identifier"),console.error(" MICROI_OS_CLIENT_TYPE - Optional OsClient type for exact token identity"),console.error(" MICROI_OS_CLIENT_NETWORK - Optional OsClient network for exact token identity"),process.exit(1));let n=new Vs(t);await n.login();let i={osClient:t.osClient||"",osClientType:t.osClientType||"",osClientNetwork:t.osClientNetwork||"",apiBaseUrl:t.apiBaseUrl,label:s$(process.env),codexMode:process.env.MICROI_CODEX_MODE==="1"},a=sx(n,i);await qW(a),e&&ad.default.watchFile(e,{interval:5e3},()=>{let s=c$(e,t.apiBaseUrl,t.osClient||"",t.osClientType||"",t.osClientNetwork||"");s&&(n.updateToken(s),console.error("[microi-mcp] Token updated from file"))});let o=()=>{e&&ad.default.unwatchFile(e),n.destroy(),process.exit(0)};process.on("SIGINT",o),process.on("SIGTERM",o)}}async function qW(t){let e=new Sl;await t.connect(e),console.error("[microi-mcp] Server started (stdio mode)")}async function VW(t,e){let r=(0,id.default)();r.use(id.default.json({type:["application/json","application/*+json"],limit:"4mb"})),r.use(id.default.text({type:["text/plain","application/json-rpc"],limit:"4mb"}));let n=new Map;r.get("/sse",async(a,o)=>{let s=a.headers["x-microi-username"]||e.username,c=a.headers["x-microi-password"]||e.password,l=a.headers["x-microi-osclient"]||e.osClient||"";if(!s||!c){o.status(401).json({error:"Authentication required",message:"Provide X-Microi-Username and X-Microi-Password in request headers, or set MICROI_USERNAME / MICROI_PASSWORD environment variables."});return}try{let u=new Vs({apiBaseUrl:e.apiBaseUrl,username:s,password:c,osClient:l,osClientType:e.osClientType,osClientNetwork:e.osClientNetwork,rsaPublicKey:e.rsaPublicKey});await u.login();let p={osClient:l||"",osClientType:e.osClientType||"",osClientNetwork:e.osClientNetwork||"",apiBaseUrl:e.apiBaseUrl,label:""},d=sx(u,p),h=new ql("/messages",o);n.set(h.sessionId,{transport:h,client:u}),o.on("close",()=>{let m=n.get(h.sessionId);m&&(m.client.destroy(),n.delete(h.sessionId),console.error(`[microi-mcp] Session ${h.sessionId} disconnected (user: ${s})`))}),console.error(`[microi-mcp] New session ${h.sessionId} (user: ${s}, osClient: ${l||"default"})`),await d.connect(h)}catch(u){let p=u instanceof Error?u.message:String(u);console.error(`[microi-mcp] SSE auth failed (user: ${s}): ${p}`),o.headersSent||o.status(401).json({error:"Authentication failed",message:p})}}),r.post("/messages",async(a,o)=>{let s=a.query.sessionId,c=n.get(s);if(!c){o.status(404).json({error:"Session not found"});return}await c.transport.handlePostMessage(a,o,jW(a))}),r.get("/health",(a,o)=>{o.json({status:"ok",server:"microi-mcp-server",version:"1.0.0",activeSessions:n.size})});let i=()=>{for(let[a,o]of n)o.client.destroy(),n.delete(a);process.exit(0)};process.on("SIGINT",i),process.on("SIGTERM",i),r.listen(t,()=>{console.error(`[microi-mcp] Server started (SSE mode) on http://localhost:${t}`),console.error(`[microi-mcp] SSE endpoint: http://localhost:${t}/sse`),console.error(`[microi-mcp] Auth: ${e.username?"env defaults available":"headers required"}`)})}LW().catch(t=>{console.error("[microi-mcp] Fatal error:",t instanceof Error?t.message:t),process.exit(1)});
563
+ - PageId: ${g?.PageId}`}]}}catch(m){return{content:[{type:"text",text:`Error: ${m instanceof Error?m.message:String(m)}`}],isError:!0}}}),i.tool("microi_list_applications",`List every online AI application for OsClient "${r}" across Web, UniApp and MicroService, including each app's complete source-file manifest by default. AI agents should call this at the beginning of application/page work so they understand existing apps before creating duplicates. For complex custom dialogs/pages, prefer extending an existing MicroService or creating one with microi_create_microservice + microi_sync_microservice_source; do not embed large HTML in V8.ConfirmTips.`,{appType:f.enum(["Web","UniApp","MicroService"]).optional().describe("Optional exact application type filter."),keyword:f.string().optional().describe("Optional case-insensitive search across name, AppKey, type and description."),includeFiles:f.boolean().optional().default(!0).describe("Include the complete file manifest for every app. Defaults to true.")},async({appType:o,keyword:s,includeFiles:c})=>{try{let l=await t.listApplications({AppType:o,Keyword:s,IncludeFiles:c!==!1});return l.Code!==1?{content:[{type:"text",text:`Error: ${l.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(l.Data,null,2)}]}}catch(l){return{content:[{type:"text",text:`Error: ${l instanceof Error?l.message:String(l)}`}],isError:!0}}}),i.tool("microi_get_application_context",`Get one Web, UniApp or MicroService application by Id/AppKey for OsClient "${r}". It returns metadata and the full file manifest by default without embedding source bodies; set includeContents=true only when the complete source is actually needed, or use microi_get_application_file for one exact file. MicroService responses also include sys_microiservice runtime/pages.`,{appIdOrKey:f.string().describe("sys_microistore.Id or AppKey."),includeContents:f.boolean().optional().default(!1).describe("Read private HDFS source contents. Defaults to false; prefer microi_get_application_file for targeted reads."),maxFileBytes:f.number().int().positive().optional().describe("Maximum bytes read per source file. Default 2MB."),maxTotalBytes:f.number().int().positive().optional().describe("Maximum total bytes read for this app. Default 50MB.")},async({appIdOrKey:o,includeContents:s,maxFileBytes:c,maxTotalBytes:l})=>{try{let u=await t.getApplicationContext({AppIdOrKey:o,IncludeContents:s===!0,...c?{MaxFileBytes:c}:{},...l?{MaxTotalBytes:l}:{}});if(u.Code!==1)return{content:[{type:"text",text:`Error: ${u.Msg}`}],isError:!0};let p=u.Data,h=(Array.isArray(p?.Files)?p.Files:[]).filter(y=>typeof y?.ContentReadError=="string"&&y.ContentReadError).length,m={...p,McpReadSummary:{RequestedContents:s===!0,ContentsComplete:s!==!0||h===0,ContentErrorCount:h}};return{content:[{type:"text",text:JSON.stringify(m,null,2)}]}}catch(u){return{content:[{type:"text",text:`Error: ${u instanceof Error?u.message:String(u)}`}],isError:!0}}}),i.tool("microi_get_application_file",`Read one exact source file from a Web, UniApp or MicroService online AI application for OsClient "${r}". Text code is returned as UTF-8 Content; binary files are returned as FileByteBase64.`,{appIdOrKey:f.string().describe("sys_microistore.Id or AppKey."),filePath:f.string().describe("Exact relative source path from the application file manifest."),maxFileBytes:f.number().int().positive().optional().describe("Maximum bytes read. Default 10MB.")},async({appIdOrKey:o,filePath:s,maxFileBytes:c})=>{try{let l=await t.getApplicationFile({AppIdOrKey:o,FilePath:s,IncludeContents:!0,...c?{MaxFileBytes:c}:{}});return l.Code!==1?{content:[{type:"text",text:`Error: ${l.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(l.Data,null,2)}]}}catch(l){return{content:[{type:"text",text:`Error: ${l instanceof Error?l.message:String(l)}`}],isError:!0}}}),i.tool("microi_scaffold_vue_microservice",`Create a safe, maintainable MicroService scaffold for OsClient "${r}" using the stable Vue 3.5.40 + Vite 7.3.6 + TypeScript 5.9.3 baseline, @vitejs/plugin-vue 6.0.8, vue-tsc 3.3.9, Composition API and <script setup lang="ts">. The minimal template keeps Microi SDK and host/internal-route integration but does not force Pinia or Vue Router; add either only when application complexity needs it. It writes .microi-micro-app.json, microi.routes.json and exactly one Vue component per declared route inside the local tenant AI\u5E94\u7528 directory. It only accepts a real absolute directory whose basename is AI\u5E94\u7528, never overwrites a different existing project, and performs a dry run until confirmExecution exactly equals appKey. After scaffolding, run npm install and npm run build, then use microi_create_microservice, microi_sync_microservice_source and microi_publish_application_directory_stream (or the legacy publisher for a tiny compatibility payload).`,{appKey:f.string().regex(/^[a-z0-9](?:[a-z0-9_-]{0,62}[a-z0-9])?$/u).describe("Stable lowercase application key and local directory name."),name:f.string().min(1).max(120).describe("Human-readable MicroService name."),description:f.string().optional().describe("Optional application description."),aiApplicationsDirectory:f.string().optional().describe("Absolute tenant AI\u5E94\u7528 directory. Defaults to MICROI_AI_APPLICATIONS_DIR injected by Microi.VSCode."),buildVersion:f.string().regex(/^v\d+\.\d+\.\d+$/u).optional().default("v0.1.0").describe("Initial semantic build version. Default v0.1.0."),routes:f.array(f.object({path:f.string().describe("Internal route path such as /context-test."),name:f.string().describe("Stable route key such as context-test."),title:f.string().describe("Page/menu title."),description:f.string().optional().describe("Optional visible page explanation."),isHome:f.boolean().optional().describe("Mark one route as the default page. The first route is used when omitted.")})).min(1).max(50).describe("Vue pages and MicroService internal routes. One .vue file is generated per item."),confirmExecution:f.string().optional().describe("Required for filesystem writes and must exactly equal appKey. Omit for a local preflight only.")},async({appKey:o,name:s,description:c,aiApplicationsDirectory:l,buildVersion:u,routes:p,confirmExecution:d})=>{try{let h=String(l||process.env.MICROI_AI_APPLICATIONS_DIR||"").trim();if(!h)return{content:[{type:"text",text:"Error: \u7F3A\u5C11 AI \u5E94\u7528\u76EE\u5F55\u3002\u8BF7\u7531 Microi.VSCode \u6CE8\u5165 MICROI_AI_APPLICATIONS_DIR\uFF0C\u6216\u663E\u5F0F\u4F20\u5165 aiApplicationsDirectory\u3002"}],isError:!0};if(d&&d!==o)return{content:[{type:"text",text:`Error: confirmExecution \u5FC5\u987B\u7CBE\u786E\u7B49\u4E8E ${o}`}],isError:!0};let m={aiApplicationsDirectory:h,appKey:o,name:s,description:c,apiBaseUrl:e.apiBaseUrl,osClient:r,buildVersion:u,routes:p,sdkSource:FP(process.env.MICROI_WORKSPACE_ROOT)},y=Qv(m);if(d!==o)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,confirmationRequired:o,targetDirectory:y.targetDirectory,appKey:y.appKey,buildVersion:y.buildVersion,routeCount:y.routes.length,routes:y.routes.map(x=>({path:x.path,name:x.name,title:x.title,sourceFile:x.sourceFile,isHome:x.isHome})),fileCount:y.files.length,files:y.files.map(x=>({Path:x.relativePath,Size:x.size,Sha256:x.sha256}))},null,2)}]};let g=jP(m);return{content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(h){return{content:[{type:"text",text:`Error: ${h instanceof Error?h.message:String(h)}`}],isError:!0}}}),i.tool("microi_get_microservice",`Get one Microi microservice / micro-app by MsKey for OsClient "${r}". Use this before publishing to inspect current BuildVersion, EntryPath and asset manifest.`,{msKey:f.string().describe("Microservice key, stored in sys_microiservice.MsKey")},async({msKey:o})=>{try{let s=await t.getMicroService(o);return s.Code!==1?{content:[{type:"text",text:`Error: ${s.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(s.Data,null,2)}]}}catch(s){return{content:[{type:"text",text:`Error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}}),i.tool("microi_create_microservice",`Create or update sys_microiservice metadata for OsClient "${r}". This only writes metadata. For generated app source/dist files, use microi_publish_microservice.`,{microService:mr.describe("Microservice metadata. Required: MsKey and MsName/Name. Optional: MsType, Runtime, StorageMode, SourceDirName, EntryPath, BuildVersion."),confirmExecution:f.string().optional().describe("Required for real writes. Pass any non-empty confirmation string after reviewing the payload.")},async({microService:o,confirmExecution:s})=>{if(!s)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,microService:o},null,2)}]};try{let c=await t.createMicroService(o);return c.Code!==1?{content:[{type:"text",text:`Error: ${c.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(c.Data,null,2)}]}}catch(c){return{content:[{type:"text",text:`Error: ${c instanceof Error?c.message:String(c)}`}],isError:!0}}}),i.tool("microi_sync_microservice_source",`Sync local microservice source files into the online AI Application for OsClient "${r}". The app is created/upserted as AppType=MicroService; source files are private and remain separate from published assets.`,{microService:mr.describe("Microservice metadata. Required: MsKey and MsName/Name. Optional: Description and SourceDirName."),sourceFiles:f.array(mr).describe("Source files. Each item needs Path/FilePath and FileByteBase64/ContentBase64. Optional: Size and Sha256."),replace:f.boolean().optional().describe("When true, remove stale private-source metadata not present in this manifest. Public runtime/build rows are always preserved."),confirmExecution:f.string().optional().describe("Required for real writes. Pass any non-empty confirmation string after reviewing the payload.")},async({microService:o,sourceFiles:s,replace:c,confirmExecution:l})=>{if(!l)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,microService:o,sourceFileCount:s.length,replace:c===!0},null,2)}]};try{let u=await t.syncMicroServiceSource({microService:o,sourceFiles:s,Replace:!1,ReplacePrivateSourceOnly:c===!0});return u.Code!==1?{content:[{type:"text",text:`Error: ${u.Msg}`}],isError:!0}:{content:[{type:"text",text:JSON.stringify(u.Data,null,2)}]}}catch(u){return{content:[{type:"text",text:`Error: ${u instanceof Error?u.message:String(u)}`}],isError:!0}}}),i.tool("microi_upload_application_asset_stream",`Stream one local built asset directly to the immutable HDFS version directory for OsClient "${r}". The file is never encoded as Base64 and never enters Jint. This is a low-level resumable primitive; normally use microi_publish_application_directory_stream to upload and atomically promote a complete directory.`,{appIdOrKey:f.string().min(1).describe("Existing sys_microistore Id or AppKey."),versionNo:f.string().regex(/^v?\d+\.\d+\.\d+$/u).describe("Immutable semantic version, e.g. v1.2.3."),relativePath:f.string().min(1).describe("POSIX-style path inside the compiled output, e.g. assets/index-abcd.js."),localFilePath:f.string().min(1).describe("Absolute or workspace-relative path of one local ordinary file."),sha256:f.string().regex(/^[a-fA-F0-9]{64}$/u).optional().describe("Optional expected SHA-256. MCP computes and verifies it when omitted."),timeoutMs:f.number().int().min(1e3).max(2*60*6e4).optional().describe("Per-file upload timeout. Default 30 minutes; maximum 2 hours."),confirmExecution:f.string().optional().describe("Required for the real upload and must exactly equal appIdOrKey.")},async({appIdOrKey:o,versionNo:s,relativePath:c,localFilePath:l,sha256:u,timeoutMs:p,confirmExecution:d})=>{try{let h=rd(c),m=Ye.default.resolve(l),y=xt.default.lstatSync(m);if(!y.isFile()||y.isSymbolicLink())throw new Error(`\u672C\u5730\u8D44\u4EA7\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\u4E14\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${m}`);let g=await r$(m);if(u&&u.toLowerCase()!==g)throw new Error("\u672C\u5730\u6587\u4EF6 SHA-256 \u4E0E\u4F20\u5165\u503C\u4E0D\u4E00\u81F4");let x=`mcp-low-${na([o,It(s),h,g]).slice(0,48)}`,v=n$({deliveryBatchId:x,appIdOrKey:o,versionNo:s,relativePath:h,sha256:g}),b={appIdOrKey:o,versionNo:s,relativePath:h,size:y.size,sha256:g,requestId:v};if(d!==o)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,confirmationRequired:o,...b},null,2)}]};let w=await t.uploadApplicationAssetStream({AppIdOrKey:o,VersionNo:s,RelativePath:h,ExpectedSha256:g,RequestId:v,FilePath:m,TimeoutMs:p});if(w.Code!==1)return{content:[{type:"text",text:`Error: ${w.Msg}`}],isError:!0};let E=a$(w,{requestId:v,versionNo:s,relativePath:h,sha256:g,size:y.size});return{content:[{type:"text",text:JSON.stringify(E,null,2)}]}}catch(h){return{content:[{type:"text",text:`Error: ${h instanceof Error?h.message:String(h)}`}],isError:!0}}}),i.tool("microi_publish_application_directory_stream",`Publish a real Web, UniApp or MicroService build directory for OsClient "${r}" using bounded multipart file streams and deterministic RequestId evidence. publishMode=stage uploads immutable version assets only; finalize rebuilds the same local manifest and promotes it without uploading; stage-and-finalize keeps the original one-call flow. Each file is capped at 128 MiB and the complete manifest at 1 GiB before upload. Paths, hashes, sizes, request ids, version preconditions and final manifest evidence are verified strictly. Old API nodes without RequestId support fail closed.`,{appIdOrKey:f.string().min(1).describe("Existing sys_microistore Id or AppKey."),versionNo:f.string().regex(/^v?\d+\.\d+\.\d+$/u).describe("Immutable semantic version, e.g. v1.2.3."),directory:f.string().min(1).describe("Local compiled output directory such as dist or unpackage/dist/build/h5."),entryPath:f.string().optional().default("index.html").describe("Entry file relative to directory. Default index.html."),routes:f.array(mr).optional().default([]).describe("Canonical v3 route snapshot input; missing routes are frozen explicitly as []."),routeSnapshotJson:f.string().max(1024*1024).optional().describe("Protocol v3 requires the exact recursive-key-sorted, array-order-preserving UTF-8 canonical JSON for routes."),routeSnapshotHash:f.string().regex(/^[a-f0-9]{64}$/u).optional().describe("Protocol v3 requires the lowercase SHA-256 of RouteSnapshotJson."),changeSummary:f.string().optional().describe("Version change summary stored in mci_ai_app_version."),sourceManifestHash:f.string().regex(/^[a-fA-F0-9]{64}$/u).optional().describe("Optional source-manifest SHA-256 returned by microi_sync_microservice_source, tying source and runtime to one delivery."),runtimeManifestHash:f.string().regex(/^[a-f0-9]{64}$/u).optional().describe("Protocol v3 requires the exact lowercase runtime-manifest SHA-256 computed during the local dry run."),deliveryBatchId:f.string().min(8).max(50).optional().describe("Optional stable delivery batch id (8-50 characters, matching the API and LastBuildTaskId). When omitted MCP derives a deterministic id from app/version/manifest so retries remain stable."),publishMode:f.enum(["stage","finalize","stage-and-finalize"]).optional().default("stage-and-finalize").describe("stage uploads immutable assets only; finalize submits the local manifest only; stage-and-finalize preserves the original one-call behavior."),protocolVersion:f.literal(3).optional().describe("Explicit protocol v3 gate. When set, publishMode must be explicit stage or finalize and every v3 precondition is required."),expectedGateEpoch:f.string().regex(/^(0|[1-9]\d*)$/u).optional().describe("Protocol v3 gate epoch as a canonical decimal bigint string."),requestId:f.string().regex(/^[A-Za-z0-9._:-]{8,100}$/u).optional().describe("Protocol v3 stable release RequestId; stage/finalize and all Pending replays must reuse it exactly."),requestFingerprint:f.string().regex(/^[a-f0-9]{64}$/u).optional().describe("Protocol v3 immutable release fingerprint."),expectedCurrentVersion:f.number().int().min(0).optional().describe("Required for finalize/stage-and-finalize and omitted only during stage. Must equal the application CurrentVersion read before staging."),expectedAppVersion:f.string().nullable().optional().describe("Application AppVersion baseline. Protocol v3 requires explicit string or null for both stage and finalize."),expectedPublishFence:f.string().regex(/^(0|[1-9]\d*)$/u).optional().describe("Protocol v3 sys_microistore.PublishFence canonical decimal bigint string."),expectedPublishRowVersion:f.string().regex(/^(0|[1-9]\d*)$/u).optional().describe("Protocol v3 sys_microistore.PublishRowVersion canonical decimal bigint string."),expectedVersionRowVersion:f.string().regex(/^(0|[1-9]\d*)$/u).nullable().optional().describe("Protocol v3 target mci_ai_app_version.RowVersion; explicit null means the row must not exist."),expectedActivePublishVersionId:f.string().min(1).nullable().optional().describe("Protocol v3 active version id baseline; explicit null means empty."),expectedCommittedPublishVersionId:f.string().min(1).nullable().optional().describe("Protocol v3 committed version id baseline; explicit null means empty."),includeSourceMaps:f.boolean().optional().default(!1).describe("Publish *.map source maps. Defaults to false to avoid source disclosure."),maxFiles:f.number().int().min(1).max(2e4).optional().describe("Safety cap checked before upload. Default and hard maximum 20,000."),maxTotalMegabytes:f.number().positive().max(1024).optional().default(1024).describe("Safety cap checked before upload. Default and hard maximum 1 GiB (1024 MiB)."),timeoutMsPerFile:f.number().int().min(1e3).max(2*60*6e4).optional().describe("Per-file upload timeout. Default 30 minutes."),allowLegacyFallback:f.boolean().optional().default(!1).describe("Deprecated compatibility flag. RequestId-capable two-phase publishing fails closed on old API nodes instead of publishing through the legacy payload."),confirmExecution:f.string().optional().describe("Required for real publishing and must exactly equal appIdOrKey. Omit for a local preflight manifest only.")},async({appIdOrKey:o,versionNo:s,directory:c,entryPath:l,routes:u,routeSnapshotJson:p,routeSnapshotHash:d,changeSummary:h,sourceManifestHash:m,runtimeManifestHash:y,deliveryBatchId:g,publishMode:x,protocolVersion:v,expectedGateEpoch:b,requestId:w,requestFingerprint:E,expectedCurrentVersion:T,expectedAppVersion:k,expectedPublishFence:$,expectedPublishRowVersion:N,expectedVersionRowVersion:P,expectedActivePublishVersionId:F,expectedCommittedPublishVersionId:oe,includeSourceMaps:ye,maxFiles:Oe,maxTotalMegabytes:Ve,timeoutMsPerFile:Ze,allowLegacyFallback:Rt,confirmExecution:Ae})=>EW(t,{appIdOrKey:o,versionNo:s,directory:c,entryPath:l,routes:u,routeSnapshotJson:p,routeSnapshotHash:d,changeSummary:h,sourceManifestHash:m,runtimeManifestHash:y,deliveryBatchId:g,publishMode:x,protocolVersion:v,expectedGateEpoch:b,requestId:w,requestFingerprint:E,expectedCurrentVersion:T,expectedAppVersion:k,expectedPublishFence:$,expectedPublishRowVersion:N,expectedVersionRowVersion:P,expectedActivePublishVersionId:F,expectedCommittedPublishVersionId:oe,includeSourceMaps:ye,maxFiles:Oe,maxTotalMegabytes:Ve,timeoutMsPerFile:Ze,allowLegacyFallback:Rt,confirmExecution:Ae})),i.tool("microi_publish_microservice","Legacy small-payload publisher for generated microservice / micro-app files in Base64. For real compiled directories or large assets, use microi_publish_application_directory_stream so bytes stream directly to HDFS and never enter JSON/Jint.",{microService:mr.describe("Microservice metadata. Required: MsKey and MsName/Name. Optional: BuildVersion, EntryPath, SourceDirName."),assets:f.array(mr).describe("Built asset files. Each item needs Path/RelativePath/FileName and FileByteBase64/ContentBase64. Mark the main HTML/JS entry with IsEntry=true or Entry=true."),routes:f.array(mr).optional().describe("Optional route/page records for sys_microiservice_page. Fields: PageKey, PageName, PageTitle, RoutePath, EntryPath, SourceDirName, SourceFile, RouteMetaJson, Sort, IsHome."),sourceManifestHash:f.string().regex(/^[a-fA-F0-9]{64}$/u).optional().describe("Optional source-manifest SHA-256 returned by source sync."),deliveryBatchId:f.string().min(8).max(50).optional().describe("Optional stable delivery batch id (8-50 characters, matching the API and LastBuildTaskId). MCP generates one when omitted."),confirmExecution:f.string().optional().describe("Required for real writes. Pass any non-empty confirmation string after reviewing the payload.")},async({microService:o,assets:s,routes:c,sourceManifestHash:l,deliveryBatchId:u,confirmExecution:p})=>{if(!p)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,microService:o,assetCount:s.length,routes:c||[]},null,2)}]};try{let d=await t.publishMicroService({microService:o,assets:s,routes:c||[],DeliveryBatchId:u||Qe.default.randomUUID(),SourceManifestHash:l||""});if(d.Code!==1)return{content:[{type:"text",text:`Error: ${d.Msg}`}],isError:!0};let h=String(o.MsKey||o.MicroServiceKey||o.AppKey||"").trim(),m=h?await t.probeMicroAppEntry(h):{ok:!1,url:"",error:"Cannot probe runtime because MsKey is missing"};for(let g of[250,750,1500]){if(m.ok||!h)break;await new Promise(x=>setTimeout(x,g)),m=await t.probeMicroAppEntry(h)}let y={...d.Data&&typeof d.Data=="object"?d.Data:{},RuntimeProbe:m,PublishedButUnavailable:!m.ok,...m.ok?{}:{Warning:"\u53D1\u5E03\u5143\u6570\u636E\u548C\u8D44\u4EA7\u5199\u5165\u5DF2\u5B8C\u6210\uFF0C\u4F46\u7A33\u5B9A\u5165\u53E3\u4E0D\u53EF\u7528\u3002\u8BF7\u68C0\u67E5 API \u8282\u70B9\u5230\u79DF\u6237 HDFS/MinIO \u516C\u6709\u6876\u7684\u670D\u52A1\u7AEF\u8BFB\u53D6\u94FE\u8DEF\uFF1B\u4E0D\u8981\u65E0\u5224\u65AD\u91CD\u590D\u53D1\u5E03\u3002"}};return{content:[{type:"text",text:JSON.stringify(y,null,2)}],...m.ok?{}:{isError:!0}}}catch(d){return{content:[{type:"text",text:`Error: ${d instanceof Error?d.message:String(d)}`}],isError:!0}}}),zP(i,t,e),NP(i,t,e),DP(i,t,e),a.flush(e.codexMode?["microi_codex"]:void 0),i}function FW(t){let e=t.trim();if(!e||!/^[A-Za-z0-9+/]+={0,2}$/.test(e)||e.length%4!==0)return"";let r=Buffer.from(e,"base64");return r.toString("base64").replace(/=+$/,"")!==e.replace(/=+$/,"")?"":r.toString("utf8")}function s$(t){let e=t.MICROI_LABEL_BASE64||"";if(e){let r=FW(e);if(r)return r}return t.MICROI_LABEL||""}function c$(t,e,r,n="",i=""){try{let a=JSON.parse(ad.default.readFileSync(t,"utf-8"));return Zu(e,r,n,i).map(o=>a[o]).find(Boolean)}catch{return}}function jW(t){let e=t.body,r=String(t.headers["content-type"]||"");if(typeof e=="string"){/^application\/json\b/i.test(r)||(t.headers["content-type"]="application/json");try{return JSON.parse(e)}catch{return e}}if(e&&typeof e=="object")return/^application\/json\b/i.test(r)||(t.headers["content-type"]="application/json"),e}async function LW(){let t={apiBaseUrl:(process.env.MICROI_API_URL||"").replace(/\/+$/,""),username:process.env.MICROI_USERNAME||"",password:process.env.MICROI_PASSWORD||"",osClient:process.env.MICROI_OS_CLIENT||"",osClientType:process.env.MICROI_OS_CLIENT_TYPE||"",osClientNetwork:process.env.MICROI_OS_CLIENT_NETWORK||"",rsaPublicKey:process.env.MICROI_RSA_PUBLIC_KEY||void 0,token:process.env.MICROI_TOKEN||void 0};/^https:\/\/(localhost|127\.0\.0\.1)(:\d+)?/i.test(t.apiBaseUrl)&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0",console.error("[microi-mcp] Detected localhost HTTPS, disabled TLS certificate verification"));let e=process.env.MICROI_TOKEN_FILE;if(e){t.tokenFilePath=e,t.authRecoveryRequestDir=process.env.MICROI_AUTH_RECOVERY_DIR||void 0;let n=c$(e,t.apiBaseUrl,t.osClient||"",t.osClientType||"",t.osClientNetwork||"");n&&(t.token=n)}if((process.env.MCP_TRANSPORT||"stdio")==="sse")t.apiBaseUrl||(console.error("Missing required: MICROI_API_URL (Microi backend API URL)"),process.exit(1)),await VW(parseInt(process.env.MCP_PORT||"3000",10),t);else{t.apiBaseUrl||(console.error("Missing required: MICROI_API_URL"),process.exit(1)),!t.token&&(!t.username||!t.password)&&(console.error("Missing required environment variables:"),console.error(" MICROI_API_URL - Microi backend API URL (e.g. https://api.example.com)"),console.error(" MICROI_TOKEN_FILE - Token file path (preferred, auto-managed by VS Code extension)"),console.error(" MICROI_AUTH_RECOVERY_DIR - Optional credential-free VS Code recovery request directory"),console.error(" MICROI_TOKEN - JWT token (fallback)"),console.error(" MICROI_USERNAME - Login username (fallback if no token)"),console.error(" MICROI_PASSWORD - Login password (fallback if no token)"),console.error("Optional:"),console.error(" MICROI_OS_CLIENT - OsClient identifier"),console.error(" MICROI_OS_CLIENT_TYPE - Optional OsClient type for exact token identity"),console.error(" MICROI_OS_CLIENT_NETWORK - Optional OsClient network for exact token identity"),process.exit(1));let n=new Vs(t);await n.login();let i={osClient:t.osClient||"",osClientType:t.osClientType||"",osClientNetwork:t.osClientNetwork||"",apiBaseUrl:t.apiBaseUrl,label:s$(process.env),codexMode:process.env.MICROI_CODEX_MODE==="1"},a=sx(n,i);await qW(a),e&&ad.default.watchFile(e,{interval:5e3},()=>{let s=c$(e,t.apiBaseUrl,t.osClient||"",t.osClientType||"",t.osClientNetwork||"");s&&(n.updateToken(s),console.error("[microi-mcp] Token updated from file"))});let o=()=>{e&&ad.default.unwatchFile(e),n.destroy(),process.exit(0)};process.on("SIGINT",o),process.on("SIGTERM",o)}}async function qW(t){let e=new Sl;await t.connect(e),console.error("[microi-mcp] Server started (stdio mode)")}async function VW(t,e){let r=(0,id.default)();r.use(id.default.json({type:["application/json","application/*+json"],limit:"4mb"})),r.use(id.default.text({type:["text/plain","application/json-rpc"],limit:"4mb"}));let n=new Map;r.get("/sse",async(a,o)=>{let s=a.headers["x-microi-username"]||e.username,c=a.headers["x-microi-password"]||e.password,l=a.headers["x-microi-osclient"]||e.osClient||"";if(!s||!c){o.status(401).json({error:"Authentication required",message:"Provide X-Microi-Username and X-Microi-Password in request headers, or set MICROI_USERNAME / MICROI_PASSWORD environment variables."});return}try{let u=new Vs({apiBaseUrl:e.apiBaseUrl,username:s,password:c,osClient:l,osClientType:e.osClientType,osClientNetwork:e.osClientNetwork,rsaPublicKey:e.rsaPublicKey});await u.login();let p={osClient:l||"",osClientType:e.osClientType||"",osClientNetwork:e.osClientNetwork||"",apiBaseUrl:e.apiBaseUrl,label:""},d=sx(u,p),h=new ql("/messages",o);n.set(h.sessionId,{transport:h,client:u}),o.on("close",()=>{let m=n.get(h.sessionId);m&&(m.client.destroy(),n.delete(h.sessionId),console.error(`[microi-mcp] Session ${h.sessionId} disconnected (user: ${s})`))}),console.error(`[microi-mcp] New session ${h.sessionId} (user: ${s}, osClient: ${l||"default"})`),await d.connect(h)}catch(u){let p=u instanceof Error?u.message:String(u);console.error(`[microi-mcp] SSE auth failed (user: ${s}): ${p}`),o.headersSent||o.status(401).json({error:"Authentication failed",message:p})}}),r.post("/messages",async(a,o)=>{let s=a.query.sessionId,c=n.get(s);if(!c){o.status(404).json({error:"Session not found"});return}await c.transport.handlePostMessage(a,o,jW(a))}),r.get("/health",(a,o)=>{o.json({status:"ok",server:"microi-mcp-server",version:"1.0.0",activeSessions:n.size})});let i=()=>{for(let[a,o]of n)o.client.destroy(),n.delete(a);process.exit(0)};process.on("SIGINT",i),process.on("SIGTERM",i),r.listen(t,()=>{console.error(`[microi-mcp] Server started (SSE mode) on http://localhost:${t}`),console.error(`[microi-mcp] SSE endpoint: http://localhost:${t}/sse`),console.error(`[microi-mcp] Auth: ${e.username?"env defaults available":"headers required"}`)})}LW().catch(t=>{console.error("[microi-mcp] Fatal error:",t instanceof Error?t.message:t),process.exit(1)});
564
564
  /*! Bundled license information:
565
565
 
566
566
  bytes/index.js:
@@ -1,13 +1,13 @@
1
1
  {
2
- "builtAt": "2026-08-06T15:39:37.381Z",
2
+ "builtAt": "2026-08-07T04:21:56.206Z",
3
3
  "source": "../microi.skills",
4
4
  "extension": {
5
5
  "name": "v8-engine",
6
- "version": "4.7.3",
6
+ "version": "4.7.4",
7
7
  "publisher": "Microi"
8
8
  },
9
9
  "skills": {
10
- "version": "4.7.3",
10
+ "version": "4.7.4",
11
11
  "versionRule": "Microi version uses three numeric parts. Each publish increments the last part; when a part exceeds 9 it carries to the previous part: 1.0.9 -> 1.1.0, 1.9.9 -> 2.0.0.",
12
12
  "managedBy": "Microi.VSCode/bump-version.js"
13
13
  },
@@ -15,10 +15,10 @@
15
15
  "dir": "dist/microi.skills",
16
16
  "files": 105,
17
17
  "skills": 58,
18
- "bytes": 1621098
18
+ "bytes": 1630404
19
19
  },
20
20
  "fileHashes": {
21
- ".microi-skills-version.json": "9d8a0469b26b691a733a6d13c5220360c18a379ba06fe740ee5e2b467985ee11",
21
+ ".microi-skills-version.json": "fb7f2280ee7f2d9001407d57fd7c3788ab5c128f2ae42c7dd8d2d3e10456deea",
22
22
  "README.md": "f88db8f5a0ad3788583b1a0787cb4c485f1027e50a5c21997da41334e2be3b82",
23
23
  "ai-engine/SKILL.md": "371b00ce9860d1516f6eb7eeaed001cbfa852c52e7c0cfccbedc4a71a113e568",
24
24
  "ai-engine/agents/openai.yaml": "40deb07801006a46b9a7f0020d13251955eeadd04c8e591160210479936f8b5b",
@@ -50,7 +50,7 @@
50
50
  "microi-deployment/SKILL.md": "26faa91740c874cfd676ceb69c1e4f8c12acfb85a2fc9fe1e8d0298ffe84b310",
51
51
  "microi-deployment/references/deployment-matrix.md": "32d9d1424bf19eae0d2cee2b1667203c830ea7610c71a806e7ddf785a0083009",
52
52
  "microi-docs-coverage/SKILL.md": "5fad3dd7b83cf01ef7a303506902c95da7ae85d60b0067d266f1e2fa2326db95",
53
- "microi-docs-coverage/references/capability-map.md": "0d3ffff2ecaf3612680a610ffa0df0b731f9ed39432605aa5720c16ce42bd93a",
53
+ "microi-docs-coverage/references/capability-map.md": "f460427304dca6f4d3ff57b79eaa3c5eab72b03726655b0c8bac1e27c844ce9b",
54
54
  "microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs": "ebd57f17f2ebae499bfa59ad17830db458590feb76f7f0f20219ff0987b33af3",
55
55
  "microi-form-engine/SKILL.md": "1afefedb468b5bab219f726bddb9ceca99547ee4149722a847a50eb9001b6927",
56
56
  "microi-form-engine/references/component-catalog.md": "68963c2d5211a4610c09fab1fa127a349339abbb7427509b183d4be72e977c7b",
@@ -58,7 +58,7 @@
58
58
  "microi-form-layout/SKILL.md": "48814c2c138e2c0fb6a11d39cf458df6a1cc28a3156c7540db1fa482a2ce6aec",
59
59
  "microi-frontend-sdk/SKILL.md": "0ff6e2df0dc01a42ca875bb6eeb92a4397c5dd4ecae0f808ccf36f1f549c58f0",
60
60
  "microi-left-right-layout/SKILL.md": "d8c482224846477b6c3068bdf94930cca6d699570f59b09f946601b0a65715f6",
61
- "microi-microservice/SKILL.md": "38b95724999b7a990c1ef992a1e5719c4ff2571b1cbd1a9b5703efdd1d19a551",
61
+ "microi-microservice/SKILL.md": "e8edecf40b0b18ab0bf3d69b0d647e16e2d471b15ef7b149ca1f6dfb16484f67",
62
62
  "microi-microservice/references/runtime-delivery.md": "704ed30623d91c971bed359403caa1f848edd07528ecbf211fa3b619ccec117d",
63
63
  "microi-mobile-app-quality/SKILL.md": "810bc23f3cfd4ee47ff3a51917b9aaa46cad8d59d2e3a880ed5f4c41b70eb1d7",
64
64
  "microi-solution-quotation/SKILL.md": "1bd47bd019a62bdc904612a7d8e2501c76df66eed9ef247544dd7fe38eba02e4",
@@ -82,7 +82,7 @@
82
82
  "search-engine/SKILL.md": "ddabf0b288d04c72985d3528e12c19bcd212a292a09593514d8385c4cb2c53a4",
83
83
  "search-engine/agents/openai.yaml": "5ddbe54965386287b00eba1cd7e38f3c7058edc437538f12ddb165f96f75a6eb",
84
84
  "spider-engine/SKILL.md": "09e9e164dc09c35bb4bebfbe77e3ba18164c89503313b28a1b4f1061e0de5e30",
85
- "translate-engine/SKILL.md": "4147778f7ee1c96e41ce9cda72c5666bfd2f9efe71df7fb91b2393aaf157b40a",
85
+ "translate-engine/SKILL.md": "384ab8a4c064f1ded98ef3890d5f65daee0001c784cd42d8560623ae64469203",
86
86
  "translate-engine/agents/openai.yaml": "8eb47e08a691219bbe22a797ddbdf73234ec77f86e5724bc735fb0738d899112",
87
87
  "ui-design/SKILL.md": "72026459d3a2da49abee8af379f0a4c13ecfc955bd61e4e0d8e398d1af4213d1",
88
88
  "ui-design/assets/pattern-showcase/app.js": "68a8d404c30096da853c6edbc7cb27f336982147ddfebccd66c03ba3d99427cd",
@@ -102,7 +102,7 @@
102
102
  "v8-export-import/SKILL.md": "e55299d75b24bcbb6248ec2572b55cd0515fb4fbfadaa1f78bcbe4a8d701a1f9",
103
103
  "v8-file-upload/SKILL.md": "3c1f9390ed6c2457796b341e45fca7f5492a9cda4419d35dae33cbfa2fc80383",
104
104
  "v8-formengine-http/SKILL.md": "d975bc116e100f91d80acf7932128de6fff78c5fcd17557d3d5a5504e3359682",
105
- "v8-frontend-events/SKILL.md": "b4265a5e6d0dbeb8c64441465caf764ffe2039decff0f16b3572ce685cf6e911",
105
+ "v8-frontend-events/SKILL.md": "1e7edb8c77e13a8489533d81f36cfc134430be6f4d7c4eab2f581713ee5f083a",
106
106
  "v8-frontend-events/references/bluetooth-print-api.md": "c5667cc6a9ed1bf385a21ddbc28e39993bc9e6d527bc31b53fb4ad582abf719e",
107
107
  "v8-frontend-events/references/bluetooth-print.md": "d57f6981d846093277c1456c84bbf9586f65db70129d007fe25fdfb12f58e589",
108
108
  "v8-http-integration/SKILL.md": "08bb5e9e7a18be162dc907cc02ab8b60ab0aee198617f3c9e1354c620b6f2dc4",
@@ -113,123 +113,123 @@
113
113
  "v8-mongodb/SKILL.md": "7da703b5dc9fb9613cca1929fd854e3d333ad7d86200b839bb78ccdc8e0cc6cf",
114
114
  "v8-mq-mqtt/SKILL.md": "3894fc353a3e9896b16b54b9e12758f0aea950968f80b7f3ec86c6652040341c",
115
115
  "v8-saas-multi-tenant/SKILL.md": "0145ddd39e109e39b601cfac45b746624f21d9dfce5951b662ac713e80f3fc72",
116
- "v8-security/SKILL.md": "7cd66077bf297d9f57b2d045cd3cba02fd120d84c22e1b58cfd35aee3bce08d2",
116
+ "v8-security/SKILL.md": "91ccb35136fce4e5c9524b9a787ca33712fac06be9c114a925e48f9b5be1e62c",
117
117
  "v8-sql-query/SKILL.md": "0ebedf72732479e24ca7528120345d468f98fbf96c21a8f45aaa64eb8ef6a488",
118
118
  "v8-table-event/SKILL.md": "ddeb3020e8c7e3002c8f8598a65208b7a2f706ee13f040037f85979c7bc1e8fd",
119
119
  "v8-template-engine/SKILL.md": "20002c2124214641b782db9deabb367c151b6be1b2d32f6689c163dcb0925cce",
120
120
  "v8-utilities/SKILL.md": "cdc1c33e2c464a89635bbcb96bf5079d9e22c58a0c70158634c2ab70fd034d3b",
121
- "v8-utilities/references/client-api-index.md": "e7b4c9dea4e83e449b9efbb23c3d19da500d8683996a2dba06c568b79adbee60",
121
+ "v8-utilities/references/client-api-index.md": "88741d5f5bb2b727c72ae553819cdccd647862414bf03f973ecacf79f9454296",
122
122
  "v8-utilities/references/platform-http-routes.md": "87579eef4e89b4c80e1757ad34ec951e0ccadb6bd15aab4f3ab247f55d5f1cd5",
123
- "v8-utilities/references/server-api-index.md": "761f4910050b217d3fecc14f4405b779a0f1661cbd8051086fd35b067a794dc8",
123
+ "v8-utilities/references/server-api-index.md": "8879c16421fccbf69177683ef201976292e7b84757f53bcf0945bad985426893",
124
124
  "v8-workflow/SKILL.md": "cd6738edd79f628948adfac898b6d58a81feb28d1ec6d1f83fc12758fb570819",
125
- "workspace-conventions/SKILL.md": "90ed592cebb82f786f3810cfb04f6b4a58af74690a4dc6818c8d819bd230c53f"
125
+ "workspace-conventions/SKILL.md": "950480ad1e5a32176823d4f024af6aecd9b37ee7423c05e2ec4c1b1a4130cb66"
126
126
  },
127
127
  "fileVersions": {
128
- ".microi-skills-version.json": "4.7.3",
129
- "README.md": "4.7.3",
130
- "ai-engine/SKILL.md": "4.7.3",
131
- "ai-engine/agents/openai.yaml": "4.7.3",
132
- "app-store/SKILL.md": "4.7.3",
133
- "app-store/agents/openai.yaml": "4.7.3",
134
- "business-blueprint/SKILL.md": "4.7.3",
135
- "datasource-engine/SKILL.md": "4.7.3",
136
- "datasource-engine/agents/openai.yaml": "4.7.3",
137
- "dos-orm/SKILL.md": "4.7.3",
138
- "dos-orm/references/api-reference.md": "4.7.3",
139
- "job-engine/SKILL.md": "4.7.3",
140
- "job-engine/agents/openai.yaml": "4.7.3",
141
- "message-notification/SKILL.md": "4.7.3",
142
- "message-notification/agents/openai.yaml": "4.7.3",
143
- "message-notification/references/contracts.md": "4.7.3",
144
- "microi-ai-app-auth.js": "4.7.3",
145
- "microi-ai-application/SKILL.md": "4.7.3",
146
- "microi-ai-application/agents/openai.yaml": "4.7.3",
147
- "microi-ai-application/references/frontend-baseline.md": "4.7.3",
148
- "microi-client-frontend/SKILL.md": "4.7.3",
149
- "microi-datasource-mapping/SKILL.md": "4.7.3",
150
- "microi-db-schema/SKILL.md": "4.7.3",
151
- "microi-db-schema/agents/openai.yaml": "4.7.3",
152
- "microi-db-schema/references/core-tables.md": "4.7.3",
153
- "microi-db-schema/references/form-component-options.md": "4.7.3",
154
- "microi-db-schema/references/schema-overview.md": "4.7.3",
155
- "microi-db-schema/references/schema.md": "4.7.3",
156
- "microi-db-schema/references/table-catalog.md": "4.7.3",
157
- "microi-deployment/SKILL.md": "4.7.3",
158
- "microi-deployment/references/deployment-matrix.md": "4.7.3",
159
- "microi-docs-coverage/SKILL.md": "4.7.3",
160
- "microi-docs-coverage/references/capability-map.md": "4.7.3",
161
- "microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs": "4.7.3",
162
- "microi-form-engine/SKILL.md": "4.7.3",
163
- "microi-form-engine/references/component-catalog.md": "4.7.3",
164
- "microi-form-engine/references/data-source-events.md": "4.7.3",
165
- "microi-form-layout/SKILL.md": "4.7.3",
166
- "microi-frontend-sdk/SKILL.md": "4.7.3",
167
- "microi-left-right-layout/SKILL.md": "4.7.3",
168
- "microi-microservice/SKILL.md": "4.7.3",
169
- "microi-microservice/references/runtime-delivery.md": "4.7.3",
170
- "microi-mobile-app-quality/SKILL.md": "4.7.3",
171
- "microi-solution-quotation/SKILL.md": "4.7.3",
172
- "microi-solution-quotation/agents/openai.yaml": "4.7.3",
173
- "microi-solution-quotation/scripts/build_solution_quote.py": "4.7.3",
174
- "microi-system-delivery/SKILL.md": "4.7.3",
175
- "microi-ui/SKILL.md": "4.7.3",
176
- "microi-uniapp-frontend/SKILL.md": "4.7.3",
177
- "microi.v8.js": "4.7.3",
178
- "module-engine/SKILL.md": "4.7.3",
179
- "module-engine/references/module-config.md": "4.7.3",
180
- "ocr-engine/SKILL.md": "4.7.3",
181
- "ocr-engine/agents/openai.yaml": "4.7.3",
182
- "page-engine/SKILL.md": "4.7.3",
183
- "performance-testing/SKILL.md": "4.7.3",
184
- "playwright-e2e/SKILL.md": "4.7.3",
185
- "print-engine/SKILL.md": "4.7.3",
186
- "production-readonly-audit/SKILL.md": "4.7.3",
187
- "report-engine/SKILL.md": "4.7.3",
188
- "report-engine/agents/openai.yaml": "4.7.3",
189
- "search-engine/SKILL.md": "4.7.3",
190
- "search-engine/agents/openai.yaml": "4.7.3",
191
- "spider-engine/SKILL.md": "4.7.3",
192
- "translate-engine/SKILL.md": "4.7.3",
193
- "translate-engine/agents/openai.yaml": "4.7.3",
194
- "ui-design/SKILL.md": "4.7.3",
195
- "ui-design/assets/pattern-showcase/app.js": "4.7.3",
196
- "ui-design/assets/pattern-showcase/index.html": "4.7.3",
197
- "ui-design/assets/pattern-showcase/styles.css": "4.7.3",
198
- "ui-design/assets/templates/MCI-DESIGN.md": "4.7.3",
199
- "ui-design/references/design-pattern-library.md": "4.7.3",
200
- "ui-design/references/mci-design-contract.md": "4.7.3",
201
- "ui-design/references/motion-and-media.md": "4.7.3",
202
- "ui-design/references/product-flow-recipes.md": "4.7.3",
203
- "uniapp-mall-assets/SKILL.md": "4.7.3",
204
- "v8-api-config/SKILL.md": "4.7.3",
205
- "v8-cache-pattern/SKILL.md": "4.7.3",
206
- "v8-crud-api/SKILL.md": "4.7.3",
207
- "v8-debugging/SKILL.md": "4.7.3",
208
- "v8-explorer-tree/SKILL.md": "4.7.3",
209
- "v8-export-import/SKILL.md": "4.7.3",
210
- "v8-file-upload/SKILL.md": "4.7.3",
211
- "v8-formengine-http/SKILL.md": "4.7.3",
212
- "v8-frontend-events/SKILL.md": "4.7.3",
213
- "v8-frontend-events/references/bluetooth-print-api.md": "4.7.3",
214
- "v8-frontend-events/references/bluetooth-print.md": "4.7.3",
215
- "v8-http-integration/SKILL.md": "4.7.3",
216
- "v8-image-processing/SKILL.md": "4.7.3",
217
- "v8-image-processing/agents/openai.yaml": "4.7.3",
218
- "v8-image-processing/references/api-reference.md": "4.7.3",
219
- "v8-menu-buttons/SKILL.md": "4.7.3",
220
- "v8-mongodb/SKILL.md": "4.7.3",
221
- "v8-mq-mqtt/SKILL.md": "4.7.3",
222
- "v8-saas-multi-tenant/SKILL.md": "4.7.3",
223
- "v8-security/SKILL.md": "4.7.3",
224
- "v8-sql-query/SKILL.md": "4.7.3",
225
- "v8-table-event/SKILL.md": "4.7.3",
226
- "v8-template-engine/SKILL.md": "4.7.3",
227
- "v8-utilities/SKILL.md": "4.7.3",
228
- "v8-utilities/references/client-api-index.md": "4.7.3",
229
- "v8-utilities/references/platform-http-routes.md": "4.7.3",
230
- "v8-utilities/references/server-api-index.md": "4.7.3",
231
- "v8-workflow/SKILL.md": "4.7.3",
232
- "workspace-conventions/SKILL.md": "4.7.3"
128
+ ".microi-skills-version.json": "4.7.4",
129
+ "README.md": "4.7.4",
130
+ "ai-engine/SKILL.md": "4.7.4",
131
+ "ai-engine/agents/openai.yaml": "4.7.4",
132
+ "app-store/SKILL.md": "4.7.4",
133
+ "app-store/agents/openai.yaml": "4.7.4",
134
+ "business-blueprint/SKILL.md": "4.7.4",
135
+ "datasource-engine/SKILL.md": "4.7.4",
136
+ "datasource-engine/agents/openai.yaml": "4.7.4",
137
+ "dos-orm/SKILL.md": "4.7.4",
138
+ "dos-orm/references/api-reference.md": "4.7.4",
139
+ "job-engine/SKILL.md": "4.7.4",
140
+ "job-engine/agents/openai.yaml": "4.7.4",
141
+ "message-notification/SKILL.md": "4.7.4",
142
+ "message-notification/agents/openai.yaml": "4.7.4",
143
+ "message-notification/references/contracts.md": "4.7.4",
144
+ "microi-ai-app-auth.js": "4.7.4",
145
+ "microi-ai-application/SKILL.md": "4.7.4",
146
+ "microi-ai-application/agents/openai.yaml": "4.7.4",
147
+ "microi-ai-application/references/frontend-baseline.md": "4.7.4",
148
+ "microi-client-frontend/SKILL.md": "4.7.4",
149
+ "microi-datasource-mapping/SKILL.md": "4.7.4",
150
+ "microi-db-schema/SKILL.md": "4.7.4",
151
+ "microi-db-schema/agents/openai.yaml": "4.7.4",
152
+ "microi-db-schema/references/core-tables.md": "4.7.4",
153
+ "microi-db-schema/references/form-component-options.md": "4.7.4",
154
+ "microi-db-schema/references/schema-overview.md": "4.7.4",
155
+ "microi-db-schema/references/schema.md": "4.7.4",
156
+ "microi-db-schema/references/table-catalog.md": "4.7.4",
157
+ "microi-deployment/SKILL.md": "4.7.4",
158
+ "microi-deployment/references/deployment-matrix.md": "4.7.4",
159
+ "microi-docs-coverage/SKILL.md": "4.7.4",
160
+ "microi-docs-coverage/references/capability-map.md": "4.7.4",
161
+ "microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs": "4.7.4",
162
+ "microi-form-engine/SKILL.md": "4.7.4",
163
+ "microi-form-engine/references/component-catalog.md": "4.7.4",
164
+ "microi-form-engine/references/data-source-events.md": "4.7.4",
165
+ "microi-form-layout/SKILL.md": "4.7.4",
166
+ "microi-frontend-sdk/SKILL.md": "4.7.4",
167
+ "microi-left-right-layout/SKILL.md": "4.7.4",
168
+ "microi-microservice/SKILL.md": "4.7.4",
169
+ "microi-microservice/references/runtime-delivery.md": "4.7.4",
170
+ "microi-mobile-app-quality/SKILL.md": "4.7.4",
171
+ "microi-solution-quotation/SKILL.md": "4.7.4",
172
+ "microi-solution-quotation/agents/openai.yaml": "4.7.4",
173
+ "microi-solution-quotation/scripts/build_solution_quote.py": "4.7.4",
174
+ "microi-system-delivery/SKILL.md": "4.7.4",
175
+ "microi-ui/SKILL.md": "4.7.4",
176
+ "microi-uniapp-frontend/SKILL.md": "4.7.4",
177
+ "microi.v8.js": "4.7.4",
178
+ "module-engine/SKILL.md": "4.7.4",
179
+ "module-engine/references/module-config.md": "4.7.4",
180
+ "ocr-engine/SKILL.md": "4.7.4",
181
+ "ocr-engine/agents/openai.yaml": "4.7.4",
182
+ "page-engine/SKILL.md": "4.7.4",
183
+ "performance-testing/SKILL.md": "4.7.4",
184
+ "playwright-e2e/SKILL.md": "4.7.4",
185
+ "print-engine/SKILL.md": "4.7.4",
186
+ "production-readonly-audit/SKILL.md": "4.7.4",
187
+ "report-engine/SKILL.md": "4.7.4",
188
+ "report-engine/agents/openai.yaml": "4.7.4",
189
+ "search-engine/SKILL.md": "4.7.4",
190
+ "search-engine/agents/openai.yaml": "4.7.4",
191
+ "spider-engine/SKILL.md": "4.7.4",
192
+ "translate-engine/SKILL.md": "4.7.4",
193
+ "translate-engine/agents/openai.yaml": "4.7.4",
194
+ "ui-design/SKILL.md": "4.7.4",
195
+ "ui-design/assets/pattern-showcase/app.js": "4.7.4",
196
+ "ui-design/assets/pattern-showcase/index.html": "4.7.4",
197
+ "ui-design/assets/pattern-showcase/styles.css": "4.7.4",
198
+ "ui-design/assets/templates/MCI-DESIGN.md": "4.7.4",
199
+ "ui-design/references/design-pattern-library.md": "4.7.4",
200
+ "ui-design/references/mci-design-contract.md": "4.7.4",
201
+ "ui-design/references/motion-and-media.md": "4.7.4",
202
+ "ui-design/references/product-flow-recipes.md": "4.7.4",
203
+ "uniapp-mall-assets/SKILL.md": "4.7.4",
204
+ "v8-api-config/SKILL.md": "4.7.4",
205
+ "v8-cache-pattern/SKILL.md": "4.7.4",
206
+ "v8-crud-api/SKILL.md": "4.7.4",
207
+ "v8-debugging/SKILL.md": "4.7.4",
208
+ "v8-explorer-tree/SKILL.md": "4.7.4",
209
+ "v8-export-import/SKILL.md": "4.7.4",
210
+ "v8-file-upload/SKILL.md": "4.7.4",
211
+ "v8-formengine-http/SKILL.md": "4.7.4",
212
+ "v8-frontend-events/SKILL.md": "4.7.4",
213
+ "v8-frontend-events/references/bluetooth-print-api.md": "4.7.4",
214
+ "v8-frontend-events/references/bluetooth-print.md": "4.7.4",
215
+ "v8-http-integration/SKILL.md": "4.7.4",
216
+ "v8-image-processing/SKILL.md": "4.7.4",
217
+ "v8-image-processing/agents/openai.yaml": "4.7.4",
218
+ "v8-image-processing/references/api-reference.md": "4.7.4",
219
+ "v8-menu-buttons/SKILL.md": "4.7.4",
220
+ "v8-mongodb/SKILL.md": "4.7.4",
221
+ "v8-mq-mqtt/SKILL.md": "4.7.4",
222
+ "v8-saas-multi-tenant/SKILL.md": "4.7.4",
223
+ "v8-security/SKILL.md": "4.7.4",
224
+ "v8-sql-query/SKILL.md": "4.7.4",
225
+ "v8-table-event/SKILL.md": "4.7.4",
226
+ "v8-template-engine/SKILL.md": "4.7.4",
227
+ "v8-utilities/SKILL.md": "4.7.4",
228
+ "v8-utilities/references/client-api-index.md": "4.7.4",
229
+ "v8-utilities/references/platform-http-routes.md": "4.7.4",
230
+ "v8-utilities/references/server-api-index.md": "4.7.4",
231
+ "v8-workflow/SKILL.md": "4.7.4",
232
+ "workspace-conventions/SKILL.md": "4.7.4"
233
233
  },
234
234
  "files": [
235
235
  ".microi-skills-version.json",
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "4.7.3",
2
+ "version": "4.7.4",
3
3
  "versionRule": "Microi version uses three numeric parts. Each publish increments the last part; when a part exceeds 9 it carries to the previous part: 1.0.9 -> 1.1.0, 1.9.9 -> 2.0.0.",
4
4
  "managedBy": "Microi.VSCode/bump-version.js",
5
- "updatedAt": "2026-08-06T15:39:27.945Z"
5
+ "updatedAt": "2026-08-07T04:21:46.680Z"
6
6
  }
@@ -26,6 +26,7 @@ Markdown。第一列是相对 `microi.doc/docs/doc/` 的路径;第二列 Skill
26
26
  | `more/dos-orm.md` | dos-orm, v8-sql-query | ORM 查询、参数和事务 |
27
27
  | `more/dos-result.md` | v8-utilities, v8-crud-api | DosResult/DosResultList 返回协议 |
28
28
  | `more/hdfs.md` | v8-file-upload | 分布式文件存储和 URL |
29
+ | `more/identity-verification.md` | v8-security, v8-utilities, microi-microservice | DiyToken、Passkey、严格人脸、一次性步进票据和个人设置 |
29
30
  | `more/office.md` | v8-export-import, microi-microservice | Office 导入导出与在线编辑集成 |
30
31
  | `more/security.md` | v8-security | 平台安全和兼容基线 |
31
32
  | `more/sys-config.md` | v8-utilities, microi-deployment | 系统/租户配置和敏感边界 |
@@ -52,8 +53,8 @@ Markdown。第一列是相对 `microi.doc/docs/doc/` 的路径;第二列 Skill
52
53
  | `v8-engine/api-engine.md` | v8-api-config, v8-utilities | 接口上下文、配置和调用 |
53
54
  | `v8-engine/apiengine-index.md` | v8-crud-api, v8-api-config | 接口引擎实战和规范 |
54
55
  | `v8-engine/form-engine.md` | v8-crud-api, v8-formengine-http | FormEngine API 与 HTTP |
55
- | `v8-engine/v8-client.md` | v8-utilities, v8-frontend-events, v8-http-integration, ai-engine, print-engine | 全部前端 V8、平台 AI、扫码和蓝牙打印 |
56
- | `v8-engine/v8-server.md` | v8-utilities, v8-api-config, v8-http-integration, ai-engine | 全部后端 V8、平台 AI 和专项路由 |
56
+ | `v8-engine/v8-client.md` | v8-utilities, v8-frontend-events, v8-http-integration, v8-security, ai-engine, print-engine | 全部前端 V8、平台 AI、强身份验证、扫码和蓝牙打印 |
57
+ | `v8-engine/v8-server.md` | v8-utilities, v8-api-config, v8-http-integration, v8-security, ai-engine | 全部后端 V8、强身份票据、平台 AI 和专项路由 |
57
58
  | `v8-engine/vs-code-plugin.md` | v8-explorer-tree, microi-client-frontend, workspace-conventions | VS Code 插件、Microi CLI、AI/MCP 初始化、类型、资源树和共享工作区 |
58
59
  | `v8-engine/where.md` | v8-crud-api, v8-sql-query | 参数化 `_Where` 条件 |
59
60
 
@@ -79,6 +79,9 @@ AppKey 稳定且只含安全字符。`microi.routes.json` 是页面事实源,
79
79
  - 创建/更新元数据:`microi_create_microservice`。
80
80
  - 同步私有源码:`microi_sync_microservice_source`。
81
81
  - 真实编译目录优先 `microi_publish_application_directory_stream` 流式发布。
82
+ - 发布动作必须明确区分两种模式:默认“源码+编译产物”先把完整工程同步到私有桶并逐文件回读 SHA-256,再把 `dist` 流式发布到公有桶;显式“仅编译产物”只更新公有桶,必须在界面中告知其他用户仍会拉取上一次私有源码,禁止暗示源码已同步。
83
+ - 私有源码同步使用 `ReplacePrivateSourceOnly` 精确清理过期源码;兼容调用可以继续接受 `replace`,但实现不得用旧式全表 `Replace=true` 删除同一应用的公有运行产物元数据。
84
+ - `mci_ai_app_file` 同时存在私有源码和公有编译产物。源码拉取/差异比较必须优先按 `StorageScope=PublicBuildStream|PublicBuildStreamArchived|PublicBuildOnly` 排除公有产物,并保留旧数据中 `HdfsPath == PublishHdfsPath` 的兼容判断;不能仅靠两个路径相等识别,否则版本路径与稳定别名不同的流式产物会被误读成私有源码。
82
85
  - 只有服务器不支持流式端点且产物很小时,才兼容 `microi_publish_microservice`。
83
86
  - 正常发布支持最多 20,000 个文件、总计 20GB;逐文件从磁盘流入 HDFS,不生成整包 Buffer/Base64。几百 MB、1GB 级项目不得自动降级到旧 Base64 发布器。
84
87
  - `StorageMode=db` 仅是显式的小型应急恢复模式,不是正常发布容量;当前最多 256 文件/5MB。超过该边界必须修复租户 HDFS/网关并恢复流式文件模式,不能调大数据库内联上限来承载大项目。
@@ -138,6 +141,7 @@ AI 生成菜单微服务时,应优先封装一个 `callMicroiHost(action, data
138
141
  ## 验收
139
142
 
140
143
  - 源码、构建文件、运行时、页面路由和菜单五层分别回读。
144
+ - 组合发布成功前,私有源码回读必须与本地源码在路径集合、文件数、字节数、逐文件 SHA-256 和规范化清单哈希上完全一致;任何缺失、多余或读取错误都要阻止运行版本切换。
141
145
  - 直接刷新友好路由与连续切换多个微应用不 404、白屏或实例名冲突。
142
146
  - Dialog/Drawer 成功、取消、错误和关闭协议正确。
143
147
  - 宿主 API 请求携带当前 Token/OsClient/菜单上下文,普通用户权限正确。
@@ -39,6 +39,8 @@ var code = V8.TranslateEngine.GetLangCode('NoAuth');
39
39
 
40
40
  LibreTranslate 的 `/frontend/settings`、API Key 管理、`/metrics` 和 Web UI 是运维控制面,不得从普通 V8/HTTP/MCP 代理。
41
41
 
42
+ 文件入口的完整名称是 `V8.TranslateEngine.TranslateFile({...})`,不要把叶子名误当全局函数。
43
+
42
44
  ## 租户与密钥
43
45
 
44
46
  - V8 调用统一绑定当前 `V8TenantContext`。普通租户传其它 `OsClient` 不会跨租户翻译或读取配置。
@@ -80,6 +82,8 @@ LibreTranslate 是动态翻译供应商,不是 `diy_lang` 的替代品。一
80
82
 
81
83
  已登录 HTTP 入口固定为 `/api/Translate/TranslateText|Detect|Languages|TranslateFile|Suggest|Health`。Controller 必须用验证后的 Token 覆盖请求体 `OsClient`,不得接受 endpoint/key/header。
82
84
 
85
+ 覆盖审计所用的标准路由为 `/api/translate/detect`、`/api/translate/languages`、`/api/translate/translatefile`、`/api/translate/suggest` 和 `/api/translate/health`;它们都服从同一登录与租户绑定规则。
86
+
83
87
  MCP 固定工具:`microi_translate`、`microi_detect_language`、`microi_list_translate_languages`、`microi_translate_file`、`microi_suggest_translation`、`microi_get_translate_health`。文件翻译需要 `confirmExecution=TRANSLATE_FILE`,建议写入需要 `confirmExecution=TRANSLATE_SUGGEST`;审计只记录长度、SHA-256、语言和输出模式,不记录文本、文件内容、本机路径或凭据。大文件结果落到新的绝对路径,不允许覆盖已有文件。
84
88
 
85
89
  ## 安全硬上限
@@ -17,6 +17,7 @@ description: Microi 前端 V8 事件与客户端能力指南。用于编写浏
17
17
  - 需求包含“蓝牙打印、标签打印、TSC/TSPL、ESC/POS、小票打印、佳博打印机”时,必须先读取 `references/bluetooth-print.md`;需要完整指令签名、编码或位图参数时,再读取 `references/bluetooth-print-api.md`。
18
18
  - 浏览器模板打印、PDF/纸张模板、`mic_print`、`PageObj`、`PrintObj` 使用 `print-engine/SKILL.md`,不要与直接蓝牙指令混为一套 API。
19
19
  - 扫码使用 `V8.Method.ScanCode`,结果从 Promise/回调取得;`V8.ScanCodeRes` 只作兼容结果槽,详见客户端 API 索引。
20
+ - 登录后的敏感操作使用 `V8.Identity.Verify` 完成 Passkey/严格人脸交互;前端只取得一次性 Ticket,后端接口引擎必须重算 `ActionHash` 并原子消费,不能把前端成功当作授权。
20
21
 
21
22
  ## 字段事件(在【字段属性】中配置)
22
23
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: v8-security
3
- description: Microi V8 安全指南。用于审查接口引擎安全、密钥管理、SQL 注入、权限检查、匿名端点、文件上传安全和租户隔离。
3
+ description: Microi V8 安全指南。用于审查 DiyToken 与权限、可逆业务秘密、Passkey/人脸步进验证、接口引擎安全、密钥管理、SQL 注入、匿名端点、文件上传和租户隔离。
4
4
  ---
5
5
 
6
6
  # Microi V8 安全最佳实践
@@ -31,6 +31,8 @@ var openaiKey = 'sk-xxxxxxxxxx';
31
31
 
32
32
  登录和管理端必须强制 HTTPS。登录 RSA 只用于避免密码在请求体、代理调试界面中直接显示,不能替代 HTTPS,也不能作为身份认证或密码存储密钥。平台为兼容已发布客户、旧前端和浏览器缓存,保留历史登录 RSA 密钥对作为缺省回退;安全修复不得直接删除该回退并造成全量客户无法登录。需要部署专属密钥时,在主租户 SaaS 引擎【后端运行配置】中成对维护 `BackendLoginRsaPrivateKey` 与 `BackendLoginRsaPublicKey`;私钥只允许可信服务端读取,匿名 `GetSysConfig` 只返回匹配公钥。源码、环境变量、普通 V8、前端业务代码和日志中仍禁止新增或输出真正的业务私钥、JWT 密钥、支付密钥及对象存储凭据。
33
33
 
34
+ 微信内容安全回调固定使用 `/api/wechatcontentsecurity/callback` 或第三方不支持 QueryString 时以 `/api/wechatcontentsecurity/callback--osclient--` 为路径前缀并追加 `{OsClient}--`;回调只能由服务端按签名和租户规则建立信任。
35
+
34
36
  吾码现有多端兼容约定是:主 SaaS 引擎 `sys_osclients.CorsAllowOrigins` 为空时默认允许全部跨域,便于本地开发、独立前端、H5 和不同租户域名访问;配置了来源后才按精确来源或通配符限制。安全修复不得把“未配置”改成默认拒绝,否则会造成所有存量部署和本地调试突然失效。CORS 不是鉴权边界,权限仍必须依赖 Token、租户隔离、菜单/表权限和服务端数据范围。
35
37
 
36
38
  吾码既有客户大量通过 `V8.Http` 访问内网设备、InfluxDB、内部 ApiEngine 和本机 sidecar。严格 SSRF 防护必须默认关闭:未配置时不得限制协议、URL 内嵌凭据、回环、私网、链路本地、云元数据或重定向。只有客户在 SaaS 引擎主租户启用 `SsrfProtectionEnabled` 后才进入严格模式,并用精确 `SsrfAllowedHosts` 放行;不要为这类普通运行参数增加 API 容器环境变量。
@@ -85,6 +87,17 @@ V8.Db.FromSql("SELECT * FROM " + V8.Param.table).ToArray();
85
87
 
86
88
  ## 2. 权限校验
87
89
 
90
+ ### DiyToken 是平台会话与权限入口,不替换为 ASP.NET Identity
91
+
92
+ DiyToken 与 `sys_user`、`OsClient`、终端 `did`、共享 Redis 登录态、角色/部门、菜单动作、表权限、数据范围和接口引擎共同构成吾码认证授权体系。它不是“只有一个 Token 字符串”的简化登录:
93
+
94
+ - 登录、SSO、OAuth、Passkey、人脸或访问密钥验证成功后,统一签发/兑换 DiyToken。
95
+ - Token 认证只证明用户和租户;服务端仍按资源、动作和数据范围授权,前端按钮不可代替。
96
+ - 用户停用、角色变化、单终端/全部终端吊销和 Token 轮换都依赖共享会话事实,不依赖单机内存。
97
+ - ASP.NET Identity 可以作为外部身份源的适配参考,但不能整体替换吾码的租户、低代码权限、V8 和在线终端协议。
98
+
99
+ 开发新登录入口时只实现“验证身份 -> 获取仍启用的 `sys_user` -> 签发 DiyToken”,禁止并行创建第二套用户、角色、权限 Token 或会话有效期规则。
100
+
88
101
  ### 平台 FormEngine 授权边界
89
102
 
90
103
  **Token 只完成身份认证,不是任意表的访问凭证。** 来自浏览器、UniApp、SDK 或其它外部客户端的通用 FormEngine 请求,必须由服务端完成以下授权,V8 或前端代码不得自行模拟、放宽:
@@ -260,6 +273,45 @@ if (old.Code === 1 && old.Data) {
260
273
  业务 V8 禁止自行查询 `sys_user`、保存密码或用 MD5/SHA1/SHA256 直接哈希密码。统一使用平台登录、重置密码和管理员用户管理接口;新密码存储必须由后端使用带盐、可调成本的专用密码哈希,并支持版本与轮换。登录 RSA 只隐藏传输报文中的明文外观,不能替代 HTTPS,也不能用于密码存储。
261
274
 
262
275
  存量 `PwdEncode=DES` 的兼容例外只能位于 `[PlatformAdminOnly]` 的 `GetSysUserPassword`:还要拒绝访问密钥会话、按当前 `OsClient` 和准确用户 Id 查询、用“解密后重新加密等于原密文”验证结果、返回 `no-store` 并写不含明文的安全审计。`PwdEncode=V8` 不假定可逆。不得把该能力暴露给 FormEngine、V8、普通角色或匿名端点。
276
+
277
+ ### 可逆业务秘密:允许加密存储和授权显示
278
+
279
+ 设备口令、第三方业务账号密码、客户明确要求再次显示的字段可以使用 `V8.EncryptHelper.DESEncode/DESDecode` 兼容机制,但必须与登录密码、支付私钥、`AuthSecret` 和基础设施密钥分开:
280
+
281
+ ```javascript
282
+ // 保存:只在可信后端执行
283
+ V8.Form.SecretCipher = V8.EncryptHelper.DESEncode(String(V8.Form.SecretPlain || ''));
284
+ V8.NotSaveField.push('SecretPlain');
285
+ ```
286
+
287
+ 显示明文必须设计为独立后端动作,并同时做到:
288
+
289
+ - 校验当前 DiyToken、准确 `OsClient`、当前用户真实角色/菜单/业务权限;高风险场景再要求下面的一次性强身份票据。
290
+ - 只解密一条明确记录,不向列表、批量导出、通用 FormEngine、匿名或访问密钥会话提供解密器。
291
+ - 只记录操作者、目标 Id、用途、结果和时间,禁止日志/审计/通知/缓存保存明文;HTTP 响应设置 `no-store`。
292
+ - 页面默认掩码,点击显示需二次确认,失焦/超时/路由离开后清除;禁止复制到 URL、LocalStorage 或前端日志。
293
+ - DES 是存量兼容格式。新高价值秘密优先由可信 C# 原子能力使用带版本的现代认证加密和集中密钥管理,V8 仍只编排授权显示,不能获取主密钥。
294
+
295
+ ### Passkey、人脸与敏感操作票据
296
+
297
+ 前端 V8 使用 `V8.Identity.Verify({Purpose,ActionHash,Method})` 完成人机交互,后端必须从数据库重读权威业务字段、按稳定版本/顺序重算 `ActionHash`,再消费票据:
298
+
299
+ ```javascript
300
+ var actionHash = V8.EncryptHelper.Sha256Hex(canonicalCommand);
301
+ var verified = V8.Method.ConsumeIdentityVerificationTicket({
302
+ Ticket: V8.Param.IdentityVerificationTicket,
303
+ Purpose: 'RevealBusinessSecret',
304
+ ActionHash: actionHash
305
+ });
306
+ if (verified.Code !== 1) return verified;
307
+ ```
308
+
309
+ - Ticket 绑定 `OsClient + UserId + Purpose + ActionHash`,共享 Redis 保存两分钟并使用原子 `GETDEL`,只能消费一次。
310
+ - 前端提交的摘要、`Verified=true`、认证器名称或方法不能作为授权事实;访问密钥会话不能使用。
311
+ - 票据只证明近期强身份,不代替菜单/表/行权限、业务状态机、事务、幂等或审计。
312
+ - 设备指纹/Face ID/Windows Hello 优先走 WebAuthn/Passkey,不需要模型 Docker;只有严格服务端人脸/活体检测才通过 `Microi Face Gateway v1` 接入独立服务。
313
+ - 完整表、SaaS 字段、API 和启用顺序见 `microi.doc/docs/doc/more/identity-verification.md`。
314
+ - 身份验证 HTTP 控制面统一位于 `/api/identityverification/`,业务页面优先使用 `V8.Identity`,不要自行复制 WebAuthn 序列化代码。
263
315
 
264
316
  ### 脱敏返回
265
317
 
@@ -384,6 +436,9 @@ try {
384
436
  - [ ] 涉及数据修改的接口校验请求参数合法性
385
437
  - [ ] 敏感数据(手机号、身份证等)脱敏返回
386
438
  - [ ] 密码只走平台认证/重置流程;禁止MD5/SHA直接存储,后端使用带盐自适应密码哈希
439
+ - [ ] 可逆业务秘密只在后端加解密;列表掩码、独立授权显示、no-store 且审计不含明文
440
+ - [ ] 新登录/SSO/Passkey/人脸入口最终签发 DiyToken,不并行创建第二套权限体系
441
+ - [ ] 敏感操作票据由后端重算 ActionHash 后原子消费,不能相信前端验证成功布尔值
387
442
  - [ ] 写操作有防重复提交机制
388
443
  - [ ] 关键操作写审计日志
389
444
  - [ ] catch 块不暴露内部错误给前端
@@ -115,8 +115,13 @@
115
115
  | `V8.SendSystemMessage(...)` | 发送站内系统消息 |
116
116
  | `await V8.Method.ScanCode()` | 扫码;成功值同时写入 `V8.ScanCodeRes` |
117
117
  | `V8.Print.*` | BLE 标签/小票打印,见蓝牙打印参考 |
118
+ | `V8.Identity.GetCapabilities()` | 读取当前租户强身份能力和本人登记状态 |
119
+ | `V8.Identity.CreateActionHash(value)` | 为稳定业务命令生成 SHA-256 摘要 |
120
+ | `V8.Identity.RegisterPasskey(options?)` | 登记当前用户 Passkey |
121
+ | `V8.Identity.Verify({Purpose,ActionHash,Method})` | 完成 Passkey/严格人脸验证并取得一次性后端票据 |
118
122
 
119
123
  扫码应优先接收 Promise 返回值;兼容代码可在调用后读取 `V8.ScanCodeRes`。
124
+ `V8.Identity` 是强身份验证模块。`V8.Identity.Verify` 的成功结果不能直接授权业务;后端必须重读权威数据、重算摘要并调用 `V8.Method.ConsumeIdentityVerificationTicket`。
120
125
  蓝牙打印完整 API 包含 `V8.Print.createNew`、`V8.Print.createNewESC`、
121
126
  `V8.Print.OpenBluetoothPage`、`V8.Print.isConnected`、
122
127
  `V8.Print.prepareSend`、`V8.Print.Send`、`V8.Print.setOneTimeData`、
@@ -50,6 +50,7 @@
50
50
  | `V8.Method.GetCurrentToken(token,osClient)` | 读取当前 Token 对象;不透传前端 |
51
51
  | `V8.Method.RefreshLoginUser(userId,osClient)` | 刷新用户登录缓存 |
52
52
  | `V8.Method.ClearUserLoginInfo(userId,osClient)` | 管理员吊销用户全部终端 Token |
53
+ | `V8.Method.ConsumeIdentityVerificationTicket({Ticket,Purpose,ActionHash})` | 按当前 DiyToken 用户、租户、用途和操作摘要原子消费一次性 Passkey/人脸票据 |
53
54
  | `V8.Method.GetPrivateFileUrl({FilePathName})` | 签发当前租户短期私有文件代理地址 |
54
55
  | `V8.Method.Upload(options)` | 受配额限制的上传 |
55
56
  | `V8.Method.AddSysLog(options)` | 结构化系统日志 |
@@ -82,6 +83,8 @@ var signature = V8.EncryptHelper.HmacSha256(secretFromConfig, payload);
82
83
  `V8.EncryptHelper.AESEncrypt`、`V8.EncryptHelper.AESDecrypt`、
83
84
  `V8.EncryptHelper.DESEncode`、`V8.EncryptHelper.DESDecode`。
84
85
 
86
+ DES 只用于明确要求取回原文的兼容业务秘密;保存和显示都在可信后端完成,列表掩码、独立授权、`no-store` 且审计不含明文。登录密码不使用摘要或可逆加密的新设计,完整分级见 `v8-security/SKILL.md`。
87
+
85
88
  MD5/SHA1 仅为兼容摘要;任何摘要都不能直接作为新密码存储方案。AES/DES/HMAC
86
89
  密钥从受控配置读取,不硬编码、不写日志、不返回客户端。
87
90
 
@@ -163,6 +163,15 @@ AI 新增或修改 Microi 配置文件时,凡是面向开发者、部署人员
163
163
  - 除上述十项外,任何业务开关、重试、超时、限额、安全策略、密钥或可执行文件路径通常都必须进入 SaaS 引擎 `sys_osclients` 的合适 Tab,并提供幂等升级、默认值、缓存刷新、敏感字段脱敏和子租户隔离。官方 License 信任链是固定例外:恢复重试次数/间隔使用代码常量,签发私钥固定只读挂载 `/app/microi_private.pem`,不得建立对应 SaaS 字段。禁止新增 `MICROI_*`、`DOS_ORM_*`、额外 `AppSettings` 节点或通用动态环境变量读取。
164
164
  - `ASPNETCORE_*`、`DOTNET_*` 是框架宿主配置;`PW_*`、MCP、构建、安装器和发布脚本变量只服务各自工具进程。它们不能成为生产 API 的业务配置入口。
165
165
  - 修改后必须用源码测试扫描生产 `.cs`、API `appsettings.json` 及在线/离线 Compose,精确断言十项白名单。不能用注释约定代替自动化守卫。
166
+
167
+ ## 身份、可逆业务秘密与敏感操作统一规范(强制)
168
+
169
+ - DiyToken 是吾码多租户、多终端、V8 和低代码权限体系的唯一会话入口。新增密码、SSO、OAuth、Passkey、人脸或其它登录方式时,验证成功后必须继续签发 DiyToken,并复用现有角色、部门、菜单、表权限、数据范围、终端吊销和 Token 轮换;禁止整体替换为 ASP.NET Identity 或并行建立第二套用户/权限 Token。
170
+ - 登录密码的新存储必须使用后端带盐、可调成本的专用密码哈希。存量 `PwdEncode=DES` 的管理员显示密码只是兼容能力,不得扩展给普通 V8、FormEngine、匿名或访问密钥会话。
171
+ - 业务明确要求再次显示原文的设备口令、第三方业务账号密码等字段,允许使用吾码可逆加密兼容机制。保存只在可信后端加密;列表/导出默认掩码;显示明文走独立授权动作,校验 DiyToken、租户和业务权限,返回 `no-store`,记录不含明文的审计,失焦/超时后清除。
172
+ - DES 是现有兼容格式,不得宣称能抵抗服务器所有者或代码执行者。新高价值秘密优先使用带版本的现代认证加密与集中密钥管理;基础设施密钥仍不得进入可编辑 V8。
173
+ - 登录后的敏感操作优先用 `V8.Identity.Verify` 申请 Passkey/严格人脸一次性票据,接口引擎从权威数据重算 `ActionHash` 后调用 `V8.Method.ConsumeIdentityVerificationTicket` 原子消费。票据不能代替菜单/表/行权限、状态机、幂等、事务或审计。
174
+ - Windows Hello、Touch ID、Face ID 和 Android 设备验证优先采用 WebAuthn/Passkey,不增加模型服务;只有服务端严格人脸与活体检测才接入独立 `Microi Face Gateway v1` 云服务或 Docker/集群。完整规范读取 `microi.skills/v8-security/SKILL.md` 与 `microi.doc/docs/doc/more/identity-verification.md`。
166
175
  - 一键安装恢复客户旧库时只允许定位精确主租户三元组;缺失则幂等创建,重复则停止,不能批量重写其它子租户。新主租户行不得持久化数据库、MongoDB 或 Redis 连接,安装器对 MinIO/OCR 等业务配置的后续更新也必须带同一三元组、活动状态条件并做唯一回读。
167
176
 
168
177
  ## 多语言优先约定
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microi.net/cli",
3
- "version": "4.7.3",
3
+ "version": "4.7.4",
4
4
  "description": "Microi吾码 AI 开发命令行:服务器连接、登录、AI/MCP 初始化与 V8 资源同步",
5
5
  "license": "MIT",
6
6
  "homepage": "https://microi.net/doc/v8-engine/vs-code-plugin.html",