@lynxship/cli 0.1.11 → 0.1.12

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 CHANGED
@@ -25,6 +25,33 @@ npx @lynxship/cli doctor --project-dir ./my-lynx-app
25
25
 
26
26
  The package exposes the `lynxship` executable.
27
27
 
28
+ ## Vue Lynx projects
29
+
30
+ LynxShip supports projects created with the official Vue Lynx scaffold. Vue
31
+ Lynx is integrated through the project's Rspeedy configuration, so LynxShip
32
+ does not swap plugins or mutate `lynx.config.*`; it invokes the project's
33
+ package-manager `build` script and then passes the generated Lynx bundle to
34
+ the selected native or non-native target pipeline.
35
+
36
+ ```bash
37
+ npm create vue-lynx@latest my-vue-lynx-app
38
+ cd my-vue-lynx-app
39
+ npm install
40
+ npx @lynxship/cli@latest init
41
+ npx @lynxship/cli@latest doctor
42
+ npx @lynxship/cli@latest build --platform android --profile production
43
+ ```
44
+
45
+ For iOS Simulator on macOS:
46
+
47
+ ```bash
48
+ npx @lynxship/cli@latest build --platform ios --simulator --no-upload
49
+ ```
50
+
51
+ The CLI generator itself creates ReactLynx templates by default. Use the
52
+ official `create-vue-lynx` scaffold when Vue is required, then add LynxShip to
53
+ that existing project with `init`.
54
+
28
55
  ## Requirements
29
56
 
30
57
  - Node.js 22 or 24 LTS, or Node.js 26 Current; Node.js 24 LTS is the
@@ -386,6 +413,18 @@ script. On macOS, `lynxship build --platform ios` installs CocoaPods before
386
413
  archiving. Xcode, CocoaPods and real Apple signing credentials are still
387
414
  required for a signed IPA; the CLI never fabricates them.
388
415
 
416
+ For an iOS Simulator build, use the dedicated simulator profile:
417
+
418
+ ```bash
419
+ lynxship doctor --project-dir ./my-lynx-app --platform ios --profile simulator
420
+ lynxship build --project-dir ./my-lynx-app --platform ios --simulator --profile simulator --no-upload
421
+ ```
422
+
423
+ This uses Xcode's `iphonesimulator` SDK, boots an available Simulator, creates
424
+ and installs a local `.app`, and does not require a physical iPhone, Apple
425
+ Distribution certificate, provisioning profile or R2 upload. Use the
426
+ production profile only for a signed device IPA.
427
+
389
428
  Run the complete first-use diagnosis before building:
390
429
 
