@lynxship/cli 0.1.20 → 0.1.21

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.
Files changed (191) hide show
  1. package/README.md +66 -5
  2. package/dist/android/signing.d.ts +16 -0
  3. package/dist/android/signing.d.ts.map +1 -0
  4. package/dist/android/signing.js +131 -0
  5. package/dist/android/types.d.ts +12 -0
  6. package/dist/android/types.d.ts.map +1 -0
  7. package/dist/android/types.js +1 -0
  8. package/dist/android-build.d.ts +4 -13
  9. package/dist/android-build.d.ts.map +1 -1
  10. package/dist/android-build.js +14 -133
  11. package/dist/android-toolchain/inspection.d.ts +3 -0
  12. package/dist/android-toolchain/inspection.d.ts.map +1 -0
  13. package/dist/android-toolchain/inspection.js +122 -0
  14. package/dist/android-toolchain/probes.d.ts +23 -0
  15. package/dist/android-toolchain/probes.d.ts.map +1 -0
  16. package/dist/android-toolchain/probes.js +183 -0
  17. package/dist/android-toolchain/types.d.ts +20 -0
  18. package/dist/android-toolchain/types.d.ts.map +1 -0
  19. package/dist/android-toolchain/types.js +1 -0
  20. package/dist/android-toolchain.d.ts +4 -20
  21. package/dist/android-toolchain.d.ts.map +1 -1
  22. package/dist/android-toolchain.js +4 -300
  23. package/dist/app-config.d.ts +25 -0
  24. package/dist/app-config.d.ts.map +1 -0
  25. package/dist/app-config.js +106 -0
  26. package/dist/bundle-build.d.ts.map +1 -1
  27. package/dist/bundle-build.js +11 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +66 -0
  31. package/dist/commands/build-execution.d.ts +41 -0
  32. package/dist/commands/build-execution.d.ts.map +1 -0
  33. package/dist/commands/build-execution.js +182 -0
  34. package/dist/commands/configuration.d.ts +19 -0
  35. package/dist/commands/configuration.d.ts.map +1 -0
  36. package/dist/commands/configuration.js +174 -0
  37. package/dist/commands/development.d.ts +22 -0
  38. package/dist/commands/development.d.ts.map +1 -0
  39. package/dist/commands/development.js +132 -0
  40. package/dist/commands/device.d.ts +3 -0
  41. package/dist/commands/device.d.ts.map +1 -0
  42. package/dist/commands/device.js +86 -0
  43. package/dist/commands/doctor.d.ts +18 -0
  44. package/dist/commands/doctor.d.ts.map +1 -0
  45. package/dist/commands/doctor.js +320 -0
  46. package/dist/commands/help.d.ts +2 -0
  47. package/dist/commands/help.d.ts.map +1 -0
  48. package/dist/commands/help.js +118 -0
  49. package/dist/commands/i18n.d.ts +14 -0
  50. package/dist/commands/i18n.d.ts.map +1 -0
  51. package/dist/commands/i18n.js +88 -0
  52. package/dist/commands/logs.d.ts +3 -0
  53. package/dist/commands/logs.d.ts.map +1 -0
  54. package/dist/commands/logs.js +51 -0
  55. package/dist/commands/metadata.d.ts +3 -0
  56. package/dist/commands/metadata.d.ts.map +1 -0
  57. package/dist/commands/metadata.js +45 -0
  58. package/dist/commands/ota.d.ts +28 -0
  59. package/dist/commands/ota.d.ts.map +1 -0
  60. package/dist/commands/ota.js +203 -0
  61. package/dist/commands/project.d.ts +14 -0
  62. package/dist/commands/project.d.ts.map +1 -0
  63. package/dist/commands/project.js +122 -0
  64. package/dist/commands/remote-build.d.ts +35 -0
  65. package/dist/commands/remote-build.d.ts.map +1 -0
  66. package/dist/commands/remote-build.js +73 -0
  67. package/dist/commands/submit.d.ts +27 -0
  68. package/dist/commands/submit.d.ts.map +1 -0
  69. package/dist/commands/submit.js +92 -0
  70. package/dist/ecosystem.d.ts +9 -0
  71. package/dist/ecosystem.d.ts.map +1 -0
  72. package/dist/ecosystem.js +95 -0
  73. package/dist/frameworks.d.ts.map +1 -1
  74. package/dist/frameworks.js +5 -1
  75. package/dist/guidance/catalog.d.ts +3 -0
  76. package/dist/guidance/catalog.d.ts.map +1 -0
  77. package/dist/guidance/catalog.js +504 -0
  78. package/dist/guidance/types.d.ts +10 -0
  79. package/dist/guidance/types.d.ts.map +1 -0
  80. package/dist/guidance/types.js +1 -0
  81. package/dist/guidance.d.ts +2 -9
  82. package/dist/guidance.d.ts.map +1 -1
  83. package/dist/guidance.js +1 -502
  84. package/dist/i18n/discovery.d.ts +14 -0
  85. package/dist/i18n/discovery.d.ts.map +1 -0
  86. package/dist/i18n/discovery.js +106 -0
  87. package/dist/i18n/plan.d.ts +12 -0
  88. package/dist/i18n/plan.d.ts.map +1 -0
  89. package/dist/i18n/plan.js +95 -0
  90. package/dist/i18n/setup.d.ts +11 -0
  91. package/dist/i18n/setup.d.ts.map +1 -0
  92. package/dist/i18n/setup.js +47 -0
  93. package/dist/i18n/types.d.ts +17 -0
  94. package/dist/i18n/types.d.ts.map +1 -0
  95. package/dist/i18n/types.js +1 -0
  96. package/dist/index.d.ts +1 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +4 -2069
  99. package/dist/ios/assets.d.ts +19 -0
  100. package/dist/ios/assets.d.ts.map +1 -0
  101. package/dist/ios/assets.js +128 -0
  102. package/dist/ios/production-build.d.ts +6 -0
  103. package/dist/ios/production-build.d.ts.map +1 -0
  104. package/dist/ios/production-build.js +200 -0
  105. package/dist/ios/project.d.ts +4 -0
  106. package/dist/ios/project.d.ts.map +1 -0
  107. package/dist/ios/project.js +43 -0
  108. package/dist/ios/simulator-build.d.ts +8 -0
  109. package/dist/ios/simulator-build.d.ts.map +1 -0
  110. package/dist/ios/simulator-build.js +188 -0
  111. package/dist/ios/simulator.d.ts +5 -0
  112. package/dist/ios/simulator.d.ts.map +1 -0
  113. package/dist/ios/simulator.js +60 -0
  114. package/dist/ios/types.d.ts +14 -0
  115. package/dist/ios/types.d.ts.map +1 -0
  116. package/dist/ios/types.js +1 -0
  117. package/dist/ios-build.d.ts +4 -37
  118. package/dist/ios-build.d.ts.map +1 -1
  119. package/dist/ios-build.js +3 -575
  120. package/dist/ios-host.d.ts.map +1 -1
  121. package/dist/ios-toolchain/inspection.d.ts +4 -0
  122. package/dist/ios-toolchain/inspection.d.ts.map +1 -0
  123. package/dist/ios-toolchain/inspection.js +211 -0
  124. package/dist/ios-toolchain/probes.d.ts +24 -0
  125. package/dist/ios-toolchain/probes.d.ts.map +1 -0
  126. package/dist/ios-toolchain/probes.js +127 -0
  127. package/dist/ios-toolchain/types.d.ts +17 -0
  128. package/dist/ios-toolchain/types.d.ts.map +1 -0
  129. package/dist/ios-toolchain/types.js +1 -0
  130. package/dist/ios-toolchain.d.ts +3 -18
  131. package/dist/ios-toolchain.d.ts.map +1 -1
  132. package/dist/ios-toolchain.js +1 -333
  133. package/dist/plugins/contracts.d.ts +28 -0
  134. package/dist/plugins/contracts.d.ts.map +1 -0
  135. package/dist/plugins/contracts.js +1 -0
  136. package/dist/plugins/discovery.d.ts +19 -0
  137. package/dist/plugins/discovery.d.ts.map +1 -0
  138. package/dist/plugins/discovery.js +250 -0
  139. package/dist/plugins/native-operations.d.ts +28 -0
  140. package/dist/plugins/native-operations.d.ts.map +1 -0
  141. package/dist/plugins/native-operations.js +217 -0
  142. package/dist/plugins/operations.d.ts +9 -0
  143. package/dist/plugins/operations.d.ts.map +1 -0
  144. package/dist/plugins/operations.js +76 -0
  145. package/dist/plugins/validation.d.ts +9 -0
  146. package/dist/plugins/validation.d.ts.map +1 -0
  147. package/dist/plugins/validation.js +52 -0
  148. package/dist/plugins.d.ts +3 -34
  149. package/dist/plugins.d.ts.map +1 -1
  150. package/dist/plugins.js +2 -595
  151. package/dist/process-runner.d.ts +4 -0
  152. package/dist/process-runner.d.ts.map +1 -1
  153. package/dist/process-runner.js +14 -0
  154. package/dist/remote.d.ts +22 -1
  155. package/dist/remote.d.ts.map +1 -1
  156. package/dist/remote.js +141 -2
  157. package/dist/rspeedy-app-config.d.ts +4 -0
  158. package/dist/rspeedy-app-config.d.ts.map +1 -0
  159. package/dist/rspeedy-app-config.js +32 -0
  160. package/dist/runtime/args.d.ts +3 -0
  161. package/dist/runtime/args.d.ts.map +1 -0
  162. package/dist/runtime/args.js +11 -0
  163. package/dist/runtime/build-commands.d.ts +3 -0
  164. package/dist/runtime/build-commands.d.ts.map +1 -0
  165. package/dist/runtime/build-commands.js +282 -0
  166. package/dist/runtime/context.d.ts +63 -0
  167. package/dist/runtime/context.d.ts.map +1 -0
  168. package/dist/runtime/context.js +179 -0
  169. package/dist/runtime/early-commands.d.ts +4 -0
  170. package/dist/runtime/early-commands.d.ts.map +1 -0
  171. package/dist/runtime/early-commands.js +284 -0
  172. package/dist/runtime/project.d.ts +8 -0
  173. package/dist/runtime/project.d.ts.map +1 -0
  174. package/dist/runtime/project.js +38 -0
  175. package/dist/runtime/state.d.ts +47 -0
  176. package/dist/runtime/state.d.ts.map +1 -0
  177. package/dist/runtime/state.js +33 -0
  178. package/dist/secure-store/backends.d.ts +20 -0
  179. package/dist/secure-store/backends.d.ts.map +1 -0
  180. package/dist/secure-store/backends.js +248 -0
  181. package/dist/secure-store/types.d.ts +27 -0
  182. package/dist/secure-store/types.d.ts.map +1 -0
  183. package/dist/secure-store/types.js +1 -0
  184. package/dist/secure-store.d.ts +2 -26
  185. package/dist/secure-store.d.ts.map +1 -1
  186. package/dist/secure-store.js +3 -245
  187. package/package.json +7 -4
  188. package/templates/android-host/app/build.gradle +1 -0
  189. package/templates/android-host/app/src/main/java/template/MainActivity.java +98 -3
  190. package/templates/ios-host/__IOS_TARGET_NAME__/DemoLynxProvider.swift +26 -16
  191. package/templates/ios-host/__IOS_TARGET_NAME__/ViewController.swift +107 -19
