@aiot-nexus/plugin-sdk 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,19 @@
1
1
  # @aiot-nexus/plugin-sdk
2
2
 
3
- > 当前 SDK 包版本为 `3.2.0`,Framework API 为 `3.2.0`,manifest schema 保持 `1`。Framework 3 将可信插件的 `mainEntry` 直接加载到 Nexus Electron 主进程;它不是安全沙箱。PluginFrame iframe 仍保持 sandbox、无 Node/Electron 访问。宿主运行模型见 [Framework v3 架构](../docs/plugin-framework-v3-architecture.md)。
3
+ > 当前 SDK 包版本为 `3.3.1`,Framework API 为 `3.2.0`,manifest schema 保持 `1`。Framework 3 将可信插件的 `mainEntry` 直接加载到 AIoT Toolkits Electron 主进程;它不是安全沙箱。PluginFrame iframe 仍保持 sandbox、无 Node/Electron 访问。宿主运行模型见 [Framework v3 架构](../docs/plugin-framework-v3-architecture.md)。
4
4
 
5
5
  本包提供公开 TypeScript 契约、PluginFrame 浏览器客户端和 `nexus-plugin` CLI。v2 项目升级前请先阅读 [v2 到 v3 迁移说明](../docs/plugin-framework-v2-v3-migration.md)。完整开发流程见 [独立插件开发指南](../docs/standalone-plugin-development-guide.md)。
6
6
 
7
+ 宿主产品名为 AIoT Toolkits,SDK 包名和 `dev:nexus` 命令保持不变。
8
+ 本仓库 SDK 的 `--host` 目录参数只查找 `AIoT Toolkits.exe`,文件不存在时直接报错。
9
+ 尚未更新的已发布 SDK 应传完整路径,例如 `--host "C:\Tools\AIoT Toolkits.exe"`;
10
+ 目录自动识别的新行为需使用包含本次修改的 SDK 构建。
11
+
7
12
  ## 兼容基线
8
13
 
9
14
  | 项目 | 固定值 |
10
15
  | --- | --- |
11
- | SDK | `3.2.0` |
16
+ | SDK | `3.3.1` |
12
17
  | Framework API | `3.2.0` |
13
18
  | Manifest schema | `1` |
14
19
  | Node 安装和构建 | `>=16.20.0 <17` |
@@ -27,7 +32,7 @@ nexus-plugin create <plugin-id> --template minimal|sqlite --output <parent-direc
27
32
  nexus-plugin upgrade [plugin-project-directory] [--check] [--sdk-source <version-or-local-tgz>]
28
33
  nexus-plugin dev [plugin-project-directory]
29
34
  nexus-plugin dev:web [plugin-project-directory]
30
- nexus-plugin dev:nexus [plugin-project-directory] --host <nexus.exe-or-directory>
35
+ nexus-plugin dev:nexus [plugin-project-directory] --host <host-exe-or-directory>
31
36
  ```
32
37
 
33
38
  仓库内 SDK 命令:
@@ -48,15 +53,15 @@ npm run publish:sdk
48
53
  registry 升级与只读预检:
49
54
 
50
55
  ```bash
51
- npx --package=@aiot-nexus/plugin-sdk@3.2.0 nexus-plugin upgrade . --check
52
- npx --package=@aiot-nexus/plugin-sdk@3.2.0 nexus-plugin upgrade .
56
+ npx --package=@aiot-nexus/plugin-sdk@3.3.1 nexus-plugin upgrade . --check
57
+ npx --package=@aiot-nexus/plugin-sdk@3.3.1 nexus-plugin upgrade .
53
58
  ```
54
59
 
55
60
  发布前或本地 tgz:
56
61
 
57
62
  ```bash
58
- npx --package=./aiot-nexus-plugin-sdk-3.2.0.tgz nexus-plugin upgrade . \
59
- --sdk-source ./aiot-nexus-plugin-sdk-3.2.0.tgz
63
+ npx --package=./aiot-nexus-plugin-sdk-3.3.1.tgz nexus-plugin upgrade . \
64
+ --sdk-source ./aiot-nexus-plugin-sdk-3.3.1.tgz
60
65
  ```
61
66
 
62
67
  CI 可以追加 `--json` 获得 `up-to-date`、`upgrade-available`、`upgraded`、`rolled-back` 或 `blocked`。`--allow-major` 只解除 major 保护,不会改写业务源码;跨 major 必须先阅读迁移指南。`--skip-project-checks` 只跳过项目 typecheck/build,安装后的 SDK、package 和 lockfile 一致性仍会验证。
@@ -67,7 +72,7 @@ CI 可以追加 `--json` 获得 `up-to-date`、`upgrade-available`、`upgraded`
67
72
 
68
73
  安装或项目验证失败时恢复备份,并在原 lockfile 存在时运行 `npm ci`。没有原 lockfile 时会运行 `npm install` 并恢复原 SDK,但无法证明完整传递依赖树与升级前一致,因此返回 `blocked` 并保留恢复数据。标准脚手架 build 的 `dist/<pluginId>` 在回滚范围内;自定义 typecheck/build 若修改其他路径,这些副作用不属于升级事务,项目必须自行管理。
69
74
 
70
- SDK `3.0.x` / `3.1.x` 插件可以由 3.2.x 宿主继续加载。升级到 SDK `3.2.0` 只提供主题能力和新的契约;插件必须引用 `--nexus-color-*` 或读取 `context.theme` 才会使用宿主主题色。没有引用新变量的插件保持原有颜色;需要自定义主题时传入 `autoApplyHostTheme: false`。原有 `hostApiVersion: 3.0.0` 可以保持不变,只有实际使用 `showHostStatusBar` 时才需要声明 `hostApiVersion: 3.1.0` 或更高。SDK 3.1.1 修复 registry 升级命令丢失 npm 包名的问题,作为历史兼容说明保留。
75
+ SDK `3.0.x` / `3.1.x` 插件可以由 3.2.x 宿主继续加载。升级到 SDK `3.2.0` 只提供主题能力和新的契约;插件必须引用 `--nexus-color-*` 或读取 `context.theme` 才会使用宿主主题色。没有引用新变量的插件保持原有颜色;需要自定义主题时传入 `autoApplyHostTheme: false`。原有 `hostApiVersion: 3.0.0` 可以保持不变,只有实际使用 `showHostStatusBar` 时才需要声明 `hostApiVersion: 3.1.0` 或更高。SDK 3.1.1 修复 registry 升级命令丢失 npm 包名的问题,SDK 3.3.0 将 `dev:nexus --host` 目录查找目标改为 `AIoT Toolkits.exe`,SDK 3.3.1 修复 npx 启动升级器时误用 `npx-cli.js` 执行 npm 命令的问题,均作为历史兼容说明保留。
71
76
 
72
77
  ## 创建插件
73
78
 
@@ -90,10 +95,10 @@ nexus-plugin create my-frame-plugin \
90
95
 
91
96
  ```bash