391
430
  ```bash
package/dist/config.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface BuildProfile {
9
9
  ios?: {
10
10
  configuration?: string;
11
11
  distribution?: string;
12
+ simulator?: boolean;
12
13
  workspace?: string;
13
14
  project?: string;
14
15
  scheme?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,GAAG,CAAC,EAAE;QACJ,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,cAAc,CAAC,EAAE;QAAE,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1C,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,cAAc,EAAE,cAU5B,CAAC;AAaF,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,cAAc,CAyChB;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,cAAc,CAAC,CAazB;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,SAAe,GAClB,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAIjC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAOrD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,GAAG,CAAC,EAAE;QACJ,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,cAAc,CAAC,EAAE;QAAE,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1C,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,cAAc,EAAE,cAsB5B,CAAC;AAaF,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,cAAc,CAyChB;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,cAAc,CAAC,CAazB;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,SAAe,GAClB,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAejC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAOrD"}
package/dist/config.js CHANGED
@@ -4,6 +4,18 @@ import { assert } from "@lynxship/contracts";
4
4
  export const DEFAULT_CONFIG = {
5
5
  runtimeVersion: { policy: "fingerprint" },
6
6
  build: {
7
+ development: {
8
+ distribution: "development",
9
+ channel: "development",
10
+ environment: "development",
11
+ ios: { configuration: "Debug" },
12
+ },
13
+ simulator: {
14
+ distribution: "development",
15
+ channel: "development",
16
+ environment: "development",
17
+ ios: { configuration: "Debug", simulator: true },
18
+ },
7
19
  production: {
8
20
  distribution: "store",
9
21
  channel: "production",
@@ -52,9 +64,20 @@ export async function loadConfig(root, options = {}) {
52
64
  }
53
65
  }
54
66
  export function resolveProfile(config, name = "production") {
55
- const profile = config.build?.[name];
67
+ const profile = config.build?.[name] ?? DEFAULT_CONFIG.build?.[name];
56
68
  assert(profile, "PROFILE_NOT_FOUND", `Build profile '${name}' was not found`);
57
- return { name, ...profile };
69
+ const inherited = DEFAULT_CONFIG.build?.[name] ?? {};
70
+ const productionIos = config.build?.production?.ios ?? {};
71
+ return {
72
+ name,
73
+ ...inherited,
74
+ ...profile,
75
+ ios: {
76
+ ...(inherited.ios ?? {}),
77
+ ...(name !== "production" ? productionIos : {}),
78
+ ...(profile.ios ?? {}),
79
+ },
80
+ };
58
81
  }
59
82
  export function platformValue(value) {
60
83
  assert(["android", "ios", "harmony", "web", "desktop"].includes(value), "PLATFORM_INVALID", "Platform must be android, ios, harmony, web or desktop");
@@ -1,6 +1,11 @@
1
1
  export interface CliGuidance {
2
2
  commands: string[];
3
3
  note?: string;
4
+ environment?: string;
4
5
  }
5
- export declare function guidanceForError(error: unknown): CliGuidance;
6
+ export interface GuidanceContext {
7
+ args?: readonly string[];
8
+ hostPlatform?: NodeJS.Platform;
9
+ }
10
+ export declare function guidanceForError(error: unknown, context?: GuidanceContext): CliGuidance;
6
11
  //# sourceMappingURL=guidance.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAiXD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAiG5D"}
1
+ {"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAChC;AAumBD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,WAAW,CAEb"}
package/dist/guidance.js CHANGED
@@ -223,6 +223,35 @@ const guidance = {
223
223
  ],
224
224
  note: "The iOS doctor checks macOS, Xcode, xcrun, CocoaPods, Xcode settings, Apple signing identities, provisioning and export options without printing certificate contents.",
225
225
  },
226
+ IOS_SIMULATOR_PLATFORM: {
227
+ commands: [
228
+ "lynxship doctor --platform ios --profile simulator",
229
+ "lynxship build --platform ios --simulator --profile simulator --no-upload",
230
+ ],
231
+ note: "The iOS Simulator target is selected with --platform ios; Android, Web and Desktop targets cannot use this flag.",
232
+ },
233
+ IOS_SIMULATOR_RUNTIME_REQUIRED: {
234
+ commands: [
235
+ "xcode-select --install",
236
+ "lynxship doctor --platform ios --profile simulator",
237
+ "lynxship build --platform ios --simulator --profile simulator --no-upload",
238
+ ],
239
+ note: "Install an iOS Simulator runtime in Xcode > Settings > Components, then rerun the doctor.",
240
+ },
241
+ IOS_SIMULATOR_ARTIFACT_MISSING: {
242
+ commands: [
243
+ "lynxship doctor --platform ios --profile simulator",
244
+ "lynxship build --platform ios --simulator --profile simulator --no-upload",
245
+ ],
246
+ note: "The simulator build must produce an .app under DerivedData before it can be installed.",
247
+ },
248
+ IOS_SIMULATOR_UPLOAD_BLOCKED: {
249
+ commands: [
250
+ "lynxship build --platform ios --simulator --profile simulator --no-upload",
251
+ "lynxship build --platform ios --profile production",
252
+ ],
253
+ note: "Simulator .app artifacts stay local; use a signed production device build for R2 upload and distribution.",
254
+ },
226
255
  IOS_COCOAPODS_REQUIRED: {
227
256
  commands: ["brew install cocoapods", "lynxship build --platform ios"],
228
257
  note: "CocoaPods is required when the iOS host contains a Podfile.",
@@ -234,15 +263,20 @@ const guidance = {
234
263
  ],
235
264
  },
236
265
  IOS_SCHEME_REQUIRED: {
237
- commands: ["lynxship inspect", "lynxship doctor --platform ios"],
266
+ commands: [
267
+ "lynxship inspect",
268
+ "lynxship doctor --platform ios --profile simulator",
269
+ "lynxship doctor --platform ios --profile production",
270
+ ],
238
271
  note: "Set build.<profile>.ios.scheme in lynxship.json.",
239
272
  },
240
273
  IOS_EXPORT_OPTIONS_REQUIRED: {
241
274
  commands: [
242
275
  "lynxship ios host init --bundle-identifier com.example.myapp",
243
276
  "lynxship doctor --platform ios",
277
+ "lynxship build --platform ios --simulator --profile simulator --no-upload",
244
278
  ],
245
- note: "Set build.<profile>.ios.exportOptionsPlist to a valid export options file.",
279
+ note: "Set build.<profile>.ios.exportOptionsPlist for a device IPA; simulator .app builds do not require export options.",
246
280
  },
247
281
  IOS_DEVICE_REQUIRED: {
248
282
  commands: [
@@ -364,7 +398,7 @@ const guidance = {
364
398
  note: "Lynx Trace and Recorder are operated from Lynx DevTool Desktop after the native -dev runtime is integrated.",
365
399
  },
366
400
  };
367
- export function guidanceForError(error) {
401
+ function guidanceForErrorBase(error) {
368
402
  const code = error.code;
369
403
  if (typeof code === "string" && guidance[code])
370
404
  return guidance[code];
@@ -461,3 +495,96 @@ export function guidanceForError(error) {
461
495
  }
462
496
  return { commands: [] };
463
497
  }
498
+ function argumentValue(args, name) {
499
+ const index = args.indexOf(name);
500
+ if (index >= 0)
501
+ return args[index + 1];
502
+ const inline = args.find((value) => value.startsWith(`${name}=`));
503
+ return inline?.slice(name.length + 1);
504
+ }
505
+ function targetFromCode(code) {
506
+ if (!code)
507
+ return undefined;
508
+ if (code.startsWith("ANDROID_"))
509
+ return "android";
510
+ if (code.startsWith("IOS_"))
511
+ return "ios";
512
+ if (code.startsWith("HARMONY_"))
513
+ return "harmony";
514
+ if (code.startsWith("WEB_"))
515
+ return "web";
516
+ if (code.startsWith("DESKTOP_"))
517
+ return "desktop";
518
+ if (code === "BUILD_ALL_MACOS_REQUIRED")
519
+ return "all";
520
+ return undefined;
521
+ }
522
+ function targetFromCommand(command) {
523
+ const match = command.match(/--platform\s+(android|ios|harmony|web|desktop|all)/);
524
+ return match?.[1];
525
+ }
526
+ function targetCommand(platform, simulator, action) {
527
+ if (platform === "all")
528
+ return action === "doctor"
529
+ ? "lynxship doctor --platform android"
530
+ : "lynxship build --platform all --profile production";
531
+ if (platform === "ios" && simulator) {
532
+ return action === "doctor"
533
+ ? "lynxship doctor --platform ios --profile simulator"
534
+ : "lynxship build --platform ios --simulator --profile simulator --no-upload";
535
+ }
536
+ if (action === "doctor")
537
+ return `lynxship doctor --platform ${platform}`;
538
+ return `lynxship build --platform ${platform} --profile production`;
539
+ }
540
+ function adaptGuidance(guidance, error, context) {
541
+ const code = error.code;
542
+ const errorCode = typeof code === "string" ? code : undefined;
543
+ const args = context.args ?? [];
544
+ const requested = argumentValue(args, "--platform");
545
+ const target = requested ?? targetFromCode(errorCode);
546
+ const simulator = args.includes("--simulator");
547
+ let commands = guidance.commands;
548
+ // Generic errors must preserve the platform the developer actually asked
549
+ // for. The old fallback to Android was misleading for iOS/Web/Desktop jobs.
550
+ if (target &&
551
+ errorCode !== "BUILD_ALL_MACOS_REQUIRED" &&
552
+ ["BUILD_", "PROFILE_NOT_FOUND"].some((prefix) => errorCode?.startsWith(prefix))) {
553
+ commands = [
554
+ targetCommand(target, target === "ios" && simulator, "doctor"),
555
+ targetCommand(target, target === "ios" && simulator, "build"),
556
+ ];
557
+ }
558
+ const hostPlatform = context.hostPlatform ?? process.platform;
559
+ const macOnly = hostPlatform !== "darwin";
560
+ let requiredEnvironment;
561
+ commands = commands.map((command) => {
562
+ const commandTarget = targetFromCommand(command);
563
+ const isIosCommand = commandTarget === "ios" ||
564
+ command.includes("ios host") ||
565
+ command.startsWith("xcode-select") ||
566
+ command.startsWith("xcrun ") ||
567
+ command.startsWith("brew ");
568
+ const isAllCommand = commandTarget === "all";
569
+ if (macOnly && isIosCommand) {
570
+ requiredEnvironment = "macOS or a macOS CI runner";
571
+ return command;
572
+ }
573
+ if (macOnly && isAllCommand) {
574
+ requiredEnvironment = "macOS or a macOS CI runner";
575
+ return command;
576
+ }
577
+ return command;
578
+ });
579
+ const notes = guidance.note ? [guidance.note] : [];
580
+ if (requiredEnvironment)
581
+ notes.push("These commands must be run on macOS or a macOS CI runner; Android remains supported on Windows, macOS and Linux.");
582
+ return {
583
+ commands,
584
+ note: notes.length > 0 ? notes.join(" ") : undefined,
585
+ environment: requiredEnvironment,
586
+ };
587
+ }
588
+ export function guidanceForError(error, context = {}) {
589
+ return adaptGuidance(guidanceForErrorBase(error), error, context);
590
+ }
package/dist/index.js CHANGED
@@ -150,6 +150,12 @@ function mobilePlatformValue(value) {
150
150
  async function renderConfigurationFooter() {
151
151
  if (!ui.interactive || ui.options.quiet)
152
152
  return;
153
+ if (rawArgs[0] === "inspect")
154
+ return;
155
+ const profileIndex = rawArgs.indexOf("--profile");
156
+ const simulatorProfile = profileIndex >= 0 && rawArgs[profileIndex + 1] === "simulator";
157
+ if (rawArgs.includes("--simulator") || simulatorProfile)
158
+ return;
153
159
  const status = await readConfigurationStatus();
154
160
  const ready = status.r2 && status.android;
155
161
  if (ready)
@@ -203,14 +209,19 @@ async function saveState(state, repository, builds, submissions) {
203
209
  }
204
210
  let stateSaveQueue = Promise.resolve();
205
211
  async function executeBuild(options, showResult = true) {
206
- const { config, profile, platform, skipUpload, wait, local, state, repository, builds, submissions, progress: sharedProgress, progressPrefix, onProgress, onEvent, skipBundleBuild, } = options;
212
+ const { config, profile, platform, skipUpload, wait, local, state, repository, builds, submissions, progress: sharedProgress, progressPrefix, onProgress, onEvent, skipBundleBuild, simulator = false, simulatorDevice, } = options;
207
213
  if (wait && !local)
208
214
  await ensureNativeHostForBuild(platform, profile, config);
215
+ // Host initialization can write lynxship.json (for example, the generated
216
+ // iOS scheme). Always read it again before resolving the build profile so a
217
+ // first build uses the host that was just created in the same invocation.
218
+ const effectiveConfig = wait && !local ? await loadConfig(root) : config;
209
219
  if (platform === "android" || platform === "ios")
210
220
  await requireAutolinkReady(root, platform);
211
- const runtime = await inspectRuntimeFingerprint(root, platform, config);
221
+ const runtime = await inspectRuntimeFingerprint(root, platform, effectiveConfig);
222
+ const resolvedBuildProfile = resolveProfile(effectiveConfig, profile);
212
223
  const job = await builds.create({
213
- projectId: configuredProjectId(config),
224
+ projectId: configuredProjectId(effectiveConfig),
214
225
  organizationId: "local_org",
215
226
  platform,
216
227
  profile,
@@ -243,7 +254,8 @@ async function executeBuild(options, showResult = true) {
243
254
  const realAndroid = platform === "android" &&
244
255
  isSupportedAndroidPlatform() &&
245
256
  (await hasAndroidHost(root));
246
- const realIos = platform === "ios" && hasIosHost(root, resolveProfile(config, profile));
257
+ const realIos = platform === "ios" &&
258
+ hasIosHost(root, resolveProfile(effectiveConfig, profile));
247
259
  if (platform === "ios" && !realIos && !local)
248
260
  assert(false, "IOS_HOST_REQUIRED", "A macOS Xcode host is required for a real iOS build. Run `lynxship ios host init --bundle-identifier <id>` or pass --bundle-identifier <id> to build.");
249
261
  if (platform === "android" && !realAndroid && !local)
@@ -253,7 +265,7 @@ async function executeBuild(options, showResult = true) {
253
265
  assert(toolchain.ok, "ANDROID_TOOLCHAIN_REQUIRED", `Android toolchain is not ready: ${formatAndroidToolchainFailure(toolchain)}`);
254
266
  await runRealAndroidBuild(job, {
255
267
  root,
256
- profile: config.build?.[profile] ?? {},
268
+ profile: resolvedBuildProfile,
257
269
  uploadArtifacts: !skipUpload,
258
270
  skipBundleBuild,
259
271
  quiet: json,
@@ -262,11 +274,13 @@ async function executeBuild(options, showResult = true) {
262
274
  });
263
275
  }
264
276
  else if (realIos) {
265
- const toolchain = await inspectIosToolchain(root, resolveProfile(config, profile));
277
+ const toolchain = await inspectIosToolchain(root, resolveProfile(effectiveConfig, profile), simulator ? "simulator" : "device");
266
278
  assert(toolchain.ok, "IOS_TOOLCHAIN_REQUIRED", `iOS toolchain is not ready: ${formatIosToolchainFailure(toolchain)}`);
267
279
  await runRealIosBuild(job, {
268
280
  root,
269
- profile: config.build?.[profile] ?? {},
281
+ profile: resolvedBuildProfile,
282
+ simulator,
283
+ simulatorDevice,
270
284
  uploadArtifacts: !skipUpload,
271
285
  skipBundleBuild,
272
286
  quiet: json,
@@ -277,7 +291,7 @@ async function executeBuild(options, showResult = true) {
277
291
  else if (!local && platform === "web") {
278
292
  await runRealWebBuild(job, {
279
293
  root,
280
- profile: resolveProfile(config, profile),
294
+ profile: resolveProfile(effectiveConfig, profile),
281
295
  uploadArtifacts: !skipUpload,
282
296
  skipBundleBuild,
283
297
  quiet: json,
@@ -288,7 +302,7 @@ async function executeBuild(options, showResult = true) {
288
302
  else if (!local && platform === "harmony") {
289
303
  await runRealHarmonyBuild(job, {
290
304
  root,
291
- profile: resolveProfile(config, profile),
305
+ profile: resolveProfile(effectiveConfig, profile),
292
306
  uploadArtifacts: !skipUpload,
293
307
  skipBundleBuild,
294
308
  quiet: json,
@@ -299,7 +313,7 @@ async function executeBuild(options, showResult = true) {
299
313
  else if (!local && platform === "desktop") {
300
314
  await runRealDesktopBuild(job, {
301
315
  root,
302
- profile: resolveProfile(config, profile),
316
+ profile: resolveProfile(effectiveConfig, profile),
303
317
  uploadArtifacts: !skipUpload,
304
318
  allowUnsigned: options.allowUnsigned,
305
319
  skipBundleBuild,
@@ -440,9 +454,11 @@ Commands:
440
454
 
441
455
  Build options:
442
456
  --platform <p> Target android, ios, harmony, web, desktop or all (default: android)
443
- --profile <name> Build profile (default: production)
457
+ --profile <name> Build profile (default: production; simulator uses simulator)
444
458
  --no-wait Queue the build without executing it locally
445
459
  --no-upload Keep the artifact local and skip R2 (CI verification)
460
+ --simulator Build and install an iOS Simulator .app locally
461
+ --device <id> Select the iOS Simulator device for a simulator build
446
462
  --allow-unsigned Allow an unsigned Desktop artifact only with --no-upload (local tests)
447
463
  --local Use the contract-only build path for tests
448
464
 
@@ -488,7 +504,7 @@ Global options:
488
504
  --application-id <id> Android package/application ID for host init
489
505
  --bundle-identifier <id> iOS bundle identifier for host init
490
506
  --library-dir <path> Native library directory for autolink codegen
491
- --simulator Install an iOS .app on a simulator with simctl
507
+ --simulator Build/install an iOS .app or install one with simctl
492
508
  --help Show this complete command reference
493
509
 
494
510
  Node support: Node 22/24 LTS or Node 26 Current. Use Node 24 LTS for production.`;
