@qpjoy/mx-launcher-standalone 0.2.0
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 +122 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +108 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @qpjoy/mx-launcher-standalone
|
|
2
|
+
|
|
3
|
+
`launcher-standalone` is the role SDK for capability owners such as MX-H2I and
|
|
4
|
+
Luopan. A standalone launcher owns the local capability plane for one product
|
|
5
|
+
channel. It can host embed apps, but it must not merge its runtime ownership
|
|
6
|
+
with another standalone channel.
|
|
7
|
+
|
|
8
|
+
## Responsibilities
|
|
9
|
+
|
|
10
|
+
Standalone owns:
|
|
11
|
+
|
|
12
|
+
- local broker lifecycle
|
|
13
|
+
- channel registry registration and heartbeat
|
|
14
|
+
- user/session bridge
|
|
15
|
+
- permission grant enforcement
|
|
16
|
+
- ProductNetwork and route policy application for its own product
|
|
17
|
+
- WireGuard, DNS, PAC, system proxy, and data-plane diagnostics
|
|
18
|
+
- release policy resolution for itself and hosted embed apps
|
|
19
|
+
- embed app download, verification, install, rollback, and launch
|
|
20
|
+
- AppCenter catalog operations when AppCenter is hosted by this channel
|
|
21
|
+
- audit events for privileged operations
|
|
22
|
+
|
|
23
|
+
Standalone does not own another standalone product. If MX-H2I and Luopan are
|
|
24
|
+
both running, each keeps its own network owner, user context, permission grants,
|
|
25
|
+
release policy, and data-plane state.
|
|
26
|
+
|
|
27
|
+
## Bootstrap
|
|
28
|
+
|
|
29
|
+
Standalone bootstrap should follow this sequence:
|
|
30
|
+
|
|
31
|
+
1. Load the product manifest and local install identity.
|
|
32
|
+
2. Fetch ProductNetwork, release policy, rollout, AppCenter catalog, and
|
|
33
|
+
capability policy from Internal.
|
|
34
|
+
3. Use signed local cache when Internal is unavailable.
|
|
35
|
+
4. Start the broker listener.
|
|
36
|
+
5. Register the channel record in the per-user registry.
|
|
37
|
+
6. Apply or repair local data plane for this product only.
|
|
38
|
+
7. Start built-in embed apps such as AppCenter when configured.
|
|
39
|
+
|
|
40
|
+
The broker may run in the Electron main process or a local helper, but it must
|
|
41
|
+
present the same protocol to embed clients.
|
|
42
|
+
|
|
43
|
+
## Broker Handshake
|
|
44
|
+
|
|
45
|
+
Embed clients connect with:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
{
|
|
49
|
+
appId: 'appcenter',
|
|
50
|
+
launcherMode: 'embed',
|
|
51
|
+
sdkVersion: '2.3.4',
|
|
52
|
+
protocolVersion: '2',
|
|
53
|
+
minBrokerAbiVersion: '2',
|
|
54
|
+
requestedCapabilities: ['catalog.read', 'app.install']
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The standalone broker must:
|
|
59
|
+
|
|
60
|
+
1. verify protocol and ABI compatibility.
|
|
61
|
+
2. verify the embed app is allowed to bind to this standalone channel.
|
|
62
|
+
3. resolve server policy or signed local cache.
|
|
63
|
+
4. issue a scoped capability session.
|
|
64
|
+
5. return only the data the embed app is permitted to see.
|
|
65
|
+
|
|
66
|
+
Do not send WireGuard keys, DNS ownership state, long-lived user tokens, or
|
|
67
|
+
system proxy handles to embed apps.
|
|
68
|
+
|
|
69
|
+
The issued session should report `networkScope: 'broker-session'`. The
|
|
70
|
+
standalone channel keeps `networkScope: 'owner'` and remains the only runtime
|
|
71
|
+
peer lease owner for that app group.
|
|
72
|
+
|
|
73
|
+
## AppCenter And Hosted Apps
|
|
74
|
+
|
|
75
|
+
AppCenter is a privileged embed app hosted by MX-H2I. It is not a standalone
|
|
76
|
+
owner. AppCenter may request catalog, install, launch, and update actions, but
|
|
77
|
+
the MX-H2I standalone broker performs the actual download, verification,
|
|
78
|
+
installation, launch, and audit.
|
|
79
|
+
|
|
80
|
+
H2O and future AppCenter applications are normal embed apps. They should not get
|
|
81
|
+
their own IP, WireGuard interface, DNS owner, or system proxy owner unless they
|
|
82
|
+
are explicitly converted into standalone products.
|
|
83
|
+
|
|
84
|
+
## Release And Install Owner
|
|
85
|
+
|
|
86
|
+
Standalone release:
|
|
87
|
+
|
|
88
|
+
- downloaded as signed OS artifacts from OSS/CDN.
|
|
89
|
+
- resolved by server release policy.
|
|
90
|
+
- applied by the standalone updater.
|
|
91
|
+
|
|
92
|
+
Hosted embed release:
|
|
93
|
+
|
|
94
|
+
- downloaded as signed embed bundles from OSS/CDN.
|
|
95
|
+
- resolved by the same server release policy.
|
|
96
|
+
- verified and installed by the standalone broker.
|
|
97
|
+
- launched through the broker with a scoped session.
|
|
98
|
+
|
|
99
|
+
NPM package versions are build-time SDK versions. User machines update from
|
|
100
|
+
signed artifacts, not from npm.
|
|
101
|
+
|
|
102
|
+
## K8s Admin Integration
|
|
103
|
+
|
|
104
|
+
K8s admin should be able to inspect and operate standalone channels:
|
|
105
|
+
|
|
106
|
+
- broker status and ABI version
|
|
107
|
+
- channel registry heartbeat
|
|
108
|
+
- active embed sessions
|
|
109
|
+
- data-plane health
|
|
110
|
+
- ProductNetwork and Domestic materialization status
|
|
111
|
+
- release and rollout status
|
|
112
|
+
- hosted embed app install state
|
|
113
|
+
- audit trail for grants, launches, installs, updates, and rollbacks
|
|
114
|
+
|
|
115
|
+
Operational actions should be idempotent:
|
|
116
|
+
|
|
117
|
+
- refresh policy cache
|
|
118
|
+
- sync AppCenter catalog
|
|
119
|
+
- force hosted app update
|
|
120
|
+
- rollback hosted app
|
|
121
|
+
- diagnose embed connection
|
|
122
|
+
- reapply standalone data plane
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { routePlanFromSnapshot, shouldRefreshNetwork, type AnonymousEnrollment, type AnonymousEnrollmentRequest, type LauncherClient, type LauncherClientOptions, type LauncherNetworkLease, type LauncherNetworkLeaseInput, type LauncherNetworkSession, type LauncherNetworkSnapshot, type LauncherNetworkSnapshotInput, type LauncherProductNetwork, type LauncherProductNetworkInput, type LauncherRoutePlan, type LauncherWireGuardKeyPair, type LauncherWireGuardKeyProvider } from '@qpjoy/mx-launcher-core';
|
|
2
|
+
export interface StandaloneLauncherOptions extends LauncherClientOptions {
|
|
3
|
+
productId?: string;
|
|
4
|
+
installId?: string;
|
|
5
|
+
deviceId?: string;
|
|
6
|
+
siteId?: string;
|
|
7
|
+
publicKey?: string;
|
|
8
|
+
privateKey?: string;
|
|
9
|
+
keyPair?: LauncherWireGuardKeyPair;
|
|
10
|
+
keyProvider?: LauncherWireGuardKeyProvider;
|
|
11
|
+
deviceLabel?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface StandaloneSnapshotOptions extends Omit<LauncherNetworkSnapshotInput, 'appId' | 'launcherMode' | 'installId' | 'deviceId' | 'siteId' | 'publicKey'> {
|
|
14
|
+
installId?: string;
|
|
15
|
+
deviceId?: string;
|
|
16
|
+
siteId?: string | null;
|
|
17
|
+
publicKey?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface StandaloneLeaseOptions extends Omit<LauncherNetworkLeaseInput, 'productId' | 'mode' | 'installId' | 'deviceId' | 'siteId' | 'publicKey'> {
|
|
20
|
+
installId?: string;
|
|
21
|
+
deviceId?: string;
|
|
22
|
+
siteId?: string | null;
|
|
23
|
+
publicKey?: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface StandaloneNetworkSessionOptions extends StandaloneLeaseOptions {
|
|
26
|
+
privateKey?: string | null;
|
|
27
|
+
keyPair?: LauncherWireGuardKeyPair | null;
|
|
28
|
+
keyProvider?: LauncherWireGuardKeyProvider | null;
|
|
29
|
+
snapshotRequestId?: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface StandaloneConnectOptions extends Omit<AnonymousEnrollmentRequest, 'productId' | 'platform' | 'installId' | 'deviceId' | 'siteId' | 'publicKey'> {
|
|
32
|
+
installId?: string;
|
|
33
|
+
deviceId?: string;
|
|
34
|
+
siteId?: string;
|
|
35
|
+
publicKey?: string;
|
|
36
|
+
platform?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface StandaloneUserConnectOptions extends StandaloneSnapshotOptions {
|
|
39
|
+
userId: string;
|
|
40
|
+
}
|
|
41
|
+
export interface StandaloneConnectionResult {
|
|
42
|
+
enrollment: AnonymousEnrollment;
|
|
43
|
+
snapshot: LauncherNetworkSnapshot;
|
|
44
|
+
routePlan: LauncherRoutePlan;
|
|
45
|
+
}
|
|
46
|
+
export interface StandaloneLauncher {
|
|
47
|
+
readonly mode: 'standalone';
|
|
48
|
+
readonly productId: string;
|
|
49
|
+
readonly client: LauncherClient;
|
|
50
|
+
listProducts(): Promise<LauncherProductNetwork[]>;
|
|
51
|
+
getProduct(productId?: string): Promise<LauncherProductNetwork>;
|
|
52
|
+
upsertProduct(productId: string, input: LauncherProductNetworkInput): Promise<LauncherProductNetwork>;
|
|
53
|
+
enrollLease(options?: StandaloneLeaseOptions): Promise<LauncherNetworkLease>;
|
|
54
|
+
connectNetwork(options?: StandaloneNetworkSessionOptions): Promise<LauncherNetworkSession>;
|
|
55
|
+
createSnapshot(options?: StandaloneSnapshotOptions): Promise<LauncherNetworkSnapshot>;
|
|
56
|
+
createRoutePlan(options?: StandaloneSnapshotOptions): Promise<LauncherRoutePlan>;
|
|
57
|
+
connectAnonymous(options?: StandaloneConnectOptions): Promise<StandaloneConnectionResult>;
|
|
58
|
+
connectUser(options: StandaloneUserConnectOptions): Promise<{
|
|
59
|
+
snapshot: LauncherNetworkSnapshot;
|
|
60
|
+
routePlan: LauncherRoutePlan;
|
|
61
|
+
}>;
|
|
62
|
+
shouldRefreshNetwork: typeof shouldRefreshNetwork;
|
|
63
|
+
}
|
|
64
|
+
export declare function createStandaloneLauncher(options: StandaloneLauncherOptions): StandaloneLauncher;
|
|
65
|
+
export { routePlanFromSnapshot, shouldRefreshNetwork };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createLauncherClient, createLauncherNetworkSession, routePlanFromSnapshot, shouldRefreshNetwork } from '@qpjoy/mx-launcher-core';
|
|
2
|
+
export function createStandaloneLauncher(options) {
|
|
3
|
+
const productId = normalizeStandaloneProductId(options.productId);
|
|
4
|
+
const client = createLauncherClient(options);
|
|
5
|
+
async function createSnapshot(input = {}) {
|
|
6
|
+
return client.createSnapshot({
|
|
7
|
+
...input,
|
|
8
|
+
appId: productId,
|
|
9
|
+
launcherMode: 'standalone',
|
|
10
|
+
installId: input.installId ?? options.installId,
|
|
11
|
+
deviceId: input.deviceId ?? options.deviceId,
|
|
12
|
+
siteId: input.siteId ?? options.siteId,
|
|
13
|
+
publicKey: input.publicKey ?? options.publicKey
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async function enrollLease(input = {}) {
|
|
17
|
+
return client.enrollLease({
|
|
18
|
+
...input,
|
|
19
|
+
appId: input.appId ?? productId,
|
|
20
|
+
productId,
|
|
21
|
+
mode: 'standalone',
|
|
22
|
+
installId: input.installId ?? options.installId,
|
|
23
|
+
deviceId: input.deviceId ?? options.deviceId,
|
|
24
|
+
siteId: input.siteId ?? options.siteId,
|
|
25
|
+
publicKey: input.publicKey ?? options.publicKey,
|
|
26
|
+
deviceLabel: input.deviceLabel ?? options.deviceLabel,
|
|
27
|
+
requestedBy: input.requestedBy ?? 'standalone-sdk'
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function connectNetwork(input = {}) {
|
|
31
|
+
return createLauncherNetworkSession(client, {
|
|
32
|
+
...input,
|
|
33
|
+
productId,
|
|
34
|
+
appId: productId,
|
|
35
|
+
mode: 'standalone',
|
|
36
|
+
launcherMode: 'standalone',
|
|
37
|
+
installId: input.installId ?? options.installId,
|
|
38
|
+
deviceId: input.deviceId ?? options.deviceId,
|
|
39
|
+
siteId: input.siteId ?? options.siteId,
|
|
40
|
+
publicKey: input.publicKey ?? options.publicKey,
|
|
41
|
+
privateKey: input.privateKey ?? options.privateKey,
|
|
42
|
+
keyPair: input.keyPair ?? options.keyPair,
|
|
43
|
+
keyProvider: input.keyProvider ?? options.keyProvider,
|
|
44
|
+
deviceLabel: input.deviceLabel ?? options.deviceLabel,
|
|
45
|
+
requestedBy: input.requestedBy ?? 'standalone-sdk'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
mode: 'standalone',
|
|
50
|
+
productId,
|
|
51
|
+
client,
|
|
52
|
+
listProducts() {
|
|
53
|
+
return client.listProducts();
|
|
54
|
+
},
|
|
55
|
+
getProduct(targetProductId = productId) {
|
|
56
|
+
return client.getProduct(targetProductId);
|
|
57
|
+
},
|
|
58
|
+
upsertProduct(targetProductId, input) {
|
|
59
|
+
return client.upsertProduct(targetProductId, input);
|
|
60
|
+
},
|
|
61
|
+
enrollLease,
|
|
62
|
+
connectNetwork,
|
|
63
|
+
createSnapshot,
|
|
64
|
+
async createRoutePlan(input) {
|
|
65
|
+
return routePlanFromSnapshot(await createSnapshot(input));
|
|
66
|
+
},
|
|
67
|
+
async connectAnonymous(input = {}) {
|
|
68
|
+
const enrollment = await client.enrollAnonymous({
|
|
69
|
+
...input,
|
|
70
|
+
productId,
|
|
71
|
+
platform: input.platform ?? 'standalone',
|
|
72
|
+
installId: input.installId ?? options.installId,
|
|
73
|
+
deviceId: input.deviceId ?? options.deviceId,
|
|
74
|
+
siteId: input.siteId ?? options.siteId,
|
|
75
|
+
publicKey: input.publicKey ?? options.publicKey,
|
|
76
|
+
deviceLabel: input.deviceLabel ?? options.deviceLabel,
|
|
77
|
+
relayMode: input.relayMode ?? 'h2i'
|
|
78
|
+
});
|
|
79
|
+
const snapshot = await createSnapshot({
|
|
80
|
+
installId: enrollment.installId,
|
|
81
|
+
deviceId: enrollment.deviceId,
|
|
82
|
+
siteId: enrollment.siteId,
|
|
83
|
+
publicKey: enrollment.publicKey ?? input.publicKey ?? options.publicKey,
|
|
84
|
+
requestId: input.requestId
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
enrollment,
|
|
88
|
+
snapshot,
|
|
89
|
+
routePlan: routePlanFromSnapshot(snapshot)
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
async connectUser(input) {
|
|
93
|
+
const snapshot = await createSnapshot(input);
|
|
94
|
+
return {
|
|
95
|
+
snapshot,
|
|
96
|
+
routePlan: routePlanFromSnapshot(snapshot)
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
shouldRefreshNetwork
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export { routePlanFromSnapshot, shouldRefreshNetwork };
|
|
103
|
+
function normalizeStandaloneProductId(productId) {
|
|
104
|
+
const trimmed = (productId ?? 'launcher').trim();
|
|
105
|
+
if (!trimmed)
|
|
106
|
+
throw new Error('Standalone launcher productId is required');
|
|
107
|
+
return trimmed;
|
|
108
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qpjoy/mx-launcher-standalone",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Standalone MX Launcher shell adapter that uses launcher-core for AppCenter-wide network control.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"package.json"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@qpjoy/mx-launcher-core": "^0.2.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.7.3"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
31
|
+
}
|
|
32
|
+
}
|