@kevlns/ship-cli 0.0.0-stage → 0.1.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/AGENTS.md +25 -0
- package/LICENSE +21 -0
- package/README.md +83 -2
- package/dist/cli.mjs +2851 -0
- package/dist/index.mjs +2879 -0
- package/docs/research/steam.md +14 -0
- package/docs/research/wechat-minigame.md +31 -0
- package/package.json +77 -4
- package/schemas/ship-config.schema.json +191 -0
- package/v-cli.plugin.json +486 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"package": "@kevlns/ship-cli",
|
|
4
|
+
"command": "ship",
|
|
5
|
+
"bin": "ship-cli",
|
|
6
|
+
"description": "Cross-platform game shipping CLI: offline validation + packaging + push to platform test channels. Steam (steamcmd/SteamPipe VDF builds, branch set-live guard) and WeChat mini games (structure/size validation, miniprogram-ci preview QR and dev-version upload). Never publishes to players on its own.",
|
|
7
|
+
"platforms": [
|
|
8
|
+
"win32",
|
|
9
|
+
"linux",
|
|
10
|
+
"darwin"
|
|
11
|
+
],
|
|
12
|
+
"runtime": {
|
|
13
|
+
"node": ">=20"
|
|
14
|
+
},
|
|
15
|
+
"environment": [
|
|
16
|
+
{
|
|
17
|
+
"name": "SHIP_STEAMCMD",
|
|
18
|
+
"description": "Optional explicit path to steamcmd executable (wins over steam.steamcmdPath in ship.config.json; PATH and common install dirs are also searched)."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "SHIP_STEAM_USERNAME",
|
|
22
|
+
"description": "Steam build-account username. Required for steam push/preview."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "SHIP_STEAM_PASSWORD",
|
|
26
|
+
"description": "Steam build-account password. Omit to use passwordless login via a cached config.vdf."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "SHIP_STEAM_TOTP",
|
|
30
|
+
"description": "Precomputed Steam Guard code for this 30s window."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "SHIP_STEAM_TOTP_SECRET",
|
|
34
|
+
"description": "Steam Guard shared secret (base64); the TOTP code is generated at run time. Preferred for CI."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "SHIP_STEAM_CONFIG_VDF",
|
|
38
|
+
"description": "base64-encoded steamcmd config/config.vdf carrying a logged-in token; seeded next to steamcmd for passwordless login. Re-entering a password invalidates the old token."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "SHIP_WX_PRIVATE_KEY",
|
|
42
|
+
"description": "Path to the WeChat code-upload private key; wins over wechat.privateKeyPath in ship.config.json."
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"agent": {
|
|
46
|
+
"whenToUse": "Shipping a built game to distribution platforms: packaging/upload to Steam (depot builds via steamcmd) or WeChat mini games (dev-version upload via miniprogram-ci), plus offline package validation and environment preflight. NOT for: making content player-visible (Steam default-branch set-live, WeChat review submission/release/gray rollout are always human steps in platform backends), building engine players (produce artifacts first), or account/credential setup beyond pointing at env vars.",
|
|
47
|
+
"globalOptions": [
|
|
48
|
+
{
|
|
49
|
+
"flags": "--project <dir>",
|
|
50
|
+
"description": "Directory containing ship.config.json (default: cwd; searched upward)."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"flags": "-V, --version",
|
|
54
|
+
"description": "Print the CLI version."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"flags": "-h, --help",
|
|
58
|
+
"description": "Show help."
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"commands": [
|
|
62
|
+
{
|
|
63
|
+
"path": [
|
|
64
|
+
"doctor"
|
|
65
|
+
],
|
|
66
|
+
"usage": "ship-cli doctor [--json]",
|
|
67
|
+
"description": "Offline preflight: config loadability, steamcmd presence, credential mode, wechat project dir, upload key, miniprogram-ci resolvability. Makes no network calls.",
|
|
68
|
+
"arguments": [],
|
|
69
|
+
"options": [
|
|
70
|
+
{
|
|
71
|
+
"flags": "--json",
|
|
72
|
+
"description": "Print the full report as JSON."
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"output": {
|
|
76
|
+
"format": "json",
|
|
77
|
+
"description": "JSON report with per-check status (ok/warn/fail); human mode prints a checklist."
|
|
78
|
+
},
|
|
79
|
+
"exitCodes": {
|
|
80
|
+
"0": "report produced (individual checks carry their own status)"
|
|
81
|
+
},
|
|
82
|
+
"safety": [
|
|
83
|
+
"read-only",
|
|
84
|
+
"no-network"
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"path": [
|
|
89
|
+
"config",
|
|
90
|
+
"init"
|
|
91
|
+
],
|
|
92
|
+
"usage": "ship-cli config init [--force] [--json]",
|
|
93
|
+
"description": "Write a ship.config.json template into the project directory. Refuses to overwrite an existing file unless --force.",
|
|
94
|
+
"arguments": [],
|
|
95
|
+
"options": [
|
|
96
|
+
{
|
|
97
|
+
"flags": "--force",
|
|
98
|
+
"description": "Overwrite an existing ship.config.json."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"flags": "--json",
|
|
102
|
+
"description": "Print JSON result."
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"output": {
|
|
106
|
+
"format": "json",
|
|
107
|
+
"description": "Human path message, or {written, force} with --json."
|
|
108
|
+
},
|
|
109
|
+
"exitCodes": {
|
|
110
|
+
"0": "written",
|
|
111
|
+
"1": "CFG_CONFIG_EXISTS without --force"
|
|
112
|
+
},
|
|
113
|
+
"safety": [
|
|
114
|
+
"writes-project-files",
|
|
115
|
+
"refuses-overwrite-without-force"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"path": [
|
|
120
|
+
"config",
|
|
121
|
+
"validate"
|
|
122
|
+
],
|
|
123
|
+
"usage": "ship-cli config validate [--json]",
|
|
124
|
+
"description": "Load, schema-validate and cross-field-validate ship.config.json (depot sources inside contentRoot, buildOutput/contentRoot not nested, etc.). Any failure prints a stable code plus a concrete fix suggestion.",
|
|
125
|
+
"arguments": [],
|
|
126
|
+
"options": [
|
|
127
|
+
{
|
|
128
|
+
"flags": "--json",
|
|
129
|
+
"description": "Print JSON summary."
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"output": {
|
|
133
|
+
"format": "json",
|
|
134
|
+
"description": "Section summary; throws with CFG_* code and details on any violation."
|
|
135
|
+
},
|
|
136
|
+
"exitCodes": {
|
|
137
|
+
"0": "valid",
|
|
138
|
+
"1": "CFG_* validation failure"
|
|
139
|
+
},
|
|
140
|
+
"safety": [
|
|
141
|
+
"read-only"
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"path": [
|
|
146
|
+
"config",
|
|
147
|
+
"show"
|
|
148
|
+
],
|
|
149
|
+
"usage": "ship-cli config show [--json]",
|
|
150
|
+
"description": "Print the normalized config (paths resolved, defaults applied where relevant).",
|
|
151
|
+
"arguments": [],
|
|
152
|
+
"options": [
|
|
153
|
+
{
|
|
154
|
+
"flags": "--json",
|
|
155
|
+
"description": "Print as JSON."
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"output": {
|
|
159
|
+
"format": "json",
|
|
160
|
+
"description": "Config document with projectRoot."
|
|
161
|
+
},
|
|
162
|
+
"exitCodes": {
|
|
163
|
+
"0": "ok",
|
|
164
|
+
"1": "config missing/invalid"
|
|
165
|
+
},
|
|
166
|
+
"safety": [
|
|
167
|
+
"read-only"
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"path": [
|
|
172
|
+
"steam",
|
|
173
|
+
"vdf"
|
|
174
|
+
],
|
|
175
|
+
"usage": "ship-cli steam vdf [--desc <text>] [--set-live <branch>] [--preview] [--json]",
|
|
176
|
+
"description": "Generate app_build_<appId>.vdf + depot_build_<id>.vdf under <buildOutput>/vdf without running steamcmd. Inspect them or hand-run steamcmd yourself.",
|
|
177
|
+
"arguments": [],
|
|
178
|
+
"options": [
|
|
179
|
+
{
|
|
180
|
+
"flags": "--desc <text>",
|
|
181
|
+
"description": "Build description recorded on the partner Builds page."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"flags": "--set-live <branch>",
|
|
185
|
+
"description": "Branch to auto set live after upload; 'default' is rejected (Valve forbids auto set-live on default)."
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"flags": "--preview",
|
|
189
|
+
"description": "Write Preview=1 (dry-run VDF)."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"flags": "--json",
|
|
193
|
+
"description": "Print generated file paths as JSON."
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
"output": {
|
|
197
|
+
"format": "json",
|
|
198
|
+
"description": "Written VDF paths + effective preview/setLive values."
|
|
199
|
+
},
|
|
200
|
+
"exitCodes": {
|
|
201
|
+
"0": "written",
|
|
202
|
+
"1": "config/validation failure (incl. STM_SETLIVE_DEFAULT)"
|
|
203
|
+
},
|
|
204
|
+
"safety": [
|
|
205
|
+
"writes-build-output-files",
|
|
206
|
+
"no-network",
|
|
207
|
+
"refuses-set-live-default"
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"path": [
|
|
212
|
+
"steam",
|
|
213
|
+
"preview"
|
|
214
|
+
],
|
|
215
|
+
"usage": "ship-cli steam preview [--desc <text>] [--json]",
|
|
216
|
+
"description": "Run steamcmd with Preview=1: the full build pipeline (login, chunking, manifest) but NOTHING is uploaded. Produces logs + a file manifest for inspection.",
|
|
217
|
+
"arguments": [],
|
|
218
|
+
"options": [
|
|
219
|
+
{
|
|
220
|
+
"flags": "--desc <text>",
|
|
221
|
+
"description": "Build description."
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"flags": "--json",
|
|
225
|
+
"description": "Print JSON summary."
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
"output": {
|
|
229
|
+
"format": "json",
|
|
230
|
+
"description": "appId, buildId (from log), log path; verdict from log analysis (steamcmd exit codes are unreliable)."
|
|
231
|
+
},
|
|
232
|
+
"exitCodes": {
|
|
233
|
+
"0": "preview pipeline succeeded",
|
|
234
|
+
"2": "steamcmd/credentials missing",
|
|
235
|
+
"3": "steamcmd run failed or outcome unknown (fail-closed)"
|
|
236
|
+
},
|
|
237
|
+
"safety": [
|
|
238
|
+
"spawns-steamcmd",
|
|
239
|
+
"no-upload",
|
|
240
|
+
"writes-build-output-files",
|
|
241
|
+
"requires-steam-credentials"
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"path": [
|
|
246
|
+
"steam",
|
|
247
|
+
"push"
|
|
248
|
+
],
|
|
249
|
+
"usage": "ship-cli steam push [--desc <text>] [--set-live <branch>] [--json]",
|
|
250
|
+
"description": "Upload a real depot build via steamcmd. Optionally set it live on a NON-default branch (Valve forbids automatic default-branch set-live; that step stays human in App Admin -> Builds).",
|
|
251
|
+
"arguments": [],
|
|
252
|
+
"options": [
|
|
253
|
+
{
|
|
254
|
+
"flags": "--desc <text>",
|
|
255
|
+
"description": "Build description recorded on the Builds page."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"flags": "--set-live <branch>",
|
|
259
|
+
"description": "Non-default branch to auto set live after upload (e.g. beta, staging). 'default' is rejected."
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"flags": "--json",
|
|
263
|
+
"description": "Print JSON summary."
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
"output": {
|
|
267
|
+
"format": "json",
|
|
268
|
+
"description": "appId, buildId, setLive branch, log path, VDF dir."
|
|
269
|
+
},
|
|
270
|
+
"exitCodes": {
|
|
271
|
+
"0": "build uploaded (+ optionally set live on non-default branch)",
|
|
272
|
+
"1": "config/validation failure (incl. STM_SETLIVE_DEFAULT)",
|
|
273
|
+
"2": "steamcmd/credentials/content missing",
|
|
274
|
+
"3": "steamcmd run failed or outcome unknown"
|
|
275
|
+
},
|
|
276
|
+
"safety": [
|
|
277
|
+
"spawns-steamcmd",
|
|
278
|
+
"uploads-build-to-steam-backend",
|
|
279
|
+
"writes-build-output-files",
|
|
280
|
+
"requires-steam-credentials",
|
|
281
|
+
"never-player-visible",
|
|
282
|
+
"refuses-set-live-default"
|
|
283
|
+
]
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"path": [
|
|
287
|
+
"steam",
|
|
288
|
+
"test"
|
|
289
|
+
],
|
|
290
|
+
"usage": "ship-cli steam test [--write-appid] [--json]",
|
|
291
|
+
"description": "Local smoke checks on the built content: steam_appid.txt presence/match (optionally write it), steam_api*.dll presence, configured executable existence, plus run/builds URLs.",
|
|
292
|
+
"arguments": [],
|
|
293
|
+
"options": [
|
|
294
|
+
{
|
|
295
|
+
"flags": "--write-appid",
|
|
296
|
+
"description": "Create steam_appid.txt in contentRoot when missing (idempotent)."
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"flags": "--json",
|
|
300
|
+
"description": "Print JSON summary."
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
"output": {
|
|
304
|
+
"format": "json",
|
|
305
|
+
"description": "Local file prerequisites including Steam API libraries for Windows/Linux/macOS; missing requirements fail with details."
|
|
306
|
+
},
|
|
307
|
+
"exitCodes": {
|
|
308
|
+
"0": "all local file prerequisites passed",
|
|
309
|
+
"1": "AppID mismatch or invalid configuration",
|
|
310
|
+
"2": "missing local file prerequisite"
|
|
311
|
+
},
|
|
312
|
+
"safety": [
|
|
313
|
+
"read-only",
|
|
314
|
+
"optionally-writes-steam-appid-txt"
|
|
315
|
+
]
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"path": [
|
|
319
|
+
"steam",
|
|
320
|
+
"open"
|
|
321
|
+
],
|
|
322
|
+
"usage": "ship-cli steam open <target>",
|
|
323
|
+
"description": "Open a Steamworks partner page in the browser. Only target: builds (App Admin -> Builds, where set-live/rollback are done by humans).",
|
|
324
|
+
"arguments": [
|
|
325
|
+
{
|
|
326
|
+
"name": "target",
|
|
327
|
+
"required": true,
|
|
328
|
+
"description": "Page to open; currently 'builds'."
|
|
329
|
+
}
|
|
330
|
+
],
|
|
331
|
+
"options": [],
|
|
332
|
+
"output": {
|
|
333
|
+
"format": "text",
|
|
334
|
+
"description": "Opens the default browser; no stdout payload."
|
|
335
|
+
},
|
|
336
|
+
"exitCodes": {
|
|
337
|
+
"0": "opened",
|
|
338
|
+
"1": "unknown target"
|
|
339
|
+
},
|
|
340
|
+
"safety": [
|
|
341
|
+
"opens-browser"
|
|
342
|
+
]
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"path": [
|
|
346
|
+
"wx",
|
|
347
|
+
"validate"
|
|
348
|
+
],
|
|
349
|
+
"usage": "ship-cli wx validate [--json]",
|
|
350
|
+
"description": "Offline structure and filtered source estimate checks (not actual compiled package size).",
|
|
351
|
+
"arguments": [],
|
|
352
|
+
"options": [
|
|
353
|
+
{
|
|
354
|
+
"flags": "--json",
|
|
355
|
+
"description": "Print the full JSON report."
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
"output": {
|
|
359
|
+
"format": "json",
|
|
360
|
+
"description": "Report with per-package sizes, limits, errors (WX_* codes) and warnings."
|
|
361
|
+
},
|
|
362
|
+
"exitCodes": {
|
|
363
|
+
"0": "no errors (warnings possible)",
|
|
364
|
+
"1": "WX_* validation errors"
|
|
365
|
+
},
|
|
366
|
+
"safety": [
|
|
367
|
+
"read-only",
|
|
368
|
+
"no-network"
|
|
369
|
+
]
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"path": [
|
|
373
|
+
"wx",
|
|
374
|
+
"preview"
|
|
375
|
+
],
|
|
376
|
+
"usage": "ship-cli wx preview [--qr-out <file>] [--desc <text>] [--robot <n>] [--json]",
|
|
377
|
+
"description": "Generate a phone-test QR code via miniprogram-ci (no version record is created). miniprogram-ci must be installed in the project.",
|
|
378
|
+
"arguments": [],
|
|
379
|
+
"options": [
|
|
380
|
+
{
|
|
381
|
+
"flags": "--qr-out <file>",
|
|
382
|
+
"description": "New PNG output outside game content; default build/ship-preview/<uuid>/qr.png."
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"flags": "--desc <text>",
|
|
386
|
+
"description": "Note shown in the dev-version list."
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"flags": "--robot <n>",
|
|
390
|
+
"description": "miniprogram-ci robot slot 1-30."
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"flags": "--json",
|
|
394
|
+
"description": "Print JSON result."
|
|
395
|
+
}
|
|
396
|
+
],
|
|
397
|
+
"output": {
|
|
398
|
+
"format": "json",
|
|
399
|
+
"description": "Result contains actual absolute qrOut, SDK result and ok; a new PNG outside game content is required."
|
|
400
|
+
},
|
|
401
|
+
"exitCodes": {
|
|
402
|
+
"0": "QR generated",
|
|
403
|
+
"1": "invalid structure, parameters or output path",
|
|
404
|
+
"2": "key/miniprogram-ci missing",
|
|
405
|
+
"4": "miniprogram-ci run failed"
|
|
406
|
+
},
|
|
407
|
+
"safety": [
|
|
408
|
+
"spawns-miniprogram-ci",
|
|
409
|
+
"requires-upload-key",
|
|
410
|
+
"no-version-created"
|
|
411
|
+
]
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"path": [
|
|
415
|
+
"wx",
|
|
416
|
+
"push"
|
|
417
|
+
],
|
|
418
|
+
"usage": "ship-cli wx push --version <text> [--desc <text>] [--robot <n>] [--json]",
|
|
419
|
+
"description": "Upload a new dev version via miniprogram-ci after running the same offline validation as wx validate (a failed validation prints the complete report with per-item fix suggestions and blocks the upload). Creates a dev version ONLY: selecting 体验版, submitting for review, release and gray rollout are human steps in the MP backend.",
|
|
420
|
+
"arguments": [],
|
|
421
|
+
"options": [
|
|
422
|
+
{
|
|
423
|
+
"flags": "--version <text>",
|
|
424
|
+
"description": "Dev-version number, e.g. 1.0.0 (required)."
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"flags": "--desc <text>",
|
|
428
|
+
"description": "Upload note / changelog shown in the backend."
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
"flags": "--robot <n>",
|
|
432
|
+
"description": "miniprogram-ci robot slot 1-30 (separate pipelines use separate robots)."
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"flags": "--json",
|
|
436
|
+
"description": "Print JSON result."
|
|
437
|
+
}
|
|
438
|
+
],
|
|
439
|
+
"output": {
|
|
440
|
+
"format": "json",
|
|
441
|
+
"description": "appid, robot, version and miniprogram-ci result incl. subPackageInfo sizes."
|
|
442
|
+
},
|
|
443
|
+
"exitCodes": {
|
|
444
|
+
"0": "uploaded as dev version",
|
|
445
|
+
"1": "validation/config failure",
|
|
446
|
+
"2": "key/miniprogram-ci missing",
|
|
447
|
+
"4": "upload failed"
|
|
448
|
+
},
|
|
449
|
+
"safety": [
|
|
450
|
+
"spawns-miniprogram-ci",
|
|
451
|
+
"uploads-dev-version",
|
|
452
|
+
"requires-upload-key",
|
|
453
|
+
"never-submits-review",
|
|
454
|
+
"never-player-visible"
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"path": [
|
|
459
|
+
"wx",
|
|
460
|
+
"open"
|
|
461
|
+
],
|
|
462
|
+
"usage": "ship-cli wx open <target>",
|
|
463
|
+
"description": "Open the WeChat MP backend in the browser. Only target: mp.",
|
|
464
|
+
"arguments": [
|
|
465
|
+
{
|
|
466
|
+
"name": "target",
|
|
467
|
+
"required": true,
|
|
468
|
+
"description": "Page to open; currently 'mp'."
|
|
469
|
+
}
|
|
470
|
+
],
|
|
471
|
+
"options": [],
|
|
472
|
+
"output": {
|
|
473
|
+
"format": "text",
|
|
474
|
+
"description": "Opens the default browser; no stdout payload."
|
|
475
|
+
},
|
|
476
|
+
"exitCodes": {
|
|
477
|
+
"0": "opened",
|
|
478
|
+
"1": "unknown target"
|
|
479
|
+
},
|
|
480
|
+
"safety": [
|
|
481
|
+
"opens-browser"
|
|
482
|
+
]
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
}
|
|
486
|
+
}
|