@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,248 @@
1
+ import { unlink } from "node:fs/promises";
2
+ import { spawn } from "node:child_process";
3
+ import { join, resolve } from "node:path";
4
+ import { assert } from "@lynxship/contracts";
5
+ import { globalLynxShipDirectory } from "../paths.js";
6
+ import { commandExists } from "../process-runner.js";
7
+ const fileName = ".credentials.dpapi.json";
8
+ const keychainService = "com.lynxship.cli.credentials";
9
+ export function credentialFile(root, global = false) {
10
+ return join(global ? globalLynxShipDirectory() : join(root, ".lynxship"), fileName);
11
+ }
12
+ function runPowerShell(command, input) {
13
+ return new Promise((resolve, reject) => {
14
+ const child = spawn(`${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, ["-NoProfile", "-NonInteractive", "-Command", command], {
15
+ windowsHide: true,
16
+ stdio: ["pipe", "pipe", "pipe"],
17
+ env: {
18
+ ...process.env,
19
+ PSModulePath: [
20
+ `${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\Modules`,
21
+ `${process.env.ProgramFiles ?? "C:\\Program Files"}\\WindowsPowerShell\\Modules`,
22
+ ].join(";"),
23
+ },
24
+ });
25
+ let output = "";
26
+ let error = "";
27
+ child.stdout.on("data", (chunk) => (output += chunk.toString()));
28
+ child.stderr.on("data", (chunk) => (error += chunk.toString()));
29
+ child.once("error", reject);
30
+ child.once("close", (code) => {
31
+ if (code !== 0)
32
+ reject(new Error(error.trim() || "Windows secure storage failed"));
33
+ else
34
+ resolve(output.trim());
35
+ });
36
+ child.stdin.end(input);
37
+ });
38
+ }
39
+ export async function protect(value) {
40
+ if (process.platform !== "win32")
41
+ return value;
42
+ return runPowerShell("Import-Module Microsoft.PowerShell.Security; $input | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString", value);
43
+ }
44
+ export async function unprotect(value) {
45
+ if (process.platform !== "win32")
46
+ return value;
47
+ return runPowerShell("Import-Module Microsoft.PowerShell.Security; $cipher = [Console]::In.ReadToEnd(); $secure = ConvertTo-SecureString $cipher; $ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure); try { [Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr) }", value);
48
+ }
49
+ export function runSecurity(args) {
50
+ return new Promise((resolveOutput, reject) => {
51
+ const child = spawn("security", args, {
52
+ stdio: ["pipe", "pipe", "pipe"],
53
+ });
54
+ let output = "";
55
+ let error = "";
56
+ child.stdout.on("data", (chunk) => (output += chunk.toString()));
57
+ child.stderr.on("data", (chunk) => (error += chunk.toString()));
58
+ child.once("error", reject);
59
+ child.once("close", (code) => {
60
+ if (code !== 0)
61
+ reject(new Error(error.trim() || "macOS Keychain operation failed"));
62
+ else
63
+ resolveOutput(output.trim());
64
+ });
65
+ child.stdin.end();
66
+ });
67
+ }
68
+ export function runSecretTool(args, input = "") {
69
+ return new Promise((resolveOutput, reject) => {
70
+ const child = spawn("secret-tool", args, {
71
+ stdio: ["pipe", "pipe", "pipe"],
72
+ });
73
+ let output = "";
74
+ let error = "";
75
+ child.stdout.on("data", (chunk) => (output += chunk.toString()));
76
+ child.stderr.on("data", (chunk) => (error += chunk.toString()));
77
+ child.once("error", reject);
78
+ child.once("close", (code) => {
79
+ if (code !== 0)
80
+ reject(new Error(error.trim() || "Linux Secret Service operation failed"));
81
+ else
82
+ resolveOutput(output.trim());
83
+ });
84
+ child.stdin.end(input);
85
+ });
86
+ }
87
+ export function keychainAccount(root, global) {
88
+ return global ? "global" : `project:${resolve(root)}`;
89
+ }
90
+ export function secretServiceAccount(root, global) {
91
+ return keychainAccount(root, global);
92
+ }
93
+ export function credentialValues(credentials) {
94
+ const values = {};
95
+ if (credentials.r2) {
96
+ values.r2AccessKeyId = credentials.r2.accessKeyId;
97
+ values.r2SecretAccessKey = credentials.r2.secretAccessKey;
98
+ }
99
+ if (credentials.android) {
100
+ values.androidKeystorePath = credentials.android.keystorePath;
101
+ values.androidKeyAlias = credentials.android.keyAlias;
102
+ values.androidKeystorePassword = credentials.android.keystorePassword;
103
+ values.androidKeyPassword = credentials.android.keyPassword;
104
+ }
105
+ if (credentials.googlePlay) {
106
+ values.googlePlayServiceAccountJson =
107
+ credentials.googlePlay.serviceAccountJson;
108
+ values.googlePlayApplicationId = credentials.googlePlay.applicationId;
109
+ values.googlePlayTrack = credentials.googlePlay.track;
110
+ values.googlePlayReleaseStatus = credentials.googlePlay.releaseStatus;
111
+ }
112
+ if (credentials.appStoreConnect) {
113
+ values.ascApiKeyId = credentials.appStoreConnect.apiKeyId;
114
+ values.ascIssuerId = credentials.appStoreConnect.issuerId;
115
+ values.ascPrivateKey = credentials.appStoreConnect.privateKey;
116
+ values.ascBundleIdentifier = credentials.appStoreConnect.bundleIdentifier;
117
+ values.ascAppId = credentials.appStoreConnect.ascAppId ?? "";
118
+ values.ascTransporterPath =
119
+ credentials.appStoreConnect.transporterPath ?? "";
120
+ }
121
+ return values;
122
+ }
123
+ export function credentialsFromValues(values) {
124
+ return {
125
+ r2: values.r2AccessKeyId && values.r2SecretAccessKey
126
+ ? {
127
+ accessKeyId: values.r2AccessKeyId,
128
+ secretAccessKey: values.r2SecretAccessKey,
129
+ }
130
+ : undefined,
131
+ android: values.androidKeystorePath &&
132
+ values.androidKeyAlias &&
133
+ values.androidKeystorePassword &&
134
+ values.androidKeyPassword
135
+ ? {
136
+ keystorePath: values.androidKeystorePath,
137
+ keyAlias: values.androidKeyAlias,
138
+ keystorePassword: values.androidKeystorePassword,
139
+ keyPassword: values.androidKeyPassword,
140
+ }
141
+ : undefined,
142
+ googlePlay: values.googlePlayServiceAccountJson &&
143
+ values.googlePlayApplicationId &&
144
+ values.googlePlayTrack &&
145
+ values.googlePlayReleaseStatus
146
+ ? {
147
+ serviceAccountJson: values.googlePlayServiceAccountJson,
148
+ applicationId: values.googlePlayApplicationId,
149
+ track: values.googlePlayTrack,
150
+ releaseStatus: values.googlePlayReleaseStatus,
151
+ }
152
+ : undefined,
153
+ appStoreConnect: values.ascApiKeyId &&
154
+ values.ascIssuerId &&
155
+ values.ascPrivateKey &&
156
+ values.ascBundleIdentifier
157
+ ? {
158
+ apiKeyId: values.ascApiKeyId,
159
+ issuerId: values.ascIssuerId,
160
+ privateKey: values.ascPrivateKey,
161
+ bundleIdentifier: values.ascBundleIdentifier,
162
+ ascAppId: values.ascAppId || undefined,
163
+ transporterPath: values.ascTransporterPath || undefined,
164
+ }
165
+ : undefined,
166
+ };
167
+ }
168
+ export async function readKeychain(root, global) {
169
+ try {
170
+ const value = await runSecurity([
171
+ "find-generic-password",
172
+ "-a",
173
+ keychainAccount(root, global),
174
+ "-s",
175
+ keychainService,
176
+ "-w",
177
+ ]);
178
+ const stored = JSON.parse(value);
179
+ assert(stored.version === 1 && stored.platform === "macos-keychain", "CLI_CREDENTIALS_INVALID", "Unsupported LynxShip Keychain record");
180
+ return credentialsFromValues(stored.values);
181
+ }
182
+ catch {
183
+ return undefined;
184
+ }
185
+ }
186
+ export async function saveKeychain(root, credentials, global) {
187
+ const record = {
188
+ version: 1,
189
+ platform: "macos-keychain",
190
+ values: credentialValues(credentials),
191
+ };
192
+ await runSecurity([
193
+ "add-generic-password",
194
+ "-U",
195
+ "-a",
196
+ keychainAccount(root, global),
197
+ "-s",
198
+ keychainService,
199
+ "-w",
200
+ JSON.stringify(record),
201
+ ]);
202
+ await unlink(credentialFile(root, global)).catch(() => undefined);
203
+ }
204
+ export async function readSecretService(root, global) {
205
+ if (process.platform !== "linux" || !commandExists("secret-tool"))
206
+ return undefined;
207
+ try {
208
+ const value = await runSecretTool([
209
+ "lookup",
210
+ "application",
211
+ keychainService,
212
+ "account",
213
+ secretServiceAccount(root, global),
214
+ ]);
215
+ if (!value)
216
+ return undefined;
217
+ const stored = JSON.parse(value);
218
+ assert(stored.version === 1 && stored.platform === "linux-secret-service", "CLI_CREDENTIALS_INVALID", "Unsupported LynxShip Linux Secret Service record");
219
+ return credentialsFromValues(stored.values);
220
+ }
221
+ catch {
222
+ return undefined;
223
+ }
224
+ }
225
+ export async function saveSecretService(root, credentials, global) {
226
+ if (process.platform !== "linux" || !commandExists("secret-tool"))
227
+ return false;
228
+ const record = {
229
+ version: 1,
230
+ platform: "linux-secret-service",
231
+ values: credentialValues(credentials),
232
+ };
233
+ try {
234
+ await runSecretTool([
235
+ "store",
236
+ "--label",
237
+ "LynxShip CLI credentials",
238
+ "application",
239
+ keychainService,
240
+ "account",
241
+ secretServiceAccount(root, global),
242
+ ], JSON.stringify(record));
243
+ return true;
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ }
@@ -0,0 +1,27 @@
1
+ export interface StoredCredentials {
2
+ r2?: {
3
+ accessKeyId: string;
4
+ secretAccessKey: string;
5
+ };
6
+ android?: {
7
+ keystorePath: string;
8
+ keyAlias: string;
9
+ keystorePassword: string;
10
+ keyPassword: string;
11
+ };
12
+ googlePlay?: {
13
+ serviceAccountJson: string;
14
+ applicationId: string;
15
+ track: string;
16
+ releaseStatus: "draft" | "completed" | "inProgress" | "halted";
17
+ };
18
+ appStoreConnect?: {
19
+ apiKeyId: string;
20
+ issuerId: string;
21
+ privateKey: string;
22
+ bundleIdentifier: string;
23
+ ascAppId?: string;
24
+ transporterPath?: string;
25
+ };
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/secure-store/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE;QACH,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;KAChE,CAAC;IACF,eAAe,CAAC,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,29 +1,5 @@
1
- export interface StoredCredentials {
2
- r2?: {
3
- accessKeyId: string;
4
- secretAccessKey: string;
5
- };
6
- android?: {
7
- keystorePath: string;
8
- keyAlias: string;
9
- keystorePassword: string;
10
- keyPassword: string;
11
- };
12
- googlePlay?: {
13
- serviceAccountJson: string;
14
- applicationId: string;
15
- track: string;
16
- releaseStatus: "draft" | "completed" | "inProgress" | "halted";
17
- };
18
- appStoreConnect?: {
19
- apiKeyId: string;
20
- issuerId: string;
21
- privateKey: string;
22
- bundleIdentifier: string;
23
- ascAppId?: string;
24
- transporterPath?: string;
25
- };
26
- }
1
+ import type { StoredCredentials } from "./secure-store/types.js";
2
+ export type { StoredCredentials } from "./secure-store/types.js";
27
3
  export declare function credentialStorageDescription(): string;
28
4
  export declare function loadCredentials(root: string): Promise<StoredCredentials>;
29
5
  export declare function saveCredentials(root: string, credentials: StoredCredentials, options?: {
@@ -1 +1 @@
1
- {"version":3,"file":"secure-store.d.ts","sourceRoot":"","sources":["../src/secure-store.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE;QACH,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;KAChE,CAAC;IACF,eAAe,CAAC,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAKrD;AAmYD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAmCf"}
1
+ {"version":3,"file":"secure-store.d.ts","sourceRoot":"","sources":["../src/secure-store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAcjE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,wBAAgB,4BAA4B,IAAI,MAAM,CAKrD;AA6ED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAmCf"}
@@ -1,9 +1,9 @@
1
1
  import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
2
- import { spawn } from "node:child_process";
3
- import { join, resolve } from "node:path";
2
+ import { join } from "node:path";
3
+ import { commandExists } from "./process-runner.js";
4
4
  import { assert } from "@lynxship/contracts";
5
5
  import { globalLynxShipDirectory } from "./paths.js";
6
- import { commandExists } from "./process-runner.js";
6
+ import { credentialFile, credentialValues, credentialsFromValues, protect, readKeychain, readSecretService, saveKeychain, saveSecretService, unprotect, } from "./secure-store/backends.js";
7
7
  export function credentialStorageDescription() {
8
8
  if (process.platform === "win32")
9
9
  return "Windows DPAPI encrypted";
@@ -13,248 +13,6 @@ export function credentialStorageDescription() {
13
13
  return "Linux Secret Service";
14
14
  return "owner-only credential file";
15
15
  }
16
- const fileName = ".credentials.dpapi.json";
17
- const keychainService = "com.lynxship.cli.credentials";
18
- function credentialFile(root, global = false) {
19
- return join(global ? globalLynxShipDirectory() : join(root, ".lynxship"), fileName);
20
- }
21
- function runPowerShell(command, input) {
22
- return new Promise((resolve, reject) => {
23
- const child = spawn(`${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, ["-NoProfile", "-NonInteractive", "-Command", command], {
24
- windowsHide: true,
25
- stdio: ["pipe", "pipe", "pipe"],
26
- env: {
27
- ...process.env,
28
- PSModulePath: [
29
- `${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\Modules`,
30
- `${process.env.ProgramFiles ?? "C:\\Program Files"}\\WindowsPowerShell\\Modules`,
31
- ].join(";"),
32
- },
33
- });
34
- let output = "";
35
- let error = "";
36
- child.stdout.on("data", (chunk) => (output += chunk.toString()));
37
- child.stderr.on("data", (chunk) => (error += chunk.toString()));
38
- child.once("error", reject);
39
- child.once("close", (code) => {
40
- if (code !== 0)
41
- reject(new Error(error.trim() || "Windows secure storage failed"));
42
- else
43
- resolve(output.trim());
44
- });
45
- child.stdin.end(input);
46
- });
47
- }
48
- async function protect(value) {
49
- if (process.platform !== "win32")
50
- return value;
51
- return runPowerShell("Import-Module Microsoft.PowerShell.Security; $input | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString", value);
52
- }
53
- async function unprotect(value) {
54
- if (process.platform !== "win32")
55
- return value;
56
- return runPowerShell("Import-Module Microsoft.PowerShell.Security; $cipher = [Console]::In.ReadToEnd(); $secure = ConvertTo-SecureString $cipher; $ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure); try { [Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr) }", value);
57
- }
58
- function runSecurity(args) {
59
- return new Promise((resolveOutput, reject) => {
60
- const child = spawn("security", args, {
61
- stdio: ["pipe", "pipe", "pipe"],
62
- });
63
- let output = "";
64
- let error = "";
65
- child.stdout.on("data", (chunk) => (output += chunk.toString()));
66
- child.stderr.on("data", (chunk) => (error += chunk.toString()));
67
- child.once("error", reject);
68
- child.once("close", (code) => {
69
- if (code !== 0)
70
- reject(new Error(error.trim() || "macOS Keychain operation failed"));
71
- else
72
- resolveOutput(output.trim());
73
- });
74
- child.stdin.end();
75
- });
76
- }
77
- function runSecretTool(args, input = "") {
78
- return new Promise((resolveOutput, reject) => {
79
- const child = spawn("secret-tool", args, {
80
- stdio: ["pipe", "pipe", "pipe"],
81
- });
82
- let output = "";
83
- let error = "";
84
- child.stdout.on("data", (chunk) => (output += chunk.toString()));
85
- child.stderr.on("data", (chunk) => (error += chunk.toString()));
86
- child.once("error", reject);
87
- child.once("close", (code) => {
88
- if (code !== 0)
89
- reject(new Error(error.trim() || "Linux Secret Service operation failed"));
90
- else
91
- resolveOutput(output.trim());
92
- });
93
- child.stdin.end(input);
94
- });
95
- }
96
- function keychainAccount(root, global) {
97
- return global ? "global" : `project:${resolve(root)}`;
98
- }
99
- function secretServiceAccount(root, global) {
100
- return keychainAccount(root, global);
101
- }
102
- function credentialValues(credentials) {
103
- const values = {};
104
- if (credentials.r2) {
105
- values.r2AccessKeyId = credentials.r2.accessKeyId;
106
- values.r2SecretAccessKey = credentials.r2.secretAccessKey;
107
- }
108
- if (credentials.android) {
109
- values.androidKeystorePath = credentials.android.keystorePath;
110
- values.androidKeyAlias = credentials.android.keyAlias;
111
- values.androidKeystorePassword = credentials.android.keystorePassword;
112
- values.androidKeyPassword = credentials.android.keyPassword;
113
- }
114
- if (credentials.googlePlay) {
115
- values.googlePlayServiceAccountJson =
116
- credentials.googlePlay.serviceAccountJson;
117
- values.googlePlayApplicationId = credentials.googlePlay.applicationId;
118
- values.googlePlayTrack = credentials.googlePlay.track;
119
- values.googlePlayReleaseStatus = credentials.googlePlay.releaseStatus;
120
- }
121
- if (credentials.appStoreConnect) {
122
- values.ascApiKeyId = credentials.appStoreConnect.apiKeyId;
123
- values.ascIssuerId = credentials.appStoreConnect.issuerId;
124
- values.ascPrivateKey = credentials.appStoreConnect.privateKey;
125
- values.ascBundleIdentifier = credentials.appStoreConnect.bundleIdentifier;
126
- values.ascAppId = credentials.appStoreConnect.ascAppId ?? "";
127
- values.ascTransporterPath =
128
- credentials.appStoreConnect.transporterPath ?? "";
129
- }
130
- return values;
131
- }
132
- function credentialsFromValues(values) {
133
- return {
134
- r2: values.r2AccessKeyId && values.r2SecretAccessKey
135
- ? {
136
- accessKeyId: values.r2AccessKeyId,
137
- secretAccessKey: values.r2SecretAccessKey,
138
- }
139
- : undefined,
140
- android: values.androidKeystorePath &&
141
- values.androidKeyAlias &&
142
- values.androidKeystorePassword &&
143
- values.androidKeyPassword
144
- ? {
145
- keystorePath: values.androidKeystorePath,
146
- keyAlias: values.androidKeyAlias,
147
- keystorePassword: values.androidKeystorePassword,
148
- keyPassword: values.androidKeyPassword,
149
- }
150
- : undefined,
151
- googlePlay: values.googlePlayServiceAccountJson &&
152
- values.googlePlayApplicationId &&
153
- values.googlePlayTrack &&
154
- values.googlePlayReleaseStatus
155
- ? {
156
- serviceAccountJson: values.googlePlayServiceAccountJson,
157
- applicationId: values.googlePlayApplicationId,
158
- track: values.googlePlayTrack,
159
- releaseStatus: values.googlePlayReleaseStatus,
160
- }
161
- : undefined,
162
- appStoreConnect: values.ascApiKeyId &&
163
- values.ascIssuerId &&
164
- values.ascPrivateKey &&
165
- values.ascBundleIdentifier
166
- ? {
167
- apiKeyId: values.ascApiKeyId,
168
- issuerId: values.ascIssuerId,
169
- privateKey: values.ascPrivateKey,
170
- bundleIdentifier: values.ascBundleIdentifier,
171
- ascAppId: values.ascAppId || undefined,
172
- transporterPath: values.ascTransporterPath || undefined,
173
- }
174
- : undefined,
175
- };
176
- }
177
- async function readKeychain(root, global) {
178
- try {
179
- const value = await runSecurity([
180
- "find-generic-password",
181
- "-a",
182
- keychainAccount(root, global),
183
- "-s",
184
- keychainService,
185
- "-w",
186
- ]);
187
- const stored = JSON.parse(value);
188
- assert(stored.version === 1 && stored.platform === "macos-keychain", "CLI_CREDENTIALS_INVALID", "Unsupported LynxShip Keychain record");
189
- return credentialsFromValues(stored.values);
190
- }
191
- catch {
192
- return undefined;
193
- }
194
- }
195
- async function saveKeychain(root, credentials, global) {
196
- const record = {
197
- version: 1,
198
- platform: "macos-keychain",
199
- values: credentialValues(credentials),
200
- };
201
- await runSecurity([
202
- "add-generic-password",
203
- "-U",
204
- "-a",
205
- keychainAccount(root, global),
206
- "-s",
207
- keychainService,
208
- "-w",
209
- JSON.stringify(record),
210
- ]);
211
- await unlink(credentialFile(root, global)).catch(() => undefined);
212
- }
213
- async function readSecretService(root, global) {
214
- if (process.platform !== "linux" || !commandExists("secret-tool"))
215
- return undefined;
216
- try {
217
- const value = await runSecretTool([
218
- "lookup",
219
- "application",
220
- keychainService,
221
- "account",
222
- secretServiceAccount(root, global),
223
- ]);
224
- if (!value)
225
- return undefined;
226
- const stored = JSON.parse(value);
227
- assert(stored.version === 1 && stored.platform === "linux-secret-service", "CLI_CREDENTIALS_INVALID", "Unsupported LynxShip Linux Secret Service record");
228
- return credentialsFromValues(stored.values);
229
- }
230
- catch {
231
- return undefined;
232
- }
233
- }
234
- async function saveSecretService(root, credentials, global) {
235
- if (process.platform !== "linux" || !commandExists("secret-tool"))
236
- return false;
237
- const record = {
238
- version: 1,
239
- platform: "linux-secret-service",
240
- values: credentialValues(credentials),
241
- };
242
- try {
243
- await runSecretTool([
244
- "store",
245
- "--label",
246
- "LynxShip CLI credentials",
247
- "application",
248
- keychainService,
249
- "account",
250
- secretServiceAccount(root, global),
251
- ], JSON.stringify(record));
252
- return true;
253
- }
254
- catch {
255
- return false;
256
- }
257
- }
258
16
  function mergeCredentials(global, project) {
259
17
  return {
260
18
  r2: project.r2 ?? global.r2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynxship/cli",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "Build, sign, store, submit and update LynxJS applications from the terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -76,17 +76,20 @@
76
76
  "chalk": "5.4.1",
77
77
  "cli-progress": "3.12.0",
78
78
  "jsdom": "29.1.1",
79
+ "jiti": "2.7.0",
79
80
  "ora": "8.2.0",
80
81
  "pngjs": "7.0.0",
81
82
  "qr-code-styling": "1.9.2",
82
83
  "qrcode-terminal": "0.12.0",
83
84
  "@lynxship/build-orchestrator": "0.1.0",
85
+ "@lynxship/db": "0.1.0",
86
+ "@lynxship/microhs": "0.1.0",
87
+ "@lynxship/i18n": "0.1.0",
84
88
  "@lynxship/contracts": "0.1.0",
85
89
  "@lynxship/plugin-api": "0.1.0",
86
- "@lynxship/db": "0.1.0",
87
- "@lynxship/submit": "0.1.0",
90
+ "@lynxship/framework": "0.1.0",
88
91
  "@lynxship/signing": "0.1.0",
89
- "@lynxship/microhs": "0.1.0"
92
+ "@lynxship/submit": "0.1.0"
90
93
  },
91
94
  "devDependencies": {
92
95
  "@types/cli-progress": "3.11.6",
@@ -30,6 +30,7 @@ android {
30
30
 
31
31
  dependencies {
32
32
  implementation "org.lynxsdk.lynx:lynx:4.0.0"
33
+ implementation "androidx.annotation:annotation:1.10.0"
33
34
  implementation "org.lynxsdk.lynx:lynx-jssdk:4.0.0"
34
35
  implementation "org.lynxsdk.lynx:lynx-trace:4.0.0"
35
36
  implementation "org.lynxsdk.lynx:primjs:4.0.0"