@@ -0,0 +1,179 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { assert, } from "@lynxship/contracts";
5
+ import { platformValue, } from "../config.js";
6
+ import { loadCredentials } from "../secure-store.js";
7
+ import { createCliUi } from "../ui/index.js";
8
+ import { globalLynxShipDirectory } from "../paths.js";
9
+ import { prompt } from "../prompt.js";
10
+ import { buildLynxBundle } from "../bundle-build.js";
11
+ import { requireAutolinkReady } from "../autolink.js";
12
+ import { exists, findProjectRoot } from "./project.js";
13
+ import { projectDirectoryFlag, readFlag } from "./args.js";
14
+ export function createCliRuntime(rawArgs) {
15
+ const args = [...rawArgs];
16
+ const ui = createCliUi(rawArgs);
17
+ const json = ui.options.json;
18
+ const root = findProjectRoot(process.cwd(), {
19
+ explicitDirectory: projectDirectoryFlag(rawArgs),
20
+ environmentDirectory: process.env.LYNXSHIP_PROJECT_DIR,
21
+ });
22
+ const flag = (name, fallback = null) => readFlag(args, name, fallback);
23
+ function printValue(value, view) {
24
+ if (json) {
25
+ console.log(JSON.stringify(typeof value === "string" ? { result: value } : value));
26
+ return;
27
+ }
28
+ if (!ui.interactive || !view) {
29
+ console.log(typeof value === "string" ? value : JSON.stringify(value, null, 2));
30
+ return;
31
+ }
32
+ ui.summary(view.title, view.rows);
33
+ ui.done(view.done);
34
+ }
35
+ async function assertInteractivePrompt(label, fallback, optionName) {
36
+ assert(ui.interactive, "CLI_INTERACTIVE_REQUIRED", `Pass ${label.toLowerCase()} with ${optionName} in non-interactive mode.`);
37
+ return prompt(label, fallback);
38
+ }
39
+ async function requireProjectRoot() {
40
+ assert(await exists(join(root, "lynxship.json")), "CLI_PROJECT_REQUIRED", "Run this command from a LynxShip project directory containing lynxship.json, or run `lynxship init` first.");
41
+ }
42
+ async function readConfigurationStatus() {
43
+ const credentials = await loadCredentials(root);
44
+ const r2Configured = (((await exists(join(root, ".lynxship", "r2.json"))) ||
45
+ (await exists(join(globalLynxShipDirectory(), "r2.json")))) &&
46
+ Boolean(credentials.r2)) ||
47
+ Boolean(process.env.CLOUDFLARE_ACCOUNT_ID &&
48
+ process.env.R2_BUCKET &&
49
+ process.env.R2_ACCESS_KEY_ID &&
50
+ process.env.R2_SECRET_ACCESS_KEY);
51
+ const android = credentials.android;
52
+ const androidConfigured = Boolean(android?.keystorePath && (await exists(android.keystorePath))) ||
53
+ Boolean(process.env.LYNXSHIP_KEYSTORE_PATH &&
54
+ (await exists(process.env.LYNXSHIP_KEYSTORE_PATH)) &&
55
+ process.env.LYNXSHIP_KEY_ALIAS &&
56
+ process.env.LYNXSHIP_KEYSTORE_PASSWORD &&
57
+ process.env.LYNXSHIP_KEY_PASSWORD);
58
+ return { r2: r2Configured, android: androidConfigured };
59
+ }
60
+ async function requireOperationalConfiguration(platform, options = {}) {
61
+ await requireProjectRoot();
62
+ const status = await readConfigurationStatus();
63
+ if (options.requireR2 !== false)
64
+ assert(status.r2, "CLI_R2_REQUIRED", "Cloudflare R2 must be configured first. Run `lynxship storage configure`.");
65
+ if (platform !== "android" || options.requireSigning === false)
66
+ return;
67
+ assert(status.android, "BUILD_SIGNING_REQUIRED", "Android signing must be configured first. Run `lynxship android configure` or provide an existing keystore.");
68
+ }
69
+ async function requireR2Configuration() {
70
+ await requireProjectRoot();
71
+ const status = await readConfigurationStatus();
72
+ assert(status.r2, "CLI_R2_REQUIRED", "Cloudflare R2 must be configured first. Run `lynxship storage configure`.");
73
+ }
74
+ function configuredProjectId(config) {
75
+ assert(typeof config.projectId === "string" && config.projectId.length > 0, "CLI_PROJECT_ID_REQUIRED", "lynxship.json must contain a generated projectId. Run `lynxship init` for a new project configuration.");
76
+ return config.projectId;
77
+ }
78
+ function mobilePlatformValue(value) {
79
+ const platform = platformValue(value);
80
+ assert(platform === "android" || platform === "ios", "PLATFORM_COMMAND_UNSUPPORTED", "This command supports only android or ios. Web, HarmonyOS and Desktop use their own build adapters.");
81
+ return platform;
82
+ }
83
+ async function renderConfigurationFooter() {
84
+ if (!ui.interactive || ui.options.quiet)
85
+ return;
86
+ if (rawArgs[0] === "inspect")
87
+ return;
88
+ if (rawArgs.includes("--remote"))
89
+ return;
90
+ const profileIndex = rawArgs.indexOf("--profile");
91
+ const simulatorProfile = profileIndex >= 0 && rawArgs[profileIndex + 1] === "simulator";
92
+ if (rawArgs.includes("--simulator") || simulatorProfile)
93
+ return;
94
+ const status = await readConfigurationStatus();
95
+ const ready = status.r2 && status.android;
96
+ if (ready)
97
+ return;
98
+ ui.configurationStatus([
99
+ {
100
+ label: "Cloudflare R2",
101
+ value: status.r2 ? "configured" : "required · storage configure",
102
+ valueColor: status.r2 ? "green" : "yellow",
103
+ },
104
+ {
105
+ label: "Android signing",
106
+ value: status.android ? "configured" : "required · android configure",
107
+ valueColor: status.android ? "green" : "yellow",
108
+ },
109
+ {
110
+ label: "Operational CLI",
111
+ value: ready ? "ready" : "blocked until setup is complete",
112
+ valueColor: ready ? "green" : "red",
113
+ },
114
+ ]);
115
+ }
116
+ async function buildSharedLynxBundle(miso) {
117
+ const progress = ui.progress("Shared Lynx bundle");
118
+ try {
119
+ progress.update(undefined, "Building the shared Lynx bundle once for native targets…");
120
+ await buildLynxBundle(root, {
121
+ quiet: json,
122
+ onOutput: (message) => progress.event(message),
123
+ miso,
124
+ });
125
+ progress.update(100, "Shared native Lynx bundle ready");
126
+ }
127
+ finally {
128
+ progress.stop();
129
+ }
130
+ }
131
+ async function initSelfHost() {
132
+ const directory = join(root, ".lynxship");
133
+ await mkdir(directory, { recursive: true });
134
+ const file = join(directory, ".env");
135
+ if (await exists(file))
136
+ return { status: "unchanged", file };
137
+ const values = {
138
+ POSTGRES_PASSWORD: randomBytes(24).toString("base64url"),
139
+ LYNXSHIP_TOKEN: randomBytes(32).toString("base64url"),
140
+ };
141
+ await writeFile(file, `${Object.entries(values)
142
+ .map(([key, value]) => `${key}=${value}`)
143
+ .join("\n")}\n`, { mode: 0o600 });
144
+ return { status: "created", file };
145
+ }
146
+ const projectCommandContext = { root, ui, flag, prompt };
147
+ const commandContext = {
148
+ root,
149
+ args,
150
+ ui,
151
+ json,
152
+ flag,
153
+ project: projectCommandContext,
154
+ printValue,
155
+ mobilePlatformValue,
156
+ };
157
+ return {
158
+ rawArgs,
159
+ args,
160
+ ui,
161
+ json,
162
+ root,
163
+ flag,
164
+ projectCommandContext,
165
+ commandContext,
166
+ printValue,
167
+ assertInteractivePrompt,
168
+ requireProjectRoot,
169
+ readConfigurationStatus,
170
+ requireOperationalConfiguration,
171
+ requireR2Configuration,
172
+ configuredProjectId,
173
+ mobilePlatformValue,
174
+ renderConfigurationFooter,
175
+ buildSharedLynxBundle,
176
+ initSelfHost,
177
+ requireAutolinkReady,
178
+ };
179
+ }
@@ -0,0 +1,4 @@
1
+ import type { CliRuntime } from "./context.js";
2
+ export declare function runEarlyCommand(context: CliRuntime, command: string): Promise<boolean>;
3
+ export declare function commandHeader(command: string, args: readonly string[]): string;
4
+ //# sourceMappingURL=early-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"early-commands.d.ts","sourceRoot":"","sources":["../../src/runtime/early-commands.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAsB,eAAe,CACnC,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CA2UlB;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,MAAM,CAIR"}
@@ -0,0 +1,284 @@
1
+ import { join } from "node:path";
2
+ import { assert } from "@lynxship/contracts";
3
+ import { initializeBuildProject, initializeProject, } from "../commands/project.js";
4
+ import { runDoctor } from "../commands/doctor.js";
5
+ import { runRspeedyCommand, runDevToolDoctor, } from "../commands/development.js";
6
+ import { runAutolinkCodegen } from "../commands/development.js";
7
+ import { runDevice } from "../commands/device.js";
8
+ import { streamNativeLogs } from "../commands/logs.js";
9
+ import { runConfigurationCommands } from "../commands/configuration.js";
10
+ import { inspectAutolink } from "../autolink.js";
11
+ import { inspectOtaHost } from "../ota-doctor.js";
12
+ import { inspectProjectPlugins, applyProjectPlugins } from "../plugins.js";
13
+ import { inspectEcosystem } from "../ecosystem.js";
14
+ import { runI18nCommand } from "../commands/i18n.js";
15
+ import { loadConfig } from "../config.js";
16
+ import { exists } from "./project.js";
17
+ import { commandTitle } from "../commands/metadata.js";
18
+ export async function runEarlyCommand(context, command) {
19
+ const { args, root, ui, flag, projectCommandContext, printValue } = context;
20
+ if (command === "init") {
21
+ ui.info("Scanning project structure…");
22
+ const file = join(root, "lynxship.json");
23
+ if (await exists(file)) {
24
+ ui.warn("lynxship.json already exists; leaving the project unchanged");
25
+ printValue({ status: "unchanged", file }, {
26
+ title: "Project",
27
+ rows: [{ label: "Configuration", value: file, valueColor: "muted" }],
28
+ done: "Project already initialized.",
29
+ });
30
+ return true;
31
+ }
32
+ await initializeProject(projectCommandContext);
33
+ const initializedConfig = await loadConfig(root);
34
+ ui.success("Created lynxship.json");
35
+ printValue({ status: "created", file }, {
36
+ title: "Initialized",
37
+ rows: [
38
+ {
39
+ label: "Project ID",
40
+ value: initializedConfig.projectId ?? "unassigned",
41
+ valueColor: "purple",
42
+ },
43
+ { label: "Configuration", value: file, valueColor: "muted" },
44
+ ],
45
+ done: "Project initialized. Run lynxship build to get started.",
46
+ });
47
+ return true;
48
+ }
49
+ if (command === "plugin") {
50
+ const subcommand = args.shift() ?? "list";
51
+ assert(["list", "doctor", "apply"].includes(subcommand), "CLI_PLUGIN_COMMAND", "Use `lynxship plugin list`, `lynxship plugin doctor` or `lynxship plugin apply`.");
52
+ await context.requireProjectRoot();
53
+ const config = await loadConfig(root);
54
+ const report = await inspectProjectPlugins(root, config);
55
+ if (subcommand === "apply") {
56
+ const platform = context.mobilePlatformValue(flag("--platform", "android"));
57
+ const profile = flag("--profile", "production");
58
+ const dryRun = args.includes("--dry-run");
59
+ const result = await applyProjectPlugins(root, config, {
60
+ platform,
61
+ profile,
62
+ mode: dryRun ? "plan" : "apply",
63
+ });
64
+ printValue({
65
+ status: dryRun ? "planned" : "applied",
66
+ platform,
67
+ profile,
68
+ plugins: result.applied,
69
+ nativeChanges: result.changes.filter((change) => change.changed)
70
+ .length,
71
+ changes: result.changes,
72
+ templates: result.templates,
73
+ cloud: result.cloud,
74
+ build: result.build,
75
+ }, {
76
+ title: `LynxShip plugins · ${platform}`,
77
+ rows: [
78
+ {
79
+ label: "Applied",
80
+ value: result.applied.length ? result.applied.join(", ") : "none",
81
+ valueColor: "green",
82
+ },
83
+ {
84
+ label: "Native changes",
85
+ value: String(result.changes.filter((change) => change.changed).length),
86
+ valueColor: "blue",
87
+ },
88
+ ],
89
+ done: dryRun
90
+ ? "No native files were modified; review the planned changes."
91
+ : "Project plugin changes are applied atomically and idempotently.",
92
+ });
93
+ return true;
94
+ }
95
+ const invalid = report.plugins.filter((plugin) => plugin.status !== "ready");
96
+ printValue(report, {
97
+ title: `LynxShip plugins · ${subcommand}`,
98
+ rows: report.plugins.length > 0
99
+ ? report.plugins.map((plugin) => ({
100
+ label: plugin.name,
101
+ value: `${plugin.status} · ${plugin.capabilities.join(", ") || "no capabilities"} · ${plugin.reason}`,
102
+ valueColor: plugin.status === "ready"
103
+ ? "green"
104
+ : plugin.status === "missing"
105
+ ? "red"
106
+ : "yellow",
107
+ }))
108
+ : [
109
+ {
110
+ label: "Plugins",
111
+ value: "none configured",
112
+ valueColor: "muted",
113
+ },
114
+ ],
115
+ done: invalid.length === 0
116
+ ? "All project plugin manifests are valid."
117
+ : "Fix the invalid plugin package before building.",
118
+ });
119
+ if (subcommand === "doctor" && invalid.length > 0)
120
+ process.exitCode = 1;
121
+ return true;
122
+ }
123
+ if (command === "ecosystem") {
124
+ const packages = await inspectEcosystem(root);
125
+ printValue({ packages }, {
126
+ title: "LynxShip ecosystem",
127
+ rows: packages.map((item) => ({
128
+ label: item.name,
129
+ value: item.installed
130
+ ? `installed ${item.version}`
131
+ : "available · not installed",
132
+ valueColor: item.installed ? "green" : "muted",
133
+ })),
134
+ done: "Ecosystem package status inspected without changing the project.",
135
+ });
136
+ return true;
137
+ }
138
+ if (command === "i18n") {
139
+ await context.requireProjectRoot();
140
+ await runI18nCommand({
141
+ root,
142
+ args,
143
+ ui,
144
+ flag,
145
+ printValue,
146
+ });
147
+ return true;
148
+ }
149
+ if (command === "doctor") {
150
+ await runDoctor({
151
+ root,
152
+ args,
153
+ ui,
154
+ flag,
155
+ printValue,
156
+ readConfigurationStatus: context.readConfigurationStatus,
157
+ });
158
+ return true;
159
+ }
160
+ if (["dev", "preview", "inspect"].includes(command)) {
161
+ await runRspeedyCommand(context.commandContext, command);
162
+ return true;
163
+ }
164
+ if (command === "profile") {
165
+ await runRspeedyCommand(context.commandContext, "build", {
166
+ ...process.env,
167
+ RSPACK_PROFILE: process.env.RSPACK_PROFILE ?? "ALL",
168
+ });
169
+ return true;
170
+ }
171
+ if (["devtool", "trace", "recorder"].includes(command)) {
172
+ const subcommand = args.shift() ?? "doctor";
173
+ assert(subcommand === "doctor", "CLI_DEVTOOL_COMMAND", "Use `lynxship devtool doctor`, `lynxship trace doctor` or `lynxship recorder doctor`.");
174
+ await runDevToolDoctor(context.commandContext, command);
175
+ return true;
176
+ }
177
+ if (command === "autolink") {
178
+ const subcommand = args.shift() ?? "check";
179
+ assert(["check", "codegen"].includes(subcommand), "CLI_AUTOLINK_COMMAND", "Use `lynxship autolink check` or `lynxship autolink codegen`");
180
+ if (subcommand === "codegen") {
181
+ await runAutolinkCodegen(context.commandContext);
182
+ return true;
183
+ }
184
+ const platform = context.mobilePlatformValue(flag("--platform", "android"));
185
+ const status = (await inspectAutolink(root))[platform];
186
+ printValue(status, {
187
+ title: `Lynx Autolink · ${platform}`,
188
+ rows: [
189
+ {
190
+ label: "Required",
191
+ value: String(status.required),
192
+ valueColor: "blue",
193
+ },
194
+ {
195
+ label: "Ready",
196
+ value: String(status.ready),
197
+ valueColor: status.ready ? "green" : "red",
198
+ },
199
+ {
200
+ label: "Status",
201
+ value: status.reason,
202
+ valueColor: status.ready ? "green" : "yellow",
203
+ },
204
+ ],
205
+ done: status.ready
206
+ ? "Autolink host integration is ready."
207
+ : "Autolink host integration needs attention.",
208
+ });
209
+ return true;
210
+ }
211
+ if (command === "ota") {
212
+ assert((args.shift() ?? "doctor") === "doctor", "CLI_OTA_COMMAND", "Only `lynxship ota doctor` is available");
213
+ const platform = context.mobilePlatformValue(flag("--platform", "android"));
214
+ const status = await inspectOtaHost(root, platform);
215
+ printValue(status, {
216
+ title: `OTA host · ${platform}`,
217
+ rows: [
218
+ {
219
+ label: "Native files",
220
+ value: String(status.files.length),
221
+ valueColor: "blue",
222
+ },
223
+ {
224
+ label: "Missing hooks",
225
+ value: status.missing.length ? status.missing.join(", ") : "none",
226
+ valueColor: status.missing.length ? "red" : "green",
227
+ },
228
+ ],
229
+ done: status.missing.length === 0
230
+ ? "Native OTA integration looks ready."
231
+ : "Integrate the LynxShip OTA client before using device OTA.",
232
+ });
233
+ return true;
234
+ }
235
+ if (command === "run") {
236
+ await context.requireProjectRoot();
237
+ await runDevice(context.commandContext);
238
+ return true;
239
+ }
240
+ if (command === "logs") {
241
+ await context.requireProjectRoot();
242
+ await streamNativeLogs(context.commandContext);
243
+ return true;
244
+ }
245
+ if (command === "self-host") {
246
+ assert((args.shift() ?? "init") === "init", "CLI_SELF_HOST_COMMAND", "Only self-host init is available in this package");
247
+ ui.info("Preparing local self-host credentials…");
248
+ const result = await context.initSelfHost();
249
+ ui.success(result.status === "created"
250
+ ? "Created protected local environment file"
251
+ : "Existing environment file preserved");
252
+ printValue(result, {
253
+ title: "Self-host setup",
254
+ rows: [
255
+ {
256
+ label: "Status",
257
+ value: result.status,
258
+ valueColor: result.status === "created" ? "green" : "yellow",
259
+ },
260
+ { label: "Environment", value: result.file, valueColor: "muted" },
261
+ ],
262
+ done: "Self-host environment is ready.",
263
+ });
264
+ return true;
265
+ }
266
+ if (["storage", "ios", "android", "store"].includes(command)) {
267
+ await runConfigurationCommands({
268
+ root,
269
+ args,
270
+ ui,
271
+ flag,
272
+ printValue,
273
+ project: projectCommandContext,
274
+ assertInteractivePrompt: context.assertInteractivePrompt,
275
+ initializeBuildProject,
276
+ mobilePlatformValue: context.mobilePlatformValue,
277
+ }, command);
278
+ return true;
279
+ }
280
+ return false;
281
+ }
282
+ export function commandHeader(command, args) {
283
+ return commandTitle(command === "update" && args[0] === "rollback" ? "rollback" : command);
284
+ }
@@ -0,0 +1,8 @@
1
+ export interface ProjectRootOptions {
2
+ explicitDirectory?: string;
3
+ environmentDirectory?: string;
4
+ }
5
+ export declare function findProjectRoot(start: string, options?: ProjectRootOptions): string;
6
+ export declare function exists(file: string): Promise<boolean>;
7
+ export declare function findLockfile(rootDirectory: string): Promise<string | null>;
8
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/runtime/project.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAWR;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3D;AAED,wBAAsB,YAAY,CAChC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB"}
@@ -0,0 +1,38 @@
1
+ import { access } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ export function findProjectRoot(start, options = {}) {
5
+ const explicit = options.explicitDirectory ?? options.environmentDirectory;
6
+ if (explicit)
7
+ return resolve(explicit);
8
+ let current = resolve(start);
9
+ while (true) {
10
+ if (existsSync(join(current, "lynxship.json")))
11
+ return current;
12
+ const parent = dirname(current);
13
+ if (parent === current)
14
+ return resolve(start);
15
+ current = parent;
16
+ }
17
+ }
18
+ export async function exists(file) {
19
+ try {
20
+ await access(file);
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ export async function findLockfile(rootDirectory) {
28
+ let current = resolve(rootDirectory);
29
+ while (true) {
30
+ for (const file of ["pnpm-lock.yaml", "package-lock.json", "yarn.lock"])
31
+ if (await exists(join(current, file)))
32
+ return join(current, file);
33
+ const parent = dirname(current);
34
+ if (parent === current)
35
+ return null;
36
+ current = parent;
37
+ }
38
+ }
@@ -0,0 +1,47 @@
1
+ import { type SigningKey } from "@lynxship/signing";
2
+ import type { BuildJob, Platform, SubmissionJob } from "@lynxship/contracts";
3
+ import { JsonRepository } from "@lynxship/db";
4
+ import { BuildOrchestrator } from "@lynxship/build-orchestrator";
5
+ import { SubmissionService } from "@lynxship/submit";
6
+ import type { RemoteCliState } from "../remote.js";
7
+ export interface CliRelease {
8
+ id: string;
9
+ manifest: {
10
+ protocolVersion: number;
11
+ projectId: string;
12
+ channel: string;
13
+ platform: Platform;
14
+ runtimeVersion: string;
15
+ sequence: number;
16
+ keyId: string;
17
+ assets: Array<{
18
+ path: string;
19
+ hash: string;
20
+ size: number;
21
+ url?: string;
22
+ }>;
23
+ };
24
+ signature: string;
25
+ message: string;
26
+ createdAt: string;
27
+ }
28
+ export interface CliState extends RemoteCliState {
29
+ builds: BuildJob[];
30
+ submissions: SubmissionJob[];
31
+ releases: CliRelease[];
32
+ signingKey: SigningKey | null;
33
+ lastRollback?: {
34
+ releaseId: string;
35
+ reason: string;
36
+ at: string;
37
+ };
38
+ }
39
+ export interface LoadedCliState {
40
+ state: CliState;
41
+ repository: JsonRepository<CliState>;
42
+ builds: BuildOrchestrator;
43
+ submissions: SubmissionService;
44
+ }
45
+ export declare function loadState(root: string): Promise<LoadedCliState>;
46
+ export declare function saveState(state: CliState, repository: JsonRepository<CliState>, builds: BuildOrchestrator, submissions: SubmissionService): Promise<void>;
47
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/runtime/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,iBAAiB,EAElB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAA0B,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE;QACR,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,QAAQ,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC3E,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAS,SAAQ,cAAc;IAC9C,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,QAAQ,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,iBAAiB,CAAC;CAChC;AAID,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAqBrE;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE,cAAc,CAAC,QAAQ,CAAC,EACpC,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,33 @@
1
+ import { createSigningKey } from "@lynxship/signing";
2
+ import { JsonRepository } from "@lynxship/db";
3
+ import { BuildOrchestrator, LocalBuildExecutor, } from "@lynxship/build-orchestrator";
4
+ import { MockSubmissionProvider, SubmissionService } from "@lynxship/submit";
5
+ import { join } from "node:path";
6
+ let stateSaveQueue = Promise.resolve();
7
+ export async function loadState(root) {
8
+ const repository = new JsonRepository(join(root, ".lynxship", "state.json"), {
9
+ builds: [],
10
+ submissions: [],
11
+ releases: [],
12
+ signingKey: null,
13
+ });
14
+ const state = await repository.read();
15
+ state.builds ??= [];
16
+ state.submissions ??= [];
17
+ state.releases ??= [];
18
+ state.signingKey ??= createSigningKey();
19
+ const builds = new BuildOrchestrator(new LocalBuildExecutor());
20
+ builds.restore(state.builds);
21
+ const submissions = new SubmissionService(new MockSubmissionProvider());
22
+ submissions.restore(state.submissions);
23
+ return { state, repository, builds, submissions };
24
+ }
25
+ export async function saveState(state, repository, builds, submissions) {
26
+ const next = stateSaveQueue.then(async () => {
27
+ state.builds = builds.list();
28
+ state.submissions = submissions.list();
29
+ await repository.write(state);
30
+ });
31
+ stateSaveQueue = next.catch(() => undefined);
32
+ await next;
33
+ }
@@ -0,0 +1,20 @@
1
+ import type { StoredCredentials } from "./types.js";
2
+ export interface EncryptedCredentials {
3
+ version: 1;
4
+ platform: "windows-dpapi" | "macos-keychain" | "linux-secret-service" | "file-mode-600";
5
+ values: Record<string, string>;
6
+ }
7
+ export declare function credentialFile(root: string, global?: boolean): string;
8
+ export declare function protect(value: string): Promise<string>;
9
+ export declare function unprotect(value: string): Promise<string>;
10
+ export declare function runSecurity(args: string[]): Promise<string>;
11
+ export declare function runSecretTool(args: string[], input?: string): Promise<string>;
12
+ export declare function keychainAccount(root: string, global: boolean): string;
13
+ export declare function secretServiceAccount(root: string, global: boolean): string;
14
+ export declare function credentialValues(credentials: StoredCredentials): Record<string, string>;
15
+ export declare function credentialsFromValues(values: Record<string, string>): StoredCredentials;
16
+ export declare function readKeychain(root: string, global: boolean): Promise<StoredCredentials | undefined>;
17
+ export declare function saveKeychain(root: string, credentials: StoredCredentials, global: boolean): Promise<void>;
18
+ export declare function readSecretService(root: string, global: boolean): Promise<StoredCredentials | undefined>;
19
+ export declare function saveSecretService(root: string, credentials: StoredCredentials, global: boolean): Promise<boolean>;
20
+ //# sourceMappingURL=backends.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends.d.ts","sourceRoot":"","sources":["../../src/secure-store/backends.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EACJ,eAAe,GACf,gBAAgB,GAChB,sBAAsB,GACtB,eAAe,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,MAAM,CAKnE;AAiCD,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5D;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB3D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBzE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAErE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAE1E;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,iBAAiB,GAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6BxB;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,iBAAiB,CAoDnB;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAoBxC;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,iBAAiB,EAC9B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAsBxC;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,iBAAiB,EAC9B,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC,CAyBlB"}