@heybox/hb-sdk 0.8.0 → 0.8.1-alpha.10
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/CHANGELOG.md +84 -0
- package/README.md +80 -7
- package/dist/cli-chunks/{build-PYCNacya.cjs → build-CtqIXxDL.cjs} +16 -10
- package/dist/cli-chunks/{context-m2W2XbL0.cjs → context-t57bMfbr.cjs} +17 -3
- package/dist/cli-chunks/{create-BdAg3WGA.cjs → create-BDOPSacf.cjs} +1 -1
- package/dist/cli-chunks/{dev-CyZuw7Yn.cjs → dev-VxjconYc.cjs} +628 -147
- package/dist/cli-chunks/{doctor-DU8rCfUF.cjs → doctor-BkfYdlnZ.cjs} +1 -1
- package/dist/cli-chunks/{index-MMW2ibQm.cjs → index-DDrytLTX.cjs} +87 -15
- package/dist/cli-chunks/{index-DATObqzK.cjs → index-lLaHzwfT.cjs} +2 -2
- package/dist/cli-chunks/{index.esm-BiAaAUFC.cjs → index.esm-Dy4qRhuo.cjs} +9 -8
- package/dist/cli-chunks/{login-B3TThMss.cjs → login-D2EX4R5_.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-BQj8YLI4.cjs → project-vite-CFhz2THK.cjs} +1 -1
- package/dist/cli-chunks/{remote-DNvI7tHH.cjs → remote-CfESZxO2.cjs} +41 -28
- package/dist/cli-chunks/{runtime-gate-BEFp1w_s.cjs → runtime-gate-BWb8QIcX.cjs} +0 -107
- package/dist/cli-chunks/{runtime-permission-env-CtL8rsjB.cjs → runtime-permission-env-BtTDFqEL.cjs} +304 -23
- package/dist/cli-chunks/{session-DjBkjaF8.cjs → session-Dkhwjqhm.cjs} +1 -1
- package/dist/cli-chunks/{skill-cR_wnaw2.cjs → skill-DOveC5jt.cjs} +83 -27
- package/dist/cli-chunks/{version-yEn1E2Bg.cjs → version-C4nE66sX.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-BM9Qo_Ta.js +101 -0
- package/dist/devtools/browser-dev-host/assets/desktop-app-launch-C2I333Yn.js +6 -0
- package/dist/devtools/browser-dev-host/assets/index-Bnb6MMTv.js +567 -0
- package/dist/devtools/browser-dev-host/assets/index-D-aNERAr.css +1 -0
- package/dist/devtools/browser-dev-host/index.html +3 -3
- package/dist/index.cjs.js +1970 -1567
- package/dist/index.esm.js +1969 -1568
- package/dist/miniapp-publish.cjs.js +61 -0
- package/dist/miniapp-publish.esm.js +59 -1
- package/dist/protocol.cjs.js +64 -13
- package/dist/protocol.esm.js +64 -13
- package/dist/templates/vanilla-vite-js/README.md.ejs +1 -1
- package/dist/templates/vanilla-vite-js/package.json.ejs +1 -0
- package/dist/templates/vanilla-vite-js/vite.config.js +1 -1
- package/dist/vite.cjs.js +1333 -18
- package/dist/vite.esm.js +1334 -20
- package/package.json +9 -11
- package/skill/SKILL.md +11 -3
- package/skill/references/api-protocol.md +8 -4
- package/skill/references/api-root.md +328 -210
- package/skill/references/cli.md +11 -3
- package/skill/references/examples.md +13 -1
- package/skill/references/recipes.md +162 -0
- package/skill/references/safety-boundaries.md +9 -3
- package/skill/skill.json +5 -5
- package/types/core/client.d.ts +9 -1
- package/types/core/sdk.d.ts +6 -0
- package/types/core/singleton.d.ts +6 -0
- package/types/devtools/device-logs.d.ts +48 -0
- package/types/index.d.ts +5 -1
- package/types/miniapp-manifest/companion-directory.d.ts +2 -0
- package/types/miniapp-manifest/companion-executable.d.ts +4 -0
- package/types/miniapp-manifest/companion-types.d.ts +38 -0
- package/types/miniapp-manifest/companions.d.ts +17 -0
- package/types/miniapp-manifest/index.d.ts +2 -0
- package/types/miniapp-manifest/node.d.ts +9 -0
- package/types/miniapp-manifest/permissions.d.ts +1 -1
- package/types/miniapp-manifest/schema.d.ts +5 -1
- package/types/miniapp-publish/index.d.ts +17 -2
- package/types/modules/companion/index.d.ts +57 -0
- package/types/modules/environment/index.d.ts +70 -0
- package/types/modules/network/index.d.ts +1 -4
- package/types/modules/network/observability.d.ts +0 -1
- package/types/protocol/dev-session.d.ts +8 -1
- package/types/protocol.d.ts +1 -1
- package/types/vite/index.d.ts +11 -4
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-TzYf9L6C.js +0 -99
- package/dist/devtools/browser-dev-host/assets/index-C5MZZDa5.js +0 -567
- package/dist/devtools/browser-dev-host/assets/index-P-ra4m1y.css +0 -1
- package/dist/devtools/browser-dev-host/assets/workbench-state-BwV7bm4n.js +0 -5
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
- packages/hb-sdk/src/vite/index.ts
|
|
10
10
|
- packages/hb-sdk/README.md
|
|
11
11
|
- apps/docs/hb-sdk/guide/quick-start.md
|
|
12
|
+
- apps/docs/hb-sdk/guide/environment.md
|
|
12
13
|
- apps/docs/hb-sdk/guide/error-handling.md
|
|
13
14
|
- apps/docs/hb-sdk/guide/lifecycle.md
|
|
15
|
+
- apps/docs/hb-sdk/guide/companion.md
|
|
14
16
|
- apps/docs/hb-sdk/guide/cli.md
|
|
15
17
|
|
|
16
18
|
## Contents
|
|
@@ -19,12 +21,13 @@
|
|
|
19
21
|
- [Public root entrypoint](#public-root-entrypoint)
|
|
20
22
|
- [Vite plugin export](#vite-plugin-export)
|
|
21
23
|
- [App-facing concepts](#app-facing-concepts)
|
|
24
|
+
- [Environment info](#environment-info)
|
|
22
25
|
- [Public modules](#public-modules)
|
|
23
26
|
- [Cloud leaderboard](#cloud-leaderboard)
|
|
24
27
|
## Package metadata
|
|
25
28
|
|
|
26
29
|
- Package: `@heybox/hb-sdk`
|
|
27
|
-
- Version at generation time: `0.8.
|
|
30
|
+
- Version at generation time: `0.8.1-alpha.10`
|
|
28
31
|
- Public root export: `@heybox/hb-sdk`
|
|
29
32
|
- Protocol export: `@heybox/hb-sdk/protocol`
|
|
30
33
|
- Vite plugin export: `@heybox/hb-sdk/vite`
|
|
@@ -47,12 +50,35 @@ export {
|
|
|
47
50
|
network,
|
|
48
51
|
ui,
|
|
49
52
|
device,
|
|
53
|
+
environment,
|
|
50
54
|
navigation,
|
|
51
55
|
cloud,
|
|
56
|
+
companion,
|
|
52
57
|
} from './core/singleton';
|
|
53
58
|
export type { MiniProgramSDKHandshakeState, MiniProgramSDKHandshakeStateHandler } from './core/handshake-state';
|
|
54
59
|
export type { MiniProgramEventHandler, MiniProgramEventName, MiniProgramEventPayloadMap } from './protocol/types';
|
|
55
60
|
export type { LoginOptions, LoginPayload, LoginResult, LoginScope, MiniProgramAuthModule } from './modules/auth';
|
|
61
|
+
export type {
|
|
62
|
+
CompanionAttachments,
|
|
63
|
+
CompanionCapability,
|
|
64
|
+
CompanionExit,
|
|
65
|
+
CompanionExitHandler,
|
|
66
|
+
CompanionInfo,
|
|
67
|
+
CompanionNotification,
|
|
68
|
+
CompanionNotificationCategory,
|
|
69
|
+
CompanionNotifications,
|
|
70
|
+
CompanionOutputHandler,
|
|
71
|
+
CompanionOwnershipLostHandler,
|
|
72
|
+
CompanionPreparationStatus,
|
|
73
|
+
CompanionPrepareOptions,
|
|
74
|
+
CompanionPrepareProgress,
|
|
75
|
+
CompanionSession,
|
|
76
|
+
CompanionStdio,
|
|
77
|
+
CompanionState,
|
|
78
|
+
CompanionStateChangeHandler,
|
|
79
|
+
CompanionTarget,
|
|
80
|
+
MiniProgramCompanionModule,
|
|
81
|
+
} from './modules/companion';
|
|
56
82
|
export type {
|
|
57
83
|
DeleteCurrentUserLeaderboardEntryPayload,
|
|
58
84
|
DeleteCurrentUserLeaderboardEntryResult,
|
|
@@ -165,6 +191,12 @@ export type {
|
|
|
165
191
|
VibratePayload,
|
|
166
192
|
VibrateResult,
|
|
167
193
|
} from './modules/device';
|
|
194
|
+
export type {
|
|
195
|
+
MiniProgramEnvironmentInfo,
|
|
196
|
+
MiniProgramEnvironmentModule,
|
|
197
|
+
MiniProgramOperatingSystemName,
|
|
198
|
+
MiniProgramRuntimeMode,
|
|
199
|
+
} from './modules/environment';
|
|
168
200
|
export type {
|
|
169
201
|
ClosePayload,
|
|
170
202
|
CloseResult,
|
|
@@ -188,7 +220,9 @@ export type {
|
|
|
188
220
|
import {
|
|
189
221
|
auth,
|
|
190
222
|
cloud,
|
|
223
|
+
companion,
|
|
191
224
|
device,
|
|
225
|
+
environment,
|
|
192
226
|
getHandshakeState,
|
|
193
227
|
navigation,
|
|
194
228
|
network,
|
|
@@ -217,8 +251,10 @@ const hbSDK = {
|
|
|
217
251
|
network,
|
|
218
252
|
ui,
|
|
219
253
|
device,
|
|
254
|
+
environment,
|
|
220
255
|
navigation,
|
|
221
256
|
cloud,
|
|
257
|
+
companion,
|
|
222
258
|
};
|
|
223
259
|
|
|
224
260
|
export default hbSDK;
|
|
@@ -229,227 +265,59 @@ export default hbSDK;
|
|
|
229
265
|
Use `@heybox/hb-sdk/vite` only in `vite.config.ts`. Do not import it from iframe mini-program business code.
|
|
230
266
|
|
|
231
267
|
```ts
|
|
232
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
233
|
-
import path from 'node:path';
|
|
234
|
-
import { HB_SDK_VERSION } from '../core/version';
|
|
235
|
-
import { MINI_DEV_CONSOLE_EVENT_TYPE, MINI_DEV_IFRAME_WINDOW_NAME, MINI_DEV_CONSOLE_INSTALL_FLAG } from '../core/mini-dev-console';
|
|
236
|
-
import {
|
|
237
|
-
MINIAPP_PLATFORM_VALUES,
|
|
238
|
-
renderMiniappManifest,
|
|
239
|
-
validateMiniappPackageVersionForBuild,
|
|
240
|
-
validateMiniappPlatforms,
|
|
241
|
-
type MiniappPlatform,
|
|
242
|
-
} from '../miniapp-manifest/schema';
|
|
243
|
-
import { readMiniappVersionFromPackageJson } from '../miniapp-manifest/node';
|
|
244
|
-
import { readMiniappPermissionsFromPackageJson } from '../miniapp-manifest/node';
|
|
245
|
-
import { getMissingPermissionsWarning } from '../miniapp-manifest/permissions';
|
|
246
|
-
import { enforceMiniappHtmlPolicy } from './html-policy';
|
|
247
|
-
import { shouldSkipMiniappPlatformCspFromEnv } from './runtime-permission-env';
|
|
248
|
-
|
|
249
268
|
export interface MiniappManifestPlugin {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
269
|
+
name: string;
|
|
270
|
+
config: (config: MiniappManifestUserConfig, env?: {
|
|
271
|
+
command: 'build' | 'serve';
|
|
272
|
+
}) => MiniappManifestUserConfig | void;
|
|
273
|
+
configResolved: (resolved: MiniappManifestResolvedConfig) => void;
|
|
274
|
+
configureServer: (server: MiniappManifestDevServer) => void;
|
|
275
|
+
/** Rollup 在构建失败后仍会跑 closeBundle;用 buildEnd 记录真实错误,避免二次校验掩盖原因。 */
|
|
276
|
+
buildEnd: (error?: Error) => void;
|
|
277
|
+
transformIndexHtml: (html: string) => string;
|
|
278
|
+
closeBundle: (this: MiniappManifestPluginContext) => Promise<void>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface MiniappManifestDevServer {
|
|
282
|
+
httpServer?: Server | null;
|
|
283
|
+
restart?: (forceOptimize?: boolean) => Promise<void>;
|
|
284
|
+
middlewares: {
|
|
285
|
+
use: (handler: (request: IncomingMessage, response: ServerResponse, next: () => void) => void) => void;
|
|
286
|
+
};
|
|
257
287
|
}
|
|
258
288
|
|
|
259
289
|
export interface MiniappManifestUserConfig {
|
|
260
|
-
|
|
261
|
-
|
|
290
|
+
base?: string;
|
|
291
|
+
define?: Record<string, string>;
|
|
262
292
|
}
|
|
263
293
|
|
|
264
294
|
export interface MiniappManifestPluginContext {
|
|
265
|
-
|
|
295
|
+
warn: (message: string) => void;
|
|
266
296
|
}
|
|
267
297
|
|
|
268
298
|
export interface MiniappManifestResolvedConfig {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
port?: number;
|
|
284
|
-
protocol?: 'ws' | 'wss';
|
|
299
|
+
root: string;
|
|
300
|
+
command: 'build' | 'serve';
|
|
301
|
+
build: {
|
|
302
|
+
outDir: string;
|
|
303
|
+
};
|
|
304
|
+
server: {
|
|
305
|
+
host?: string | boolean;
|
|
306
|
+
https?: boolean | Record<string, unknown>;
|
|
307
|
+
port?: number;
|
|
308
|
+
hmr?: boolean | {
|
|
309
|
+
clientPort?: number;
|
|
310
|
+
host?: string;
|
|
311
|
+
port?: number;
|
|
312
|
+
protocol?: 'ws' | 'wss';
|
|
285
313
|
};
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
export { MINIAPP_PLATFORM_VALUES };
|
|
291
|
-
export type { MiniappPlatform };
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* dev serve 时在入口 HTML 最早位置注入无依赖 console 捕获 bootstrap。
|
|
295
|
-
*
|
|
296
|
-
* 必须内联完整 patch 逻辑而非等 SDK 模块加载:vite client、业务代码的 console 调用
|
|
297
|
-
* 都可能早于 SDK 单例初始化(如 [vite] connecting)。捕获条件:
|
|
298
|
-
* 1. 构建期只有 vite serve 走到本分支(生产构建无此代码,双保险之 define 常量仍注入给 SDK 侧);
|
|
299
|
-
* 2. 运行期仅调试 iframe(window.name 标记由调试台 iframe.name 与本 bootstrap 设置)激活。
|
|
300
|
-
* 转发经 window.parent.postMessage(CSP connect-src 不约束 postMessage),失败静默,
|
|
301
|
-
* 严禁回打 console 造成自触发循环。SDK 侧 installMiniDevConsoleForwarding 安装时
|
|
302
|
-
* 检测同款 INSTALL_FLAG,不会重复 patch。
|
|
303
|
-
*/
|
|
304
|
-
function injectMiniDevConsoleBootstrap(html: string): string {
|
|
305
|
-
const bootstrap = `<script>(function(){var LEVELS=['log','debug','info','warn','error'];var FLAG='${MINI_DEV_CONSOLE_INSTALL_FLAG}';var TYPE='${MINI_DEV_CONSOLE_EVENT_TYPE}';if(window.name!=='${MINI_DEV_IFRAME_WINDOW_NAME}')window.name='${MINI_DEV_IFRAME_WINDOW_NAME}';if(window[FLAG])return;var patched=false;for(var i=0;i<LEVELS.length;i++)(function(level){var original=console[level];if(typeof original!=='function')return;console[level]=function(){try{parent.postMessage({type:TYPE,detail:{level:level,args:Array.prototype.slice.call(arguments),timestamp:Date.now()}},'*')}catch(e){}return original.apply(this,arguments)};patched=true})(LEVELS[i]);if(patched)window[FLAG]=true})();</script>`;
|
|
306
|
-
const headIndex = html.indexOf('<head>');
|
|
307
|
-
if (headIndex < 0) {
|
|
308
|
-
return html;
|
|
309
|
-
}
|
|
310
|
-
const insertAt = headIndex + '<head>'.length;
|
|
311
|
-
return `${html.slice(0, insertAt)}${bootstrap}${html.slice(insertAt)}`;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
export interface MiniappManifestOptions {
|
|
315
|
-
platforms: readonly MiniappPlatform[];
|
|
314
|
+
};
|
|
315
|
+
plugins?: readonly {
|
|
316
|
+
name?: string;
|
|
317
|
+
}[];
|
|
316
318
|
}
|
|
317
319
|
|
|
318
|
-
|
|
319
|
-
const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
|
|
320
|
-
const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
|
|
321
|
-
|
|
322
|
-
export function miniappManifest(options: MiniappManifestOptions): MiniappManifestPlugin {
|
|
323
|
-
const platforms = validateMiniappPlatforms(options?.platforms, 'miniappManifest().platforms');
|
|
324
|
-
const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
|
|
325
|
-
let root = process.cwd();
|
|
326
|
-
let outDir = 'dist';
|
|
327
|
-
let command: 'build' | 'serve' = 'build';
|
|
328
|
-
let hmrWebSocketUrl: string | undefined;
|
|
329
|
-
/** 构建链路中更早的失败原因;closeBundle 不得再用产物校验覆盖它。 */
|
|
330
|
-
let priorBuildError: Error | undefined;
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
name: 'heybox-miniapp-manifest',
|
|
334
|
-
config(config, env) {
|
|
335
|
-
assertMiniappViteBase(config.base);
|
|
336
|
-
// command 在本钩子时序早于 configResolved,必须取自 env 命令而非闭包;
|
|
337
|
-
// 旧调用方(测试等)未传 env 时按 build 处理。
|
|
338
|
-
const isServe = env?.command === 'serve';
|
|
339
|
-
return {
|
|
340
|
-
...(config.base === undefined ? { base: './' } : {}),
|
|
341
|
-
define: {
|
|
342
|
-
[sdkVersionBuildConstant]: JSON.stringify(resolveSdkVersion()),
|
|
343
|
-
// 仅 vite serve(hb-sdk dev)注入;vite build / hb-sdk build 永不注入,捕获模块被死代码消除。
|
|
344
|
-
...(isServe ? { [miniDevLoggingConstant]: 'true' } : {}),
|
|
345
|
-
},
|
|
346
|
-
};
|
|
347
|
-
},
|
|
348
|
-
configResolved(resolved) {
|
|
349
|
-
if (resolved.plugins) {
|
|
350
|
-
const manifestPluginCount = resolved.plugins.filter((plugin) => plugin.name === 'heybox-miniapp-manifest').length;
|
|
351
|
-
if (manifestPluginCount !== 1) {
|
|
352
|
-
throw new Error(`Vite 配置必须且只能包含一个 miniappManifest() 插件,当前数量:${manifestPluginCount}`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
root = resolved.root;
|
|
356
|
-
outDir = resolved.build.outDir;
|
|
357
|
-
command = resolved.command;
|
|
358
|
-
hmrWebSocketUrl = resolveHmrWebSocketUrl(resolved);
|
|
359
|
-
},
|
|
360
|
-
buildEnd(error) {
|
|
361
|
-
if (error) {
|
|
362
|
-
priorBuildError = priorBuildError ?? toError(error);
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
transformIndexHtml(html) {
|
|
366
|
-
const enforced = enforceMiniappHtmlPolicy(html, {
|
|
367
|
-
...(command === 'serve' ? { hmrWebSocketUrl } : {}),
|
|
368
|
-
skipPlatformCsp,
|
|
369
|
-
});
|
|
370
|
-
return command === 'serve' ? injectMiniDevConsoleBootstrap(enforced) : enforced;
|
|
371
|
-
}, async closeBundle() {
|
|
372
|
-
// Rollup 在 buildStart/transform 失败后仍会调用 closeBundle。
|
|
373
|
-
// 若此时再抛「缺 index.html」等二次错误,会掩盖真实失败原因。
|
|
374
|
-
if (priorBuildError) {
|
|
375
|
-
throw priorBuildError;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
|
|
379
|
-
const sdkVersion = resolveSdkVersion();
|
|
380
|
-
const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
|
|
381
|
-
if (!parsedPermissions.declared) this.warn(getMissingPermissionsWarning());
|
|
382
|
-
const outputRoot = path.resolve(root, outDir);
|
|
383
|
-
const htmlFiles = collectHtmlFiles(outputRoot);
|
|
384
|
-
const unexpectedHtmlFiles = htmlFiles.filter((file) => path.relative(outputRoot, file) !== 'index.html');
|
|
385
|
-
if (unexpectedHtmlFiles.length > 0) {
|
|
386
|
-
throw new Error(
|
|
387
|
-
`工坊小程序只支持单页应用,构建产物不允许额外 HTML:${unexpectedHtmlFiles
|
|
388
|
-
.map((file) => path.relative(outputRoot, file).split(path.sep).join('/'))
|
|
389
|
-
.join(', ')}`,
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
const indexHtmlPath = path.join(outputRoot, 'index.html');
|
|
393
|
-
if (!htmlFiles.includes(indexHtmlPath)) {
|
|
394
|
-
throw new Error('构建产物必须包含入口 dist/index.html');
|
|
395
|
-
}
|
|
396
|
-
writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(readFileSync(indexHtmlPath, 'utf8'), { skipPlatformCsp }));
|
|
397
|
-
|
|
398
|
-
const manifestPath = path.join(outputRoot, 'manifest.json');
|
|
399
|
-
mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
400
|
-
writeFileSync(
|
|
401
|
-
manifestPath,
|
|
402
|
-
renderMiniappManifest({
|
|
403
|
-
version,
|
|
404
|
-
sdkVersion,
|
|
405
|
-
platforms,
|
|
406
|
-
...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
|
|
407
|
-
}),
|
|
408
|
-
);
|
|
409
|
-
},
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function assertMiniappViteBase(base: string | undefined) {
|
|
414
|
-
if (base !== undefined && base !== '' && base !== './') {
|
|
415
|
-
throw new Error(`工坊小程序 Vite base 仅支持空字符串或 "./",当前值:${base}`);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function toError(error: unknown): Error {
|
|
420
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function collectHtmlFiles(root: string) {
|
|
424
|
-
const files: string[] = [];
|
|
425
|
-
if (!existsSync(root)) return files;
|
|
426
|
-
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
427
|
-
const fullPath = path.join(root, entry.name);
|
|
428
|
-
if (entry.isDirectory()) files.push(...collectHtmlFiles(fullPath));
|
|
429
|
-
else if (entry.isFile() && path.extname(entry.name).toLowerCase() === '.html') files.push(fullPath);
|
|
430
|
-
}
|
|
431
|
-
return files;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function resolveSdkVersion() {
|
|
435
|
-
if (HB_SDK_VERSION !== sdkVersionPlaceholder) return HB_SDK_VERSION;
|
|
436
|
-
const packageJson = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) as { version?: unknown };
|
|
437
|
-
if (typeof packageJson.version !== 'string' || !packageJson.version) {
|
|
438
|
-
throw new Error('未能读取 @heybox/hb-sdk 当前版本号');
|
|
439
|
-
}
|
|
440
|
-
return packageJson.version;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function resolveHmrWebSocketUrl(resolved: MiniappManifestResolvedConfig) {
|
|
444
|
-
const hmr = resolved.server.hmr;
|
|
445
|
-
if (hmr === false) return undefined;
|
|
446
|
-
const options = typeof hmr === 'object' ? hmr : {};
|
|
447
|
-
const protocol = options.protocol ?? (resolved.server.https ? 'wss' : 'ws');
|
|
448
|
-
const configuredHost = options.host ?? resolved.server.host;
|
|
449
|
-
const host = typeof configuredHost === 'string' && configuredHost !== '0.0.0.0' ? configuredHost : '127.0.0.1';
|
|
450
|
-
const port = options.clientPort ?? options.port ?? resolved.server.port;
|
|
451
|
-
return `${protocol}://${host}${port ? `:${port}` : ''}`;
|
|
452
|
-
}
|
|
320
|
+
export declare function miniappManifest(): MiniappManifestPlugin;
|
|
453
321
|
```
|
|
454
322
|
|
|
455
323
|
## 生产构建
|
|
@@ -458,7 +326,7 @@ function resolveHmrWebSocketUrl(resolved: MiniappManifestResolvedConfig) {
|
|
|
458
326
|
hb-sdk build [--env <name>] [--verbose]
|
|
459
327
|
```
|
|
460
328
|
|
|
461
|
-
`hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `vite.config.ts`
|
|
329
|
+
`hb-sdk build` 直接使用项目安装的 Vite,先清理再生成固定的 `dist/`,并校验小程序入口、Manifest 和可上传产物。它不执行类型检查,不要求 CLI 登录或绑定小程序,也不访问远端服务。项目必须在 `package.json#heybox.platforms` 声明目标平台,并在 `vite.config.ts` 中显式注册无参的 `miniappManifest()`;配置与构建产物不一致时构建失败。
|
|
462
330
|
|
|
463
331
|
直接运行 `hb-sdk build` 或 `vite build` 时默认注入平台 CSP。`hb-sdk remote deploy` 会在构建前读取绑定小程序的远端批准结果,并与当前版本声明取交集;仅当有效 `network` 权限启用时才跳过平台 CSP,`useOfficialDomain` 不参与该判定。权限缺失、非法或读取失败时继续注入,Runtime Gate、Manifest 与 HTML 构建检查始终保留。
|
|
464
332
|
|
|
@@ -648,6 +516,96 @@ async function saveFromUserAction(text: string) {
|
|
|
648
516
|
|
|
649
517
|
大多数小程序页面都应该使用默认实例。0.6 起不再对业务代码提供独立实例工厂。
|
|
650
518
|
|
|
519
|
+
## Environment info
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
# 环境信息
|
|
523
|
+
|
|
524
|
+
`environment` 模块提供当前小程序实例的静态环境快照,包括运行模式、小黑盒 App 版本、canonical 小程序身份、操作系统和 SDK 版本。该能力无需在 `heybox.permissions` 中声明,也不会触发用户授权。
|
|
525
|
+
|
|
526
|
+
## 推荐用法
|
|
527
|
+
|
|
528
|
+
优先使用异步方法。SDK 尚未完成握手时,`getInfo()` 会自动等待:
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
import { environment } from '@heybox/hb-sdk'
|
|
532
|
+
|
|
533
|
+
const info = await environment.getInfo()
|
|
534
|
+
|
|
535
|
+
console.log(info.runtime.mode)
|
|
536
|
+
console.log(info.host.appVersion)
|
|
537
|
+
console.log(info.miniProgram.id, info.miniProgram.version)
|
|
538
|
+
console.log(info.operatingSystem.name, info.operatingSystem.version)
|
|
539
|
+
console.log(info.sdk.version)
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
返回对象采用固定结构并深度冻结:
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
interface MiniProgramEnvironmentInfo {
|
|
546
|
+
readonly runtime: {
|
|
547
|
+
readonly mode: 'production' | 'preview' | 'development' | 'unknown'
|
|
548
|
+
}
|
|
549
|
+
readonly host: {
|
|
550
|
+
readonly appVersion: string | null
|
|
551
|
+
}
|
|
552
|
+
readonly miniProgram: {
|
|
553
|
+
readonly id: string | null
|
|
554
|
+
readonly version: string | null
|
|
555
|
+
}
|
|
556
|
+
readonly operatingSystem: {
|
|
557
|
+
readonly name: 'android' | 'ios' | 'ohos' | 'windows' | 'macos' | 'linux' | 'unknown'
|
|
558
|
+
readonly version: string | null
|
|
559
|
+
}
|
|
560
|
+
readonly sdk: {
|
|
561
|
+
readonly version: string
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## 同步读取
|
|
567
|
+
|
|
568
|
+
只有已经确认 SDK 为 `ready` 时才能调用 `getInfoSync()`。SDK 会在派发 `ready` 状态前缓存环境快照,因此可以在握手状态订阅中同步读取:
|
|
569
|
+
|
|
570
|
+
```ts
|
|
571
|
+
import { environment, onHandshakeStateChange } from '@heybox/hb-sdk'
|
|
572
|
+
|
|
573
|
+
const unsubscribe = onHandshakeStateChange(state => {
|
|
574
|
+
if (state.status !== 'ready') return
|
|
575
|
+
|
|
576
|
+
const info = environment.getInfoSync()
|
|
577
|
+
console.log(info.operatingSystem.name)
|
|
578
|
+
})
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
握手完成前调用会抛出 `HbMiniProgramSDKError`,错误码为 `ENVIRONMENT_NOT_READY`。通常不需要自行等待握手;不要求同步执行的代码直接使用 `await environment.getInfo()`。
|
|
582
|
+
|
|
583
|
+
## 字段语义
|
|
584
|
+
|
|
585
|
+
| 字段 | 语义 |
|
|
586
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
587
|
+
| `runtime.mode` | 当前实例的粗粒度运行模式。正式发布为 `production`,版本预览为 `preview`,本地或 Dev Session 为 `development`。 |
|
|
588
|
+
| `host.appVersion` | 当前小黑盒 App 版本。它是 Host 提供的不透明字符串。 |
|
|
589
|
+
| `miniProgram.id` | 平台确认的 canonical `mini_program_id`,不返回数字 ID、用户 ID 或其他旧 ID。 |
|
|
590
|
+
| `miniProgram.version` | 当前加载的小程序版本,是不透明字符串。 |
|
|
591
|
+
| `operatingSystem.name` | Host 明确提供的标准系统枚举,不从小程序 iframe 的 UA 推断。 |
|
|
592
|
+
| `operatingSystem.version` | Host 明确提供的系统版本;当前 Host 未提供时为 `null`。 |
|
|
593
|
+
| `sdk.version` | 当前页面实际运行的 `@heybox/hb-sdk` 版本,遵循 npm SemVer。 |
|
|
594
|
+
|
|
595
|
+
除 `sdk.version` 外,其他版本字段均不承诺 SemVer 或大小比较语义。需要判断某项能力是否可用时,应依据能力调用结果进行降级,不要仅比较 App 或系统版本。
|
|
596
|
+
|
|
597
|
+
## 缺失与兼容
|
|
598
|
+
|
|
599
|
+
旧 Host、匿名本地调试或未提供某个字段的 Host 仍会返回完整结构:缺失字符串为 `null`,未知枚举为 `'unknown'`。非法 Host 字段只会被逐项降级,不会阻断 SDK 握手。
|
|
600
|
+
|
|
601
|
+
Browser Mock 在 Runtime 启动时使用当前设备预设作为操作系统名称,不读取开发者电脑的 UA。切换设备预设会保留当前 Runtime 和页面状态;需要重新生成环境快照时,使用调试台的重启操作。
|
|
602
|
+
|
|
603
|
+
## 信任边界
|
|
604
|
+
|
|
605
|
+
环境信息仅用于界面适配、兼容降级、埋点和诊断,不能用于鉴权、权限控制或风控。小程序提交给服务端的任何环境字段都可能被修改;服务端安全判断必须使用自身认证上下文或平台受信数据。
|
|
606
|
+
|
|
607
|
+
该 API 不提供设备型号、品牌、UA、CPU、内存、设备唯一标识、账号信息或凭据。屏幕和安全区域继续通过 [`viewport.getWindowInfo()`](https://docs.xiaoheihe.cn/hb_sdk/reference/sdk/viewport/getWindowInfo/) 获取。
|
|
608
|
+
|
|
651
609
|
|
|
652
610
|
# 错误处理
|
|
653
611
|
|
|
@@ -686,8 +644,13 @@ try {
|
|
|
686
644
|
- `AUTHORIZATION_CANCELLED` 表示用户取消、拒绝或关闭授权页面,应正常结束当前操作。
|
|
687
645
|
- `SERVER_API_REQUIRED` 表示当前用户数据必须经开发者服务端 OpenAPI 获取,不应在页面重试对应 Host API。
|
|
688
646
|
- `METHOD_FORBIDDEN` 表示当前 Host 未实现或关闭对应能力。V1 files/download 在 Mobile、Web 和 Browser Dev Host 都会得到该错误。
|
|
647
|
+
- `ENVIRONMENT_NOT_READY` 表示在 SDK 完成握手前调用了 `environment.getInfoSync()`;一般改用会自动等待的 `environment.getInfo()`。
|
|
689
648
|
- `FILE_PICKER_CANCELLED` 表示用户取消了外部文件或目录选择,应正常结束当前操作。
|
|
690
649
|
- `DOWNLOAD_CANCELLED` 表示取消意图先于 Host 提交生效;Host 已完成提交时仍返回成功。`DOWNLOAD_TIMEOUT` 表示网络空闲超时,失败不会改动原目标。
|
|
650
|
+
- `COMPANION_NOT_PREPARED` 表示启动前尚未显式准备;应由新的用户操作调用 `companion.prepare()`,不要在 `launch()` 中自动重试。
|
|
651
|
+
- `COMPANION_SESSION_OWNERSHIP_LOST` 表示 Session controller 已由新的 Runtime generation 接管;停止旧 controller 写入,需要继续取得控制权时重新取得活动 Session。
|
|
652
|
+
- `COMPANION_DESCRIPTOR_UNAVAILABLE` 表示授权或描述符暂时不可用,Runtime 会保留 `retryable: true`。
|
|
653
|
+
- `COMPANION_INTEGRITY_FAILED`、`COMPANION_ARCHIVE_INVALID` 或操作系统拦截都不能绕过;停止重试并修正归档后提交新审核版本。
|
|
691
654
|
- `DOWNLOAD_HTTP_STATUS` 的 `data` 只包含脱敏后的 `status`、可选 `statusText` 和安全响应头。
|
|
692
655
|
- 超时或运行环境不可用时,允许用户重试或退出当前流程。
|
|
693
656
|
- 上报 `code`、`message` 和必要的业务上下文,不要上报用户凭据或敏感数据。
|
|
@@ -741,6 +704,8 @@ function reportSDKError(errorCode: string, message: string, data?: unknown) {
|
|
|
741
704
|
picker 取消、授权撤销、文件缺失、quota、下载状态或取消,不依赖 `message` 文案,也不要记录
|
|
742
705
|
真实路径、内部 handle 或响应凭据头。
|
|
743
706
|
|
|
707
|
+
Companion 失败仍使用 `HbMiniProgramSDKError`。prepare 与 launch 的授权取消属于两次独立用户决定;业务不得在一次点击中自动串联授权,也不得用 Browser Fake 成功替代 Windows/macOS 真机错误处理验证。完整状态机见[受管桌面程序](https://docs.xiaoheihe.cn/hb_sdk/guide/companion)。
|
|
708
|
+
|
|
744
709
|
## Public modules
|
|
745
710
|
|
|
746
711
|
## 能力概览
|
|
@@ -750,6 +715,7 @@ SDK 还提供 `getHandshakeState()` / `onHandshakeStateChange()` 管理持久握
|
|
|
750
715
|
| 模块 | 用途 |
|
|
751
716
|
| ------------ | ----------------------------------------------- |
|
|
752
717
|
| `auth` | 获取交给开发者服务端交换的短期授权码 |
|
|
718
|
+
| `environment` | 读取当前实例的冻结环境快照 |
|
|
753
719
|
| `user` | 读取 Host 当前用户资料、隔离身份或撤销授权 |
|
|
754
720
|
| `share` | 打开分享、复制链接或截图分享流程 |
|
|
755
721
|
| `ui` | 展示 Toast 和 Loading |
|
|
@@ -760,6 +726,7 @@ SDK 还提供 `getHandshakeState()` / `onHandshakeStateChange()` 管理持久握
|
|
|
760
726
|
| `files` | 声明受控 sandbox 与用户选择授权的文件、目录操作 |
|
|
761
727
|
| `cloud` | 使用小程序云端排行榜 |
|
|
762
728
|
| `network` | 发起经过平台授权的网络请求,并声明受控流式下载 |
|
|
729
|
+
| `companion` | 准备、启动并控制当前版本审核过的桌面辅助应用 |
|
|
763
730
|
|
|
764
731
|
具体方法、参数和返回值以 [API Reference](https://docs.xiaoheihe.cn/hb_sdk/reference/) 为准,常见组合写法见 [Recipes](https://docs.xiaoheihe.cn/hb_sdk/recipes/)。
|
|
765
732
|
|
|
@@ -818,6 +785,157 @@ onUnmounted(stopLifecycleEvents)
|
|
|
818
785
|
- 组件或页面销毁时清理 `on` 注册的监听,避免重复响应。
|
|
819
786
|
- 生命周期事件只派发给注册当时存在的监听器,不会重放;一次性 `launch` 信息应在页面启动阶段通过业务能力调用处理。
|
|
820
787
|
- 收到 `unload` 后,当前 SDK 上下文不可恢复,未完成请求会失败;不要在同一页面上下文继续重试能力调用。
|
|
788
|
+
- Companion preparation 与进程 Session 由 Host 持有,页面 `hide` 或 reload 不会自动取消或终止;新页面分别通过 `getPreparationStatus()` 与 `getActiveSession()` 恢复观察和控制。
|
|
789
|
+
- 新 Runtime generation 取得活动 Companion Session 后,旧 controller 会收到 ownership-lost 并失去写权限;stdio 输出可能 at-least-once 重放,业务必须按自身协议去重。
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
# 受管桌面程序
|
|
793
|
+
|
|
794
|
+
`companion` 用于准备和启动随当前小程序审核版本发布的桌面辅助应用。V1 仅支持新 PC Host 的
|
|
795
|
+
`windows-x64` 与 `macos-arm64`;老 PC、Mobile、Web 和 Linux 不在支持范围内。
|
|
796
|
+
|
|
797
|
+
## 声明与构建
|
|
798
|
+
|
|
799
|
+
项目必须在 `package.json` 显式声明高风险权限:
|
|
800
|
+
|
|
801
|
+
```json
|
|
802
|
+
{
|
|
803
|
+
"heybox": {
|
|
804
|
+
"permissions": {
|
|
805
|
+
"companion": { "enabled": true }
|
|
806
|
+
},
|
|
807
|
+
"companions": {
|
|
808
|
+
"runner": {
|
|
809
|
+
"displayName": "桌面辅助程序",
|
|
810
|
+
"capabilities": ["stdio"],
|
|
811
|
+
"targets": {
|
|
812
|
+
"windows-x64": {
|
|
813
|
+
"source": "companion/runner-windows-x64",
|
|
814
|
+
"entrypoint": { "kind": "executable", "path": "runner.exe" },
|
|
815
|
+
"elevation": "none",
|
|
816
|
+
"args": []
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
归档统一通过 `package.json#heybox.companions` 配置,`source` 相对该 `package.json` 所在目录解析。
|
|
826
|
+
Vite 中使用 `miniappManifest({ platforms: ['windows'] })` 等构建配置;不要在 Vite 配置中复制 Companion 声明。
|
|
827
|
+
构建和本地调试读取同一份声明。构建会检查 ZIP 结构和固定入口,复制到
|
|
828
|
+
`dist/companions/<alias>/<target>.zip`,并生成大小与 SHA-256;Web 文件和 Companion 文件进入同一上传
|
|
829
|
+
session 与同一审核版本。页面不能读取部署后的 `manifest.json`,也不能取得真实 CDN URL 或本机安装路径。
|
|
830
|
+
|
|
831
|
+
`hb-sdk dev` 的真实 PC 联调可以直接使用这里声明的本地 ZIP,不要求先上传版本,但项目必须已绑定且 COA
|
|
832
|
+
批准 `companion`。dev server 只向 loopback 提供固定 Manifest/ZIP 路由,CLI 用短期票据绑定当前 dev
|
|
833
|
+
进程的 canonical snapshot。ZIP 或配置变化后会校验并切换到新快照,已有 Session 保持原有产物;
|
|
834
|
+
新产物必须重新准备并启动。校验失败时调试台显示阻塞原因,不会悄悄使用旧文件或线上产物。
|
|
835
|
+
单次开发服务最多保留 32 份快照、累计 ZIP 大小 1 GiB;达到上限后停止接纳新快照,
|
|
836
|
+
请先结束调试 Session,再重启开发服务,已有快照不会被自动逐出。
|
|
837
|
+
未配置本地 Companion 时使用 Browser Fake 做状态联调,真实 PC 不会回退到线上 candidate。
|
|
838
|
+
|
|
839
|
+
构建与 PC 安装都会检查真实二进制格式:Windows 为 AMD64 PE,macOS 为包含 arm64 的 Mach-O;
|
|
840
|
+
脚本即使改名或有执行权限也会被拒绝。授权弹窗展示产物来源与签名状态,本地 ZIP 明确标记
|
|
841
|
+
“本地开发产物,未经过版本审核”。
|
|
842
|
+
`.app` 从 XML 或 binary Info.plist 的 `CFBundleExecutable` 精确选择主入口。
|
|
843
|
+
|
|
844
|
+
Manifest 只接受固定入口和固定 args。V1 仅支持普通权限启动,`elevation` 只能省略或设为 `none`;
|
|
845
|
+
`required` 会在构建时明确失败,管理员权限启动留待后续版本。公开 API 不接受 executable path、动态 args、
|
|
846
|
+
cwd、environment、shell 或任意命令;不要把 `files.sandbox` 中的普通文件当作可执行对象。
|
|
847
|
+
`displayName` 最多 120 个 Unicode code point;args 最多 64 项,单项最多 1024 UTF-8 bytes、总计最多
|
|
848
|
+
16384 UTF-8 bytes,并拒绝 NUL、CR 和 LF。
|
|
849
|
+
|
|
850
|
+
## 准备与启动
|
|
851
|
+
|
|
852
|
+
```ts
|
|
853
|
+
import { companion } from '@heybox/hb-sdk'
|
|
854
|
+
|
|
855
|
+
// 分别绑定到「准备」与「启动」按钮,不能在一个点击中串联。
|
|
856
|
+
function prepareFromUserAction() {
|
|
857
|
+
return companion.prepare('runner', {
|
|
858
|
+
onProgress(progress) {
|
|
859
|
+
renderProgress(progress)
|
|
860
|
+
},
|
|
861
|
+
})
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function launchFromUserAction() {
|
|
865
|
+
return companion.launch('runner')
|
|
866
|
+
}
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
`prepare()` 和 `launch()` 必须分别由新的可信用户手势触发。每次真正开始新的准备操作前,Runtime 展示一次
|
|
870
|
+
Host 可信授权;已 ready 或加入同一在途准备时不会重复授权。每次真正创建新进程前,`launch()` 再展示独立
|
|
871
|
+
授权,不能复用 prepare 的手势或授权。`launch()` 不会隐式调用 `prepare()`,未准备时稳定失败。
|
|
872
|
+
|
|
873
|
+
准备操作属于 Host:页面 reload、最小化或进入后台不会取消。新页面可用
|
|
874
|
+
`getPreparationStatus()` 查询状态,并通过相同 alias 加入在途操作;显式取消使用
|
|
875
|
+
`cancelPreparation(alias)` 或 `AbortSignal`。页面 reload 只断开观察者,不会向 Host 发送取消;原子提交已经开始时,最终提交结果优先于取消意图。
|
|
876
|
+
|
|
877
|
+
## Session 与 stdio
|
|
878
|
+
|
|
879
|
+
同一小程序 identity 与 alias 同时最多有一个活动 Session。`getActiveSession(alias)` 会取得当前活动 Session
|
|
880
|
+
的控制权;新 Runtime generation 接管后,旧 controller 的写入、关闭 stdin、终止、附件和通知操作都会失败。
|
|
881
|
+
进程退出后 `getActiveSession()` 返回 `undefined`,业务结果应由小程序自行持久化。
|
|
882
|
+
|
|
883
|
+
stdio 仅在 Manifest 声明 `stdio` 时存在,输入输出都是原始 `Uint8Array`。SDK 不做 UTF-8、换行、NDJSON、
|
|
884
|
+
RPC 或业务状态解析。输出采用 at-least-once 投递,收到 chunk 后 SDK 会确认 sequence;断连与重取控制权可能
|
|
885
|
+
导致重复,因此业务 framing 必须携带可去重标识,并能处理拆包、粘包和重放。
|
|
886
|
+
|
|
887
|
+
```ts
|
|
888
|
+
const session = await companion.getActiveSession('runner')
|
|
889
|
+
if (session?.stdio) {
|
|
890
|
+
const stop = session.stdio.onStdout(bytes => decodeBusinessFrame(bytes))
|
|
891
|
+
await session.stdio.write(encodeBusinessFrame({ type: 'status' }))
|
|
892
|
+
// 组件销毁时调用 stop();业务退出协议可先写入,再 closeStdin()。
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
平台不提供语义跨平台不稳定的 request-close。业务可先通过自身 stdio 协议请求正常退出,或关闭 stdin;
|
|
897
|
+
`terminate()` 是强制结束完整受管进程树。页面隐藏或 reload 不结束 Session,用户真正关闭小程序窗口时由 Host
|
|
898
|
+
处理完整进程树生命周期。
|
|
899
|
+
|
|
900
|
+
## 可选能力
|
|
901
|
+
|
|
902
|
+
- `attachments`:`session.attachments.open(id)` 返回只读 `FileHandle`;opaque id 来自桌面程序自身业务协议,页面无法指定物理路径。
|
|
903
|
+
- `notifications`:页面解析业务协议后,可请求 Host 展示固定类别通知;页面完全断开时不承诺业务通知。
|
|
904
|
+
- `stdio`:仅提供原始字节双向流,不提供文本编码或 RPC。
|
|
905
|
+
|
|
906
|
+
只有 Manifest 声明且 Host 实现的能力才出现在 Session。不要根据操作系统或属性存在性猜测支持情况。
|
|
907
|
+
|
|
908
|
+
## 调试与验收
|
|
909
|
+
|
|
910
|
+
调试台分别显示浏览器、手机与 PC Companion 的就绪状态,以及本地 alias、平台和产物摘要短前缀。
|
|
911
|
+
PC 会在页面闲置时继续复核授权;明确撤销权限会结束进程树,临时网络故障只允许有时限的宽限,
|
|
912
|
+
不会让已失去授权的进程无限运行。关闭开发服务前请先停止调试进程。
|
|
913
|
+
|
|
914
|
+
Browser Dev Host 只提供确定性的 Fake 场景,用于开发准备进度、Session 状态、stdio framing、错误 UI 与恢复逻辑。
|
|
915
|
+
Fake 不选择、不下载、不解压、不启动本机程序,也不经过操作系统安全检查或真实进程组。
|
|
916
|
+
因此 Browser Fake、Mobile 或 Web 的结果不能作为桌面发布证据;发布前必须分别在目标 Windows/macOS 新 PC Host
|
|
917
|
+
完成归档摘要、准备取消、启动、stdio、退出与进程树清理验收。
|
|
918
|
+
|
|
919
|
+
## 失败处理
|
|
920
|
+
|
|
921
|
+
- `USER_GESTURE_REQUIRED`:当前 prepare 或 launch 缺少新的可信用户手势。
|
|
922
|
+
- `USER_CANCELLED`:用户拒绝或关闭本次 Host 授权,应正常结束流程。
|
|
923
|
+
- `COMPANION_NOT_PREPARED`:先由新的用户操作显式调用 `prepare()`。
|
|
924
|
+
- `COMPANION_INTEGRITY_FAILED` / `COMPANION_ARCHIVE_INVALID`:停止重试并提交新审核版本,不能绕过摘要或系统安全检查。
|
|
925
|
+
- `COMPANION_SESSION_OWNERSHIP_LOST`:当前 Runtime 已失去 controller;需要时重新调用 `getActiveSession()`。
|
|
926
|
+
- `COMPANION_DESCRIPTOR_UNAVAILABLE`:授权或描述符暂时不可用,可在有界重试策略下重试。
|
|
927
|
+
- `COMPANION_ELEVATION_DENIED`:为未来管理员权限启动保留的兼容错误码;V1 构建不接受 `elevation: 'required'`。
|
|
928
|
+
- `METHOD_FORBIDDEN` / `COMPANION_UNSUPPORTED`:当前 Host 或平台未实现,不要循环重试或用任意执行替代。
|
|
929
|
+
|
|
930
|
+
完整类型与方法见 [API Reference](https://docs.xiaoheihe.cn/hb_sdk/reference/)。
|
|
931
|
+
|
|
932
|
+
## 完成检查
|
|
933
|
+
|
|
934
|
+
- 权限、目标 ZIP、固定入口和固定参数都随同一版本提交审核。
|
|
935
|
+
- prepare 与 launch 使用两次独立可信用户操作和 Host 授权。
|
|
936
|
+
- stdio 业务协议处理了原始字节 framing、at-least-once 去重和断连恢复。
|
|
937
|
+
- 未向页面暴露或传入 URL、物理路径、动态 args、环境变量、shell 或 PID。
|
|
938
|
+
- Browser Fake 只用于开发,目标平台均已完成新 PC 真机验收。
|
|
821
939
|
|
|
822
940
|
## 云端排行榜
|
|
823
941
|
|