@@ -508,7 +524,7 @@ async function looksLikeLynxProject() {
508
524
  ...packageJson.dependencies,
509
525
  ...packageJson.devDependencies,
510
526
  };
511
- return (Object.keys(dependencies).some((name) => name.startsWith("@lynx-js/")) ||
527
+ return (Object.keys(dependencies).some((name) => name.startsWith("@lynx-js/") || name === "vue-lynx") ||
512
528
  Object.values(packageJson.scripts ?? {}).some((script) => script.includes("rspeedy")));
513
529
  }
514
530
  catch {
@@ -885,12 +901,7 @@ async function main() {
885
901
  const nativeCredentialStore = !credentialStore.includes("owner-only");
886
902
  const androidHost = doctorPlatform === "android" ? await hasAndroidHost(root) : false;
887
903
  const doctorProfileName = flag("--profile", "production");
888
- const doctorProfile = config.build?.[doctorProfileName]
889
- ? resolveProfile(config, doctorProfileName)
890
- : {
891
- name: doctorProfileName,
892
- ...(DEFAULT_CONFIG.build?.production ?? {}),
893
- };
904
+ const doctorProfile = resolveProfile(config, doctorProfileName);
894
905
  const targetToolchain = doctorPlatform === "web"
895
906
  ? await inspectWebTarget(root, doctorProfile)
896
907
  : doctorPlatform === "harmony"
@@ -904,7 +915,9 @@ async function main() {
904
915
  ? await inspectAndroidToolchain(root)
905
916
  : undefined;
906
917
  const iosToolchain = doctorPlatform === "ios"
907
- ? await inspectIosToolchain(root, doctorProfile)
918
+ ? await inspectIosToolchain(root, doctorProfile, doctorProfile.ios?.simulator || doctorProfileName === "simulator"
919
+ ? "simulator"
920
+ : "device")
908
921
  : undefined;
909
922
  if (androidToolchain && args.includes("--fix")) {
910
923
  await fixAndroidToolchain(root, androidToolchain, (message) => confirm(message), (line) => ui.info(line));
@@ -988,11 +1001,19 @@ async function main() {
988
1001
  }))),
989
1002
  {
990
1003
  name: "cloudflare-r2",
991
- ok: configuration.r2,
992
- status: configuration.r2 ? "pass" : "fail",
993
- value: configuration.r2
994
- ? "configured"
995
- : "missing · fix: lynxship storage configure",
1004
+ ok: doctorPlatform === "ios" && doctorProfile.ios?.simulator
1005
+ ? true
1006
+ : configuration.r2,
1007
+ status: doctorPlatform === "ios" && doctorProfile.ios?.simulator
1008
+ ? "pass"
1009
+ : configuration.r2
1010
+ ? "pass"
1011
+ : "fail",
1012
+ value: doctorPlatform === "ios" && doctorProfile.ios?.simulator
1013
+ ? "not required for iOS Simulator"
1014
+ : configuration.r2
1015
+ ? "configured"
1016
+ : "missing · fix: lynxship storage configure",
996
1017
  },
997
1018
  ...(doctorPlatform === "android"
998
1019
  ? [
@@ -1619,7 +1640,9 @@ async function main() {
1619
1640
  const platformArgument = flag("--platform", "android");
1620
1641
  const buildAll = subcommand === "all" || platformArgument === "all";
1621
1642
  const platform = buildAll ? "android" : platformValue(platformArgument);
1622
- const skipUpload = args.includes("--no-upload");
1643
+ const simulator = args.includes("--simulator");
1644
+ const skipUpload = args.includes("--no-upload") || simulator;
1645
+ assert(!simulator || platformArgument === "ios", "IOS_SIMULATOR_PLATFORM", "The --simulator option is only available with --platform ios.");
1623
1646
  const allowUnsigned = args.includes("--allow-unsigned");
1624
1647
  assert(!allowUnsigned || skipUpload, "CLI_UNSIGNED_UPLOAD_BLOCKED", "--allow-unsigned is only available with --no-upload and can never upload an unsigned Desktop artifact.");
1625
1648
  await requireOperationalConfiguration(platform, { requireR2: !skipUpload });
@@ -1646,7 +1669,8 @@ async function main() {
1646
1669
  }
1647
1670
  assert(subcommand === "create" || buildAll, "CLI_BUILD_COMMAND", `Unknown build command: ${subcommand}`);
1648
1671
  const config = await loadConfig(root);
1649
- const profile = flag("--profile", "production");
1672
+ const profile = flag("--profile", simulator ? "simulator" : "production");
1673
+ const simulatorDevice = flag("--device") ?? undefined;
1650
1674
  const wait = !args.includes("--no-wait");
1651
1675
  const local = args.includes("--local");
1652
1676
  const platforms = buildAll
@@ -1666,6 +1690,8 @@ async function main() {
1666
1690
  allowUnsigned,
1667
1691
  wait,
1668
1692
  local,
1693
+ simulator,
1694
+ simulatorDevice,
1669
1695
  state,
1670
1696
  repository,
1671
1697
  builds,
@@ -1695,6 +1721,8 @@ async function main() {
1695
1721
  allowUnsigned,
1696
1722
  wait,
1697
1723
  local,
1724
+ simulator,
1725
+ simulatorDevice,
1698
1726
  state,
1699
1727
  repository,
1700
1728
  builds,
@@ -1781,7 +1809,7 @@ void main()
1781
1809
  .catch((error) => {
1782
1810
  const message = error instanceof Error ? error.message : "Unknown error";
1783
1811
  const code = error.code ?? "CLI_ERROR";
1784
- const nextSteps = guidanceForError(error);
1812
+ const nextSteps = guidanceForError(error, { args: rawArgs });
1785
1813
  if (json) {
1786
1814
  console.log(JSON.stringify({
1787
1815
  error: message,
@@ -1790,6 +1818,9 @@ void main()
1790
1818
  ? {
1791
1819
  nextSteps: nextSteps.commands,
1792
1820
  ...(nextSteps.note ? { note: nextSteps.note } : {}),
1821
+ ...(nextSteps.environment
1822
+ ? { environment: nextSteps.environment }
1823
+ : {}),
1793
1824
  }
1794
1825
  : {}),
1795
1826
  }));
@@ -4,6 +4,8 @@ interface IosBuildOptions {
4
4
  root: string;
5
5
  profile: BuildProfile;
6
6
  uploadArtifacts?: boolean;
7
+ simulator?: boolean;
8
+ simulatorDevice?: string;
7
9
  skipBundleBuild?: boolean;
8
10
  quiet?: boolean;
9
11
  onEvent?: (message: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ios-build.d.ts","sourceRoot":"","sources":["../src/ios-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAcxE;AAsDD,wBAAsB,eAAe,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAuMnB"}
1
+ {"version":3,"file":"ios-build.d.ts","sourceRoot":"","sources":["../src/ios-build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAwRD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAcxE;AAsDD,wBAAsB,eAAe,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAwMnB"}
package/dist/ios-build.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { copyFile, mkdir, readdir, readFile } from "node:fs/promises";
2
3
  import { existsSync, readdirSync } from "node:fs";
3
4
  import { join, resolve } from "node:path";
@@ -5,8 +6,177 @@ import { assert, sha256 } from "@lynxship/contracts";
5
6
  import { transitionBuild } from "@lynxship/build-orchestrator";
6
7
  import { loadR2, uploadR2Artifact } from "./r2.js";
7
8
  import { nativeArtifactName } from "./artifact-name.js";
8
- import { commandExists, runProcess } from "./process-runner.js";
9
+ import { captureProcess, commandExists, runProcess } from "./process-runner.js";
9
10
  import { buildLynxBundle } from "./bundle-build.js";
11
+ async function listSimulatorDevices(root, filter) {
12
+ const result = await captureProcess("xcrun", ["simctl", "list", "devices", filter, "--json"], { cwd: root });
13
+ if (result.code !== 0)
14
+ return [];
15
+ try {
16
+ const parsed = JSON.parse(result.stdout);
17
+ return Object.values(parsed.devices ?? {}).flat();
18
+ }
19
+ catch {
20
+ return [];
21
+ }
22
+ }
23
+ async function selectSimulatorDevice(root, requested) {
24
+ if (requested)
25
+ return requested;
26
+ const booted = (await listSimulatorDevices(root, "booted"))[0];
27
+ if (booted)
28
+ return booted.udid;
29
+ const available = (await listSimulatorDevices(root, "available")).find((device) => device.isAvailable !== false);
30
+ assert(available, "IOS_SIMULATOR_RUNTIME_REQUIRED", "No available iOS Simulator device was found. Install an iOS Simulator runtime in Xcode, then rerun the build.");
31
+ return available.udid;
32
+ }
33
+ async function ensureSimulatorBooted(root, device, onEvent, quiet) {
34
+ const booted = (await listSimulatorDevices(root, "booted")).some((entry) => entry.udid === device);
35
+ if (!booted) {
36
+ onEvent?.(`Booting iOS Simulator ${device}…`);
37
+ await runProcess("xcrun", ["simctl", "boot", device], {
38
+ cwd: root,
39
+ quiet,
40
+ onOutput: onEvent,
41
+ });
42
+ }
43
+ await runProcess("xcrun", ["simctl", "bootstatus", device, "-b"], {
44
+ cwd: root,
45
+ quiet,
46
+ onOutput: onEvent,
47
+ });
48
+ }
49
+ async function findSimulatorApp(root, derivedData, configuration, scheme) {
50
+ const products = join(derivedData, "Build", "Products", `${configuration}-iphonesimulator`);
51
+ const expected = join(products, `${scheme}.app`);
52
+ if (existsSync(expected))
53
+ return expected;
54
+ const entries = await readdir(products, { withFileTypes: true }).catch(() => []);
55
+ const app = entries.find((entry) => entry.isDirectory() && entry.name.endsWith(".app"));
56
+ assert(app, "IOS_SIMULATOR_ARTIFACT_MISSING", `Xcode did not produce a Simulator .app under ${products}`);
57
+ return join(products, app.name);
58
+ }
59
+ async function hashDirectory(directory) {
60
+ const hash = createHash("sha256");
61
+ let size = 0;
62
+ async function visit(current, relative) {
63
+ const entries = await readdir(current, { withFileTypes: true });
64
+ entries.sort((left, right) => left.name.localeCompare(right.name));
65
+ for (const entry of entries) {
66
+ const entryRelative = relative ? `${relative}/${entry.name}` : entry.name;
67
+ const entryPath = join(current, entry.name);
68
+ if (entry.isDirectory()) {
69
+ hash.update(`dir:${entryRelative}\n`);
70
+ await visit(entryPath, entryRelative);
71
+ }
72
+ else {
73
+ const content = await readFile(entryPath);
74
+ size += content.length;
75
+ hash.update(`file:${entryRelative}:${content.length}\n`);
76
+ hash.update(content.toString("latin1"), "latin1");
77
+ }
78
+ }
79
+ }
80
+ await visit(directory, "");
81
+ return { hash: hash.digest("hex"), size };
82
+ }
83
+ async function runRealIosSimulatorBuild(job, options) {
84
+ assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS Simulator builds require macOS with Xcode");
85
+ assert(commandExists("xcodebuild"), "IOS_XCODE_REQUIRED", "xcodebuild was not found. Install Xcode and select its command-line tools.");
86
+ assert(commandExists("xcrun"), "IOS_XCRUN_REQUIRED", "xcrun was not found. Install Xcode command-line tools.");
87
+ const project = findProject(options.root, options.profile);
88
+ const ios = options.profile.ios ?? {};
89
+ assert(ios.scheme, "IOS_SCHEME_REQUIRED", "Configure ios.scheme in the selected Simulator build profile");
90
+ const projectFlag = project.endsWith(".xcworkspace")
91
+ ? "-workspace"
92
+ : "-project";
93
+ const configuration = ios.configuration ?? "Debug";
94
+ const device = await selectSimulatorDevice(options.root, options.simulatorDevice);
95
+ await installCocoaPods(options.root, options.quiet, options.onEvent);
96
+ const directory = join(options.root, ".lynxship", "ios", job.id, "simulator");
97
+ const derivedData = join(directory, "derived-data");
98
+ const step = (message, progress) => {
99
+ options.onEvent?.(message);
100
+ options.onProgress?.(progress, message);
101
+ };
102
+ try {
103
+ assert(options.uploadArtifacts !== true, "IOS_SIMULATOR_UPLOAD_BLOCKED", "iOS Simulator .app builds stay local. Use --no-upload; simulator artifacts are not store artifacts.");
104
+ await mkdir(directory, { recursive: true });
105
+ transitionBuild(job, "uploading_source", "iOS Simulator source prepared");
106
+ if (options.skipBundleBuild) {
107
+ step("Using shared Lynx bundle", 5);
108
+ }
109
+ else {
110
+ step("Building Lynx bundle with Rspeedy…", 5);
111
+ await buildLynxBundle(options.root, {
112
+ quiet: options.quiet,
113
+ onOutput: options.onEvent,
114
+ });
115
+ }
116
+ if (ios.bundleScript) {
117
+ step("Syncing bundle into the iOS host…", 8);
118
+ await runProcess(process.execPath, [resolve(options.root, ios.bundleScript)], {
119
+ cwd: options.root,
120
+ quiet: options.quiet,
121
+ onOutput: options.onEvent,
122
+ });
123
+ }
124
+ transitionBuild(job, "queued", "iOS Simulator build queued locally");
125
+ transitionBuild(job, "provisioning", "iOS Simulator destination selected");
126
+ transitionBuild(job, "installing_dependencies", "iOS Simulator dependencies ready");
127
+ await ensureSimulatorBooted(options.root, device, options.onEvent, options.quiet);
128
+ step("Building iOS Simulator .app…", 15);
129
+ transitionBuild(job, "building", "iOS Simulator build started");
130
+ await runProcess("xcodebuild", [
131
+ projectFlag,
132
+ project,
133
+ "-scheme",
134
+ ios.scheme,
135
+ "-configuration",
136
+ configuration,
137
+ "-sdk",
138
+ "iphonesimulator",
139
+ "-destination",
140
+ `id=${device}`,
141
+ "-derivedDataPath",
142
+ derivedData,
143
+ "CODE_SIGNING_ALLOWED=NO",
144
+ "CODE_SIGNING_REQUIRED=NO",
145
+ "build",
146
+ ], { cwd: options.root, quiet: options.quiet, onOutput: options.onEvent });
147
+ const appPath = await findSimulatorApp(options.root, derivedData, configuration, ios.scheme);
148
+ step("Installing .app in iOS Simulator…", 75);
149
+ await runProcess("xcrun", ["simctl", "install", device, appPath], {
150
+ cwd: options.root,
151
+ quiet: options.quiet,
152
+ onOutput: options.onEvent,
153
+ });
154
+ const artifact = await hashDirectory(appPath);
155
+ const artifactName = nativeArtifactName("app");
156
+ job.attempts += 1;
157
+ job.artifact = {
158
+ name: artifactName,
159
+ hash: artifact.hash,
160
+ path: appPath,
161
+ size: artifact.size,
162
+ contentType: "application/octet-stream",
163
+ };
164
+ transitionBuild(job, "signing", "Simulator app verification completed");
165
+ transitionBuild(job, "uploading_artifacts", "Simulator app collected locally");
166
+ step(`Simulator app ready: ${artifactName}`, 100);
167
+ return transitionBuild(job, "success", "iOS Simulator app created");
168
+ }
169
+ catch (error) {
170
+ if (!["success", "failed", "canceled", "timed_out"].includes(job.state))
171
+ transitionBuild(job, "failed", error instanceof Error ? error.message : "iOS Simulator build failed");
172
+ job.logs.push({
173
+ level: "error",
174
+ message: error instanceof Error ? error.message : "iOS Simulator build failed",
175
+ at: new Date().toISOString(),
176
+ });
177
+ throw error;
178
+ }
179
+ }
10
180
  export function hasIosHost(root, profile) {
11
181
  if (process.platform !== "darwin")
12
182
  return false;
@@ -66,6 +236,8 @@ async function findIpa(directory) {
66
236
  return join(directory, ipa.name);
67
237
  }
68
238
  export async function runRealIosBuild(job, options) {
239
+ if (options.simulator)
240
+ return runRealIosSimulatorBuild(job, options);
69
241
  assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS builds require macOS with Xcode");
70
242
  assert(commandExists("xcodebuild"), "IOS_XCODE_REQUIRED", "xcodebuild was not found. Install Xcode and select its command-line tools.");
71
243
  assert(commandExists("xcrun"), "IOS_XCRUN_REQUIRED", "xcrun was not found. Install Xcode command-line tools.");
@@ -14,6 +14,7 @@ export interface IosToolchainReport {
14
14
  scheme?: string;
15
15
  automaticSigning: boolean;
16
16
  }
17
- export declare function inspectIosToolchain(root: string, profile: BuildProfile): Promise<IosToolchainReport>;
17
+ export type IosBuildTarget = "device" | "simulator";
18
+ export declare function inspectIosToolchain(root: string, profile: BuildProfile, target?: IosBuildTarget): Promise<IosToolchainReport>;
18
19
  export declare function formatIosToolchainFailure(report: IosToolchainReport): string;
19
20
  //# sourceMappingURL=ios-toolchain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ios-toolchain.d.ts","sourceRoot":"","sources":["../src/ios-toolchain.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAoKD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,kBAAkB,CAAC,CA4T7B;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAQ5E"}
1
+ {"version":3,"file":"ios-toolchain.d.ts","sourceRoot":"","sources":["../src/ios-toolchain.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,WAAW,CAAC;AAuKpD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,cAAyB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CA2W7B;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAQ5E"}
@@ -86,10 +86,11 @@ async function hasValidProvisioningProfile(root, bundleIdentifier) {
86
86
  }
87
87
  return false;
88
88
  }
89
- async function probeProject(root, context, profile) {
89
+ async function probeProject(root, context, profile, target) {
90
90
  if (!context.path || !context.scheme || !commandExists("xcodebuild"))
91
91
  return context;
92
- const configuration = profile.ios?.configuration ?? "Release";
92
+ const configuration = profile.ios?.configuration ??
93
+ (target === "simulator" ? "Debug" : "Release");
93
94
  const settings = await captureProcess("xcodebuild", [
94
95
  context.flag,
95
96
  context.path,
@@ -124,16 +125,19 @@ async function probeCommand(command, args, cwd) {
124
125
  return false;
125
126
  }
126
127
  }
127
- export async function inspectIosToolchain(root, profile) {
128
+ export async function inspectIosToolchain(root, profile, target = "device") {
128
129
  const checks = [];
129
130
  const isMac = platform() === "darwin";
130
131
  const host = findHost(root, profile);
131
132
  const hostExists = Boolean(host && existsSync(host));
132
133
  const scheme = profile.ios?.scheme;
134
+ const simulator = target === "simulator";
133
135
  const exportOptions = profile.ios?.exportOptionsPlist
134
136
  ? resolve(root, profile.ios.exportOptionsPlist)
135
137
  : undefined;
136
- checks.push(check("ios-platform", isMac ? "pass" : "fail", isMac ? "macOS" : `${platform()} · macOS is required for an IPA`, isMac
138
+ checks.push(check("ios-platform", isMac ? "pass" : "fail", isMac
139
+ ? "macOS"
140
+ : `${platform()} · macOS is required for an iOS ${simulator ? "Simulator app" : "IPA"}`, isMac
137
141
  ? undefined
138
142
  : "Run the iOS build on a macOS machine or macOS CI runner"));
139
143
  if (!isMac) {
@@ -168,12 +172,12 @@ export async function inspectIosToolchain(root, profile) {
168
172
  checks.push(check("ios-host", hostExists ? "pass" : "fail", hostExists ? host : "Xcode project or workspace not found", hostExists
169
173
  ? undefined
170
174
  : "lynxship ios host init --bundle-identifier com.example.myapp"));
171
- checks.push(check("ios-scheme", scheme ? "pass" : "fail", scheme ?? "missing from the production build profile", scheme ? undefined : "Set build.production.ios.scheme in lynxship.json"));
175
+ checks.push(check("ios-scheme", scheme ? "pass" : "fail", scheme ?? "missing from the selected build profile", scheme ? undefined : "Set ios.scheme in the selected build profile"));
172
176
  const context = await probeProject(root, {
173
177
  path: hostExists ? host : undefined,
174
178
  flag: host ? projectFlag(host) : "-project",
175
179
  scheme,
176
- }, profile);
180
+ }, profile, target);
177
181
  const settingsOutput = context.settings
178
182
  ? commandOutput(context.settings)
179
183
  : "";
@@ -190,29 +194,40 @@ export async function inspectIosToolchain(root, profile) {
190
194
  : "Open the project in Xcode and verify the scheme is shared and buildable"));
191
195
  const automaticSigning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Automatic";
192
196
  const team = setting(settingsOutput, "DEVELOPMENT_TEAM");
193
- checks.push(check("apple-team", context.settings?.code !== 0 || !context.settings
194
- ? "warn"
195
- : team
196
- ? "pass"
197
- : "fail", team ?? "team not resolved from Xcode build settings", team
197
+ checks.push(check("apple-team", simulator
198
+ ? "pass"
199
+ : context.settings?.code !== 0 || !context.settings
200
+ ? "warn"
201
+ : team
202
+ ? "pass"
203
+ : "fail", simulator
204
+ ? "not required for iOS Simulator"
205
+ : (team ?? "team not resolved from Xcode build settings"), simulator || team
198
206
  ? undefined
199
207
  : "Select the Apple Developer Team in Xcode Signing & Capabilities"));
200
208
  const distribution = profile.distribution ?? profile.ios?.distribution ?? "store";
201
- const identityResult = await (commandExists("security")
202
- ? captureProcess("security", ["find-identity", "-v", "-p", "codesigning"], {
203
- cwd: root,
204
- })
205
- : undefined);
209
+ const identityResult = simulator
210
+ ? undefined
211
+ : await (commandExists("security")
212
+ ? captureProcess("security", ["find-identity", "-v", "-p", "codesigning"], {
213
+ cwd: root,
214
+ })
215
+ : undefined);
206
216
  const identityOutput = identityResult ? commandOutput(identityResult) : "";
207
217
  const identityCount = Number(identityOutput.match(/(\d+) valid identities found/)?.[1] ?? 0);
208
218
  const requiredIdentity = distribution === "store" ? "Apple Distribution" : "Apple Development";
209
- checks.push(check("apple-signing-identity", identityCount === 0 || !identityOutput.includes(requiredIdentity)
210
- ? "fail"
211
- : "pass", identityCount === 0
212
- ? "no valid code-signing identity found"
213
- : identityOutput.includes(requiredIdentity)
214
- ? `${requiredIdentity} identity available`
215
- : `${requiredIdentity} identity not found`, identityCount > 0 && identityOutput.includes(requiredIdentity)
219
+ checks.push(check("apple-signing-identity", simulator
220
+ ? "pass"
221
+ : identityCount === 0 || !identityOutput.includes(requiredIdentity)
222
+ ? "fail"
223
+ : "pass", simulator
224
+ ? "not required for iOS Simulator"
225
+ : identityCount === 0
226
+ ? "no valid code-signing identity found"
227
+ : identityOutput.includes(requiredIdentity)
228
+ ? `${requiredIdentity} identity available`
229
+ : `${requiredIdentity} identity not found`, simulator ||
230
+ (identityCount > 0 && identityOutput.includes(requiredIdentity))
216
231
  ? undefined
217
232
  : "Install or select the Apple certificate in Xcode Settings > Accounts, then rerun doctor"));
218
233
  const podfile = join(root, "ios", "Podfile");
@@ -241,40 +256,49 @@ export async function inspectIosToolchain(root, profile) {
241
256
  "development",
242
257
  "enterprise",
243
258
  ]);
244
- checks.push(check("export-options", !exportOptions
245
- ? "fail"
246
- : !existsSync(exportOptions)
259
+ checks.push(check("export-options", simulator
260
+ ? "pass"
261
+ : !exportOptions
247
262
  ? "fail"
248
- : !exportMethod || !supportedExportMethods.has(exportMethod)
263
+ : !existsSync(exportOptions)
249
264
  ? "fail"
250
- : "pass", !exportOptions
251
- ? "missing from the production build profile"
252
- : !existsSync(exportOptions)
253
- ? `${exportOptions} not found`
254
- : exportMethod && supportedExportMethods.has(exportMethod)
255
- ? `method: ${exportMethod}`
256
- : "method missing or invalid for an iOS export", exportOptions &&
257
- existsSync(exportOptions) &&
258
- exportMethod &&
259
- supportedExportMethods.has(exportMethod)
265
+ : !exportMethod || !supportedExportMethods.has(exportMethod)
266
+ ? "fail"
267
+ : "pass", simulator
268
+ ? "not required for iOS Simulator"
269
+ : !exportOptions
270
+ ? "missing from the production build profile"
271
+ : !existsSync(exportOptions)
272
+ ? `${exportOptions} not found`
273
+ : exportMethod && supportedExportMethods.has(exportMethod)
274
+ ? `method: ${exportMethod}`
275
+ : "method missing or invalid for an iOS export", simulator ||
276
+ (exportOptions &&
277
+ existsSync(exportOptions) &&
278
+ exportMethod &&
279
+ supportedExportMethods.has(exportMethod))
260
280
  ? undefined
261
281
  : "Set build.production.ios.exportOptionsPlist to a valid ExportOptions.plist"));
262
282
  const manualProvisioning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Manual" ||
263
283
  exportOptionsContent?.includes("<key>provisioningProfiles</key>") === true;
264
284
  const profilesAvailable = await hasValidProvisioningProfile(root, setting(settingsOutput, "PRODUCT_BUNDLE_IDENTIFIER"));
265
- checks.push(check("ios-provisioning", manualProvisioning && !profilesAvailable
266
- ? "fail"
267
- : automaticSigning && !profilesAvailable
268
- ? "warn"
269
- : "pass", manualProvisioning && !profilesAvailable
270
- ? "manual signing selected but no local provisioning profile was found"
271
- : automaticSigning && !profilesAvailable
272
- ? "Xcode-managed provisioning will be requested during the build"
273
- : profilesAvailable
274
- ? "local provisioning profile available"
275
- : "not required by the detected signing mode", manualProvisioning && !profilesAvailable
276
- ? "Download a matching .mobileprovision/.provisionprofile from Apple Developer and install it in Xcode"
277
- : undefined));
285
+ checks.push(check("ios-provisioning", simulator
286
+ ? "pass"
287
+ : manualProvisioning && !profilesAvailable
288
+ ? "fail"
289
+ : automaticSigning && !profilesAvailable
290
+ ? "warn"
291
+ : "pass", simulator
292
+ ? "not required for iOS Simulator"
293
+ : manualProvisioning && !profilesAvailable
294
+ ? "manual signing selected but no local provisioning profile was found"
295
+ : automaticSigning && !profilesAvailable
296
+ ? "Xcode-managed provisioning will be requested during the build"
297
+ : profilesAvailable
298
+ ? "local provisioning profile available"
299
+ : "not required by the detected signing mode", simulator || !(manualProvisioning && !profilesAvailable)
300
+ ? undefined
301
+ : "Download a matching .mobileprovision/.provisionprofile from Apple Developer and install it in Xcode"));
278
302
  const simctlAvailable = await probeCommand("xcrun", ["--find", "simctl"], root);
279
303
  const devicectlAvailable = await probeCommand("xcrun", ["--find", "devicectl"], root);
280
304
  checks.push(check("ios-device-tools", simctlAvailable || devicectlAvailable ? "pass" : "warn", simctlAvailable && devicectlAvailable
@@ -286,6 +310,18 @@ export async function inspectIosToolchain(root, profile) {
286
310
  : "simctl and devicectl unavailable", simctlAvailable || devicectlAvailable
287
311
  ? undefined
288
312
  : "Install the full Xcode toolchain, then rerun lynxship doctor --platform ios"));
313
+ if (simulator) {
314
+ const runtimeResult = await (commandExists("xcrun")
315
+ ? captureProcess("xcrun", ["simctl", "list", "runtimes"], { cwd: root })
316
+ : undefined);
317
+ const runtimeOutput = runtimeResult ? commandOutput(runtimeResult) : "";
318
+ const hasRuntime = /iOS\s+[0-9]/.test(runtimeOutput);
319
+ checks.push(check("ios-simulator-runtime", hasRuntime ? "pass" : "fail", hasRuntime
320
+ ? "iOS Simulator runtime available"
321
+ : "no iOS Simulator runtime found", hasRuntime
322
+ ? undefined
323
+ : "Open Xcode > Settings > Components and install an iOS Simulator runtime"));
324
+ }
289
325
  const failures = checks.filter((item) => item.status === "fail");
290
326
  return {
291
327
  ok: failures.length === 0,
@@ -33,7 +33,7 @@ export declare const pill: {
33
33
  error: (text: string) => string;
34
34
  };
35
35
  export declare function finalLine(message: string, success?: boolean): void;
36
- export declare function nextSteps(commands: string[], note?: string): void;
36
+ export declare function nextSteps(commands: string[], note?: string, environment?: string): void;
37
37
  export declare function downloadArtifact(url: string, expiresAt?: string, enabled?: boolean): void;
38
38
  export declare function devServerQr(url: string, enabled?: boolean): void;
39
39
  //# sourceMappingURL=components.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/ui/components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,eAAO,MAAM,GAAG;qBACG,MAAM,GAAG,IAAI;kBAGhB,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;kBAGd,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;CAG7B,CAAC;AAgBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6C9D;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,IAAI,IAAI,IAAI,CAAC;CACd;AA6CD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoBnD;AA6BD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,cAAc,CAuEhB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,IAAI,IAAI,CAAC;CACd;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,aAAa,CA2BlE;AAYD,eAAO,MAAM,IAAI;oBACC,MAAM;iBACT,MAAM;iBACN,MAAM;kBACL,MAAM;CACrB,CAAC;AAEF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAE/D;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAOjE;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,UAAO,GACb,IAAI,CAQN;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAK7D"}
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/ui/components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,eAAO,MAAM,GAAG;qBACG,MAAM,GAAG,IAAI;kBAGhB,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;kBAGd,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;CAG7B,CAAC;AAgBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6C9D;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,IAAI,IAAI,IAAI,CAAC;CACd;AA6CD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoBnD;AA6BD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,cAAc,CAuEhB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,IAAI,IAAI,CAAC;CACd;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,aAAa,CA2BlE;AAYD,eAAO,MAAM,IAAI;oBACC,MAAM;iBACT,MAAM;iBACN,MAAM;kBACL,MAAM;CACrB,CAAC;AAEF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAE/D;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,UAAO,GACb,IAAI,CAQN;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAK7D"}
@@ -265,10 +265,12 @@ export const pill = {
265
265
  export function finalLine(message, success = true) {
266
266
  console.log(`\n${success ? c.brand("◆") : c.red("◆")} ${c.text(message)}`);
267
267
  }
268
- export function nextSteps(commands, note) {
268
+ export function nextSteps(commands, note, environment) {
269
269
  if (commands.length === 0)
270
270
  return;
271
271
  console.error(`\n${c.yellow("Next steps")}`);
272
+ if (environment)
273
+ console.error(` ${c.muted(`Run on ${environment}:`)}`);
272
274
  commands.forEach((command, index) => {
273
275
  console.error(` ${c.muted(`${index + 1}.`)} ${c.teal(command)}`);
274
276
  });
package/dist/ui/index.js CHANGED
@@ -43,7 +43,7 @@ export class CliUi {
43
43
  nextSteps(guidance) {
44
44
  if (this.options.json || this.options.quiet)
45
45
  return;
46
- nextSteps(guidance.commands, guidance.note);
46
+ nextSteps(guidance.commands, guidance.note, guidance.environment);
47
47
  }
48
48
  summary(title, rows) {
49
49
  if (this.interactive)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynxship/cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Build, sign, store, submit and update LynxJS applications from the terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -68,11 +68,11 @@
68
68
  "cli-progress": "3.12.0",
69
69
  "ora": "8.2.0",
70
70
  "qrcode-terminal": "0.12.0",
71
- "@lynxship/signing": "0.1.0",
72
- "@lynxship/db": "0.1.0",
73
71
  "@lynxship/build-orchestrator": "0.1.0",
72
+ "@lynxship/db": "0.1.0",
73
+ "@lynxship/submit": "0.1.0",
74
74
  "@lynxship/contracts": "0.1.0",
75
- "@lynxship/submit": "0.1.0"
75
+ "@lynxship/signing": "0.1.0"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@types/cli-progress": "3.11.6",