@ishlabs/cli 0.28.0 → 0.28.1

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.
@@ -493,9 +493,28 @@ Examples:
493
493
  chatMode = readChatMode(iteration.details);
494
494
  isPair = chatMode === "participant_pair";
495
495
  }
496
- if (!iterationHasContent(iteration.details, modality)) {
497
- const flagHint = describeRequiredContentFlag(modality, isPair ? "participant_pair" : undefined);
496
+ // Native (ios/android) interactive iterations name their target via
497
+ // `app_artifact`, which is OPTIONAL at create time: `ish iteration create
498
+ // --platform ios` with no --app stores it as "chosen at run time" (no
499
+ // app_artifact) and the app is supplied here via --app. The web app's
500
+ // "Run on your device" panel generates exactly that command
501
+ // (`ish study run --local --platform ios --app ./Build.app …`), so a
502
+ // run-time --app must satisfy the content requirement even though the
503
+ // iteration itself carries no stored target. `iterationHasContent` only
504
+ // sees the persisted details, so layer the run-time flag on top here.
505
+ const iterationPlatform = normalizePlatform(readIterationDetails(iteration.details).platform);
506
+ const isNativeIteration = iterationPlatform === "ios" || iterationPlatform === "android";
507
+ const runtimeAppProvided = isNativeIteration && typeof opts.app === "string" && opts.app.trim().length > 0;
508
+ if (!iterationHasContent(iteration.details, modality) && !runtimeAppProvided) {
498
509
  const iterAlias = tagAlias(ALIAS_PREFIX.iteration, iterationId);
510
+ if (isNativeIteration) {
511
+ // The target is an app, not a URL — point at --app, not --url.
512
+ const ext = iterationPlatform === "ios" ? ".app" : ".apk";
513
+ throw new Error(`Iteration "${iterationLabel}" (${iterAlias}) is a native ${iterationPlatform} iteration with no app set. ` +
514
+ `Pass \`--app <path-to-${ext}-or-bundle-id>\` on this run, ` +
515
+ `or store one on the iteration via \`ish iteration update ${iterAlias} --details-json '{"app_artifact":"<bundle-id-or-path>"}'\`, then retry.`);
516
+ }
517
+ const flagHint = describeRequiredContentFlag(modality, isPair ? "participant_pair" : undefined);
499
518
  throw new Error(`Iteration "${iterationLabel}" (${iterAlias}) has no ${isMedia ? "content" : isPair ? "people/scenarios" : isChat ? "endpoint" : "URL"} configured yet. ` +
500
519
  `Add ${isMedia ? "content" : isPair ? "the pair-mode payload" : isChat ? "an endpoint" : "a URL"} with ` +
501
520
  `\`ish iteration create --study ${resolvedStudy} ${flagHint}\` ` +
package/dist/lib/docs.js CHANGED
@@ -4423,6 +4423,14 @@ The platform defaults to the iteration's; \`--app\` on \`study run\` overrides t
4423
4423
  stored target with a fresh local build. The WebDriverAgent runner cold-starts
4424
4424
  slowly the first time (~30-60s) and is then reused across participants.
4425
4425
 
4426
+ If the iteration was created **without** \`--app\` (its target is "chosen at run
4427
+ time"), supply the app on the run instead — there's nothing stored to default
4428
+ from:
4429
+
4430
+ \`\`\`
4431
+ ish study run --local --platform ios --app ./Build.app --all --wait
4432
+ \`\`\`
4433
+
4426
4434
  ## 3b. Parallel runs — \`--parallel N\` (iOS + Android)
4427
4435
 
4428
4436
  \`\`\`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ishlabs/cli",
3
- "version": "0.28.0",
3
+ "version": "0.28.1",
4
4
  "description": "The command-line interface for ish",
5
5
  "type": "module",
6
6
  "bin": {