@microi.net/cli 5.9.4 → 5.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codebuddy-plugin/marketplace.json +2 -2
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/LICENSE +21 -21
- package/README.md +71 -71
- package/assets/build-meta.json +7 -7
- package/assets/feature-matrix.json +138 -138
- package/assets/logo.svg +4 -4
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/codex-marketplace.json +20 -20
- package/scripts/mcp-codex-stdio-adapter.js +189 -189
- package/scripts/mcp-server.js +1 -0
- package/scripts/mcp-trae-windows-launcher.cmd +21 -21
- package/scripts/microi-cli-mcp.js +7 -7
- package/scripts/microi-cli.js +44 -49
- package/scripts/microi-codex-broker.js +466 -466
- package/scripts/microi-codex-router.js +635 -634
- package/scripts/microi-skills.meta.json +248 -248
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +55 -46
- package/skills/ai-engine/agents/openai.yaml +4 -4
- package/skills/app-store/agents/openai.yaml +4 -4
- package/skills/datasource-engine/agents/openai.yaml +4 -4
- package/skills/job-engine/agents/openai.yaml +4 -4
- package/skills/message-notification/agents/openai.yaml +5 -5
- package/skills/microi-ai-app-auth.js +652 -652
- package/skills/microi-ai-application/agents/openai.yaml +4 -4
- package/skills/microi-codex/SKILL.md +11 -6
- package/skills/microi-codex-installer/SKILL.md +1 -1
- package/skills/microi-codex-installer/agents/openai.yaml +7 -7
- package/skills/microi-db-schema/agents/openai.yaml +4 -4
- package/skills/microi-deployment/SKILL.md +6 -4
- package/skills/microi-docs-coverage/references/capability-map.md +1 -1
- package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
- package/skills/microi-microservice/SKILL.md +5 -0
- package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
- package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
- package/skills/microi-system-delivery/SKILL.md +18 -6
- package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +39 -1
- package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +5 -3
- package/skills/microi-uniapp-frontend/SKILL.md +3 -1
- package/skills/microi.v8.js +1921 -1921
- package/skills/module-engine/SKILL.md +26 -0
- package/skills/ocr-engine/agents/openai.yaml +4 -4
- package/skills/page-engine/SKILL.md +21 -0
- package/skills/page-engine/examples/compact-dashboard.json +1444 -1444
- package/skills/playwright-e2e/SKILL.md +5 -4
- package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +3 -3
- package/skills/report-engine/SKILL.md +12 -0
- package/skills/report-engine/agents/openai.yaml +4 -4
- package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
- package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
- package/skills/scripts/sync-embedded-skills.mjs +46 -46
- package/skills/scripts/validate-progressive-disclosure.mjs +57 -57
- package/skills/search-engine/agents/openai.yaml +4 -4
- package/skills/translate-engine/agents/openai.yaml +4 -4
- package/skills/ui-design/assets/pattern-showcase/app.js +54 -54
- package/skills/ui-design/assets/pattern-showcase/index.html +163 -163
- package/skills/ui-design/assets/pattern-showcase/styles.css +311 -311
- package/skills/unity-integration/agents/openai.yaml +4 -4
- package/skills/v8-file-upload/SKILL.md +2 -0
- package/skills/v8-image-processing/agents/openai.yaml +4 -4
- package/skills/v8-mongodb/SKILL.md +2 -1
- package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
- package/skills/v8-security/SKILL.md +6 -0
- package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
- package/skills/vision-engine/agents/openai.yaml +4 -4
- package/skills/workspace-conventions/SKILL.md +5 -4
- package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +13 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "Microi Unity 集成"
|
|
3
|
-
short_description: "交付 Unity UPM、WebGL、V8 与可安装 AI 应用"
|
|
4
|
-
default_prompt: "使用 $unity-integration 设计、实现并分层验收完整的 Microi Unity WebGL 应用。"
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Microi Unity 集成"
|
|
3
|
+
short_description: "交付 Unity UPM、WebGL、V8 与可安装 AI 应用"
|
|
4
|
+
default_prompt: "使用 $unity-integration 设计、实现并分层验收完整的 Microi Unity WebGL 应用。"
|
|
@@ -9,6 +9,8 @@ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI
|
|
|
9
9
|
|
|
10
10
|
你正在为 Microi 吾码平台编写文件上传/下载/返回相关代码。平台分布式存储(HDFS)支持阿里云OSS、MinIO、亚马逊S3,存储方案由 SaaS 引擎按租户配置。上传对象的 `Content-Type` 由框架按实际对象路径统一确定,覆盖 HTML、JS/MJS、CSS、JSON/MAP、SVG、字体与 WASM,未知后缀保留 `application/octet-stream`。MinIO、S3、OSS 普通与分片上传使用同一映射;该映射不替代上传权限、内容和配额校验。旧对象不会因后端升级自动改变元数据:先核对字节数和 SHA-256,再修正类型或按正式应用发布流程产生新版本。不要把源码与公有编译产物混在一起,也不要关闭 `nosniff` 掩盖类型错误。
|
|
11
11
|
|
|
12
|
+
文件与发布测试默认使用当前 MCP 连接下、当前租户实际生效的 HDFS Provider;先只读核实提供商与对象范围,沿已授权隔离前缀执行公私权限、原始字节、哈希、MIME 与写后回读断言。已确定当前环境后,不再为其它 OSS/MinIO/S3 反复询问或新建存储;仅明确的提供商缺陷、迁移或兼容任务才扩展矩阵,并逐项报告真实执行与未覆盖范围。提供商不可用或断言失败必须保留真实错误,不能换提供商或省略回读来冒充通过。
|
|
13
|
+
|
|
12
14
|
公开入口覆盖 `V8.uploadFile`、多文件 `V8.uploadFiles` 与 MCP `microi_upload_file_base64`。多文件上传必须限制并发、逐文件返回结果;Base64 工具只接受明确文件名、大小和租户内目标范围,写后回读路径、大小与哈希。
|
|
13
15
|
|
|
14
16
|
## MCP stdio 与完整包的传输边界
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "V8 图像处理"
|
|
3
|
-
short_description: "编写安全、可验证的 V8.Image 图像处理与合并代码"
|
|
4
|
-
default_prompt: "使用 $v8-image-processing 编写一个将水印按指定位置叠加到主图并返回图片的 V8 接口引擎。"
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "V8 图像处理"
|
|
3
|
+
short_description: "编写安全、可验证的 V8.Image 图像处理与合并代码"
|
|
4
|
+
default_prompt: "使用 $v8-image-processing 编写一个将水印按指定位置叠加到主图并返回图片的 V8 接口引擎。"
|
|
@@ -15,7 +15,8 @@ description: Microi V8 MongoDB 指南。用于使用 V8.MongoDb AddFormData、Up
|
|
|
15
15
|
|
|
16
16
|
- `reports wire version 6 ... requires at least 9` 是驱动与服务端不兼容,不是日志为空,也不是 Token 失效。先记录原始错误和服务端真实协议,不能把 wire version 6 推断成 MongoDB 4.2。
|
|
17
17
|
- 平台兼容镜像以含安全修复的驱动 3.11.2 构建,恢复平台 CRUD/日志所需的 MongoDB 3.6 协议路径;不是降级至 2.30,也不声明上游继续维护 3.6。来源、单文件补丁和摘要维护在 `Microi.Server/Microi.MongoDB.Driver/`。
|
|
18
|
-
-
|
|
18
|
+
- 普通开发与全部默认测试先从当前 MCP 同源只读诊断发现真实 MongoDB 版本,沿已授权隔离范围执行认证、BSON、读写、索引、幂等重放、游标和租户隔离断言;不为旧版或其它版本重复询问、建库或新增服务。配置标签和 wire version 不能冒充实际版本回读。
|
|
19
|
+
- 只有任务明确修改或验证存量兼容层、旧版本缺陷或跨版本升级时,才按该兼容目标扩展真实矩阵(例如需支持的 3.6 协议端与当前 MCP 实际版本),不固定假设当前端是 7.x。每个版本必须独立回读和实际执行,未执行记未覆盖;不得只验证编译或 ping,也不得把驱动兼容当作修复旧数据库服务端自身漏洞。
|
|
19
20
|
- C# 驱动改动需要更新后端镜像才生效。镜像更新后通过 `microi_query_system_observability` 回读日志、队列持久化和错误状态;查询失败应原样报错,禁止返回成功空表。
|
|
20
21
|
|
|
21
22
|
| 方法 | 说明 |
|
|
@@ -1,203 +1,203 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const workspaceRoot = path.resolve(scriptDir, '..', '..', '..');
|
|
7
|
-
|
|
8
|
-
const files = {
|
|
9
|
-
skill: 'microi.skills/v8-mq-mqtt/SKILL.md',
|
|
10
|
-
reference: 'microi.skills/v8-mq-mqtt/references/mqtt-production.md',
|
|
11
|
-
mqttDoc: 'microi.doc/docs/doc/system-engine/mqtt-engine.md',
|
|
12
|
-
v8Doc: 'microi.doc/docs/doc/v8-engine/v8-server.md',
|
|
13
|
-
runtime: 'Microi.Server/Microi.MQTT/MicroiMQTT.cs',
|
|
14
|
-
model: 'Microi.Server/Microi.Core/Model/MqttParam.cs',
|
|
15
|
-
mqttInterface: 'Microi.Server/Microi.Core/Interface/IMicroiMQTT.cs',
|
|
16
|
-
tenantSecurity: 'Microi.Server/Microi.Core/SaaSEngine/TenantConfigurationSecurity.cs',
|
|
17
|
-
platformRuntime: 'Microi.Server/Microi.Core/V8Engine/Runtime/V8Method.PlatformPluginRuntimes.cs',
|
|
18
|
-
platformApiEngine: 'Microi.Server/OfficialApplications/Resource/platform-mqtt.js'
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const content = {};
|
|
22
|
-
const failures = [];
|
|
23
|
-
|
|
24
|
-
for (const [name, relativePath] of Object.entries(files)) {
|
|
25
|
-
const absolutePath = path.join(workspaceRoot, relativePath);
|
|
26
|
-
if (!fs.existsSync(absolutePath)) {
|
|
27
|
-
failures.push(`${name}: 文件不存在 ${relativePath}`);
|
|
28
|
-
content[name] = '';
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
content[name] = fs.readFileSync(absolutePath, 'utf8');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function collectMatches(text, regex) {
|
|
35
|
-
const values = [];
|
|
36
|
-
for (const match of text.matchAll(regex)) values.push(match[1]);
|
|
37
|
-
return values;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function requireTokens(targetName, tokens) {
|
|
41
|
-
const text = content[targetName] || '';
|
|
42
|
-
for (const token of tokens) {
|
|
43
|
-
if (!text.includes(token)) failures.push(`${targetName}: 缺少 ${token}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function requireAny(targetName, alternatives, label) {
|
|
48
|
-
const text = content[targetName] || '';
|
|
49
|
-
if (!alternatives.some((token) => text.includes(token))) {
|
|
50
|
-
failures.push(`${targetName}: 缺少 ${label}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const runtimeEvents = new Set([
|
|
55
|
-
...collectMatches(
|
|
56
|
-
content.runtime,
|
|
57
|
-
/RunMqttV8EngineAsync\([^,\r\n]+,\s*"([A-Za-z]+)"/g
|
|
58
|
-
),
|
|
59
|
-
...collectMatches(
|
|
60
|
-
content.runtime,
|
|
61
|
-
/FireV8EventForAllTenantsAsync\(\s*"([A-Za-z]+)"/g
|
|
62
|
-
)
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
const expectedEvents = [
|
|
66
|
-
'StartServer',
|
|
67
|
-
'Connected',
|
|
68
|
-
'Disconnected',
|
|
69
|
-
'Subscribing',
|
|
70
|
-
'MessageReceived',
|
|
71
|
-
'MessageChanged',
|
|
72
|
-
'StopServer'
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
for (const eventName of expectedEvents) {
|
|
76
|
-
if (!runtimeEvents.has(eventName)) failures.push(`runtime: 未提取到事件 ${eventName}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const mqttProperties = new Set(collectMatches(
|
|
80
|
-
content.model,
|
|
81
|
-
/public\s+[A-Za-z0-9_<>,.\s]+\s+([A-Za-z0-9_]+)\s*\{\s*get;\s*set;\s*\}/g
|
|
82
|
-
));
|
|
83
|
-
|
|
84
|
-
const expectedProperties = [
|
|
85
|
-
'ClientId',
|
|
86
|
-
'Payload',
|
|
87
|
-
'PayloadRaw',
|
|
88
|
-
'Topic',
|
|
89
|
-
'OsClient',
|
|
90
|
-
'UserName',
|
|
91
|
-
'Qos',
|
|
92
|
-
'Retain',
|
|
93
|
-
'UserProperties'
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
for (const propertyName of expectedProperties) {
|
|
97
|
-
if (!mqttProperties.has(propertyName)) failures.push(`model: 未提取到属性 ${propertyName}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const sourceEvents = [...runtimeEvents].sort();
|
|
101
|
-
const sourceProperties = [...mqttProperties].sort();
|
|
102
|
-
for (const targetName of ['reference', 'mqttDoc', 'v8Doc']) {
|
|
103
|
-
requireTokens(targetName, sourceEvents);
|
|
104
|
-
requireTokens(targetName, sourceProperties);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const configurationTokens = [
|
|
108
|
-
'MqttEnable',
|
|
109
|
-
'MqttPort',
|
|
110
|
-
'MqttUseTls',
|
|
111
|
-
'MqttTlsPort',
|
|
112
|
-
'MqttCertPath',
|
|
113
|
-
'MqttCertPassword',
|
|
114
|
-
'MqttFallbackPort',
|
|
115
|
-
'MqttWsPort',
|
|
116
|
-
'MqttAccount',
|
|
117
|
-
'MqttPwd',
|
|
118
|
-
'MqttApiEngine',
|
|
119
|
-
'MqttAllowAnonymous',
|
|
120
|
-
'MqttTopicIsolation'
|
|
121
|
-
];
|
|
122
|
-
|
|
123
|
-
const securityTokens = [
|
|
124
|
-
'tenant/{lowerOsClient}',
|
|
125
|
-
'$SYS',
|
|
126
|
-
'$share',
|
|
127
|
-
'ResponseTopic',
|
|
128
|
-
'Code != 1',
|
|
129
|
-
'StaleDisconnectIgnored'
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
const operationsTokens = [
|
|
133
|
-
'mci_mqtt_client',
|
|
134
|
-
'mci_mqtt_log',
|
|
135
|
-
'ApiEngineId',
|
|
136
|
-
'IMicroiMQTT.PublishAsync',
|
|
137
|
-
'ConnectedClients',
|
|
138
|
-
'GetConnectedClients',
|
|
139
|
-
'外部 Broker',
|
|
140
|
-
'独立 MQTT 节点'
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
requireTokens('platformRuntime', [
|
|
144
|
-
'ManageMqtt',
|
|
145
|
-
'RequireCurrentTenantSuperAdmin',
|
|
146
|
-
'IMicroiMQTT',
|
|
147
|
-
'GetConnectedClients',
|
|
148
|
-
'PublishAsync'
|
|
149
|
-
]);
|
|
150
|
-
requireTokens('platformApiEngine', ['V8.Method.ManageMqtt']);
|
|
151
|
-
|
|
152
|
-
requireTokens('skill', ['references/mqtt-production.md', 'platform-mqtt']);
|
|
153
|
-
requireTokens('reference', configurationTokens);
|
|
154
|
-
|
|
155
|
-
requireTokens('reference', securityTokens);
|
|
156
|
-
requireTokens('reference', operationsTokens);
|
|
157
|
-
|
|
158
|
-
requireTokens(
|
|
159
|
-
'mqttDoc',
|
|
160
|
-
configurationTokens.filter((token) => token !== 'MqttAllowAnonymous')
|
|
161
|
-
);
|
|
162
|
-
requireAny(
|
|
163
|
-
'mqttDoc',
|
|
164
|
-
['MqttAllowAnonymous', '匿名连接只可能由主租户显式开启'],
|
|
165
|
-
'主租户匿名连接边界'
|
|
166
|
-
);
|
|
167
|
-
requireTokens(
|
|
168
|
-
'mqttDoc',
|
|
169
|
-
securityTokens.filter(
|
|
170
|
-
(token) => token !== 'StaleDisconnectIgnored' && token !== 'tenant/{lowerOsClient}'
|
|
171
|
-
)
|
|
172
|
-
);
|
|
173
|
-
requireAny(
|
|
174
|
-
'mqttDoc',
|
|
175
|
-
['tenant/{lowerOsClient}', 'tenant/<lowerOsClient>'],
|
|
176
|
-
'标准租户 Topic 模板'
|
|
177
|
-
);
|
|
178
|
-
requireTokens('v8Doc', ['## V8.MQTT', 'IMicroiMQTT.PublishAsync', 'Code != 1']);
|
|
179
|
-
|
|
180
|
-
requireTokens('runtime', [
|
|
181
|
-
'NormalizeTopic',
|
|
182
|
-
'ResponseTopic',
|
|
183
|
-
'StaleDisconnectIgnored',
|
|
184
|
-
'$share/',
|
|
185
|
-
'$SYS/',
|
|
186
|
-
'mci_mqtt_client',
|
|
187
|
-
'mci_mqtt_log',
|
|
188
|
-
'PublishRejectedByV8'
|
|
189
|
-
]);
|
|
190
|
-
requireTokens('mqttInterface', ['PublishAsync(string osClient', 'GetConnectedClients(string osClient)']);
|
|
191
|
-
requireTokens('tenantSecurity', ['NormalizeMqttTopic', 'HasTenantServiceCredentialCollision']);
|
|
192
|
-
|
|
193
|
-
if (failures.length > 0) {
|
|
194
|
-
console.error('MQTT Skill 覆盖检查失败:');
|
|
195
|
-
for (const failure of failures) console.error(`- ${failure}`);
|
|
196
|
-
process.exitCode = 1;
|
|
197
|
-
} else {
|
|
198
|
-
console.log(
|
|
199
|
-
`MQTT Skill 覆盖检查通过:${sourceEvents.length} 个事件、` +
|
|
200
|
-
`${sourceProperties.length} 个 V8.MQTT 字段、` +
|
|
201
|
-
`${configurationTokens.length} 个配置项及生产安全/部署契约均已覆盖。`
|
|
202
|
-
);
|
|
203
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const workspaceRoot = path.resolve(scriptDir, '..', '..', '..');
|
|
7
|
+
|
|
8
|
+
const files = {
|
|
9
|
+
skill: 'microi.skills/v8-mq-mqtt/SKILL.md',
|
|
10
|
+
reference: 'microi.skills/v8-mq-mqtt/references/mqtt-production.md',
|
|
11
|
+
mqttDoc: 'microi.doc/docs/doc/system-engine/mqtt-engine.md',
|
|
12
|
+
v8Doc: 'microi.doc/docs/doc/v8-engine/v8-server.md',
|
|
13
|
+
runtime: 'Microi.Server/Microi.MQTT/MicroiMQTT.cs',
|
|
14
|
+
model: 'Microi.Server/Microi.Core/Model/MqttParam.cs',
|
|
15
|
+
mqttInterface: 'Microi.Server/Microi.Core/Interface/IMicroiMQTT.cs',
|
|
16
|
+
tenantSecurity: 'Microi.Server/Microi.Core/SaaSEngine/TenantConfigurationSecurity.cs',
|
|
17
|
+
platformRuntime: 'Microi.Server/Microi.Core/V8Engine/Runtime/V8Method.PlatformPluginRuntimes.cs',
|
|
18
|
+
platformApiEngine: 'Microi.Server/OfficialApplications/Resource/platform-mqtt.js'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const content = {};
|
|
22
|
+
const failures = [];
|
|
23
|
+
|
|
24
|
+
for (const [name, relativePath] of Object.entries(files)) {
|
|
25
|
+
const absolutePath = path.join(workspaceRoot, relativePath);
|
|
26
|
+
if (!fs.existsSync(absolutePath)) {
|
|
27
|
+
failures.push(`${name}: 文件不存在 ${relativePath}`);
|
|
28
|
+
content[name] = '';
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
content[name] = fs.readFileSync(absolutePath, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function collectMatches(text, regex) {
|
|
35
|
+
const values = [];
|
|
36
|
+
for (const match of text.matchAll(regex)) values.push(match[1]);
|
|
37
|
+
return values;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requireTokens(targetName, tokens) {
|
|
41
|
+
const text = content[targetName] || '';
|
|
42
|
+
for (const token of tokens) {
|
|
43
|
+
if (!text.includes(token)) failures.push(`${targetName}: 缺少 ${token}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function requireAny(targetName, alternatives, label) {
|
|
48
|
+
const text = content[targetName] || '';
|
|
49
|
+
if (!alternatives.some((token) => text.includes(token))) {
|
|
50
|
+
failures.push(`${targetName}: 缺少 ${label}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const runtimeEvents = new Set([
|
|
55
|
+
...collectMatches(
|
|
56
|
+
content.runtime,
|
|
57
|
+
/RunMqttV8EngineAsync\([^,\r\n]+,\s*"([A-Za-z]+)"/g
|
|
58
|
+
),
|
|
59
|
+
...collectMatches(
|
|
60
|
+
content.runtime,
|
|
61
|
+
/FireV8EventForAllTenantsAsync\(\s*"([A-Za-z]+)"/g
|
|
62
|
+
)
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
const expectedEvents = [
|
|
66
|
+
'StartServer',
|
|
67
|
+
'Connected',
|
|
68
|
+
'Disconnected',
|
|
69
|
+
'Subscribing',
|
|
70
|
+
'MessageReceived',
|
|
71
|
+
'MessageChanged',
|
|
72
|
+
'StopServer'
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
for (const eventName of expectedEvents) {
|
|
76
|
+
if (!runtimeEvents.has(eventName)) failures.push(`runtime: 未提取到事件 ${eventName}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mqttProperties = new Set(collectMatches(
|
|
80
|
+
content.model,
|
|
81
|
+
/public\s+[A-Za-z0-9_<>,.\s]+\s+([A-Za-z0-9_]+)\s*\{\s*get;\s*set;\s*\}/g
|
|
82
|
+
));
|
|
83
|
+
|
|
84
|
+
const expectedProperties = [
|
|
85
|
+
'ClientId',
|
|
86
|
+
'Payload',
|
|
87
|
+
'PayloadRaw',
|
|
88
|
+
'Topic',
|
|
89
|
+
'OsClient',
|
|
90
|
+
'UserName',
|
|
91
|
+
'Qos',
|
|
92
|
+
'Retain',
|
|
93
|
+
'UserProperties'
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
for (const propertyName of expectedProperties) {
|
|
97
|
+
if (!mqttProperties.has(propertyName)) failures.push(`model: 未提取到属性 ${propertyName}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const sourceEvents = [...runtimeEvents].sort();
|
|
101
|
+
const sourceProperties = [...mqttProperties].sort();
|
|
102
|
+
for (const targetName of ['reference', 'mqttDoc', 'v8Doc']) {
|
|
103
|
+
requireTokens(targetName, sourceEvents);
|
|
104
|
+
requireTokens(targetName, sourceProperties);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const configurationTokens = [
|
|
108
|
+
'MqttEnable',
|
|
109
|
+
'MqttPort',
|
|
110
|
+
'MqttUseTls',
|
|
111
|
+
'MqttTlsPort',
|
|
112
|
+
'MqttCertPath',
|
|
113
|
+
'MqttCertPassword',
|
|
114
|
+
'MqttFallbackPort',
|
|
115
|
+
'MqttWsPort',
|
|
116
|
+
'MqttAccount',
|
|
117
|
+
'MqttPwd',
|
|
118
|
+
'MqttApiEngine',
|
|
119
|
+
'MqttAllowAnonymous',
|
|
120
|
+
'MqttTopicIsolation'
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
const securityTokens = [
|
|
124
|
+
'tenant/{lowerOsClient}',
|
|
125
|
+
'$SYS',
|
|
126
|
+
'$share',
|
|
127
|
+
'ResponseTopic',
|
|
128
|
+
'Code != 1',
|
|
129
|
+
'StaleDisconnectIgnored'
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const operationsTokens = [
|
|
133
|
+
'mci_mqtt_client',
|
|
134
|
+
'mci_mqtt_log',
|
|
135
|
+
'ApiEngineId',
|
|
136
|
+
'IMicroiMQTT.PublishAsync',
|
|
137
|
+
'ConnectedClients',
|
|
138
|
+
'GetConnectedClients',
|
|
139
|
+
'外部 Broker',
|
|
140
|
+
'独立 MQTT 节点'
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
requireTokens('platformRuntime', [
|
|
144
|
+
'ManageMqtt',
|
|
145
|
+
'RequireCurrentTenantSuperAdmin',
|
|
146
|
+
'IMicroiMQTT',
|
|
147
|
+
'GetConnectedClients',
|
|
148
|
+
'PublishAsync'
|
|
149
|
+
]);
|
|
150
|
+
requireTokens('platformApiEngine', ['V8.Method.ManageMqtt']);
|
|
151
|
+
|
|
152
|
+
requireTokens('skill', ['references/mqtt-production.md', 'platform-mqtt']);
|
|
153
|
+
requireTokens('reference', configurationTokens);
|
|
154
|
+
|
|
155
|
+
requireTokens('reference', securityTokens);
|
|
156
|
+
requireTokens('reference', operationsTokens);
|
|
157
|
+
|
|
158
|
+
requireTokens(
|
|
159
|
+
'mqttDoc',
|
|
160
|
+
configurationTokens.filter((token) => token !== 'MqttAllowAnonymous')
|
|
161
|
+
);
|
|
162
|
+
requireAny(
|
|
163
|
+
'mqttDoc',
|
|
164
|
+
['MqttAllowAnonymous', '匿名连接只可能由主租户显式开启'],
|
|
165
|
+
'主租户匿名连接边界'
|
|
166
|
+
);
|
|
167
|
+
requireTokens(
|
|
168
|
+
'mqttDoc',
|
|
169
|
+
securityTokens.filter(
|
|
170
|
+
(token) => token !== 'StaleDisconnectIgnored' && token !== 'tenant/{lowerOsClient}'
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
requireAny(
|
|
174
|
+
'mqttDoc',
|
|
175
|
+
['tenant/{lowerOsClient}', 'tenant/<lowerOsClient>'],
|
|
176
|
+
'标准租户 Topic 模板'
|
|
177
|
+
);
|
|
178
|
+
requireTokens('v8Doc', ['## V8.MQTT', 'IMicroiMQTT.PublishAsync', 'Code != 1']);
|
|
179
|
+
|
|
180
|
+
requireTokens('runtime', [
|
|
181
|
+
'NormalizeTopic',
|
|
182
|
+
'ResponseTopic',
|
|
183
|
+
'StaleDisconnectIgnored',
|
|
184
|
+
'$share/',
|
|
185
|
+
'$SYS/',
|
|
186
|
+
'mci_mqtt_client',
|
|
187
|
+
'mci_mqtt_log',
|
|
188
|
+
'PublishRejectedByV8'
|
|
189
|
+
]);
|
|
190
|
+
requireTokens('mqttInterface', ['PublishAsync(string osClient', 'GetConnectedClients(string osClient)']);
|
|
191
|
+
requireTokens('tenantSecurity', ['NormalizeMqttTopic', 'HasTenantServiceCredentialCollision']);
|
|
192
|
+
|
|
193
|
+
if (failures.length > 0) {
|
|
194
|
+
console.error('MQTT Skill 覆盖检查失败:');
|
|
195
|
+
for (const failure of failures) console.error(`- ${failure}`);
|
|
196
|
+
process.exitCode = 1;
|
|
197
|
+
} else {
|
|
198
|
+
console.log(
|
|
199
|
+
`MQTT Skill 覆盖检查通过:${sourceEvents.length} 个事件、` +
|
|
200
|
+
`${sourceProperties.length} 个 V8.MQTT 字段、` +
|
|
201
|
+
`${configurationTokens.length} 个配置项及生产安全/部署契约均已覆盖。`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
@@ -32,6 +32,12 @@ Preview 和确认均重验当前客户端、回调及 Scope;账号变化通过
|
|
|
32
32
|
先升级前后端和系统设置/SaaS 应用,再登记客户端并真实验证绑定、登录、解绑及隐藏入口。
|
|
33
33
|
通用 MCP Schema、字段、私密配置、接口引擎与商城工具足够完成配置,无需新增 Microi 专用管理工具。
|
|
34
34
|
|
|
35
|
+
外部账号的列表与解绑同样必须先验证本人 DiyToken,再由可信宿主以精确租户和 Managed Key
|
|
36
|
+
调用 `RunTrustedProtocolAsync`;绑定业务在进入租户 Hook 前消费 `RequireManagedProtocolContext`。
|
|
37
|
+
不能仅因已有 `CurrentUser` 就放开 StopHttp,也不能把尚未消费的协议授权交给可编辑 Hook。
|
|
38
|
+
列表/解绑提示“此接口已禁止http调用”时,配套检查后端不低于 8.5.10、SaaS引擎不低于 v8.4.23,
|
|
39
|
+
保持内部绑定接口 `StopHttp=1`,并验收匿名、伪造可信参数、跨用户解绑及 Hook 重入均被拒绝。
|
|
40
|
+
|
|
35
41
|
微信网站扫码登录先读取当前租户能力、`WeChatLoginEnabled` 和私密设置元信息,
|
|
36
42
|
再核对开放平台已审核网站应用的微信登录权限与授权回调域名。
|
|
37
43
|
`Login.WeChat.ClientId` 保存 AppID,`Login.WeChat.ClientSecret` 通过专用密钥工具保存,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "V8 TCP 原始字节"
|
|
3
|
-
short_description: "通过后端 V8 安全发送和接收 TCP 原始字节数据"
|
|
4
|
-
default_prompt: "使用 $v8-tcp-integration 编写一个安全、可验证的 Microi 后端 V8 TCP 设备集成。"
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "V8 TCP 原始字节"
|
|
3
|
+
short_description: "通过后端 V8 安全发送和接收 TCP 原始字节数据"
|
|
4
|
+
default_prompt: "使用 $v8-tcp-integration 编写一个安全、可验证的 Microi 后端 V8 TCP 设备集成。"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "视觉引擎"
|
|
3
|
-
short_description: "构建与验收数据库优先、AI回退的通用视觉识别"
|
|
4
|
-
default_prompt: "Use $vision-engine to build and validate tenant-isolated Microi.Vision workflows."
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "视觉引擎"
|
|
3
|
+
short_description: "构建与验收数据库优先、AI回退的通用视觉识别"
|
|
4
|
+
default_prompt: "Use $vision-engine to build and validate tenant-isolated Microi.Vision workflows."
|
|
@@ -6,7 +6,7 @@ description: 每次 Microi 任务开始前必读的基础规范。用于识别
|
|
|
6
6
|
# Microi 工作区全局约定
|
|
7
7
|
|
|
8
8
|
<!-- microi-progressive:begin -->
|
|
9
|
-
<!-- microi-progressive:chunk id=workspace-conventions-000 sha256=
|
|
9
|
+
<!-- microi-progressive:chunk id=workspace-conventions-000 sha256=164c8870cc7e7ffa37c8889372c8962760aa04bb386fcf9368be7c4d231fdca6 -->
|
|
10
10
|
## 任务启动前 Skill 读取规则(强制)
|
|
11
11
|
|
|
12
12
|
AI 处理任何 Microi 低代码、V8、MCP、OpenClaw、采集引擎、前端、后端、UniApp、文档、测试或交付任务前,必须先按任务类型读取相关 `microi.skills/**/SKILL.md`。不能等到写代码或出问题后才补读。
|
|
@@ -22,7 +22,7 @@ AI 处理任何 Microi 低代码、V8、MCP、OpenClaw、采集引擎、前端
|
|
|
22
22
|
- 任何吾码开发任务必须读取总控中的[完整系统开发默认交付清单(原推荐提示词 1–8)](../microi-system-delivery/references/progressive-01-标准工作流.md#完整系统开发默认交付清单原推荐提示词-18),并按总控的相关 Skill 表继续读取表单、模块、数据源、V8、页面、报表及验收等实际涉及的专项规范。不得仅凭 Skill 名称、摘要或聊天记忆执行,也不把全部 Skills 全文拼进一个提示词。
|
|
23
23
|
- 按用户意图确定执行范围:完整系统开发逐项执行八项清单;局部修改只执行相关条目,保留已有业务资源和无关配置;只读分析只读取规范和事实,不创建资源、验收数据或执行发布。需求明确表示新建完整系统时,不因省略“完整”二字就降为方案或样例。
|
|
24
24
|
- 默认读取规则不额外授权生产数据写入、客户部署或全库重建。连接可由当前项目和唯一已登录 Profile 自动确定;多个目标无法唯一匹配时只澄清必要的连接信息,不要求用户选择或记住 Skill。明显属于其它平台或与吾码无关的任务按实际项目规范执行。
|
|
25
|
-
- 生成的 `AGENTS.md`、`CLAUDE.md`、Copilot/Cursor 指令及可移植吾码 Skill 只保留默认入口指针和读取顺序;详细八项清单仍由总控参考文件唯一维护。工作区未初始化或必要 Skill 缺失时先按现有安装/初始化规范恢复,并准确说明实际可读取状态,不能声称 AI
|
|
25
|
+
- 生成的 `AGENTS.md`、`CLAUDE.md`、Copilot/Cursor 指令及可移植吾码 Skill 只保留默认入口指针和读取顺序;详细八项清单仍由总控参考文件唯一维护。工作区未初始化或必要 Skill 缺失时先按现有安装/初始化规范恢复,并准确说明实际可读取状态,不能声称 AI 已自动加载不存在的规则。默认路由不代表任意模型 100% 遵循;开工说明须列出实际读取的基础入口、总控、完整清单参考及本次专项 Skills,并映射到交付计划,不能只凭路径存在、MCP 连通或文件摘要声称已读。原八项及报表、菜单先行增补的详细要求统一读取总控参考文件。
|
|
26
26
|
|
|
27
27
|
### 吾码创建人身份识别(强制)
|
|
28
28
|
|
|
@@ -50,7 +50,7 @@ AI 处理任何 Microi 低代码、V8、MCP、OpenClaw、采集引擎、前端
|
|
|
50
50
|
- 平台 C# 单元/组件/集成测试归入 `Microi.Server/Microi.Tests`。平台前端、平台内置应用包与平台 V8 的 Node 行为测试可留在责任源码旁,由 `Microi.Tests/run-tests.ps1` 自动发现执行;平台真实浏览器、数据库及第三方集成归入 Full 或平台专项验收,不得冒充离线单测。新增公共后端能力同时补相应 HTTP 闭环。
|
|
51
51
|
- 官方或社区独立业务应用、游戏及客户项目的测试归应用自身源码根的 `tests/` 或独立测试工程,由该应用自己的测试和发布入口执行;不得放入 `Microi.Tests`、创建转调包装或接入平台 Quick/Full。官方发布身份不改变测试归属。应用开发中涉及平台通用缺陷时,仅把通用最小回归夹具加入平台门禁,应用业务断言仍在应用项目维护。
|
|
52
52
|
- `Microi一键编译发布.sh` 的所有 PC/API 镜像路径(包括仅推送、热修复)都必须先通过 Full;缺少环境、零用例、失败、取消、跳过、待办或无法解析测试结果一律停止。禁止关闭断言、删测试、排除失败项目或修改门禁阈值来发布。
|
|
53
|
-
- 测试成功必须绑定本次候选源码和实际构建上下文的内容哈希,构建后、每次推送前复核。旧产物没有可验证回执或源码/产物漂移时必须重建重测,禁止“当前源码通过测试 + 推送另一份旧 DLL/前端 dist”。
|
|
53
|
+
- 测试成功必须绑定本次候选源码和实际构建上下文的内容哈希,构建后、每次推送前复核。旧产物没有可验证回执或源码/产物漂移时必须重建重测,禁止“当前源码通过测试 + 推送另一份旧 DLL/前端 dist”。 所有测试默认使用当前 MCP 经同源只读发现的 MongoDB 实际版本与当前租户实际生效的 HDFS 提供商;目标唯一时直接沿已授权隔离范围执行,不为旧 MongoDB 或其它 OSS/MinIO/S3 反复询问、新建服务或替换当前提供商。只有明确的兼容、迁移或提供商缺陷任务才扩展真实矩阵;未执行项如实记未覆盖,不以同一端点冒充多版本,也不因环境发现获得生产业务写入授权。
|
|
54
54
|
- 交付记录逐项列明修复与测试映射、执行数量、未覆盖边界和镜像摘要。覆盖率与 Full 都不能证明所有租户业务、任意生产数据和第三方系统绝对无误;真实客户路径仍需只读验收。详细矩阵见 `microi-system-delivery/references/progressive-02-自动化测试必须覆盖的坑.md`。
|
|
55
55
|
|
|
56
56
|
### Microi吾码非阻塞自动更新(强制)
|
|
@@ -95,7 +95,7 @@ Microi 平台后端功能必须默认按多节点部署设计:多个 API/Worke
|
|
|
95
95
|
- 若要求宿主机掉电或 `kill -9` 窗口内也绝对零丢失,必须在返回业务成功前取得外部持久消息队列、共享 outbox 或同步 WAL 的持久化确认;“内存队列随后异步落盘”不能宣称覆盖尚未持久化的强杀窗口。
|
|
96
96
|
|
|
97
97
|
<!-- /microi-progressive:chunk -->
|
|
98
|
-
<!-- microi-progressive:chunk id=workspace-conventions-003 sha256=
|
|
98
|
+
<!-- microi-progressive:chunk id=workspace-conventions-003 sha256=7e7a88134f51f7e5712e9a41cfe516bae45ea8e9cb418a93cc39adeff3f3cbb4 -->
|
|
99
99
|
## 本地资源与 OOM 保护规范(强制)
|
|
100
100
|
|
|
101
101
|
AI 在用户本机启动 Node.js、Vite、Webpack、dotnet build、Java、Docker build、浏览器自动化、压力测试或其他可能长时间占用 CPU/内存的进程前,必须先评估资源,不得为了“让构建跑过”无限抬高堆内存或 Worker 数。
|
|
@@ -106,6 +106,7 @@ AI 在用户本机启动 Node.js、Vite、Webpack、dotnet build、Java、Docker
|
|
|
106
106
|
- 禁止将 `--max-old-space-size`、JVM heap、Docker memory 或类似上限设为接近物理内存总量。除非用户明确授权独占构建窗口,单个 AI 启动的进程树不得持续占用超过物理内存的 25%。
|
|
107
107
|
- 后台/长任务必须记录根 PID、子进程、启动时间和独立日志,每 15-30 秒监测一次进程树内存与全机可用内存。任务失败、中断或达阈值时必须停止整个子进程树,不得遗留孤儿 Node/dotnet/Java 进程。
|
|
108
108
|
- 全量构建无法在上述阈值内完成时,先停止并改用定向 lint、类型检查、按模块构建或按测试文件验证。如仍必须进行全量验收,应明确报告资源瓶颈,交由 CI/专用构建机或经用户明确同意的独占时段执行,禁止在用户正在使用的 VS Code 会话里硬跑。
|
|
109
|
+
- **本地测试程序与依赖生命周期(强制)**:优先复用健康源码/Vite,或在隔离目录启动已编译的吾码程序;共用已授权开发/测试依赖,用库、租户和命名空间隔离,不按 AI 任务或节点批量创建 Docker/Compose。新建本地 Docker 测试依赖须有必要性、最小清单及用户针对该动作的授权,一般开发、Full 或镜像发布不自动包含此动作。每轮成功、失败、取消及资源阻塞退出均收回自建进程、浏览器和临时容器并独立回读,保留既有共享服务;报告内存不足前先收回本任务遗留占用。详见[本地测试程序与依赖生命周期](references/progressive-02-microi-net-api-本地启动约定.md#本地测试程序与依赖生命周期强制)。
|
|
109
110
|
|
|
110
111
|
### `Microi.Client` 框架前端构建频率(强制)
|
|
111
112
|
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
4
|
|
|
5
|
+
<!-- microi-progressive:chunk id=workspace-conventions-039 sha256=8cfc7d1e2a6a0a766845c79775efe8c7fb15ef805a47e8bc1965128e348f3062 -->
|
|
6
|
+
## 本地测试程序与依赖生命周期(强制)
|
|
7
|
+
|
|
8
|
+
- 启动前盘点当前源码/Vite/API、数据库、Redis、MongoDB、MinIO、端口及其归属。健康且适用的开发/测试服务优先复用;已有服务不满足时,优先直接启动本地源码,或把本轮编译产物复制到任务隔离目录启动,避免锁住共享构建输出。登记实际源码、配置来源、PID、创建时间、工作目录和监听端口;不能启动另一份旧 DLL 冒充当前候选。
|
|
9
|
+
- 多节点 HTTP、并发和租户隔离测试优先使用多个本机吾码程序进程,连接同组已授权依赖;库、租户、Schema、Redis DB/Key 前缀和存储命名空间负责测试隔离。`fixture`、独立候选目录和 Full 不等于每个 AI 对话或节点都要创建一整套 Docker。复用仅限已授权开发/测试资源,不向生产业务库注入测试数据,也不对共享服务进行破坏性故障测试。
|
|
10
|
+
- 普通联调、登录、表单、V8 和浏览器验收不默认创建本地 Docker/Compose。确实只能通过指定引擎/版本、容器安装或镜像行为验证时,先说明既有依赖为何不能满足、最少容器清单、资源上限和收尾方式,并取得对该动作的明确授权;已授权同一清单不重复询问。一般开发、Full 和镜像发布授权不自动授权新增数据库等测试服务。已授权镜像的构建、推送和不启动容器的镜像字节检查按各自制品流程执行,不能扩张为启动整套测试基础设施。
|
|
11
|
+
- 创建或借用前建立资源台账,区分既有、借用和本轮新建;容器登记完整 Id、任务标签、数据挂载及消费者,进程登记 PID、创建时间、命令入口、工作目录和后代。共享依赖有唯一协调者,消费者退出不删除其它任务仍在使用的服务,也不能把他人资源仅凭名称认领为本任务资源。既有共享开发服务必要重启后的替换进程继承共享身份,收尾时保持健康;任务专用实例仍须结束,不能改称“共享服务”来免除清理。
|
|
12
|
+
- 每轮验证成功、失败、超时、取消、异常或资源门限不足退出时,都立即执行收尾,不等整个功能交付完成。脚本用 `finally` 并处理可捕获的中断;启动到一半失败也回收已创建资源,外层监督程序退出时由已登记归属的清理器兜底。停止本轮自建 API/Vite/测试程序、watcher、浏览器及其子进程,以及获准新建的临时容器;不因“待续”长期保留运行资源。确需继续后台运行时,记录用户要求、负责人和有效期限。
|
|
13
|
+
- 清理前重验精确身份,正常停止后独立回读进程及后代已退出、监听/连接已消失、容器已停止或移除;只输出白名单状态,不回显口令、Env 或连接串。既有或借用的共享实例保持原状态,不按进程名全杀,不执行全局 Docker prune、WSL/Docker 重启或删全部编排。
|
|
14
|
+
- 数据保留与运行资源分开处理:移除临时容器前核对实际持久卷;没有数据卷时,先归档唯一数据或只停止保留容器。Redis 等关闭持久化时,保留空卷不能代替保存内存状态,应先通过现有安全连接显式保存并校验落盘。资源紧张时避免默认使用 fork 式后台保存;保存失败或超时不得无限重试或删除唯一数据,应先停止写入消费者、保留容器与卷、列明失败,并采用有界恢复方案或请求数据处置决定。默认不删除数据卷、绑定目录、镜像及测试证据,不使用 `rm -v` 或 `down -v`;删除这些数据需要对应的明确授权。
|
|
15
|
+
- 资源不足时先盘点并收回本任务闲置/遗留的进程和临时容器,再重新测量,不能留下自建数据库占用后要求用户关闭应用。测试结果、清理结果、数据保留和渠道发布分别记录;清理失败必须列出精确资源及原因,不能声称“已全部退出”或用减少用例、关闭断言、放宽发布门禁代替恢复环境。
|
|
16
|
+
|
|
17
|
+
<!-- /microi-progressive:chunk -->
|
|
5
18
|
<!-- microi-progressive:chunk id=workspace-conventions-024 sha256=92c453b5fafa36d68098b3da5817d0de260a3cf454d8cae63f71a3f65e57bbdc -->
|
|
6
19
|
## Microi.net.Api 本地启动约定
|
|
7
20
|
|