@playdrop/playdrop-cli 0.6.6 → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.6.6",
3
- "build": 3,
2
+ "version": "0.6.7",
3
+ "build": 1,
4
4
  "platforms": {
5
5
  "ios": {
6
6
  "minimumVersion": "16.0"
@@ -26,12 +26,12 @@
26
26
  },
27
27
  "clients": {
28
28
  "web": {
29
- "minimumVersion": "0.6.6",
30
- "minimumBuild": 3
29
+ "minimumVersion": "0.6.7",
30
+ "minimumBuild": 1
31
31
  },
32
32
  "admin": {
33
- "minimumVersion": "0.6.6",
34
- "minimumBuild": 3
33
+ "minimumVersion": "0.6.7",
34
+ "minimumBuild": 1
35
35
  },
36
36
  "apple": {
37
37
  "minimumVersion": "0.3.10",
@@ -46,7 +46,7 @@
46
46
  "minimumBuild": 1
47
47
  },
48
48
  "cli": {
49
- "minimumVersion": "0.6.6"
49
+ "minimumVersion": "0.6.7"
50
50
  }
51
51
  }
52
52
  }
@@ -13,6 +13,20 @@ const devRuntimeAssets_1 = require("../commands/devRuntimeAssets");
13
13
  const registration_1 = require("./registration");
14
14
  const DEFAULT_HOSTED_LAUNCH_TIMEOUT_MS = 15000;
15
15
  const POST_READY_SETTLE_MS = 750;
16
+ const CAPTURE_SURFACE = 'DESKTOP';
17
+ function resolvePostAuthHostedLaunchState(controllerMode) {
18
+ return controllerMode === 'REQUIRED' ? 'controller_required' : 'ready';
19
+ }
20
+ function resolveAnonymousHostedLaunchState(input) {
21
+ const surfaceTargets = Array.isArray(input.surfaceTargets) ? input.surfaceTargets : [];
22
+ if (!surfaceTargets.includes(CAPTURE_SURFACE)) {
23
+ return 'surface_unsupported';
24
+ }
25
+ if (input.authMode === 'REQUIRED') {
26
+ return 'login_required';
27
+ }
28
+ return resolvePostAuthHostedLaunchState(input.controllerMode);
29
+ }
16
30
  function normalizeLaunchCheckMessage(message) {
17
31
  const normalized = typeof message === 'string' ? message.trim() : '';
18
32
  return normalized || 'Hosted app failed to launch.';
@@ -93,7 +107,11 @@ async function runHostedLaunchCheckWithCapture(options) {
93
107
  if (result.hostedLaunchState?.state !== expectedHostedLaunchState) {
94
108
  return buildFailedLaunchCheckResult('startup_failure', expectedHostedLaunchState === 'login_required'
95
109
  ? 'Hosted app did not stop at the PlayDrop sign-in gate.'
96
- : 'Hosted app never reported the ready state.', options.artifactFingerprint ?? null);
110
+ : expectedHostedLaunchState === 'controller_required'
111
+ ? 'Hosted app did not stop at the PlayDrop controller gate.'
112
+ : expectedHostedLaunchState === 'surface_unsupported'
113
+ ? 'Hosted app did not stop at the PlayDrop unsupported-surface gate.'
114
+ : 'Hosted app never reported the ready state.', options.artifactFingerprint ?? null);
97
115
  }
98
116
  return buildPassedLaunchCheckResult(options.artifactFingerprint ?? null);
99
117
  }
@@ -161,8 +179,9 @@ async function runLocalHostedLaunchCheck(input) {
161
179
  creatorUsername: input.creatorUsername,
162
180
  task: input.task,
163
181
  });
164
- const authMode = mountedRuntime.runtimeAssetManifest.response.localAppMetadata.authMode;
165
- if (authMode === 'REQUIRED') {
182
+ const localAppMetadata = mountedRuntime.runtimeAssetManifest.response.localAppMetadata;
183
+ const anonymousExpectedState = resolveAnonymousHostedLaunchState(localAppMetadata);
184
+ if (anonymousExpectedState === 'login_required') {
166
185
  const registeredApp = await (0, registration_1.fetchRegisteredAppShell)(input.client, input.creatorUsername, input.task.name);
167
186
  if (!registeredApp?.id) {
168
187
  return buildRegistrationRequiredLaunchCheckFailure(input.task.name);
@@ -176,7 +195,7 @@ async function runLocalHostedLaunchCheck(input) {
176
195
  launchCheck: true,
177
196
  }),
178
197
  timeoutMs: input.timeoutMs,
179
- expectedHostedLaunchState: 'login_required',
198
+ expectedHostedLaunchState: anonymousExpectedState,
180
199
  });