92
97
  npm run pack:sdk
93
- nexus-plugin create my-frame-plugin --sdk-source ./package/aiot-nexus-plugin-sdk-3.2.0.tgz --install
98
+ nexus-plugin create my-frame-plugin --sdk-source ./package/aiot-nexus-plugin-sdk-3.3.1.tgz --install
94
99
  ```
95
100
 
96
- 生成插件后,可以在 `src/manifest.json` 的业务入口中静态控制 Nexus 宿主状态栏。相关 manifest 片段如下,其余必填字段沿用脚手架生成值:
101
+ 生成插件后,可以在 `src/manifest.json` 的业务入口中静态控制 AIoT Toolkits 宿主状态栏。相关 manifest 片段如下,其余必填字段沿用脚手架生成值:
97
102
 
98
103
  ```json
99
104
  {
@@ -183,9 +188,9 @@ function failure(code: string, message: string) {
183
188
  - `context.host.*` 直接返回 `NexusResult<T>`,不需要 `await`。
184
189
  - 注册的 operation handler 可以返回 Promise,可等待异步文件对话框、网络或其他用户交互。
185
190
  - operation 默认没有业务超时。浏览器客户端 `requestTimeoutMs` 默认是 `0`;20 秒仅用于 iframe 初始化握手。
186
- - 同步 Electron API 也能调用,但 `showOpenDialogSync()`、同步文件 I/O、长 CPU 任务会阻塞 Nexus 主进程、所有窗口和插件,应优先使用异步版本。
191
+ - 同步 Electron API 也能调用,但 `showOpenDialogSync()`、同步文件 I/O、长 CPU 任务会阻塞 AIoT Toolkits 主进程、所有窗口和插件,应优先使用异步版本。
187
192
  - Framework 不跟踪插件直接创建的 Electron/Node 资源。插件必须在 `deactivate` 中自行清理。
188
- - Node 模块图和原生模块不能安全热卸载。已加载 `mainEntry`、依赖、原生模块或相关 manifest 变化必须重启 Nexus。
193
+ - Node 模块图和原生模块不能安全热卸载。已加载 `mainEntry`、依赖、原生模块或相关 manifest 变化必须重启 AIoT Toolkits。
189
194
 
190
195
  Framework 3 不再提供 `context.host.electron` Facade,也没有 `electronHostControl` 权限。manifest 权限只描述 Host API 能力,不限制可信 `mainEntry` 直接调用 Electron/Node。SDK 保留 `createPluginElectronIpcChannel()` 和 `createPluginElectronProtocolScheme()` 作为命名约定帮助函数;宿主不会自动隔离或清理对应注册。
191
196
 
@@ -234,7 +239,7 @@ SDK 升级本身只提供能力。插件需要在 CSS 中引用 `var(--nexus-col
234
239
  | `npm run dev:web` | 不执行 | 不可用 | 只调试 Renderer;`invokeMain` 返回 `PluginCapabilityUnsupported`。 |
235
240
  | `npm run dev` | Electron 调试宿主主进程直接加载 | 可用 | 调试真实生命周期、对话框、SQLite 和插件窗口。 |
236
241
  | `npm run dev:debug` | 同 `dev` | 可用 | Electron 主进程以 `--inspect-brk=9229` 启动。 |
237
- | `npm run dev:nexus -- --host ...` | 正式 Nexus 主进程直接加载 | 可用 | 最终集成和监听更新。 |
242
+ | `npm run dev:nexus -- --host ...` | 正式 AIoT Toolkits 主进程直接加载 | 可用 | 最终集成和监听更新。 |
238
243
 
239
244
  Electron 调试壳仅保留 context 工具栏;插件 iframe 无边距、无边框,填满剩余窗口,不设置最小宽高,也不根据内容上报限制尺寸。Renderer DevTools 默认打开。
240
245
 
@@ -244,12 +249,12 @@ VS Code 模板提供 `Debug Nexus plugin` 和 `Attach Nexus plugin main process
244
249
 
245
250
  | 修改范围 | 处理方式 |
246
251
  | --- | --- |
247
- | `src/renderer/**` | 只构建 Renderer 并重载 iframe,Nexus PID 不变。 |
252
+ | `src/renderer/**` | 只构建 Renderer 并重载 iframe,AIoT Toolkits PID 不变。 |
248
253
  | 无 `mainEntry` 插件的安全 manifest 展示/贡献字段 | 原子替换 manifest 并重扫,PID 不变。 |
249
- | 有 `mainEntry` 插件的 manifest 变化 | 完整构建并重启 Nexus。 |
250
- | `src/main.*`、依赖、原生模块、构建脚本 | 完整构建并重启 Nexus。 |
254
+ | 有 `mainEntry` 插件的 manifest 变化 | 完整构建并重启 AIoT Toolkits。 |
255
+ | `src/main.*`、依赖、原生模块、构建脚本 | 完整构建并重启 AIoT Toolkits。 |
251
256
 
252
- 构建失败保留旧部署;新宿主启动或就绪失败会恢复旧包和旧宿主。`--inspect` / `--inspect-brk` 调试的是 Nexus Electron 主进程,因此会同时暂停或暴露宿主代码。按 `Ctrl+C` 时 CLI 关闭自己启动的宿主。
257
+ 构建失败保留旧部署;新宿主启动或就绪失败会恢复旧包和旧宿主。`--inspect` / `--inspect-brk` 调试的是 AIoT Toolkits Electron 主进程,因此会同时暂停或暴露宿主代码。按 `Ctrl+C` 时 CLI 关闭自己启动的宿主。
253
258
 
254
259
  ## 发布前检查
255
260
 
@@ -262,4 +267,4 @@ VS Code 模板提供 `Debug Nexus plugin` 和 `Attach Nexus plugin main process
262
267
  7. `deactivate` 关闭窗口、Session 监听、IPC、protocol、快捷键、定时器、数据库和子进程。
263
268
  8. 没有同步长任务、无限载荷、任意命令/SQL/路径透传或日志敏感信息。
264
269
  9. 主题集成明确引用 `--nexus-color-*` / `context.theme`,或显式关闭 `autoApplyHostTheme`。
265
- 10. 运行 `npm test`、插件构建、正式 Nexus 集成和 Windows 7 真机验收。
270
+ 10. 运行 `npm test`、插件构建、正式 AIoT Toolkits 集成和 Windows 7 真机验收。
@@ -1,6 +1,7 @@
1
1
  export declare const PLUGIN_FRAMEWORK_VERSION = "3.2.0";
2
2
  export declare const PLUGIN_MANIFEST_SCHEMA_VERSION = 1;
3
3
  export declare const PLUGIN_COMPATIBLE_VERSION_PATTERN = "^(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?(\\.(0|[1-9][0-9]*))?$";
4
+ export declare const PLUGIN_VERSION_PATTERN = "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-((?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$";
4
5
  export interface ParsedPluginVersion {
5
6
  major: number;
6
7
  minor: number;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isPluginFrameworkVersionCompatible = exports.comparePluginVersions = exports.parsePluginCompatibleVersion = exports.PLUGIN_COMPATIBLE_VERSION_PATTERN = exports.PLUGIN_MANIFEST_SCHEMA_VERSION = exports.PLUGIN_FRAMEWORK_VERSION = void 0;
3
+ exports.isPluginFrameworkVersionCompatible = exports.comparePluginVersions = exports.parsePluginCompatibleVersion = exports.PLUGIN_VERSION_PATTERN = exports.PLUGIN_COMPATIBLE_VERSION_PATTERN = exports.PLUGIN_MANIFEST_SCHEMA_VERSION = exports.PLUGIN_FRAMEWORK_VERSION = void 0;
4
4
  exports.PLUGIN_FRAMEWORK_VERSION = "3.2.0";
5
5
  exports.PLUGIN_MANIFEST_SCHEMA_VERSION = 1;
6
6
  exports.PLUGIN_COMPATIBLE_VERSION_PATTERN = "^(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?(\\.(0|[1-9][0-9]*))?$";
7
+ exports.PLUGIN_VERSION_PATTERN = "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-((?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$";
7
8
  function parsePluginCompatibleVersion(value) {
8
9
  const match = /^(0|[1-9][0-9]*)(?:\.(0|[1-9][0-9]*))?(?:\.(0|[1-9][0-9]*))?$/.exec(value.trim());
9
10
  if (!match) {
@@ -20,7 +20,7 @@
20
20
  "pluginId": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]{1,63}$" },
21
21
  "displayNameKey": { "type": "string", "minLength": 1 },
22
22
  "descriptionKey": { "type": "string" },
23
- "pluginVersion": { "type": "string", "minLength": 1 },
23
+ "pluginVersion": { "type": "string", "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-((?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$" },
24
24
  "hostApiVersion": { "type": "string", "pattern": "^(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?(\\.(0|[1-9][0-9]*))?$" },
25
25
  "minHostVersion": { "type": "string", "pattern": "^(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?(\\.(0|[1-9][0-9]*))?$" },
26
26
  "supportedCultures": {
@@ -1,3 +1,3 @@
1
1
  {
2
- "nexusApplicationVersion": "0.0.1"
2
+ "nexusApplicationVersion": "0.0.3"
3
3
  }
@@ -102,7 +102,7 @@ function usage() {
102
102
  " nexus-plugin dev:web [plugin-project-directory] [--no-build] [--no-open] [--port <port>]",
103
103
  " [--language <culture>] [--theme system|light|dark] [--surface business|tool|settings]",
104
104
  " [--contribution-id <id>]",
105
- " nexus-plugin dev:nexus [plugin-project-directory] --host <nexus.exe-or-directory>",
105
+ " nexus-plugin dev:nexus [plugin-project-directory] --host <host-exe-or-directory>",
106
106
  " [--inspect[=<port>] | --inspect-brk[=<port>]] [--no-devtools]",
107
107
  " [--language <culture>] [--theme system|light|dark] [--surface business|tool|settings]",
108
108
  " [--contribution-id <id>]"
@@ -247,7 +247,7 @@ function runNexusDev(args, io) {
247
247
  }
248
248
  }
249
249
  if (!options.hostPath) {
250
- return usageError(io, "dev:nexus requires --host <nexus.exe-or-directory>.");
250
+ return usageError(io, "dev:nexus requires --host <host-exe-or-directory>.");
251
251
  }
252
252
  return (0, nexus_dev_1.runPluginNexusDev)(options, io).catch((error) => {
253
253
  if (error instanceof dev_1.PluginDevError) {
@@ -34,9 +34,9 @@ function resolveNexusExecutable(hostPath) {
34
34
  throw new dev_1.PluginDevError("Nexus host path does not exist: " + resolved);
35
35
  }
36
36
  const stat = fs.statSync(resolved);
37
- const executable = stat.isDirectory() ? path.join(resolved, "nexus.exe") : resolved;
37
+ const executable = stat.isDirectory() ? path.join(resolved, "AIoT Toolkits.exe") : resolved;
38
38
  if (!fs.existsSync(executable) || !fs.statSync(executable).isFile()) {
39
- throw new dev_1.PluginDevError("Nexus executable is missing: " + executable);
39
+ throw new dev_1.PluginDevError("AIoT Toolkits executable is missing: " + executable);
40
40
  }
41
41
  if (process.platform === "win32" && path.extname(executable).toLowerCase() !== ".exe") {
42
42
  throw new dev_1.PluginDevError("Nexus host must be a Windows executable: " + executable);
@@ -218,7 +218,7 @@ function createPackageJson(options, sdkSource) {
218
218
  name: options.pluginId,
219
219
  version: "0.1.0",
220
220
  private: true,
221
- description: options.displayName + " independent plugin for AIoT Nexus.",
221
+ description: options.displayName + " independent plugin for AIoT Toolkits.",
222
222
  scripts
223
223
  };
224
224
  if (options.template === "sqlite") {
@@ -1,4 +1,4 @@
1
- export declare const PLUGIN_SDK_VERSION = "3.2.0";
1
+ export declare const PLUGIN_SDK_VERSION = "3.3.1";
2
2
  export declare const PLUGIN_SDK_NODE_ENGINE = ">=16.20.0 <17";
3
3
  export declare const PLUGIN_SDK_NPM_ENGINE = ">=6.13.4";
4
4
  export declare const PLUGIN_SDK_ELECTRON_VERSION = "22.3.27";
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getPluginSdkInfo = exports.PLUGIN_SDK_TYPES_NODE_VERSION = exports.PLUGIN_SDK_TYPESCRIPT_VERSION = exports.PLUGIN_SDK_ELECTRON_VERSION = exports.PLUGIN_SDK_NPM_ENGINE = exports.PLUGIN_SDK_NODE_ENGINE = exports.PLUGIN_SDK_VERSION = void 0;
4
4
  const plugin_framework_version_1 = require("../.generated/plugin-framework-version");
5
- exports.PLUGIN_SDK_VERSION = "3.2.0";
5
+ exports.PLUGIN_SDK_VERSION = "3.3.1";
6
6
  exports.PLUGIN_SDK_NODE_ENGINE = ">=16.20.0 <17";
7
7
  exports.PLUGIN_SDK_NPM_ENGINE = ">=6.13.4";
8
8
  exports.PLUGIN_SDK_ELECTRON_VERSION = "22.3.27";
@@ -42,3 +42,9 @@ export interface PluginSdkUpgradeRuntime {
42
42
  }
43
43
  export declare function runPluginSdkUpgrade(options: PluginSdkUpgradeOptions, runtime?: PluginSdkUpgradeRuntime): PluginSdkUpgradeResult;
44
44
  export declare function formatPluginSdkUpgradeResult(result: PluginSdkUpgradeResult): string;
45
+ /**
46
+ * npm_execpath points at npm-cli.js under `npm run`, but at npx-cli.js under
47
+ * `npx`; routing npm commands through npx-cli.js fails on the first npm-only
48
+ * flag. npx-cli.js and npm-cli.js live in the same npm bin directory.
49
+ */
50
+ export declare function resolveNpmCliEntryPoint(npmExecPath: string | undefined): string | undefined;
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.formatPluginSdkUpgradeResult = exports.runPluginSdkUpgrade = void 0;
26
+ exports.resolveNpmCliEntryPoint = exports.formatPluginSdkUpgradeResult = exports.runPluginSdkUpgrade = void 0;
27
27
  const child_process_1 = require("child_process");
28
28
  const fs = __importStar(require("fs"));
29
29
  const path = __importStar(require("path"));
@@ -1070,12 +1070,32 @@ function defaultRuntime() {
1070
1070
  };
1071
1071
  }
1072
1072
  function runNpm(args, cwd) {
1073
- const npmExecPath = process.env.npm_execpath;
1074
- if (npmExecPath) {
1075
- return runProcess(process.execPath, [npmExecPath, ...args], cwd);
1073
+ const npmCliEntryPoint = resolveNpmCliEntryPoint(process.env.npm_execpath);
1074
+ if (npmCliEntryPoint) {
1075
+ return runProcess(process.execPath, [npmCliEntryPoint, ...args], cwd);
1076
1076
  }
1077
1077
  return runProcess(process.platform === "win32" ? "npm.cmd" : "npm", args, cwd);
1078
1078
  }
1079
+ /**
1080
+ * npm_execpath points at npm-cli.js under `npm run`, but at npx-cli.js under
1081
+ * `npx`; routing npm commands through npx-cli.js fails on the first npm-only
1082
+ * flag. npx-cli.js and npm-cli.js live in the same npm bin directory.
1083
+ */
1084
+ function resolveNpmCliEntryPoint(npmExecPath) {
1085
+ if (!npmExecPath) {
1086
+ return undefined;
1087
+ }
1088
+ const base = path.basename(npmExecPath).toLowerCase();
1089
+ if (base === "npm-cli.js") {
1090
+ return npmExecPath;
1091
+ }
1092
+ if (base === "npx-cli.js") {
1093
+ const sibling = path.join(path.dirname(npmExecPath), "npm-cli.js");
1094
+ return fs.existsSync(sibling) ? sibling : undefined;
1095
+ }
1096
+ return undefined;
1097
+ }
1098
+ exports.resolveNpmCliEntryPoint = resolveNpmCliEntryPoint;
1079
1099
  function runProcess(command, args, cwd) {
1080
1100
  const result = (0, child_process_1.spawnSync)(command, args, {
1081
1101
  cwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-nexus/plugin-sdk",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "Types, PluginFrame client, and CLI for AIoT Nexus independent plugins.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -1,6 +1,6 @@
1
1
  # __DISPLAY_NAME_MARKDOWN__
2
2
 
3
- AIoT Nexus independent PluginFrame plugin generated from the `minimal` template.
3
+ AIoT Toolkits independent PluginFrame plugin generated from the `minimal` template.
4
4
 
5
5
  ```bash
6
6
  npm install
@@ -8,14 +8,14 @@ npm run typecheck
8
8
  npm run build
9
9
  npm run dev:web
10
10
  npm run dev
11
- npm run dev:nexus -- --host C:\path\to\nexus.exe
11
+ npm run dev:nexus -- --host "C:\path\to\AIoT Toolkits.exe"
12
12
  ```
13
13
 
14
14
  SDK checks for a newer `@aiot-nexus/plugin-sdk` in two steps. The package `postinstall` refreshes a user cache; npm 7+ hides dependency lifecycle output by default, so use `npm install --foreground-scripts` to see it. The `nexus-plugin` CLI reads that cache and prints npm-style notices when it is fresh; missing or older-than-24-hours cache data is checked in the background without blocking the command. `npm install --ignore-scripts` skips only the install check, while CLI checks remain available.
15
15
 
16
16
  The check follows the npm-configured registry, falls back to the official npm registry when needed, and silently ignores offline, timeout, malformed-response, or corrupt-cache failures. Use `NEXUS_PLUGIN_SDK_UPDATE_CHECK=0` or `NO_UPDATE_NOTIFIER=1` to disable both checks. Notices never modify the project. When one appears, substitute the exact version printed by the notice for `X.Y.Z`: `npx --package=@aiot-nexus/plugin-sdk@X.Y.Z nexus-plugin upgrade . --check`, then repeat without `--check`. The transaction synchronizes package.json, package-lock.json, and node_modules, runs project checks, and rolls back on failure. A major update additionally requires `--allow-major` and the migration guide linked by the CLI; source and manifest files are never rewritten.
17
17
 
18
- The deployable plugin package is `dist/__PLUGIN_ID__/`. For a manual installation, stop Nexus before replacing `Plugins/__PLUGIN_ID__/`, then start it again. During development, use `npm run dev:nexus` so the CLI coordinates Nexus shutdown, atomic replacement, and restart for trusted `mainEntry` changes.
18
+ The deployable plugin package is `dist/__PLUGIN_ID__/`. For a manual installation, stop AIoT Toolkits before replacing `Plugins/__PLUGIN_ID__/`, then start it again. During development, use `npm run dev:nexus` so the CLI coordinates AIoT Toolkits shutdown, atomic replacement, and restart for trusted `mainEntry` changes.
19
19
 
20
20
  ## Native Electron file dialogs
21
21
 
@@ -97,21 +97,21 @@ export async function chooseSavePath(): Promise<string | undefined> {
97
97
  }
98
98
  ```
99
99
 
100
- Call these helpers from an operation registered with `context.host.registerRendererMethod`, then let the renderer invoke that narrow operation through `client.invokeMain()`. Return the `NexusResult` from `chooseAndReadText` directly so the renderer receives the stable `FileNotReadable`, `FileTooLarge`, or `FileReadFailed` code. Do not expose Electron objects or unrestricted file paths to the iframe. For larger files, stream or paginate in `mainEntry` and return only bounded chunks or summaries instead of the whole file. `npm run dev:web` is renderer-only and deliberately has no `mainEntry`, Node filesystem, or Electron main-process API; use `npm run dev` or a formal Nexus host to test these dialogs.
100
+ Call these helpers from an operation registered with `context.host.registerRendererMethod`, then let the renderer invoke that narrow operation through `client.invokeMain()`. Return the `NexusResult` from `chooseAndReadText` directly so the renderer receives the stable `FileNotReadable`, `FileTooLarge`, or `FileReadFailed` code. Do not expose Electron objects or unrestricted file paths to the iframe. For larger files, stream or paginate in `mainEntry` and return only bounded chunks or summaries instead of the whole file. `npm run dev:web` is renderer-only and deliberately has no `mainEntry`, Node filesystem, or Electron main-process API; use `npm run dev` or a formal AIoT Toolkits host to test these dialogs.
101
101
 
102
102
  The renderer calls the trusted-main `demo.echo` operation through `@aiot-nexus/plugin-sdk`. The main entry also calls the directly imported `app.getName()` during activation, so `npm run dev` verifies the real Electron main-process path. Run `npm run validate` after changing the manifest or output layout.
103
103
 
104
- The renderer follows `context.language` and `context.themeMode` from Nexus, subscribes to in-place context updates without reloading the iframe, and includes `en-US` and `zh-CN` message catalogs. The plugin page has no language or theme selector; the controls shown by `npm run dev` belong to the debug host and simulate Nexus context changes. Below that toolbar, the iframe has no host padding, border, minimum size, or content-driven height and fills the remaining host viewport. Add or edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
104
+ The renderer follows `context.language` and `context.themeMode` from AIoT Toolkits, subscribes to in-place context updates without reloading the iframe, and includes `en-US` and `zh-CN` message catalogs. The plugin page has no language or theme selector; the controls shown by `npm run dev` belong to the debug host and simulate AIoT Toolkits context changes. Below that toolbar, the iframe has no host padding, border, minimum size, or content-driven height and fills the remaining host viewport. Add or edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
105
105
 
106
106
  SDK 3.2 automatically maps the active `context.theme` palette to `--nexus-color-*` variables and sets `data-nexus-theme="light|dark"` on the document root. This template consumes those variables with local light/dark fallbacks, so it still renders on an older host. Merely upgrading the SDK does not recolor custom CSS: use the namespaced variables or read the optional palette explicitly, for example `const primary = context.theme?.light.primary`.
107
107
 
108
108
  For a plugin-owned theme, create the client with `createPluginFrameClient({ autoApplyHostTheme: false })`. The palette remains available from `await client.waitForContext()`, but the SDK will not write CSS variables or `data-nexus-theme`. It never overwrites plugin-owned `--bg`, `--accent`, `data-theme`, or `color-scheme` values.
109
109
 
110
- The current SDK has no generic business-event subscription API. For a long-running operation, register a fast `job.start` operation that returns a `taskId`, then expose `job.getStatus` and optional `job.cancel` for renderer polling. See the Nexus repository's plugin IPC and event notification example for the full contract and code.
110
+ The current SDK has no generic business-event subscription API. For a long-running operation, register a fast `job.start` operation that returns a `taskId`, then expose `job.getStatus` and optional `job.cancel` for renderer polling. See the AIoT Toolkits repository's plugin IPC and event notification example for the full contract and code.
111
111
 
112
- Use `npm run dev:web` for renderer-only UI work in the system browser. It runs `build:web`, writes an isolated package under `.nexus-plugin-dev/web-package/`, builds with source maps, serves only on `127.0.0.1:4173`, and opens the browser automatically. It does not execute `mainEntry`, provide Node or Electron main-process APIs, or overwrite the deployable `dist` package. Language and theme controls still simulate Nexus context, while `invokeMain` is intentionally unavailable. Pass `-- --no-open` or `-- --port <port>` when needed; use `npm run dev` for Host API and Electron integration.
112
+ Use `npm run dev:web` for renderer-only UI work in the system browser. It runs `build:web`, writes an isolated package under `.nexus-plugin-dev/web-package/`, builds with source maps, serves only on `127.0.0.1:4173`, and opens the browser automatically. It does not execute `mainEntry`, provide Node or Electron main-process APIs, or overwrite the deployable `dist` package. Language and theme controls still simulate AIoT Toolkits context, while `invokeMain` is intentionally unavailable. Pass `-- --no-open` or `-- --port <port>` when needed; use `npm run dev` for Host API and Electron integration.
113
113
 
114
- Use `npm run dev:nexus -- --host C:\path\to\nexus.exe` for live development against a compiled, packaged Nexus. The CLI atomically deploys this plugin to `<exeDir>/Plugins/__PLUGIN_ID__/`. Renderer changes rebuild and reload the iframe without changing the Nexus PID; safe manifest changes rescan contributions in the same process; `mainEntry`, dependency, native-module, or restart-sensitive manifest changes stop Nexus, replace the complete package, and start a new Nexus process because trusted modules cannot be unloaded safely from Node's module cache. Failed builds keep the previous deployment; launch or readiness failures restore the previous package and host. Add `--no-devtools`, `--inspect[=<port>]`, `--inspect-brk[=<port>]`, `--surface`, or `--contribution-id` after the second `--` as needed.
114
+ Use `npm run dev:nexus -- --host "C:\path\to\AIoT Toolkits.exe"` for live development against a compiled, packaged AIoT Toolkits. The CLI atomically deploys this plugin to `<exeDir>/Plugins/__PLUGIN_ID__/`. Renderer changes rebuild and reload the iframe without changing the AIoT Toolkits PID; safe manifest changes rescan contributions in the same process; `mainEntry`, dependency, native-module, or restart-sensitive manifest changes stop AIoT Toolkits, replace the complete package, and start a new AIoT Toolkits process because trusted modules cannot be unloaded safely from Node's module cache. Failed builds keep the previous deployment; launch or readiness failures restore the previous package and host. Add `--no-devtools`, `--inspect[=<port>]`, `--inspect-brk[=<port>]`, `--surface`, or `--contribution-id` after the second `--` as needed.
115
115
 
116
116
  When this project was created from a local SDK archive, `vendor/aiot-nexus-plugin-sdk.tgz` is part of the source project and must be kept when the project is moved or committed. An explicit local upgrade copies the target archive to `vendor/aiot-nexus-plugin-sdk-<version>.tgz` and updates the exact file dependency.
117
117
 
@@ -119,4 +119,4 @@ For a local SDK archive, the tgz version remains the plugin's current SDK versio
119
119
 
120
120
  For trusted-main breakpoints, open this plugin directory in VS Code, set breakpoints in `src/main.ts`, and run `Debug Nexus plugin`. To attach manually, start `npm run dev:debug` and select `Attach Nexus plugin main process (9229)`. Renderer DevTools opens automatically, with `src/renderer/app.ts` available through the development source map. `npm run dev` rebuilds before launch; use `npm run dev -- --no-build` only when intentionally reusing the current `dist` output.
121
121
 
122
- The independent host stores data under `.nexus-plugin-dev/` and mirrors the PluginFrame protocol and current Host API surface. Its simulated Nexus application version defaults to the version recorded by the SDK build; pass `npm run dev -- --host-version <X.Y.Z>` to exercise another compatibility branch. Run the deployable `dist/__PLUGIN_ID__/` in Nexus before delivery because the standalone host does not reproduce the full application shell or routing state.
122
+ The independent host stores data under `.nexus-plugin-dev/` and mirrors the PluginFrame protocol and current Host API surface. Its simulated AIoT Toolkits application version defaults to the version recorded by the SDK build; pass `npm run dev -- --host-version <X.Y.Z>` to exercise another compatibility branch. Run the deployable `dist/__PLUGIN_ID__/` in AIoT Toolkits before delivery because the standalone host does not reproduce the full application shell or routing state.
@@ -1,6 +1,6 @@
1
1
  # __DISPLAY_NAME_MARKDOWN__
2
2
 
3
- AIoT Nexus independent PluginFrame plugin generated from the `sqlite` template.
3
+ AIoT Toolkits independent PluginFrame plugin generated from the `sqlite` template.
4
4
 
5
5
  ```bash
6
6
  npm install
@@ -8,7 +8,7 @@ npm run typecheck
8
8
  npm run build
9
9
  npm run dev:web
10
10
  npm run dev
11
- npm run dev:nexus -- --host C:\path\to\nexus.exe
11
+ npm run dev:nexus -- --host "C:\path\to\AIoT Toolkits.exe"
12
12
  ```
13
13
 
14
14
  SDK checks for a newer `@aiot-nexus/plugin-sdk` in two steps. The package `postinstall` refreshes a user cache; npm 7+ hides dependency lifecycle output by default, so use `npm install --foreground-scripts` to see it. The `nexus-plugin` CLI reads that cache and prints npm-style notices when it is fresh; missing or older-than-24-hours cache data is checked in the background without blocking the command. `npm install --ignore-scripts` skips only the install check, while CLI checks remain available.
@@ -17,7 +17,7 @@ The check follows the npm-configured registry, falls back to the official npm re
17
17
 
18
18
  Every `npm run build` rebuilds `better-sqlite3@8.7.0` for Electron 22.3.27 with `@electron/rebuild@3.7.2` before packaging. `npm run rebuild:native` remains available as a standalone diagnostic command.
19
19
 
20
- The deployable plugin package is `dist/__PLUGIN_ID__/`. For a manual installation, stop Nexus before replacing `Plugins/__PLUGIN_ID__/`, then start it again. During development, use `npm run dev:nexus` so the CLI coordinates Nexus shutdown, atomic replacement, and restart for trusted `mainEntry` changes. Runtime data is stored in the host-provided plugin data directory and is not written into the plugin package.
20
+ The deployable plugin package is `dist/__PLUGIN_ID__/`. For a manual installation, stop AIoT Toolkits before replacing `Plugins/__PLUGIN_ID__/`, then start it again. During development, use `npm run dev:nexus` so the CLI coordinates AIoT Toolkits shutdown, atomic replacement, and restart for trusted `mainEntry` changes. Runtime data is stored in the host-provided plugin data directory and is not written into the plugin package.
21
21
 
22
22
  The template is a schema-aware SQLite database browser. It opens `plugin.db` in the plugin data directory by default and initializes the `plugin_records` sample table there. **Open Database** calls Electron's directly imported `dialog.showOpenDialog()` from trusted `mainEntry`; it accepts a single existing SQLite file, including files without an extension. The renderer only displays the selected path and invokes parameterized operations. It never receives an Electron object and does not expose a path input, arbitrary SQL, DDL, `ATTACH`, or a SQL console.
23
23
 
@@ -43,23 +43,23 @@ if (!result.canceled && result.filePaths[0]) {
43
43
  }
44
44
  ```
45
45
 
46
- For related workflows, use `properties: ["openDirectory", "createDirectory"]` to choose a folder or `dialog.showSaveDialog()` to choose a save path. These calls belong in trusted `mainEntry`; the renderer should invoke a registered business operation such as `sqlite.chooseDatabase` through `client.invokeMain()` and receive only the result it needs. Native dialogs are unavailable in `npm run dev:web`; use `npm run dev` or a formal Nexus host.
46
+ For related workflows, use `properties: ["openDirectory", "createDirectory"]` to choose a folder or `dialog.showSaveDialog()` to choose a save path. These calls belong in trusted `mainEntry`; the renderer should invoke a registered business operation such as `sqlite.chooseDatabase` through `client.invokeMain()` and receive only the result it needs. Native dialogs are unavailable in `npm run dev:web`; use `npm run dev` or a formal AIoT Toolkits host.
47
47
 
48
48
  External files are opened read/write only after a separate candidate connection can query `sqlite_schema`. Canceling the dialog or selecting a corrupt, locked, or inaccessible file keeps the current connection. The latest external path is atomically stored as `sqlite-browser-state.json` in the plugin data directory. Startup restores it when valid; otherwise the template clears the saved selection, falls back to `plugin.db`, and returns a visible notice. External databases do not receive the sample schema or journal pragma.
49
49
 
50
50
  The table selector lists ordinary user tables from the `main` schema. Dynamic headers show declared type, SQLite affinity, primary-key order, NULL, default, and generated-column metadata. The editor supports default/NULL/value modes for inserts and unchanged/NULL/value modes for updates. INTEGER input uses exact decimal integers, REAL must be finite, NUMERIC accepts integer, real, or text values, and BLOB input uses even-length hexadecimal text. The 1 MiB field and 4 MiB row limits count decoded BLOB bytes rather than hexadecimal characters. Ordinary rowid tables prefer an unshadowed built-in rowid; `WITHOUT ROWID` tables, or tables that shadow every rowid alias, fall back only to primary-key columns that are all non-NULL. A table without a unique locator remains browsable and insertable, but update and delete are unavailable. Primary-key locators larger than 16 KiB are also treated as browse-only so a page cannot copy unbounded keys into the trusted main process and iframe. Update and delete run in a transaction and must affect exactly one row. TEXT previews are capped at 16 KiB of UTF-8 data; BLOB previews show the byte length and first 32 bytes.
51
51
 
52
- The renderer follows `context.language` and `context.themeMode` from Nexus and preserves unfinished editor values during in-place context updates. The plugin page has no language or theme selector; the controls shown by `npm run dev` belong to the debug host. Below that toolbar, the iframe has no host padding, border, minimum size, or content-driven height and fills the remaining host viewport. This SDK template deliberately has no Quasar dependency, so its data grid uses a native `<table>` as a dependency-minimized template exception. It still provides pagination, contained horizontal scrolling, light/dark themes, `en-US` / `zh-CN`, and busy/empty/error states. Edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
52
+ The renderer follows `context.language` and `context.themeMode` from AIoT Toolkits and preserves unfinished editor values during in-place context updates. The plugin page has no language or theme selector; the controls shown by `npm run dev` belong to the debug host. Below that toolbar, the iframe has no host padding, border, minimum size, or content-driven height and fills the remaining host viewport. This SDK template deliberately has no Quasar dependency, so its data grid uses a native `<table>` as a dependency-minimized template exception. It still provides pagination, contained horizontal scrolling, light/dark themes, `en-US` / `zh-CN`, and busy/empty/error states. Edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
53
53
 
54
54
  SDK 3.2 automatically maps the active `context.theme` palette to `--nexus-color-*` variables and sets `data-nexus-theme="light|dark"` on the document root. This template consumes those variables with local light/dark fallbacks, so it still renders on an older host. Merely upgrading the SDK does not recolor custom CSS: use the namespaced variables or read the optional palette explicitly, for example `const primary = context.theme?.light.primary`.
55
55
 
56
56
  For a plugin-owned theme, create the client with `createPluginFrameClient({ autoApplyHostTheme: false })`. The palette remains available from `await client.waitForContext()`, but the SDK will not write CSS variables or `data-nexus-theme`. It never overwrites plugin-owned `--bg`, `--accent`, `data-theme`, or `color-scheme` values.
57
57
 
58
- The current SDK has no generic business-event subscription API. For a long-running operation, register a fast `job.start` operation that returns a `taskId`, then expose `job.getStatus` and optional `job.cancel` for renderer polling. See the Nexus repository's plugin IPC and event notification example for the full contract and code.
58
+ The current SDK has no generic business-event subscription API. For a long-running operation, register a fast `job.start` operation that returns a `taskId`, then expose `job.getStatus` and optional `job.cancel` for renderer polling. See the AIoT Toolkits repository's plugin IPC and event notification example for the full contract and code.
59
59
 
60
- Use `npm run dev:web` for renderer-only layout, styling, localization, and responsive checks in the system browser. It runs `build:web`, writes an isolated package under `.nexus-plugin-dev/web-package/`, and does not rebuild or load `better-sqlite3`, execute `mainEntry`, provide Node or Electron main-process APIs, copy native runtime files, or overwrite the deployable `dist` package. It builds with source maps, serves only on `127.0.0.1:4173`, and opens the browser automatically. The page explicitly reports `PluginCapabilityUnsupported`; file selection, table loading, and CRUD require `npm run dev` or a formal Nexus host. Pass `-- --no-open` or `-- --port <port>` when needed.
60
+ Use `npm run dev:web` for renderer-only layout, styling, localization, and responsive checks in the system browser. It runs `build:web`, writes an isolated package under `.nexus-plugin-dev/web-package/`, and does not rebuild or load `better-sqlite3`, execute `mainEntry`, provide Node or Electron main-process APIs, copy native runtime files, or overwrite the deployable `dist` package. It builds with source maps, serves only on `127.0.0.1:4173`, and opens the browser automatically. The page explicitly reports `PluginCapabilityUnsupported`; file selection, table loading, and CRUD require `npm run dev` or a formal AIoT Toolkits host. Pass `-- --no-open` or `-- --port <port>` when needed.
61
61
 
62
- Use `npm run dev:nexus -- --host C:\path\to\nexus.exe` to run the plugin inside a compiled, packaged Nexus. The initial deployment and every full package replacement are atomic. Renderer-only and safe manifest updates keep the Nexus PID; `mainEntry`, dependency, native-module, or restart-sensitive manifest changes run the complete build (including the Electron 22.3.27 native rebuild), stop Nexus, replace the package, and start a new Nexus process. The database remains under the host-provided plugin data directory across host restarts. Failed builds keep the previous deployment, and launch or readiness failures roll the package back and restore the previous host. Add `--no-devtools`, `--inspect[=<port>]`, `--inspect-brk[=<port>]`, `--surface`, or `--contribution-id` after the second `--` as needed.
62
+ Use `npm run dev:nexus -- --host "C:\path\to\AIoT Toolkits.exe"` to run the plugin inside a compiled, packaged AIoT Toolkits. The initial deployment and every full package replacement are atomic. Renderer-only and safe manifest updates keep the AIoT Toolkits PID; `mainEntry`, dependency, native-module, or restart-sensitive manifest changes run the complete build (including the Electron 22.3.27 native rebuild), stop AIoT Toolkits, replace the package, and start a new AIoT Toolkits process. The database remains under the host-provided plugin data directory across host restarts. Failed builds keep the previous deployment, and launch or readiness failures roll the package back and restore the previous host. Add `--no-devtools`, `--inspect[=<port>]`, `--inspect-brk[=<port>]`, `--surface`, or `--contribution-id` after the second `--` as needed.
63
63
 
64
64
  When this project was created from a local SDK archive, `vendor/aiot-nexus-plugin-sdk.tgz` is part of the source project and must be kept when the project is moved or committed. An explicit local upgrade copies the target archive to `vendor/aiot-nexus-plugin-sdk-<version>.tgz` and updates the exact file dependency.
65
65
 
@@ -67,4 +67,4 @@ For a local SDK archive, the tgz version remains the plugin's current SDK versio
67
67
 
68
68
  For trusted-main breakpoints, open this plugin directory in VS Code, set breakpoints in `src/main.ts`, and run `Debug Nexus plugin`. To attach manually, start `npm run dev:debug` and select `Attach Nexus plugin main process (9229)`. Renderer DevTools opens automatically, with `src/renderer/app.ts` available through the development source map. The default debug build also runs the Electron 22.3.27 native rebuild before launch. Use `npm run dev -- --no-build` only when intentionally reusing a compatible current `dist` output.
69
69
 
70
- The independent host stores the SQLite database and settings under `.nexus-plugin-dev/data/` and mirrors the PluginFrame protocol and current Host API surface. Its simulated Nexus application version defaults to the version recorded by the SDK build; pass `npm run dev -- --host-version <X.Y.Z>` to exercise another compatibility branch. Run the deployable `dist/__PLUGIN_ID__/` in Nexus before delivery because the standalone host does not reproduce the full application shell or routing state.
70
+ The independent host stores the SQLite database and settings under `.nexus-plugin-dev/data/` and mirrors the PluginFrame protocol and current Host API surface. Its simulated AIoT Toolkits application version defaults to the version recorded by the SDK build; pass `npm run dev -- --host-version <X.Y.Z>` to exercise another compatibility branch. Run the deployable `dist/__PLUGIN_ID__/` in AIoT Toolkits before delivery because the standalone host does not reproduce the full application shell or routing state.