@heybox/hb-sdk 0.8.1-alpha.3 → 0.8.1-alpha.7
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 +40 -0
- package/README.md +47 -3
- package/dist/cli-chunks/{build-B5Sbi8Sq.cjs → build-C8oN_Ms9.cjs} +7 -5
- package/dist/cli-chunks/{context-DuxaBL3Y.cjs → context-DZkqvg9S.cjs} +8 -3
- package/dist/cli-chunks/{create-CIfZrg3F.cjs → create-CToSHRfO.cjs} +1 -1
- package/dist/cli-chunks/{dev-CFubP5kW.cjs → dev-CTA0lsLD.cjs} +479 -140
- package/dist/cli-chunks/{doctor-IPKn3PBn.cjs → doctor-DRa_kjP1.cjs} +1 -1
- package/dist/cli-chunks/{index-KHhdZ8eZ.cjs → index-Bkl5YmtN.cjs} +2 -2
- package/dist/cli-chunks/{index-DCJN-V1y.cjs → index-zNpZDWhc.cjs} +87 -15
- package/dist/cli-chunks/{index.esm-8hgZlNF8.cjs → index.esm-CGoUW47e.cjs} +9 -8
- package/dist/cli-chunks/{login-CoB9jXxC.cjs → login-CCEPk2Ok.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-DMxdOWsU.cjs → project-vite-couFw5bL.cjs} +1 -1
- package/dist/cli-chunks/{remote-D4ZcngLn.cjs → remote-DQ70o-Km.cjs} +33 -24
- package/dist/cli-chunks/{runtime-permission-env-XAZFauvZ.cjs → runtime-permission-env-BN43V7d0.cjs} +181 -16
- package/dist/cli-chunks/{session-BnoHxzFq.cjs → session-C1R_39si.cjs} +1 -1
- package/dist/cli-chunks/{skill-CDEJtATn.cjs → skill-CVXOE5jH.cjs} +83 -27
- package/dist/cli-chunks/{version-BGzCIQhE.cjs → version-DbDVL6vm.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-CH6L1ZJI.js +101 -0
- package/dist/devtools/browser-dev-host/assets/desktop-app-launch-DnPRjCa3.js +6 -0
- package/dist/devtools/browser-dev-host/assets/index-D-aNERAr.css +1 -0
- package/dist/devtools/browser-dev-host/assets/index-DocojUxT.js +567 -0
- package/dist/devtools/browser-dev-host/index.html +3 -3
- package/dist/index.cjs.js +1893 -1571
- package/dist/index.esm.js +1893 -1572
- 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/vite.cjs.js +1316 -10
- package/dist/vite.esm.js +1317 -12
- package/package.json +7 -7
- package/skill/SKILL.md +8 -3
- package/skill/references/api-protocol.md +3 -3
- package/skill/references/api-root.md +224 -208
- package/skill/references/cli.md +7 -1
- package/skill/references/examples.md +4 -0
- package/skill/references/recipes.md +161 -0
- package/skill/references/safety-boundaries.md +8 -2
- package/skill/skill.json +5 -5
- package/types/core/client.d.ts +2 -0
- package/types/core/sdk.d.ts +3 -0
- package/types/core/singleton.d.ts +3 -0
- package/types/devtools/device-logs.d.ts +48 -0
- package/types/index.d.ts +3 -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 +7 -0
- package/types/miniapp-manifest/permissions.d.ts +1 -1
- package/types/miniapp-manifest/schema.d.ts +4 -1
- package/types/miniapp-publish/index.d.ts +17 -2
- package/types/modules/companion/index.d.ts +57 -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 +6 -0
- package/types/vite/index.d.ts +10 -0
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-DzEst9n7.js +0 -99
- package/dist/devtools/browser-dev-host/assets/index-B-NHlLsr.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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heybox/hb-sdk",
|
|
3
|
-
"version": "0.8.1-alpha.
|
|
3
|
+
"version": "0.8.1-alpha.7",
|
|
4
4
|
"sideEffects": [
|
|
5
5
|
"./src/index.ts",
|
|
6
6
|
"./src/core/singleton.ts",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"skills": "1.5.23",
|
|
63
63
|
"undici": "^7.28.0",
|
|
64
64
|
"ws": "^8.18.0",
|
|
65
|
-
"@heybox/hb-sdk-protocol": "0.8.1-alpha.
|
|
65
|
+
"@heybox/hb-sdk-protocol": "0.8.1-alpha.7"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"vite": ">=5"
|
|
@@ -113,13 +113,13 @@
|
|
|
113
113
|
"vue-tsc": "^3.3.7",
|
|
114
114
|
"vite": "^8.0.12",
|
|
115
115
|
"vitest": "^3.2.4",
|
|
116
|
+
"@heybox/hb-api": "~1.28.3",
|
|
116
117
|
"@heybox-domain/heybox-theme": "~0.1.0",
|
|
118
|
+
"@heybox/hb-sdk-runtime": "~0.8.1-alpha.7",
|
|
119
|
+
"@heybox/runtime-policy": "~0.2.0",
|
|
117
120
|
"@heybox-domain/heybox-vue3-ui": "~0.1.0",
|
|
118
|
-
"@heybox/hb-sdk-runtime": "~0.8.1-alpha.3",
|
|
119
|
-
"@heybox/hb-api": "~1.28.2",
|
|
120
|
-
"@heybox/runtime": "~0.2.0",
|
|
121
121
|
"@heybox/runtime-transport-fetch": "~0.2.0",
|
|
122
|
-
"@heybox/runtime
|
|
122
|
+
"@heybox/runtime": "~0.2.0"
|
|
123
123
|
},
|
|
124
124
|
"publishConfig": {
|
|
125
125
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
"test:types:characterization": "tsc -p tsconfig.characterization.json --noEmit --pretty false",
|
|
176
176
|
"test:types:browser-dev-host": "vue-tsc -p tsconfig.browser-dev-host.json --noEmit --pretty false",
|
|
177
177
|
"test:types": "pnpm run test:types:characterization && pnpm run test:types:browser-dev-host",
|
|
178
|
-
"test:e2e:browser-dev-host": "pnpm run build:
|
|
178
|
+
"test:e2e:browser-dev-host": "pnpm run build:lib && pnpm run build:cli:bundle && NODE_OPTIONS='--conditions=heybox' vitest run --config vitest.browser-dev-host-e2e.config.ts",
|
|
179
179
|
"test:create-template:artifact": "node scripts/test-create-template-artifact.mjs",
|
|
180
180
|
"test:unit:coverage": "NODE_OPTIONS='--conditions=heybox' vitest run --coverage",
|
|
181
181
|
"test:vite": "pnpm run build:hb-sdk-protocol && pnpm run build:hb-api-contract && vitest run --config vitest.vite.config.ts",
|
package/skill/SKILL.md
CHANGED
|
@@ -21,6 +21,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
|
|
|
21
21
|
1. For root SDK imports, singleton usage, modules, and errors, read `references/api-root.md`.
|
|
22
22
|
2. For host/runtime protocol contracts only, read `references/api-protocol.md`. Do not use this reference for mini-program business code.
|
|
23
23
|
3. For common business flows, read `references/recipes.md`.
|
|
24
|
+
For managed desktop programs, read the `Managed Companion` section and preserve its trust boundaries.
|
|
24
25
|
4. For CLI commands, production builds, local debugging, device debugging, CLI login, Agent Skill doctor, and update reminders, read `references/cli.md`.
|
|
25
26
|
5. For allowed/forbidden capabilities and security boundaries, read `references/safety-boundaries.md`.
|
|
26
27
|
6. For Vite build manifest behavior, read `references/api-root.md` and `references/safety-boundaries.md`.
|
|
@@ -52,14 +53,16 @@ Apply these instructions when writing, reviewing, or debugging code that consume
|
|
|
52
53
|
12. Use `share.showShareMenu({ extra })` to open the share menu or `share.copyLink({ extra })` to copy and return the mini-program share link. `share.showShareMenu()` always uses the platform `common_share` landing page and does not accept a custom `url`. Read the JSON-compatible page state synchronously with `share.getExtra()` after launch, validate the developer-defined fields, and fall back to the default page when it returns `undefined`.
|
|
53
54
|
13. New PC enables the retained `files` and `network.download()` contract with persistent sandbox storage, single-file/directory pickers, exact File saving, and public-network streaming downloads. Mobile, Web, Browser Dev Host, and legacy PC return `METHOD_FORBIDDEN` and provide no memory/Blob fallback. Call `files.pickFiles()` / `pickDirectory()` / `saveFile()` only from trusted user actions; `saveFile()` accepts only `suggestedName`, `remove()` only deletes sandbox objects, and non-empty directories require `remove({ recursive: true })`.
|
|
54
55
|
14. Use `network.download()` only with an SDK-created File/Directory target. It is GET-only, does not follow redirects, and exposes local `AbortSignal` / progress callbacks without sending functions over the bridge. Abort is a cancellation intent; a Host commit that already won still resolves successfully.
|
|
55
|
-
15. Use `
|
|
56
|
-
16.
|
|
56
|
+
15. Use `companion.prepare()` and `companion.launch()` only from separate trusted user actions. Launch does not prepare implicitly. Treat stdio as raw `Uint8Array` with at-least-once output delivery, and implement business framing and sequence de-duplication explicitly.
|
|
57
|
+
16. Use `environment.getInfo()` for the immutable runtime, Host App, canonical Mini-program, operating-system, and SDK version snapshot. It waits for the handshake automatically. Use `environment.getInfoSync()` only after `getHandshakeState().status === 'ready'` or inside a ready-state subscription; before that it throws `ENVIRONMENT_NOT_READY`.
|
|
58
|
+
17. Treat missing environment strings as `null` and unknown enum values as `unknown`. Only `sdk.version` is SemVer; do not compare the other opaque version strings or use any environment field for authentication, authorization, or risk control.
|
|
57
59
|
|
|
58
60
|
## Step 5: Use CLI workflows
|
|
59
61
|
|
|
60
62
|
1. Use `hb-sdk create <project-name>` to scaffold a workshop mini-program.
|
|
61
63
|
2. Use `hb-sdk dev` for Browser Mock and Mobile App debugging. Browser debugging can start without CLI login, project binding, or a remote Dev Context; capabilities that need those inputs fail explicitly. Mobile uses the single QR entry with the `open_inapp` and `heybox://` `openWindow` wrapper around a LAN short URL, then opens `heybox-mini-dev://sandbox` with the complete launch context.
|
|
62
64
|
3. The debugging page does not edit permissions. Treat the validated remote permission snapshot as canonical Runtime input. Use `--port`, `--browser-dev-host-port`, and `--no-open` to control local endpoints and browser opening. Select a Mobile network interface the device can reach. `launch.json` is a LAN discovery document, not authentication, encryption, signing, or HMAC protection.
|
|
65
|
+
For real PC Companion debugging, configure local artifacts in `package.json#heybox.companions`, keep `miniappManifest()` enabled, bind the project, and ensure COA has approved `companion`. `hb-sdk dev` validates immutable snapshots without requiring an upload; config or artifact changes rotate the snapshot while existing Sessions retain their original artifact. Prepare and launch the new artifact explicitly. Check separate Browser, Mobile and PC Companion readiness in the debugging workbench. Without a local declaration, use Browser Fake only for state/UI integration.
|
|
63
66
|
4. Use `hb-sdk build [--env <name>] [--verbose]` as the recommended production build entry. It directly owns the Vite build, always cleans and writes `dist/`, and works without CLI login, project binding, or network access.
|
|
64
67
|
5. Declare the actual supported platforms in `package.json#heybox.platforms` and keep the no-argument `miniappManifest()` explicitly enabled in `vite.config.ts`; `hb-sdk build` must fail when the declaration, Manifest, or Runtime gate output is missing or inconsistent.
|
|
65
68
|
6. Keep project typechecking in `scripts.build`, for example `vue-tsc --noEmit && hb-sdk build`; `hb-sdk build` does not run typechecking or invoke `scripts.build` itself.
|
|
@@ -90,7 +93,8 @@ For workshop mini-program business code:
|
|
|
90
93
|
7. Do not use `network.request()` to reach platform-reserved runtime auth or OpenAPI internal paths.
|
|
91
94
|
8. Do not expose credentials or describe internal Host authorization state machines and routes in app-facing guidance.
|
|
92
95
|
9. Do not invent string paths, File System Access API handles, Blob downloads, uploads, Range/resume, external deletion, move, append, or persistent external grants. Public file operations use only SDK-created handles; deletion is limited to SDK sandbox handles.
|
|
93
|
-
10. Do not
|
|
96
|
+
10. Do not pass executable paths, dynamic args, cwd, environment variables, shell commands, URLs, hashes, PID, or native handles through `companion`; reviewed Manifest declarations are the only launch source.
|
|
97
|
+
11. Do not treat `environment.*` as a device-fingerprint or trusted backend signal. It intentionally excludes account data, device identifiers, model, UA, CPU, and memory; use `viewport.getWindowInfo()` for screen geometry.
|
|
94
98
|
|
|
95
99
|
For CLI and local development:
|
|
96
100
|
|
|
@@ -101,6 +105,7 @@ For CLI and local development:
|
|
|
101
105
|
5. Do not suggest changing or resetting permissions in the `hb-sdk dev` debugging page; that UI does not exist. Diagnose permission behavior from the remote snapshot and capability result.
|
|
102
106
|
6. Treat browser Mock results as development feedback only. Validate permissions, identity flows, and user interactions again in a real Heybox client before publishing.
|
|
103
107
|
7. The Browser Dev Host offers iPhone 16 Pro Max (`440 x 956`) and Pixel 9 Pro (`410 x 914`) presets. Switching a preset must preserve the iframe, Runtime session, and page state. Authorization/action dialogs, Toast, Loading, and vibration feedback render inside the preview. Use the upper-right QR popover as the only Mobile QR entry.
|
|
108
|
+
8. Companion Fake scenarios do not execute a local program and are not Windows/macOS release evidence. V1 only supports normal-privilege launch and rejects `elevation: 'required'` at build time. Require target-platform new PC validation for archive integrity, stdio, exit, and process-tree cleanup.
|
|
104
109
|
|
|
105
110
|
For host/runtime/protocol-maintenance code:
|
|
106
111
|
|
|
@@ -299,12 +299,12 @@ Reference 由 `@heybox/hb-sdk` 的公开导出与源码注释自动生成,不
|
|
|
299
299
|
|
|
300
300
|
| 导出面 | Classes | Functions | Interfaces | Types | Constants |
|
|
301
301
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
302
|
-
| Root API | 2 | 4 |
|
|
302
|
+
| Root API | 2 | 4 | 75 | 73 | 3 |
|
|
303
303
|
| Protocol API | 0 | 13 | 58 | 90 | 47 |
|
|
304
304
|
| Miniapp Publish API | 0 | 5 | 2 | 0 | 0 |
|
|
305
|
-
| Vite API | 0 | 1 |
|
|
305
|
+
| Vite API | 0 | 1 | 5 | 1 | 2 |
|
|
306
306
|
|
|
307
|
-
<!-- Generated by apps/docs/hb-sdk/scripts/generate-api-docs.ts; schemaVersion=4; fingerprint=
|
|
307
|
+
<!-- Generated by apps/docs/hb-sdk/scripts/generate-api-docs.ts; schemaVersion=4; fingerprint=a80eea1bb8b0d64255afcb4a664911f025aeae33f3f766c9b126f55668400178 -->
|
|
308
308
|
|
|
309
309
|
## SDK API
|
|
310
310
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
- apps/docs/hb-sdk/guide/environment.md
|
|
13
13
|
- apps/docs/hb-sdk/guide/error-handling.md
|
|
14
14
|
- apps/docs/hb-sdk/guide/lifecycle.md
|
|
15
|
+
- apps/docs/hb-sdk/guide/companion.md
|
|
15
16
|
- apps/docs/hb-sdk/guide/cli.md
|
|
16
17
|
|
|
17
18
|
## Contents
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
## Package metadata
|
|
27
28
|
|
|
28
29
|
- Package: `@heybox/hb-sdk`
|
|
29
|
-
- Version at generation time: `0.8.1-alpha.
|
|
30
|
+
- Version at generation time: `0.8.1-alpha.7`
|
|
30
31
|
- Public root export: `@heybox/hb-sdk`
|
|
31
32
|
- Protocol export: `@heybox/hb-sdk/protocol`
|
|
32
33
|
- Vite plugin export: `@heybox/hb-sdk/vite`
|
|
@@ -52,10 +53,32 @@ export {
|
|
|
52
53
|
environment,
|
|
53
54
|
navigation,
|
|
54
55
|
cloud,
|
|
56
|
+
companion,
|
|
55
57
|
} from './core/singleton';
|
|
56
58
|
export type { MiniProgramSDKHandshakeState, MiniProgramSDKHandshakeStateHandler } from './core/handshake-state';
|
|
57
59
|
export type { MiniProgramEventHandler, MiniProgramEventName, MiniProgramEventPayloadMap } from './protocol/types';
|
|
58
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';
|
|
59
82
|
export type {
|
|
60
83
|
DeleteCurrentUserLeaderboardEntryPayload,
|
|
61
84
|
DeleteCurrentUserLeaderboardEntryResult,
|
|
@@ -197,6 +220,7 @@ export type {
|
|
|
197
220
|
import {
|
|
198
221
|
auth,
|
|
199
222
|
cloud,
|
|
223
|
+
companion,
|
|
200
224
|
device,
|
|
201
225
|
environment,
|
|
202
226
|
getHandshakeState,
|
|
@@ -230,6 +254,7 @@ const hbSDK = {
|
|
|
230
254
|
environment,
|
|
231
255
|
navigation,
|
|
232
256
|
cloud,
|
|
257
|
+
companion,
|
|
233
258
|
};
|
|
234
259
|
|
|
235
260
|
export default hbSDK;
|
|
@@ -240,226 +265,59 @@ export default hbSDK;
|
|
|
240
265
|
Use `@heybox/hb-sdk/vite` only in `vite.config.ts`. Do not import it from iframe mini-program business code.
|
|
241
266
|
|
|
242
267
|
```ts
|
|
243
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
244
|
-
import path from 'node:path';
|
|
245
|
-
import { HB_SDK_VERSION } from '../core/version';
|
|
246
|
-
import { MINI_DEV_CONSOLE_EVENT_TYPE, MINI_DEV_IFRAME_WINDOW_NAME, MINI_DEV_CONSOLE_INSTALL_FLAG } from '../core/mini-dev-console';
|
|
247
|
-
import {
|
|
248
|
-
MINIAPP_PLATFORM_VALUES,
|
|
249
|
-
renderMiniappManifest,
|
|
250
|
-
validateMiniappPackageVersionForBuild,
|
|
251
|
-
type MiniappPlatform,
|
|
252
|
-
} from '../miniapp-manifest/schema';
|
|
253
|
-
import {
|
|
254
|
-
readMiniappPermissionsFromPackageJson,
|
|
255
|
-
readMiniappPlatformsFromPackageJson,
|
|
256
|
-
readMiniappVersionFromPackageJson,
|
|
257
|
-
} from '../miniapp-manifest/node';
|
|
258
|
-
import { getMissingPermissionsWarning } from '../miniapp-manifest/permissions';
|
|
259
|
-
import { enforceMiniappHtmlPolicy } from './html-policy';
|
|
260
|
-
import { shouldSkipMiniappPlatformCspFromEnv } from './runtime-permission-env';
|
|
261
|
-
|
|
262
268
|
export interface MiniappManifestPlugin {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
};
|
|
270
287
|
}
|
|
271
288
|
|
|
272
289
|
export interface MiniappManifestUserConfig {
|
|
273
|
-
|
|
274
|
-
|
|
290
|
+
base?: string;
|
|
291
|
+
define?: Record<string, string>;
|
|
275
292
|
}
|
|
276
293
|
|
|
277
294
|
export interface MiniappManifestPluginContext {
|
|
278
|
-
|
|
295
|
+
warn: (message: string) => void;
|
|
279
296
|
}
|
|
280
297
|
|
|
281
298
|
export interface MiniappManifestResolvedConfig {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
port?: number;
|
|
297
|
-
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';
|
|
298
313
|
};
|
|
299
|
-
|
|
300
|
-
|
|
314
|
+
};
|
|
315
|
+
plugins?: readonly {
|
|
316
|
+
name?: string;
|
|
317
|
+
}[];
|
|
301
318
|
}
|
|
302
319
|
|
|
303
|
-
export
|
|
304
|
-
export type { MiniappPlatform };
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* dev serve 时在入口 HTML 最早位置注入无依赖 console 捕获 bootstrap。
|
|
308
|
-
*
|
|
309
|
-
* 必须内联完整 patch 逻辑而非等 SDK 模块加载:vite client、业务代码的 console 调用
|
|
310
|
-
* 都可能早于 SDK 单例初始化(如 [vite] connecting)。捕获条件:
|
|
311
|
-
* 1. 构建期只有 vite serve 走到本分支(生产构建无此代码,双保险之 define 常量仍注入给 SDK 侧);
|
|
312
|
-
* 2. 运行期仅调试 iframe(window.name 标记由调试台 iframe.name 与本 bootstrap 设置)激活。
|
|
313
|
-
* 转发经 window.parent.postMessage(CSP connect-src 不约束 postMessage),失败静默,
|
|
314
|
-
* 严禁回打 console 造成自触发循环。SDK 侧 installMiniDevConsoleForwarding 安装时
|
|
315
|
-
* 检测同款 INSTALL_FLAG,不会重复 patch。
|
|
316
|
-
*/
|
|
317
|
-
function injectMiniDevConsoleBootstrap(html: string): string {
|
|
318
|
-
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>`;
|
|
319
|
-
const headIndex = html.indexOf('<head>');
|
|
320
|
-
if (headIndex < 0) {
|
|
321
|
-
return html;
|
|
322
|
-
}
|
|
323
|
-
const insertAt = headIndex + '<head>'.length;
|
|
324
|
-
return `${html.slice(0, insertAt)}${bootstrap}${html.slice(insertAt)}`;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
|
|
328
|
-
const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
|
|
329
|
-
const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
|
|
330
|
-
|
|
331
|
-
export function miniappManifest(): MiniappManifestPlugin {
|
|
332
|
-
const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
|
|
333
|
-
let root = process.cwd();
|
|
334
|
-
let outDir = 'dist';
|
|
335
|
-
let command: 'build' | 'serve' = 'build';
|
|
336
|
-
let hmrWebSocketUrl: string | undefined;
|
|
337
|
-
/** 构建链路中更早的失败原因;closeBundle 不得再用产物校验覆盖它。 */
|
|
338
|
-
let priorBuildError: Error | undefined;
|
|
339
|
-
|
|
340
|
-
return {
|
|
341
|
-
name: 'heybox-miniapp-manifest',
|
|
342
|
-
config(config, env) {
|
|
343
|
-
assertMiniappViteBase(config.base);
|
|
344
|
-
// command 在本钩子时序早于 configResolved,必须取自 env 命令而非闭包;
|
|
345
|
-
// 旧调用方(测试等)未传 env 时按 build 处理。
|
|
346
|
-
const isServe = env?.command === 'serve';
|
|
347
|
-
return {
|
|
348
|
-
...(config.base === undefined ? { base: './' } : {}),
|
|
349
|
-
define: {
|
|
350
|
-
[sdkVersionBuildConstant]: JSON.stringify(resolveSdkVersion()),
|
|
351
|
-
// 仅 vite serve(hb-sdk dev)注入;vite build / hb-sdk build 永不注入,捕获模块被死代码消除。
|
|
352
|
-
...(isServe ? { [miniDevLoggingConstant]: 'true' } : {}),
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
},
|
|
356
|
-
configResolved(resolved) {
|
|
357
|
-
if (resolved.plugins) {
|
|
358
|
-
const manifestPluginCount = resolved.plugins.filter((plugin) => plugin.name === 'heybox-miniapp-manifest').length;
|
|
359
|
-
if (manifestPluginCount !== 1) {
|
|
360
|
-
throw new Error(`Vite 配置必须且只能包含一个 miniappManifest() 插件,当前数量:${manifestPluginCount}`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
root = resolved.root;
|
|
364
|
-
outDir = resolved.build.outDir;
|
|
365
|
-
command = resolved.command;
|
|
366
|
-
hmrWebSocketUrl = resolveHmrWebSocketUrl(resolved);
|
|
367
|
-
},
|
|
368
|
-
buildEnd(error) {
|
|
369
|
-
if (error) {
|
|
370
|
-
priorBuildError = priorBuildError ?? toError(error);
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
transformIndexHtml(html) {
|
|
374
|
-
const enforced = enforceMiniappHtmlPolicy(html, {
|
|
375
|
-
...(command === 'serve' ? { hmrWebSocketUrl } : {}),
|
|
376
|
-
skipPlatformCsp,
|
|
377
|
-
});
|
|
378
|
-
return command === 'serve' ? injectMiniDevConsoleBootstrap(enforced) : enforced;
|
|
379
|
-
},
|
|
380
|
-
async closeBundle() {
|
|
381
|
-
// Rollup 在 buildStart/transform 失败后仍会调用 closeBundle。
|
|
382
|
-
// 若此时再抛「缺 index.html」等二次错误,会掩盖真实失败原因。
|
|
383
|
-
if (priorBuildError) {
|
|
384
|
-
throw priorBuildError;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
|
|
388
|
-
const sdkVersion = resolveSdkVersion();
|
|
389
|
-
const platforms = readMiniappPlatformsFromPackageJson(root);
|
|
390
|
-
const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
|
|
391
|
-
if (!parsedPermissions.declared) this.warn(getMissingPermissionsWarning());
|
|
392
|
-
const outputRoot = path.resolve(root, outDir);
|
|
393
|
-
const htmlFiles = collectHtmlFiles(outputRoot);
|
|
394
|
-
const unexpectedHtmlFiles = htmlFiles.filter((file) => path.relative(outputRoot, file) !== 'index.html');
|
|
395
|
-
if (unexpectedHtmlFiles.length > 0) {
|
|
396
|
-
throw new Error(
|
|
397
|
-
`工坊小程序只支持单页应用,构建产物不允许额外 HTML:${unexpectedHtmlFiles
|
|
398
|
-
.map((file) => path.relative(outputRoot, file).split(path.sep).join('/'))
|
|
399
|
-
.join(', ')}`,
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
const indexHtmlPath = path.join(outputRoot, 'index.html');
|
|
403
|
-
if (!htmlFiles.includes(indexHtmlPath)) {
|
|
404
|
-
throw new Error('构建产物必须包含入口 dist/index.html');
|
|
405
|
-
}
|
|
406
|
-
writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(readFileSync(indexHtmlPath, 'utf8'), { skipPlatformCsp }));
|
|
407
|
-
|
|
408
|
-
const manifestPath = path.join(outputRoot, 'manifest.json');
|
|
409
|
-
mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
410
|
-
writeFileSync(
|
|
411
|
-
manifestPath,
|
|
412
|
-
renderMiniappManifest({
|
|
413
|
-
version,
|
|
414
|
-
sdkVersion,
|
|
415
|
-
platforms,
|
|
416
|
-
...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
|
|
417
|
-
}),
|
|
418
|
-
);
|
|
419
|
-
},
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function assertMiniappViteBase(base: string | undefined) {
|
|
424
|
-
if (base !== undefined && base !== '' && base !== './') {
|
|
425
|
-
throw new Error(`工坊小程序 Vite base 仅支持空字符串或 "./",当前值:${base}`);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function toError(error: unknown): Error {
|
|
430
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
function collectHtmlFiles(root: string) {
|
|
434
|
-
const files: string[] = [];
|
|
435
|
-
if (!existsSync(root)) return files;
|
|
436
|
-
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
437
|
-
const fullPath = path.join(root, entry.name);
|
|
438
|
-
if (entry.isDirectory()) files.push(...collectHtmlFiles(fullPath));
|
|
439
|
-
else if (entry.isFile() && path.extname(entry.name).toLowerCase() === '.html') files.push(fullPath);
|
|
440
|
-
}
|
|
441
|
-
return files;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function resolveSdkVersion() {
|
|
445
|
-
if (HB_SDK_VERSION !== sdkVersionPlaceholder) return HB_SDK_VERSION;
|
|
446
|
-
const packageJson = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) as { version?: unknown };
|
|
447
|
-
if (typeof packageJson.version !== 'string' || !packageJson.version) {
|
|
448
|
-
throw new Error('未能读取 @heybox/hb-sdk 当前版本号');
|
|
449
|
-
}
|
|
450
|
-
return packageJson.version;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function resolveHmrWebSocketUrl(resolved: MiniappManifestResolvedConfig) {
|
|
454
|
-
const hmr = resolved.server.hmr;
|
|
455
|
-
if (hmr === false) return undefined;
|
|
456
|
-
const options = typeof hmr === 'object' ? hmr : {};
|
|
457
|
-
const protocol = options.protocol ?? (resolved.server.https ? 'wss' : 'ws');
|
|
458
|
-
const configuredHost = options.host ?? resolved.server.host;
|
|
459
|
-
const host = typeof configuredHost === 'string' && configuredHost !== '0.0.0.0' ? configuredHost : '127.0.0.1';
|
|
460
|
-
const port = options.clientPort ?? options.port ?? resolved.server.port;
|
|
461
|
-
return `${protocol}://${host}${port ? `:${port}` : ''}`;
|
|
462
|
-
}
|
|
320
|
+
export declare function miniappManifest(): MiniappManifestPlugin;
|
|
463
321
|
```
|
|
464
322
|
|
|
465
323
|
## 生产构建
|
|
@@ -789,6 +647,10 @@ try {
|
|
|
789
647
|
- `ENVIRONMENT_NOT_READY` 表示在 SDK 完成握手前调用了 `environment.getInfoSync()`;一般改用会自动等待的 `environment.getInfo()`。
|
|
790
648
|
- `FILE_PICKER_CANCELLED` 表示用户取消了外部文件或目录选择,应正常结束当前操作。
|
|
791
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` 或操作系统拦截都不能绕过;停止重试并修正归档后提交新审核版本。
|
|
792
654
|
- `DOWNLOAD_HTTP_STATUS` 的 `data` 只包含脱敏后的 `status`、可选 `statusText` 和安全响应头。
|
|
793
655
|
- 超时或运行环境不可用时,允许用户重试或退出当前流程。
|
|
794
656
|
- 上报 `code`、`message` 和必要的业务上下文,不要上报用户凭据或敏感数据。
|
|
@@ -842,6 +704,8 @@ function reportSDKError(errorCode: string, message: string, data?: unknown) {
|
|
|
842
704
|
picker 取消、授权撤销、文件缺失、quota、下载状态或取消,不依赖 `message` 文案,也不要记录
|
|
843
705
|
真实路径、内部 handle 或响应凭据头。
|
|
844
706
|
|
|
707
|
+
Companion 失败仍使用 `HbMiniProgramSDKError`。prepare 与 launch 的授权取消属于两次独立用户决定;业务不得在一次点击中自动串联授权,也不得用 Browser Fake 成功替代 Windows/macOS 真机错误处理验证。完整状态机见[受管桌面程序](https://docs.xiaoheihe.cn/hb_sdk/guide/companion)。
|
|
708
|
+
|
|
845
709
|
## Public modules
|
|
846
710
|
|
|
847
711
|
## 能力概览
|
|
@@ -862,6 +726,7 @@ SDK 还提供 `getHandshakeState()` / `onHandshakeStateChange()` 管理持久握
|
|
|
862
726
|
| `files` | 声明受控 sandbox 与用户选择授权的文件、目录操作 |
|
|
863
727
|
| `cloud` | 使用小程序云端排行榜 |
|
|
864
728
|
| `network` | 发起经过平台授权的网络请求,并声明受控流式下载 |
|
|
729
|
+
| `companion` | 准备、启动并控制当前版本审核过的桌面辅助应用 |
|
|
865
730
|
|
|
866
731
|
具体方法、参数和返回值以 [API Reference](https://docs.xiaoheihe.cn/hb_sdk/reference/) 为准,常见组合写法见 [Recipes](https://docs.xiaoheihe.cn/hb_sdk/recipes/)。
|
|
867
732
|
|
|
@@ -920,6 +785,157 @@ onUnmounted(stopLifecycleEvents)
|
|
|
920
785
|
- 组件或页面销毁时清理 `on` 注册的监听,避免重复响应。
|
|
921
786
|
- 生命周期事件只派发给注册当时存在的监听器,不会重放;一次性 `launch` 信息应在页面启动阶段通过业务能力调用处理。
|
|
922
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 真机验收。
|
|
923
939
|
|
|
924
940
|
## 云端排行榜
|
|
925
941
|
|