181
200
  if (anonymousGateResult.status !== 'PASSED') {
182
201
  return anonymousGateResult;
@@ -190,7 +209,7 @@ async function runLocalHostedLaunchCheck(input) {
190
209
  launchCheck: true,
191
210
  }),
192
211
  timeoutMs: input.timeoutMs,
193
- expectedHostedLaunchState: 'ready',
212
+ expectedHostedLaunchState: resolvePostAuthHostedLaunchState(localAppMetadata.controllerMode),
194
213
  token: input.token ?? null,
195
214
  user: input.currentUser ?? null,
196
215
  savedSessionBootstrap: true,
@@ -205,6 +224,7 @@ async function runLocalHostedLaunchCheck(input) {
205
224
  launchCheck: true,
206
225
  }),
207
226
  timeoutMs: input.timeoutMs,
227
+ expectedHostedLaunchState: anonymousExpectedState,
208
228
  });
209
229
  }
210
230
  finally {
@@ -215,9 +235,9 @@ async function runLocalHostedLaunchCheck(input) {
215
235
  }
216
236
  async function runUploadedHostedLaunchCheck(input) {
217
237
  const preview = await input.client.fetchAppUploadLaunchCheckPreview(input.creatorUsername, input.task.name, input.sessionId);
218
- const authMode = preview.localAppMetadata.authMode;
238
+ const anonymousExpectedState = resolveAnonymousHostedLaunchState(preview.localAppMetadata);
219
239
  let result;
220
- if (authMode === 'REQUIRED') {
240
+ if (anonymousExpectedState === 'login_required') {
221
241
  if (!preview.app?.id) {
222
242
  result = buildRegistrationRequiredLaunchCheckFailure(input.task.name);
223
243
  }
@@ -226,7 +246,7 @@ async function runUploadedHostedLaunchCheck(input) {
226
246
  targetUrl: appendLaunchCheckParams(preview.launchUrl, 'anonymous'),
227
247
  artifactFingerprint: preview.session.manifestHash,
228
248
  timeoutMs: input.timeoutMs,
229
- expectedHostedLaunchState: 'login_required',
249
+ expectedHostedLaunchState: anonymousExpectedState,
230
250
  });
231
251
  if (anonymousGateResult.status !== 'PASSED') {
232
252
  result = anonymousGateResult;
@@ -236,7 +256,7 @@ async function runUploadedHostedLaunchCheck(input) {
236
256
  targetUrl: appendLaunchCheckParams(preview.launchUrl, 'viewer'),
237
257
  artifactFingerprint: preview.session.manifestHash,
238
258
  timeoutMs: input.timeoutMs,
239
- expectedHostedLaunchState: 'ready',
259
+ expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
240
260
  token: input.token ?? null,
241
261
  user: input.currentUser ?? null,
242
262
  savedSessionBootstrap: true,
@@ -249,7 +269,7 @@ async function runUploadedHostedLaunchCheck(input) {
249
269
  targetUrl: appendLaunchCheckParams(preview.launchUrl, 'anonymous'),
250
270
  artifactFingerprint: preview.session.manifestHash,
251
271
  timeoutMs: input.timeoutMs,
252
- expectedHostedLaunchState: 'ready',
272
+ expectedHostedLaunchState: anonymousExpectedState,
253
273
  });
254
274
  }
255
275
  const recorded = await input.client.recordAppUploadLaunchCheck(input.creatorUsername, input.task.name, input.sessionId, {
@@ -23,12 +23,17 @@ export type RunCaptureOptions = {
23
23
  savedSessionBootstrap?: boolean;
24
24
  enableCaptureBridge?: boolean;
25
25
  requireHostedLaunchReady?: boolean;
26
- expectedHostedLaunchState?: 'ready' | 'login_required';
26
+ expectedHostedLaunchState?: HostedLaunchExpectedState;
27
27
  };
28
+ export type HostedLaunchExpectedState = 'ready' | 'login_required' | 'controller_required' | 'surface_unsupported';
28
29
  export type CaptureHostedLaunchState = {
29
30
  state: 'ready';
30
31
  } | {
31
32
  state: 'login_required';
33
+ } | {
34
+ state: 'controller_required';
35
+ } | {
36
+ state: 'surface_unsupported';
32
37
  } | {
33
38
  state: 'error';
34
39
  errorCode: string | null;
@@ -92,6 +92,12 @@ function normalizeHostedLaunchStatePayload(payload) {
92
92
  if (state === 'login_required') {
93
93
  return { state: 'login_required' };
94
94
  }
95
+ if (state === 'controller_required') {
96
+ return { state: 'controller_required' };
97
+ }
98
+ if (state === 'surface_unsupported') {
99
+ return { state: 'surface_unsupported' };
100
+ }
95
101
  if (state !== 'error') {
96
102
  return null;
97
103
  }
@@ -117,6 +123,24 @@ function formatHostedLaunchError(state) {
117
123
  }
118
124
  return parts.join(' ');
119
125
  }
126
+ function formatHostedLaunchStateMismatch(actualState, expectedState) {
127
+ if (expectedState === "login_required") {
128
+ return `Hosted app reported ${actualState} instead of login_required.`;
129
+ }
130
+ if (expectedState === "controller_required") {
131
+ return `Hosted app reported ${actualState} instead of controller_required.`;
132
+ }
133
+ if (expectedState === "surface_unsupported") {
134
+ return `Hosted app reported ${actualState} instead of surface_unsupported.`;
135
+ }
136
+ return `Hosted app reported ${actualState} instead of ready.`;
137
+ }
138
+ function shouldSettleHostedLaunchWaiter(state, expectedState) {
139
+ if (state.state === "error" || !expectedState) {
140
+ return true;
141
+ }
142
+ return state.state === expectedState;
143
+ }
120
144
  async function writeLogFile(logPath, lines) {
121
145
  if (!logPath || logPath.trim().length === 0) {
122
146
  return;
@@ -195,6 +219,9 @@ async function runCapture(options) {
195
219
  if (!hostedLaunchWaiter || hostedLaunchWaiterSettled) {
196
220
  return;
197
221
  }
222
+ if (!shouldSettleHostedLaunchWaiter(state, expectedHostedLaunchState)) {
223
+ return;
224
+ }
198
225
  hostedLaunchWaiterSettled = true;
199
226
  resolveHostedLaunchWaiter?.(state);
200
227
  };
@@ -442,24 +469,35 @@ async function runCapture(options) {
442
469
  if (expectedHostedLaunchState === 'login_required' && frameCount > 0) {
443
470
  throw new Error('The Playdrop auth gate booted the app frame unexpectedly.');
444
471
  }
472
+ if (expectedHostedLaunchState === 'controller_required' && frameCount > 0) {
473
+ throw new Error('The Playdrop controller gate booted the app frame unexpectedly.');
474
+ }
475
+ if (expectedHostedLaunchState === 'surface_unsupported' && frameCount > 0) {
476
+ throw new Error('The Playdrop surface gate booted the app frame unexpectedly.');
477
+ }
445
478
  return currentUrl;
446
479
  };
447
480
  if (hostedLaunchWaiter) {
448
481
  const launchState = await Promise.race([
449
482
  hostedLaunchWaiter,
450
483
  page.waitForTimeout(options.timeoutMs).then(() => {
484
+ if (hostedLaunchState && expectedHostedLaunchState && hostedLaunchState.state !== expectedHostedLaunchState) {
485
+ throw new Error(formatHostedLaunchStateMismatch(hostedLaunchState.state, expectedHostedLaunchState));
486
+ }
451
487
  throw new Error(expectedHostedLaunchState === 'login_required'
452
488
  ? 'Hosted app did not stop at the PlayDrop sign-in gate before the launch-check timeout.'
453
- : 'Hosted app did not reach the ready state before the launch-check timeout.');
489
+ : expectedHostedLaunchState === 'controller_required'
490
+ ? 'Hosted app did not stop at the PlayDrop controller gate before the launch-check timeout.'
491
+ : expectedHostedLaunchState === 'surface_unsupported'
492
+ ? 'Hosted app did not stop at the PlayDrop unsupported-surface gate before the launch-check timeout.'
493
+ : 'Hosted app did not reach the ready state before the launch-check timeout.');
454
494
  }),
455
495
  ]);
456
496
  if (launchState.state === 'error') {
457
497
  throw new Error(formatHostedLaunchError(launchState));
458
498
  }
459
499
  if (expectedHostedLaunchState && launchState.state !== expectedHostedLaunchState) {
460
- throw new Error(expectedHostedLaunchState === 'login_required'
461
- ? 'Hosted app did not stop at the PlayDrop sign-in gate.'
462
- : `Hosted app reported ${launchState.state} instead of ready.`);
500
+ throw new Error(formatHostedLaunchStateMismatch(launchState.state, expectedHostedLaunchState));
463
501
  }
464
502
  }
465
503
  finalUrl = await assertPageState();
@@ -13,15 +13,44 @@ const path_1 = __importDefault(require("path"));
13
13
  const fs_1 = require("fs");
14
14
  const config_1 = require("@playdrop/config");
15
15
  let cachedCliPackageVersion = null;
16
+ function assertValidCliVersion(version) {
17
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
18
+ throw new Error(`invalid_cli_package_version:${version}`);
19
+ }
20
+ return version;
21
+ }
22
+ function readVersionFromJson(pathname, field) {
23
+ try {
24
+ const parsed = JSON.parse((0, fs_1.readFileSync)(pathname, 'utf8'));
25
+ const value = typeof parsed[field] === 'string' ? parsed[field].trim() : '';
26
+ if (!value) {
27
+ return null;
28
+ }
29
+ return assertValidCliVersion(value);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
16
35
  function readCliPackageVersion() {
17
36
  if (cachedCliPackageVersion) {
18
37
  return cachedCliPackageVersion;
19
38
  }
39
+ const candidateMetaPaths = [
40
+ path_1.default.resolve(__dirname, '..', 'config', 'client-meta.json'),
41
+ path_1.default.resolve(__dirname, '..', '..', '..', 'config', 'client-meta.json'),
42
+ ];
43
+ for (const candidatePath of candidateMetaPaths) {
44
+ const clientMetaVersion = readVersionFromJson(candidatePath, 'version');
45
+ if (clientMetaVersion) {
46
+ cachedCliPackageVersion = clientMetaVersion;
47
+ return clientMetaVersion;
48
+ }
49
+ }
20
50
  const packageJsonPath = path_1.default.resolve(__dirname, '..', 'package.json');
21
- const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf8'));
22
- const version = typeof packageJson.version === 'string' ? packageJson.version.trim() : '';
23
- if (!/^\d+\.\d+\.\d+$/.test(version)) {
24
- throw new Error(`invalid_cli_package_version:${version}`);
51
+ const version = readVersionFromJson(packageJsonPath, 'version');
52
+ if (!version) {
53
+ throw new Error(`invalid_cli_package_version:${packageJsonPath}`);
25
54
  }
26
55
  cachedCliPackageVersion = version;
27
56
  return version;
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.6.6",
3
- "build": 3,
2
+ "version": "0.6.7",
3
+ "build": 1,
4
4
  "platforms": {
5
5
  "ios": {
6
6
  "minimumVersion": "16.0"
@@ -26,12 +26,12 @@
26
26
  },
27
27
  "clients": {
28
28
  "web": {
29
- "minimumVersion": "0.6.6",
30
- "minimumBuild": 3
29
+ "minimumVersion": "0.6.7",
30
+ "minimumBuild": 1
31
31
  },
32
32
  "admin": {
33
- "minimumVersion": "0.6.6",
34
- "minimumBuild": 3
33
+ "minimumVersion": "0.6.7",
34
+ "minimumBuild": 1
35
35
  },
36
36
  "apple": {
37
37
  "minimumVersion": "0.3.10",
@@ -46,7 +46,7 @@
46
46
  "minimumBuild": 1
47
47
  },
48
48
  "cli": {
49
- "minimumVersion": "0.6.6"
49
+ "minimumVersion": "0.6.7"
50
50
  }
51
51
  }
52
52
  }