@qomicex/cli 0.1.1 → 0.1.2
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/dist/commands/bump.js +30 -0
- package/dist/index.js +13 -2
- package/dist/lib/permissions.js +1 -0
- package/dist/lib/signature.js +1 -1
- package/dist/lib/store.js +1 -1
- package/package.json +1 -1
- package/skills/qomicex-plugin/SKILL.md +65 -65
- package/skills/qomicex-plugin/debugging.md +55 -55
- package/skills/qomicex-plugin/manifest-schema.md +130 -130
- package/skills/qomicex-plugin/permissions.md +98 -98
- package/skills/qomicex-plugin/plugin-api.md +112 -112
- package/skills/qomicex-plugin/rules.md +45 -45
- package/skills/qomicex-plugin/signing.md +80 -80
- package/skills/qomicex-plugin/theme.md +58 -58
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// qomicex bump — 递增 manifest.json 版本号(major/minor/patch)。
|
|
2
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { readManifestFile } from "../lib/project.js";
|
|
5
|
+
import { fail, info } from "../lib/io.js";
|
|
6
|
+
const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/;
|
|
7
|
+
export async function bumpCommand(opts) {
|
|
8
|
+
const manifestFile = join(process.cwd(), 'manifest.json');
|
|
9
|
+
if (!existsSync(manifestFile))
|
|
10
|
+
fail('当前目录不是插件项目(缺少 manifest.json)');
|
|
11
|
+
const manifest = readManifestFile(manifestFile);
|
|
12
|
+
const current = String(manifest.version ?? '');
|
|
13
|
+
const next = opts.version ?? (() => {
|
|
14
|
+
const m = SEMVER_RE.exec(current);
|
|
15
|
+
if (!m)
|
|
16
|
+
fail(`当前版本不是合法 semver: ${current}`);
|
|
17
|
+
const [major, minor, patch] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
18
|
+
if (opts.part === 'major')
|
|
19
|
+
return `${major + 1}.0.0`;
|
|
20
|
+
if (opts.part === 'patch')
|
|
21
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
22
|
+
return `${major}.${minor + 1}.0`;
|
|
23
|
+
})();
|
|
24
|
+
if (!SEMVER_RE.test(next))
|
|
25
|
+
fail(`目标版本不是合法 semver: ${next}`);
|
|
26
|
+
manifest.version = next;
|
|
27
|
+
writeFileSync(manifestFile, JSON.stringify(manifest, null, 2) + '\n');
|
|
28
|
+
info(`✔ manifest.json version: ${current} → ${next}`);
|
|
29
|
+
return next;
|
|
30
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// qomicex CLI 入口:create / dev / pack / verify / publish
|
|
2
|
+
// qomicex CLI 入口:create / dev / pack / verify / bump / publish
|
|
3
3
|
import { createCommand } from "./commands/create.js";
|
|
4
4
|
import { devCommand } from "./commands/dev.js";
|
|
5
5
|
import { packCommand } from "./commands/pack.js";
|
|
6
6
|
import { verifyCommand } from "./commands/verify.js";
|
|
7
7
|
import { publishCommand } from "./commands/publish.js";
|
|
8
|
+
import { bumpCommand } from "./commands/bump.js";
|
|
8
9
|
import { fail } from "./lib/io.js";
|
|
9
|
-
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
// 版本号单一来源:package.json(bump 只改一处)
|
|
12
|
+
const VERSION = createRequire(import.meta.url)('../package.json').version;
|
|
10
13
|
function parseArgs(argv) {
|
|
11
14
|
const positional = [];
|
|
12
15
|
const options = {};
|
|
@@ -49,6 +52,8 @@ qomicex v${VERSION} — Qomicex 插件生态 CLI
|
|
|
49
52
|
构建并打 .qplugin(manifest.json 在 zip 根)
|
|
50
53
|
qomicex verify [--package <f>]
|
|
51
54
|
manifest 合法性 + 权限最小化 + 长循环告警 + 签名检查
|
|
55
|
+
qomicex bump <major|minor|patch> [--version <v>]
|
|
56
|
+
递增 manifest.json 版本号(或 --version 直接指定)
|
|
52
57
|
qomicex publish [--key <k>] [--slug <s>] [--changelog <c>] [--api <url>] [--org-id <id>] [--package <f>] [--yes]
|
|
53
58
|
设备流登录 → 注册签名公钥 → 签名 → 上传到商店
|
|
54
59
|
qomicex --help | -h 显示帮助
|
|
@@ -89,6 +94,12 @@ async function main() {
|
|
|
89
94
|
case 'verify':
|
|
90
95
|
await verifyCommand({ package: typeof options['package'] === 'string' ? options['package'] : undefined });
|
|
91
96
|
break;
|
|
97
|
+
case 'bump':
|
|
98
|
+
await bumpCommand({
|
|
99
|
+
part: positional[0] ?? 'minor',
|
|
100
|
+
version: typeof options['version'] === 'string' ? options['version'] : undefined,
|
|
101
|
+
});
|
|
102
|
+
break;
|
|
92
103
|
case 'publish':
|
|
93
104
|
await publishCommand({
|
|
94
105
|
key: typeof options['key'] === 'string' ? options['key'] : undefined,
|
package/dist/lib/permissions.js
CHANGED
|
@@ -44,6 +44,7 @@ export const METHOD_PERMISSIONS = {
|
|
|
44
44
|
registerMethod: 'config:write', callPlugin: 'network:fetch', callWasm: 'wasm:execute', listWasmPlugins: 'wasm:execute',
|
|
45
45
|
readText: 'filesystem:read', readBytes: 'filesystem:read', writeText: 'filesystem:write', writeBytes: 'filesystem:write', deleteFile: 'filesystem:write', execCommand: 'shell:execute',
|
|
46
46
|
navigate: 'config:read', showToast: 'ui:toast', getSystemInfo: 'system:info', openUrl: 'system:notification', listPlugins: 'plugin:list',
|
|
47
|
+
getThemeColor: 'config:read', applyThemeOverride: 'config:write', clearThemeOverride: 'config:write',
|
|
47
48
|
'overlay.create': 'ui:sub_window', 'overlay.show': 'ui:sub_window', 'overlay.hide': 'ui:sub_window',
|
|
48
49
|
'overlay.destroy': 'ui:sub_window', 'overlay.setHtml': 'ui:sub_window', 'overlay.setPosition': 'ui:sub_window',
|
|
49
50
|
'download.addTask': 'download:manage', 'download.progress': 'download:manage', 'download.cancel': 'download:manage', 'download.list': 'download:manage', 'download.registerInstall': 'instance:write',
|
package/dist/lib/signature.js
CHANGED
|
@@ -8,7 +8,7 @@ export const SIGNATURE_FILE = 'signature.json';
|
|
|
8
8
|
export const CERT_FILE = 'signature.cert.json';
|
|
9
9
|
export const ALG = 'Ed25519';
|
|
10
10
|
/** 商店签名根公钥(raw base64),与 launcher plugin_signature.rs ROOT_PUBLIC_KEY_B64 一致 */
|
|
11
|
-
export const STORE_ROOT_PUBLIC_KEY_B64 = '
|
|
11
|
+
export const STORE_ROOT_PUBLIC_KEY_B64 = 'sPKcrc6QR5gcOnQMdq21Jo3yqxN7Mbm61OYxZnKuHE0=';
|
|
12
12
|
/** Ed25519 PKCS#8 DER 固定前缀(前 16 字节),后接 32 字节 seed */
|
|
13
13
|
const ED25519_PKCS8_PREFIX = Uint8Array.from([0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20]);
|
|
14
14
|
export function bytesToBase64(bytes) {
|
package/dist/lib/store.js
CHANGED
|
@@ -65,7 +65,7 @@ export async function fetchMinePlugins(base, token) {
|
|
|
65
65
|
}
|
|
66
66
|
/** 新建插件(store 返回 201 {plugin:{id}}),slug 占用抛 409。 */
|
|
67
67
|
export async function createPlugin(base, token, input) {
|
|
68
|
-
const r = await request(base, '/plugins
|
|
68
|
+
const r = await request(base, '/plugins', {
|
|
69
69
|
method: 'POST',
|
|
70
70
|
body: JSON.stringify({
|
|
71
71
|
slug: input.slug,
|
package/package.json
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
# Qomicex 插件开发技能包(qomicex-plugin)
|
|
2
|
-
|
|
3
|
-
面向 AI agent(Claude / opencode / Cursor 等)的 Qomicex 启动器插件(`.qplugin`)开发技能包,随 `@qomicex/cli` 分发。本技能把"生成合规插件"所需的全部事实(manifest 校验、权限目录、桥 API、主题 token、签名、调试)收敛到一个目录,避免 AI 臆造字段。
|
|
4
|
-
|
|
5
|
-
## 何时使用
|
|
6
|
-
|
|
7
|
-
用户要求**开发 / 修改 / 审查 / 打包 / 发布** Qomicex 启动器插件时加载本技能。判断依据:涉及 `manifest.json`、`__PLUGIN_API__`、`entry.frontend`、`contributes`、权限声明、`.qplugin` 打包等关键词。
|
|
8
|
-
|
|
9
|
-
## 文件导航(建议全读,勿跳)
|
|
10
|
-
|
|
11
|
-
| 文件 | 内容 |
|
|
12
|
-
|------|------|
|
|
13
|
-
| `manifest-schema.md` | manifest.json 全字段 + layers 语义 + render 默认 iframe + dependencies + contributes |
|
|
14
|
-
| `permissions.md` | 权限目录(normal/warning/danger)+ 每个权限对应的桥 API 方法 |
|
|
15
|
-
| `plugin-api.md` | 桥 API(`__PLUGIN_API__`)签名速查 |
|
|
16
|
-
| `theme.md` | 主题语义 token 三级体系 + `var()` 消费约定 + 主题贡献 |
|
|
17
|
-
| `signing.md` | Ed25519 签名流程(生成密钥 → pack --key → publish) |
|
|
18
|
-
| `debugging.md` | harness 热重载调试 |
|
|
19
|
-
| `rules.md` | AI 生成插件的硬性规则(必须逐条遵守) |
|
|
20
|
-
|
|
21
|
-
## 完整流程(create → dev → verify → pack → publish)
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# 1. 生成项目(id 需反向域名格式,如 com.example.demo)
|
|
25
|
-
qomicex create com.example.demo
|
|
26
|
-
|
|
27
|
-
# 2. 本地调试(仓库内自动进 harness,固定端口 1420;仓库外裸 Vite 默认 5173)
|
|
28
|
-
cd com.example.demo && pnpm install && qomicex dev
|
|
29
|
-
|
|
30
|
-
# 3. 校验(提交/打包前必跑,0 error 才算通过)
|
|
31
|
-
qomicex verify
|
|
32
|
-
|
|
33
|
-
# 4. 打包(tsc && vite build → release/<id>-<version>.qplugin)
|
|
34
|
-
qomicex pack --version 0.2.0
|
|
35
|
-
qomicex pack --key ./dev-key.pem # 附签名(仅 signature.json;完整证书链走 publish)
|
|
36
|
-
|
|
37
|
-
# 5. 发布(设备流登录 → 商店签发证书 → 签名 → 上传)
|
|
38
|
-
export QOMICEX_SIGN_KEY=<私钥 base64/PEM>
|
|
39
|
-
qomicex publish
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## 常见错误与规避
|
|
43
|
-
|
|
44
|
-
| 错误 | 规避 |
|
|
45
|
-
|------|------|
|
|
46
|
-
| `id` 不含点 / 大写 / 连续双点 | id 用 `^[a-z0-9]+([.-][a-z0-9]+)*$`,3-128 字符,含至少一个点 |
|
|
47
|
-
| `version` 非 semver | 严格 `数字.数字.数字`(可带 `-预发布` / `+构建号`) |
|
|
48
|
-
| `entry.frontend` 指向非 `.html` | `verify` 警告:frontend 应指向 `.html`(如 `dist/index.html`) |
|
|
49
|
-
| 声明了 frontend 但 `layers` 无 `l2`/`l3` | 无法渲染 UI;UI 插件务必声明 `l2`(iframe 沙箱,默认推荐) |
|
|
50
|
-
| 权限声明与源码调用不一致 | `verify` 会做权限最小化扫描,声明未用 / 用了未声明**都会报错** |
|
|
51
|
-
| 相对 import 不带扩展名 | Vite 强制:`import { x } from './api.ts'`,漏 `.ts/.tsx` 直接构建失败 |
|
|
52
|
-
| 插件里 `fetch()` 外部 URL 被 CORS / SSRF 拦 | 用 `proxyFetch` / `proxyFetchStream`(带 SSRF 防护) |
|
|
53
|
-
| 沙箱内 `window.open` 被拦 | 用 `callBackend('/system/open-url', { url })` 或 `openUrl` |
|
|
54
|
-
| `vite.config.ts` 没设 `base: './'` | 产物 `/assets/...` 会被解析成站点根,插件白屏 |
|
|
55
|
-
| 长循环 / 无界 `setInterval` | `verify` 会告警;放 Worker / WASM / 后端 |
|
|
56
|
-
| 硬编码 API key 进插件源码 | 无密钥存储;让用户经 `setSettings` 配置,运行时 `getSettings` 读取 |
|
|
57
|
-
|
|
58
|
-
## 事实一致红线
|
|
59
|
-
|
|
60
|
-
- 本技能内容与代码事实对齐(`src/plugins/types.ts`、CLI `src/lib/*`、公开文档 `docs/plugins/plugin-api.md`)。字段含义有疑问时**以代码为准**,不确定就标注,不要虚构 API 或字段。
|
|
61
|
-
- `packages/plugin-ui`(`@qomicex/plugin-ui`)的组件库使用参见 `tailwind.config.js` 的 `@qomicex/plugin-ui/tailwind-preset`,主题类名(`bg-primary`、`text-muted-foreground` 等)直接消费语义 token。
|
|
62
|
-
|
|
63
|
-
## 起步提示词(给 AI 用)
|
|
64
|
-
|
|
65
|
-
> 你是 Qomicex 插件开发工程师。先读 `skills/qomicex-plugin/` 下的 SKILL.md 与各分册(尤其 rules.md),再按流程工作:`qomicex create <id>` 生成项目 → 实现功能 → `qomicex verify` 过 0 error → `qomicex pack` 出包。manifest 字段、权限、API 签名一律以技能包内文档为准,不臆造。
|
|
1
|
+
# Qomicex 插件开发技能包(qomicex-plugin)
|
|
2
|
+
|
|
3
|
+
面向 AI agent(Claude / opencode / Cursor 等)的 Qomicex 启动器插件(`.qplugin`)开发技能包,随 `@qomicex/cli` 分发。本技能把"生成合规插件"所需的全部事实(manifest 校验、权限目录、桥 API、主题 token、签名、调试)收敛到一个目录,避免 AI 臆造字段。
|
|
4
|
+
|
|
5
|
+
## 何时使用
|
|
6
|
+
|
|
7
|
+
用户要求**开发 / 修改 / 审查 / 打包 / 发布** Qomicex 启动器插件时加载本技能。判断依据:涉及 `manifest.json`、`__PLUGIN_API__`、`entry.frontend`、`contributes`、权限声明、`.qplugin` 打包等关键词。
|
|
8
|
+
|
|
9
|
+
## 文件导航(建议全读,勿跳)
|
|
10
|
+
|
|
11
|
+
| 文件 | 内容 |
|
|
12
|
+
|------|------|
|
|
13
|
+
| `manifest-schema.md` | manifest.json 全字段 + layers 语义 + render 默认 iframe + dependencies + contributes |
|
|
14
|
+
| `permissions.md` | 权限目录(normal/warning/danger)+ 每个权限对应的桥 API 方法 |
|
|
15
|
+
| `plugin-api.md` | 桥 API(`__PLUGIN_API__`)签名速查 |
|
|
16
|
+
| `theme.md` | 主题语义 token 三级体系 + `var()` 消费约定 + 主题贡献 |
|
|
17
|
+
| `signing.md` | Ed25519 签名流程(生成密钥 → pack --key → publish) |
|
|
18
|
+
| `debugging.md` | harness 热重载调试 |
|
|
19
|
+
| `rules.md` | AI 生成插件的硬性规则(必须逐条遵守) |
|
|
20
|
+
|
|
21
|
+
## 完整流程(create → dev → verify → pack → publish)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 1. 生成项目(id 需反向域名格式,如 com.example.demo)
|
|
25
|
+
qomicex create com.example.demo
|
|
26
|
+
|
|
27
|
+
# 2. 本地调试(仓库内自动进 harness,固定端口 1420;仓库外裸 Vite 默认 5173)
|
|
28
|
+
cd com.example.demo && pnpm install && qomicex dev
|
|
29
|
+
|
|
30
|
+
# 3. 校验(提交/打包前必跑,0 error 才算通过)
|
|
31
|
+
qomicex verify
|
|
32
|
+
|
|
33
|
+
# 4. 打包(tsc && vite build → release/<id>-<version>.qplugin)
|
|
34
|
+
qomicex pack --version 0.2.0
|
|
35
|
+
qomicex pack --key ./dev-key.pem # 附签名(仅 signature.json;完整证书链走 publish)
|
|
36
|
+
|
|
37
|
+
# 5. 发布(设备流登录 → 商店签发证书 → 签名 → 上传)
|
|
38
|
+
export QOMICEX_SIGN_KEY=<私钥 base64/PEM>
|
|
39
|
+
qomicex publish
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 常见错误与规避
|
|
43
|
+
|
|
44
|
+
| 错误 | 规避 |
|
|
45
|
+
|------|------|
|
|
46
|
+
| `id` 不含点 / 大写 / 连续双点 | id 用 `^[a-z0-9]+([.-][a-z0-9]+)*$`,3-128 字符,含至少一个点 |
|
|
47
|
+
| `version` 非 semver | 严格 `数字.数字.数字`(可带 `-预发布` / `+构建号`) |
|
|
48
|
+
| `entry.frontend` 指向非 `.html` | `verify` 警告:frontend 应指向 `.html`(如 `dist/index.html`) |
|
|
49
|
+
| 声明了 frontend 但 `layers` 无 `l2`/`l3` | 无法渲染 UI;UI 插件务必声明 `l2`(iframe 沙箱,默认推荐) |
|
|
50
|
+
| 权限声明与源码调用不一致 | `verify` 会做权限最小化扫描,声明未用 / 用了未声明**都会报错** |
|
|
51
|
+
| 相对 import 不带扩展名 | Vite 强制:`import { x } from './api.ts'`,漏 `.ts/.tsx` 直接构建失败 |
|
|
52
|
+
| 插件里 `fetch()` 外部 URL 被 CORS / SSRF 拦 | 用 `proxyFetch` / `proxyFetchStream`(带 SSRF 防护) |
|
|
53
|
+
| 沙箱内 `window.open` 被拦 | 用 `callBackend('/system/open-url', { url })` 或 `openUrl` |
|
|
54
|
+
| `vite.config.ts` 没设 `base: './'` | 产物 `/assets/...` 会被解析成站点根,插件白屏 |
|
|
55
|
+
| 长循环 / 无界 `setInterval` | `verify` 会告警;放 Worker / WASM / 后端 |
|
|
56
|
+
| 硬编码 API key 进插件源码 | 无密钥存储;让用户经 `setSettings` 配置,运行时 `getSettings` 读取 |
|
|
57
|
+
|
|
58
|
+
## 事实一致红线
|
|
59
|
+
|
|
60
|
+
- 本技能内容与代码事实对齐(`src/plugins/types.ts`、CLI `src/lib/*`、公开文档 `docs/plugins/plugin-api.md`)。字段含义有疑问时**以代码为准**,不确定就标注,不要虚构 API 或字段。
|
|
61
|
+
- `packages/plugin-ui`(`@qomicex/plugin-ui`)的组件库使用参见 `tailwind.config.js` 的 `@qomicex/plugin-ui/tailwind-preset`,主题类名(`bg-primary`、`text-muted-foreground` 等)直接消费语义 token。
|
|
62
|
+
|
|
63
|
+
## 起步提示词(给 AI 用)
|
|
64
|
+
|
|
65
|
+
> 你是 Qomicex 插件开发工程师。先读 `skills/qomicex-plugin/` 下的 SKILL.md 与各分册(尤其 rules.md),再按流程工作:`qomicex create <id>` 生成项目 → 实现功能 → `qomicex verify` 过 0 error → `qomicex pack` 出包。manifest 字段、权限、API 签名一律以技能包内文档为准,不臆造。
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
# Harness 热重载调试
|
|
2
|
-
|
|
3
|
-
调试实现源:`packages/qomicex-cli/src/commands/dev.ts` + `scripts/harness/run.mjs`。
|
|
4
|
-
|
|
5
|
-
## qomicex dev 的两条路径
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
qomicex dev # 默认
|
|
9
|
-
qomicex dev --port 3000
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
`dev` 命令从当前目录向上查找 `scripts/harness/run.mjs`:
|
|
13
|
-
|
|
14
|
-
| 场景 | 行为 |
|
|
15
|
-
|------|------|
|
|
16
|
-
| **仓库内(harness 模式)** | 检测到 harness → spawn `scripts/harness/run.mjs`,进入完整调试环境。**插件必须位于 `plugins-dev/{id}`**(harness 从该目录定位)。`--port` 在此模式不生效(固定 1420) |
|
|
17
|
-
| **仓库外(裸 Vite)** | 回退为 Vite dev server(默认 5173)+ 在项目根写 `.qomicex-dev.json`(dev 源插件配置,供手动注册 dev 源) |
|
|
18
|
-
|
|
19
|
-
## harness 模式做什么
|
|
20
|
-
|
|
21
|
-
不启动 Tauri、不启动 Rust 后端,纯浏览器调试:
|
|
22
|
-
|
|
23
|
-
1. 起 stub mock server(`scripts/harness/stub.mjs`,固定 `:5100`)。
|
|
24
|
-
2. 复用已有 Vite dev(`:1420`);未运行则自动 spawn `pnpm run dev`。
|
|
25
|
-
3. `addInitScript` 注入 **Tauri API mock**(`window.__TAURI_INTERNALS__` 等,跨导航保留)——否则前端在纯浏览器里无法挂载。
|
|
26
|
-
4. 打开插件页 `http://127.0.0.1:1420/plugins/p/{pluginId}`。
|
|
27
|
-
5. `fs.watch` 监听插件 `src/`(含 `index.html`/`theme.css`/`overlay.html`/`vite.config.ts`)→ 变更后重建(`pnpm run build`)→ 整页 reload(iframe 重新挂载)。
|
|
28
|
-
|
|
29
|
-
**前置要求**:Playwright + Chromium:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pnpm add -D playwright
|
|
33
|
-
pnpm exec playwright install chromium
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 手动运行 harness
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
node scripts/harness/run.mjs <pluginId> [--headed] [--mock file.json] [--build-cmd "pnpm run build"]
|
|
40
|
-
# 或仓库根:pnpm run harness -- hello-plugin
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
| 参数 | 说明 |
|
|
44
|
-
|------|------|
|
|
45
|
-
| `--headed` | 有头模式(默认 headless,看不到窗口;调试用 --headed) |
|
|
46
|
-
| `--mock file.json` | 自定义 stub 返回(mock 数据) |
|
|
47
|
-
| `--build-cmd "..."` | 覆盖重建命令(默认 `pnpm run build`) |
|
|
48
|
-
|
|
49
|
-
## 注意事项
|
|
50
|
-
|
|
51
|
-
- **网络请求转发**:前端直连 `:5000` 的 `/api/**` 请求被 route 到 stub `:5100`。stub 返回什么,插件就拿到什么——需要真实后端数据时请先跑 Rust 后端或改 mock。
|
|
52
|
-
- **热重载陷阱**:浏览器(Chromium) ≠ WebView2,复合/backdrop-filter 等行为可能有差异,结论需在真实 Tauri 复核。
|
|
53
|
-
- **停止**:`Ctrl+C`,会一并清理 stub / Vite。
|
|
54
|
-
- **浏览器直开降级**:独立 `pnpm dev`(不经 harness)时 `window.__PLUGIN_API__` 为 `null`,`getApi()` 返回 null,UI 需优雅降级(模板已处理)。
|
|
55
|
-
- 只读操作可放心跑;避免在调试页触发写数据 / 启动实例类操作。
|
|
1
|
+
# Harness 热重载调试
|
|
2
|
+
|
|
3
|
+
调试实现源:`packages/qomicex-cli/src/commands/dev.ts` + `scripts/harness/run.mjs`。
|
|
4
|
+
|
|
5
|
+
## qomicex dev 的两条路径
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
qomicex dev # 默认
|
|
9
|
+
qomicex dev --port 3000
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`dev` 命令从当前目录向上查找 `scripts/harness/run.mjs`:
|
|
13
|
+
|
|
14
|
+
| 场景 | 行为 |
|
|
15
|
+
|------|------|
|
|
16
|
+
| **仓库内(harness 模式)** | 检测到 harness → spawn `scripts/harness/run.mjs`,进入完整调试环境。**插件必须位于 `plugins-dev/{id}`**(harness 从该目录定位)。`--port` 在此模式不生效(固定 1420) |
|
|
17
|
+
| **仓库外(裸 Vite)** | 回退为 Vite dev server(默认 5173)+ 在项目根写 `.qomicex-dev.json`(dev 源插件配置,供手动注册 dev 源) |
|
|
18
|
+
|
|
19
|
+
## harness 模式做什么
|
|
20
|
+
|
|
21
|
+
不启动 Tauri、不启动 Rust 后端,纯浏览器调试:
|
|
22
|
+
|
|
23
|
+
1. 起 stub mock server(`scripts/harness/stub.mjs`,固定 `:5100`)。
|
|
24
|
+
2. 复用已有 Vite dev(`:1420`);未运行则自动 spawn `pnpm run dev`。
|
|
25
|
+
3. `addInitScript` 注入 **Tauri API mock**(`window.__TAURI_INTERNALS__` 等,跨导航保留)——否则前端在纯浏览器里无法挂载。
|
|
26
|
+
4. 打开插件页 `http://127.0.0.1:1420/plugins/p/{pluginId}`。
|
|
27
|
+
5. `fs.watch` 监听插件 `src/`(含 `index.html`/`theme.css`/`overlay.html`/`vite.config.ts`)→ 变更后重建(`pnpm run build`)→ 整页 reload(iframe 重新挂载)。
|
|
28
|
+
|
|
29
|
+
**前置要求**:Playwright + Chromium:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add -D playwright
|
|
33
|
+
pnpm exec playwright install chromium
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 手动运行 harness
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
node scripts/harness/run.mjs <pluginId> [--headed] [--mock file.json] [--build-cmd "pnpm run build"]
|
|
40
|
+
# 或仓库根:pnpm run harness -- hello-plugin
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| 参数 | 说明 |
|
|
44
|
+
|------|------|
|
|
45
|
+
| `--headed` | 有头模式(默认 headless,看不到窗口;调试用 --headed) |
|
|
46
|
+
| `--mock file.json` | 自定义 stub 返回(mock 数据) |
|
|
47
|
+
| `--build-cmd "..."` | 覆盖重建命令(默认 `pnpm run build`) |
|
|
48
|
+
|
|
49
|
+
## 注意事项
|
|
50
|
+
|
|
51
|
+
- **网络请求转发**:前端直连 `:5000` 的 `/api/**` 请求被 route 到 stub `:5100`。stub 返回什么,插件就拿到什么——需要真实后端数据时请先跑 Rust 后端或改 mock。
|
|
52
|
+
- **热重载陷阱**:浏览器(Chromium) ≠ WebView2,复合/backdrop-filter 等行为可能有差异,结论需在真实 Tauri 复核。
|
|
53
|
+
- **停止**:`Ctrl+C`,会一并清理 stub / Vite。
|
|
54
|
+
- **浏览器直开降级**:独立 `pnpm dev`(不经 harness)时 `window.__PLUGIN_API__` 为 `null`,`getApi()` 返回 null,UI 需优雅降级(模板已处理)。
|
|
55
|
+
- 只读操作可放心跑;避免在调试页触发写数据 / 启动实例类操作。
|
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
# manifest.json 字段全解
|
|
2
|
-
|
|
3
|
-
`manifest.json` 位于 `.qplugin` 包**根目录**,是插件唯一身份文件。本册字段以 `src/plugins/types.ts` 的 `PluginManifest` 与 CLI 校验器(`packages/qomicex-cli/src/lib/manifest.ts`)为准,与启动器 store 上传校验一致。
|
|
4
|
-
|
|
5
|
-
## 完整示例
|
|
6
|
-
|
|
7
|
-
```json
|
|
8
|
-
{
|
|
9
|
-
"id": "com.example.demo",
|
|
10
|
-
"name": "示例插件",
|
|
11
|
-
"version": "0.1.0",
|
|
12
|
-
"minLauncherVersion": "0.1.0",
|
|
13
|
-
"layers": ["l2"],
|
|
14
|
-
"permissions": ["config:read", "ui:toast", "network:cors_proxy"],
|
|
15
|
-
"dependencies": [
|
|
16
|
-
{ "id": "top.qomicex.markdown", "version": ">=1.0.0", "optional": false }
|
|
17
|
-
],
|
|
18
|
-
"entry": {
|
|
19
|
-
"frontend": "dist/index.html",
|
|
20
|
-
"theme": "dist/theme.css"
|
|
21
|
-
},
|
|
22
|
-
"render": "iframe",
|
|
23
|
-
"contributes": {
|
|
24
|
-
"menuItems": [
|
|
25
|
-
{ "path": "/plugins/p/com.example.demo", "label": "示例插件", "icon": "🧩", "action": "page" }
|
|
26
|
-
],
|
|
27
|
-
"overlay": {
|
|
28
|
-
"file": "dist/overlay.html",
|
|
29
|
-
"title": "示例悬浮窗",
|
|
30
|
-
"width": 380,
|
|
31
|
-
"height": 500,
|
|
32
|
-
"minimizable": true,
|
|
33
|
-
"resizable": true
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"icon": "fa-solid fa-puzzle-piece"
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## 字段总表
|
|
41
|
-
|
|
42
|
-
| 字段 | 类型 | 必填 | 说明 |
|
|
43
|
-
|------|------|------|------|
|
|
44
|
-
| `id` | string | ✅ | 插件唯一 ID。格式 `^[a-z0-9]+([.-][a-z0-9]+)*$`,3-128 字符,**必须含至少一个点**(反向域名,如 `com.example.demo`)。含点但非法字符 → error;不含点 → warning。一经发布不要更改(用作安装目录 `plugins/{id}/`) |
|
|
45
|
-
| `name` | string | ✅ | 显示名,非空 |
|
|
46
|
-
| `version` | string | ✅ | 严格 semver:`^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$`,如 `1.2.0`、`0.1.0-beta.1` |
|
|
47
|
-
| `minLauncherVersion` | string | ✅(CLI 校验) | 最低启动器版本。CLI 校验必填;运行时行为以启动器为准 |
|
|
48
|
-
| `layers` | string[] | ✅ | 图层声明,至少一项,值 ∈ `l0`/`l1`/`l2`/`l3`。声明 frontend 但无 `l2`/`l3` → 无法渲染 UI(warning) |
|
|
49
|
-
| `permissions` | string[] | ✅ | 权限声明,值必须是权限目录中的 id(见 `permissions.md`)。未知权限 → warning |
|
|
50
|
-
| `dependencies` | PluginDependency[] | 可选 | 前置插件依赖,见下 |
|
|
51
|
-
| `entry` | object | ✅ | 入口声明,`frontend`/`backend`/`theme` **至少一个** |
|
|
52
|
-
| `render` | 'inline' \| 'iframe' | 可选 | **默认 `iframe`**(沙箱)。仅显式 `"inline"` 走内联渲染(与主界面同 window) |
|
|
53
|
-
| `contributes` | object | 可选 | 扩展点 |
|
|
54
|
-
| `icon` | string | 可选 | 顶层图标(插件管理/列表显示;库插件建议用顶层 icon 而非 menuItems) |
|
|
55
|
-
|
|
56
|
-
## entry 对象
|
|
57
|
-
|
|
58
|
-
| 字段 | 类型 | 说明 |
|
|
59
|
-
|------|------|------|
|
|
60
|
-
| `frontend` | string | 插件页面入口,`.qplugin` 内相对路径,应指向 `.html`(如 `dist/index.html`)。声明了 frontend 的插件才会被激活并渲染到 `/plugins/p/:id` |
|
|
61
|
-
| `theme` | string | 主题 CSS 路径(如 `dist/theme.css`),激活时注入。引用 `dist/` 下文件但源码在根目录时,`qomicex pack` 会自动拷入 dist |
|
|
62
|
-
| `backend` | string | 保留字段,当前未使用(以代码为准) |
|
|
63
|
-
|
|
64
|
-
## layers 图层语义
|
|
65
|
-
|
|
66
|
-
| 层级 | 技术 | 说明 |
|
|
67
|
-
|------|------|------|
|
|
68
|
-
| `l0` | 静态声明 | 主题 / 声明式内容,纯声明无执行能力 |
|
|
69
|
-
| `l1` | 声明式 | 新增下载源 / 镜像 / 端点等声明(当前为预留层级) |
|
|
70
|
-
| `l2` | JS 前端沙箱 | **UI 插件默认层级**。iframe 沙箱(`sandbox="allow-scripts"`,opaque origin,与主界面 DOM/CSS 隔离),经 postMessage 桥做权限检查,`__PLUGIN_API__` 全量可用,`registerMethod`/`callPlugin` 跨窗口中转 |
|
|
71
|
-
| `l3` | WASM(wasmtime) | 后端沙箱执行 `plugin.wasm`,Host API 权限门控。包内含 `plugin.wasm` + 声明 `l3` 即被加载 |
|
|
72
|
-
|
|
73
|
-
- **render 默认 iframe**:带 `entry.frontend` 的插件默认走 iframe 沙箱。内联渲染(`"render":"inline"`)与主界面同 window,仅适合需要访问主界面 DOM 的轻量插件。
|
|
74
|
-
- `layers` 可声明多个(如 `["l2","l3"]`)。
|
|
75
|
-
- 纯 `["l3"]` 且无 `entry.frontend` 的插件不会自动激活渲染 UI。
|
|
76
|
-
|
|
77
|
-
## dependencies 依赖语法
|
|
78
|
-
|
|
79
|
-
```json
|
|
80
|
-
"dependencies": [
|
|
81
|
-
{ "id": "top.qomicex.markdown", "version": ">=1.0.0", "optional": false }
|
|
82
|
-
]
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
| 字段 | 类型 | 说明 |
|
|
86
|
-
|------|------|------|
|
|
87
|
-
| `id` | string | 被依赖插件 id |
|
|
88
|
-
| `version` | string | 可选,版本约束 |
|
|
89
|
-
| `optional` | boolean | 可选,是否非必装(缺省 false) |
|
|
90
|
-
|
|
91
|
-
- 安装时检查必装前置,缺失拒绝安装(`PLUGIN_MISSING_DEPENDENCY`)。
|
|
92
|
-
- 激活时检查前置已启用,缺失则本插件禁用。
|
|
93
|
-
- 激活顺序由依赖拓扑排序保证。
|
|
94
|
-
- `version` 约束写法(以代码 / 公开文档为准):`>=1.0.0`、`<=2.0.0`、`>1.0`、`<2.0`、`=1.2.0`、裸版本 `1.2.0`、空格分隔多约束 `">=1.0 <2.0"`。不支持 `^`/`~`/`*`/`||`。
|
|
95
|
-
|
|
96
|
-
## contributes 扩展点
|
|
97
|
-
|
|
98
|
-
| 字段 | 类型 | 说明 |
|
|
99
|
-
|------|------|------|
|
|
100
|
-
| `downloadSources` | string[] | 保留(当前未使用,以代码为准) |
|
|
101
|
-
| `commands` | string[] | 保留(当前未使用,以代码为准) |
|
|
102
|
-
| `settingsPages` | string[] | 保留(当前未使用,以代码为准) |
|
|
103
|
-
| `menuItems` | PluginMenuItem[] | 侧边栏入口列表 |
|
|
104
|
-
| `overlay` | object | 悬浮窗配置 |
|
|
105
|
-
|
|
106
|
-
### menuItems 数组元素
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
{ path: string; label: string; icon?: string; action?: 'page' | 'overlay' }
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
- `path`:入口目标路由(如 `/plugins/p/:id`)。
|
|
113
|
-
- `icon`:emoji / 文本 / 绝对 URL / 包内相对路径(`dist/icon.svg`,启动器自动解析为 `http://localhost:5000/api/plugins/{id}/files/dist/icon.svg`)。
|
|
114
|
-
- `action`:`"page"`(跳转页面,默认)或 `"overlay"`(打开悬浮窗,需配合 `contributes.overlay`)。
|
|
115
|
-
|
|
116
|
-
### overlay 对象
|
|
117
|
-
|
|
118
|
-
```ts
|
|
119
|
-
{ file: string; title?: string; width?: number; height?: number; minimizable?: boolean; resizable?: boolean }
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
- `file`:悬浮窗 HTML 文件路径(必填,指向 `.html`,如 `dist/overlay.html`)。
|
|
123
|
-
- `title` / `width` / `height` / `minimizable` / `resizable`:可选,视觉/行为参数(具体默认值以启动器代码为准)。
|
|
124
|
-
|
|
125
|
-
## CLI 校验行为(`qomicex verify` 目录模式)
|
|
126
|
-
|
|
127
|
-
- manifest 合法性:id / name / version(semver) / minLauncherVersion / layers / permissions / entry / contributes。
|
|
128
|
-
- 权限最小化:对比 `permissions` 与源码实际调用的桥方法(`METHOD_PERMISSIONS` 表),**声明未用 / 用了未声明都会报错**。
|
|
129
|
-
- 长循环告警:`while(true)`、`for(;;)`、无界 `setInterval`。
|
|
130
|
-
- 校验通过标准:**0 error**(warning 可接受但建议消除)。
|
|
1
|
+
# manifest.json 字段全解
|
|
2
|
+
|
|
3
|
+
`manifest.json` 位于 `.qplugin` 包**根目录**,是插件唯一身份文件。本册字段以 `src/plugins/types.ts` 的 `PluginManifest` 与 CLI 校验器(`packages/qomicex-cli/src/lib/manifest.ts`)为准,与启动器 store 上传校验一致。
|
|
4
|
+
|
|
5
|
+
## 完整示例
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"id": "com.example.demo",
|
|
10
|
+
"name": "示例插件",
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"minLauncherVersion": "0.1.0",
|
|
13
|
+
"layers": ["l2"],
|
|
14
|
+
"permissions": ["config:read", "ui:toast", "network:cors_proxy"],
|
|
15
|
+
"dependencies": [
|
|
16
|
+
{ "id": "top.qomicex.markdown", "version": ">=1.0.0", "optional": false }
|
|
17
|
+
],
|
|
18
|
+
"entry": {
|
|
19
|
+
"frontend": "dist/index.html",
|
|
20
|
+
"theme": "dist/theme.css"
|
|
21
|
+
},
|
|
22
|
+
"render": "iframe",
|
|
23
|
+
"contributes": {
|
|
24
|
+
"menuItems": [
|
|
25
|
+
{ "path": "/plugins/p/com.example.demo", "label": "示例插件", "icon": "🧩", "action": "page" }
|
|
26
|
+
],
|
|
27
|
+
"overlay": {
|
|
28
|
+
"file": "dist/overlay.html",
|
|
29
|
+
"title": "示例悬浮窗",
|
|
30
|
+
"width": 380,
|
|
31
|
+
"height": 500,
|
|
32
|
+
"minimizable": true,
|
|
33
|
+
"resizable": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"icon": "fa-solid fa-puzzle-piece"
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 字段总表
|
|
41
|
+
|
|
42
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
43
|
+
|------|------|------|------|
|
|
44
|
+
| `id` | string | ✅ | 插件唯一 ID。格式 `^[a-z0-9]+([.-][a-z0-9]+)*$`,3-128 字符,**必须含至少一个点**(反向域名,如 `com.example.demo`)。含点但非法字符 → error;不含点 → warning。一经发布不要更改(用作安装目录 `plugins/{id}/`) |
|
|
45
|
+
| `name` | string | ✅ | 显示名,非空 |
|
|
46
|
+
| `version` | string | ✅ | 严格 semver:`^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$`,如 `1.2.0`、`0.1.0-beta.1` |
|
|
47
|
+
| `minLauncherVersion` | string | ✅(CLI 校验) | 最低启动器版本。CLI 校验必填;运行时行为以启动器为准 |
|
|
48
|
+
| `layers` | string[] | ✅ | 图层声明,至少一项,值 ∈ `l0`/`l1`/`l2`/`l3`。声明 frontend 但无 `l2`/`l3` → 无法渲染 UI(warning) |
|
|
49
|
+
| `permissions` | string[] | ✅ | 权限声明,值必须是权限目录中的 id(见 `permissions.md`)。未知权限 → warning |
|
|
50
|
+
| `dependencies` | PluginDependency[] | 可选 | 前置插件依赖,见下 |
|
|
51
|
+
| `entry` | object | ✅ | 入口声明,`frontend`/`backend`/`theme` **至少一个** |
|
|
52
|
+
| `render` | 'inline' \| 'iframe' | 可选 | **默认 `iframe`**(沙箱)。仅显式 `"inline"` 走内联渲染(与主界面同 window) |
|
|
53
|
+
| `contributes` | object | 可选 | 扩展点 |
|
|
54
|
+
| `icon` | string | 可选 | 顶层图标(插件管理/列表显示;库插件建议用顶层 icon 而非 menuItems) |
|
|
55
|
+
|
|
56
|
+
## entry 对象
|
|
57
|
+
|
|
58
|
+
| 字段 | 类型 | 说明 |
|
|
59
|
+
|------|------|------|
|
|
60
|
+
| `frontend` | string | 插件页面入口,`.qplugin` 内相对路径,应指向 `.html`(如 `dist/index.html`)。声明了 frontend 的插件才会被激活并渲染到 `/plugins/p/:id` |
|
|
61
|
+
| `theme` | string | 主题 CSS 路径(如 `dist/theme.css`),激活时注入。引用 `dist/` 下文件但源码在根目录时,`qomicex pack` 会自动拷入 dist |
|
|
62
|
+
| `backend` | string | 保留字段,当前未使用(以代码为准) |
|
|
63
|
+
|
|
64
|
+
## layers 图层语义
|
|
65
|
+
|
|
66
|
+
| 层级 | 技术 | 说明 |
|
|
67
|
+
|------|------|------|
|
|
68
|
+
| `l0` | 静态声明 | 主题 / 声明式内容,纯声明无执行能力 |
|
|
69
|
+
| `l1` | 声明式 | 新增下载源 / 镜像 / 端点等声明(当前为预留层级) |
|
|
70
|
+
| `l2` | JS 前端沙箱 | **UI 插件默认层级**。iframe 沙箱(`sandbox="allow-scripts"`,opaque origin,与主界面 DOM/CSS 隔离),经 postMessage 桥做权限检查,`__PLUGIN_API__` 全量可用,`registerMethod`/`callPlugin` 跨窗口中转 |
|
|
71
|
+
| `l3` | WASM(wasmtime) | 后端沙箱执行 `plugin.wasm`,Host API 权限门控。包内含 `plugin.wasm` + 声明 `l3` 即被加载 |
|
|
72
|
+
|
|
73
|
+
- **render 默认 iframe**:带 `entry.frontend` 的插件默认走 iframe 沙箱。内联渲染(`"render":"inline"`)与主界面同 window,仅适合需要访问主界面 DOM 的轻量插件。
|
|
74
|
+
- `layers` 可声明多个(如 `["l2","l3"]`)。
|
|
75
|
+
- 纯 `["l3"]` 且无 `entry.frontend` 的插件不会自动激活渲染 UI。
|
|
76
|
+
|
|
77
|
+
## dependencies 依赖语法
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
"dependencies": [
|
|
81
|
+
{ "id": "top.qomicex.markdown", "version": ">=1.0.0", "optional": false }
|
|
82
|
+
]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| 字段 | 类型 | 说明 |
|
|
86
|
+
|------|------|------|
|
|
87
|
+
| `id` | string | 被依赖插件 id |
|
|
88
|
+
| `version` | string | 可选,版本约束 |
|
|
89
|
+
| `optional` | boolean | 可选,是否非必装(缺省 false) |
|
|
90
|
+
|
|
91
|
+
- 安装时检查必装前置,缺失拒绝安装(`PLUGIN_MISSING_DEPENDENCY`)。
|
|
92
|
+
- 激活时检查前置已启用,缺失则本插件禁用。
|
|
93
|
+
- 激活顺序由依赖拓扑排序保证。
|
|
94
|
+
- `version` 约束写法(以代码 / 公开文档为准):`>=1.0.0`、`<=2.0.0`、`>1.0`、`<2.0`、`=1.2.0`、裸版本 `1.2.0`、空格分隔多约束 `">=1.0 <2.0"`。不支持 `^`/`~`/`*`/`||`。
|
|
95
|
+
|
|
96
|
+
## contributes 扩展点
|
|
97
|
+
|
|
98
|
+
| 字段 | 类型 | 说明 |
|
|
99
|
+
|------|------|------|
|
|
100
|
+
| `downloadSources` | string[] | 保留(当前未使用,以代码为准) |
|
|
101
|
+
| `commands` | string[] | 保留(当前未使用,以代码为准) |
|
|
102
|
+
| `settingsPages` | string[] | 保留(当前未使用,以代码为准) |
|
|
103
|
+
| `menuItems` | PluginMenuItem[] | 侧边栏入口列表 |
|
|
104
|
+
| `overlay` | object | 悬浮窗配置 |
|
|
105
|
+
|
|
106
|
+
### menuItems 数组元素
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
{ path: string; label: string; icon?: string; action?: 'page' | 'overlay' }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- `path`:入口目标路由(如 `/plugins/p/:id`)。
|
|
113
|
+
- `icon`:emoji / 文本 / 绝对 URL / 包内相对路径(`dist/icon.svg`,启动器自动解析为 `http://localhost:5000/api/plugins/{id}/files/dist/icon.svg`)。
|
|
114
|
+
- `action`:`"page"`(跳转页面,默认)或 `"overlay"`(打开悬浮窗,需配合 `contributes.overlay`)。
|
|
115
|
+
|
|
116
|
+
### overlay 对象
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
{ file: string; title?: string; width?: number; height?: number; minimizable?: boolean; resizable?: boolean }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `file`:悬浮窗 HTML 文件路径(必填,指向 `.html`,如 `dist/overlay.html`)。
|
|
123
|
+
- `title` / `width` / `height` / `minimizable` / `resizable`:可选,视觉/行为参数(具体默认值以启动器代码为准)。
|
|
124
|
+
|
|
125
|
+
## CLI 校验行为(`qomicex verify` 目录模式)
|
|
126
|
+
|
|
127
|
+
- manifest 合法性:id / name / version(semver) / minLauncherVersion / layers / permissions / entry / contributes。
|
|
128
|
+
- 权限最小化:对比 `permissions` 与源码实际调用的桥方法(`METHOD_PERMISSIONS` 表),**声明未用 / 用了未声明都会报错**。
|
|
129
|
+
- 长循环告警:`while(true)`、`for(;;)`、无界 `setInterval`。
|
|
130
|
+
- 校验通过标准:**0 error**(warning 可接受但建议消除)。
|