@wlfi-agent/cli 1.4.18 → 1.4.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wlfi-agent/cli",
3
- "version": "1.4.18",
3
+ "version": "1.4.19",
4
4
  "description": "Single-entrypoint WLFI agent CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -165,6 +165,24 @@ const sudoSession = createSudoSession({
165
165
  ),
166
166
  });
167
167
 
168
+ function isSudoWrappedInvocation(): boolean {
169
+ return (
170
+ typeof process.geteuid === 'function' &&
171
+ process.geteuid() === 0 &&
172
+ typeof process.env.SUDO_UID === 'string' &&
173
+ process.env.SUDO_UID.trim().length > 0
174
+ );
175
+ }
176
+
177
+ function assertNotInvokedViaSudo(commandName: string): void {
178
+ if (!isSudoWrappedInvocation()) {
179
+ return;
180
+ }
181
+ throw new Error(
182
+ `run \`wlfi-agent ${commandName}\` as your normal macOS user, not with sudo; the CLI prompts for sudo internally and running it as root can target the wrong local WLFI home`,
183
+ );
184
+ }
185
+
168
186
  function createProgress(message: string, enabled = true): ProgressHandle {
169
187
  if (!enabled) {
170
188
  return {
@@ -226,6 +244,51 @@ function print(payload: unknown, asJson: boolean | undefined): void {
226
244
  /* c8 ignore stop */
227
245
  }
228
246
 
247
+ async function managedPathExists(targetPath: string): Promise<boolean> {
248
+ const result = await sudoSession.run(['/bin/test', '-e', targetPath]);
249
+ if (result.code === 0) {
250
+ return true;
251
+ }
252
+ if (
253
+ result.code === 1 &&
254
+ !/password is required|try again|authentication failed|sorry/iu.test(result.stderr)
255
+ ) {
256
+ return false;
257
+ }
258
+ throw new Error(
259
+ result.stderr.trim() ||
260
+ result.stdout.trim() ||
261
+ `failed to inspect managed path '${targetPath}' (exit code ${result.code})`,
262
+ );
263
+ }
264
+
265
+ async function assertManagedUninstallArtifactsRemoved(targetPaths: string[]): Promise<void> {
266
+ const remaining: string[] = [];
267
+ for (const targetPath of targetPaths) {
268
+ if (await managedPathExists(targetPath)) {
269
+ remaining.push(targetPath);
270
+ }
271
+ }
272
+ if (remaining.length > 0) {
273
+ throw new Error(
274
+ `admin uninstall left managed root-owned files behind: ${remaining.join(', ')}`,
275
+ );
276
+ }
277
+ }
278
+
279
+ function assertLocalUninstallArtifactsRemoved(result: CleanupLocalAdminUninstallStateResult): void {
280
+ const remaining: string[] = [];
281
+ if (result.config.existed && fs.existsSync(result.config.path)) {
282
+ remaining.push(result.config.path);
283
+ }
284
+ if (result.wlfiHome.existed && fs.existsSync(result.wlfiHome.path)) {
285
+ remaining.push(result.wlfiHome.path);
286
+ }
287
+ if (remaining.length > 0) {
288
+ throw new Error(`admin uninstall left local WLFI files behind: ${remaining.join(', ')}`);
289
+ }
290
+ }
291
+
229
292
  function printResetSummary(result: {
230
293
  daemon: {
231
294
  label: string;
@@ -456,6 +519,7 @@ async function confirmReset(options: AdminResetOptions): Promise<void> {
456
519
  }
457
520
 
458
521
  async function runAdminReset(options: AdminResetOptions): Promise<void> {
522
+ assertNotInvokedViaSudo('admin reset');
459
523
  await confirmReset(options);
460
524
  const showProgress = !options.json;
461
525
  const keychainAccount = os.userInfo().username;
@@ -607,6 +671,7 @@ function printUninstallSummary(result: {
607
671
  }
608
672
 
609
673
  async function runAdminUninstall(options: AdminUninstallOptions): Promise<void> {
674
+ assertNotInvokedViaSudo('admin uninstall');
610
675
  await confirmUninstall(options);
611
676
  const showProgress = !options.json;
612
677
  const keychainAccount = os.userInfo().username;
@@ -670,11 +735,29 @@ async function runAdminUninstall(options: AdminUninstallOptions): Promise<void>
670
735
  || `failed to delete managed root-owned files (exit code ${deleteRootArtifactsResult.code})`,
671
736
  );
672
737
  }
673
- rootProgress.succeed('Managed root-owned files removed');
738
+ try {
739
+ await assertManagedUninstallArtifactsRemoved([
740
+ DEFAULT_LAUNCH_DAEMON_PLIST,
741
+ DEFAULT_MANAGED_ROOT_DIR,
742
+ DEFAULT_MANAGED_STATE_DIR,
743
+ DEFAULT_MANAGED_LOG_DIR,
744
+ ]);
745
+ rootProgress.succeed('Managed root-owned files removed');
746
+ } catch (error) {
747
+ rootProgress.fail();
748
+ throw error;
749
+ }
674
750
 
675
751
  const localProgress = createProgress('Removing local WLFI files and credentials', showProgress);
676
- const local = cleanupLocalAdminUninstallState();
677
- localProgress.succeed('Local WLFI files and credentials removed');
752
+ let local: CleanupLocalAdminUninstallStateResult;
753
+ try {
754
+ local = cleanupLocalAdminUninstallState();
755
+ assertLocalUninstallArtifactsRemoved(local);
756
+ localProgress.succeed('Local WLFI files and credentials removed');
757
+ } catch (error) {
758
+ localProgress.fail();
759
+ throw error;
760
+ }
678
761
 
679
762
  const result = {
680
763
  command: 'uninstall',
@@ -393,6 +393,35 @@ function resolveStateFile(): string {
393
393
  return path.resolve(DEFAULT_MANAGED_STATE_FILE);
394
394
  }
395
395
 
396
+ function resolveDefaultActiveChainForFreshSetup(
397
+ config: WlfiConfig,
398
+ ): { chainId: number; chainName: string; rpcUrl?: string } | null {
399
+ try {
400
+ const profile = resolveCliNetworkProfile('bsc', config);
401
+ return {
402
+ chainId: profile.chainId,
403
+ chainName: profile.key?.trim() || profile.name.trim().toLowerCase() || 'bsc',
404
+ ...(profile.rpcUrl?.trim() ? { rpcUrl: profile.rpcUrl.trim() } : {}),
405
+ };
406
+ } catch {
407
+ return null;
408
+ }
409
+ }
410
+
411
+ function shouldSeedDefaultActiveChain(
412
+ options: Pick<AdminSetupOptions, 'network' | 'rpcUrl' | 'chainName'>,
413
+ config: WlfiConfig,
414
+ ): boolean {
415
+ return (
416
+ !options.network &&
417
+ !options.rpcUrl &&
418
+ !options.chainName &&
419
+ config.chainId === undefined &&
420
+ !config.chainName?.trim() &&
421
+ !config.rpcUrl?.trim()
422
+ );
423
+ }
424
+
396
425
  export function createAdminSetupPlan(
397
426
  options: AdminSetupOptions,
398
427
  deps: CreateAdminSetupPlanDeps = {},
@@ -1293,6 +1322,9 @@ async function runAdminSetup(options: AdminSetupOptions): Promise<void> {
1293
1322
  const config = readConfig();
1294
1323
  const daemonSocket = resolveDaemonSocket(options.daemonSocket);
1295
1324
  const stateFile = resolveStateFile();
1325
+ const defaultActiveChain = shouldSeedDefaultActiveChain(options, config)
1326
+ ? resolveDefaultActiveChainForFreshSetup(config)
1327
+ : null;
1296
1328
  const reusableWallet = options.reuseExistingWallet
1297
1329
  ? resolveReusableWalletSetupTarget(config)
1298
1330
  : null;
@@ -1588,6 +1620,13 @@ async function runAdminSetup(options: AdminSetupOptions): Promise<void> {
1588
1620
  const persistedConfig = writeConfig({
1589
1621
  daemonSocket,
1590
1622
  stateFile,
1623
+ ...(defaultActiveChain
1624
+ ? {
1625
+ chainId: defaultActiveChain.chainId,
1626
+ chainName: defaultActiveChain.chainName,
1627
+ ...(defaultActiveChain.rpcUrl ? { rpcUrl: defaultActiveChain.rpcUrl } : {}),
1628
+ }
1629
+ : {}),
1591
1630
  });
1592
1631
 
1593
1632
  printCliPayload(