@wlfi-agent/cli 1.4.12 → 1.4.14
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/Cargo.lock +3968 -0
- package/Cargo.toml +50 -0
- package/README.md +426 -6
- package/crates/vault-cli-admin/Cargo.toml +26 -0
- package/crates/vault-cli-admin/src/io_utils.rs +500 -0
- package/crates/vault-cli-admin/src/main.rs +3990 -0
- package/crates/vault-cli-admin/src/shared_config.rs +624 -0
- package/crates/vault-cli-admin/src/tui/amounts.rs +180 -0
- package/crates/vault-cli-admin/src/tui/token_rpc.rs +250 -0
- package/crates/vault-cli-admin/src/tui/utils.rs +82 -0
- package/crates/vault-cli-admin/src/tui.rs +3410 -0
- package/crates/vault-cli-agent/Cargo.toml +24 -0
- package/crates/vault-cli-agent/src/io_utils.rs +576 -0
- package/crates/vault-cli-agent/src/main.rs +833 -0
- package/crates/vault-cli-daemon/Cargo.toml +28 -0
- package/crates/vault-cli-daemon/src/bin/wlfi-agent-system-keychain.rs +216 -0
- package/crates/vault-cli-daemon/src/main.rs +644 -0
- package/crates/vault-cli-daemon/src/relay_sync.rs +894 -0
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +167 -0
- package/crates/vault-daemon/Cargo.toml +32 -0
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +1041 -0
- package/crates/vault-daemon/src/daemon_parts/core_helpers.rs +1256 -0
- package/crates/vault-daemon/src/daemon_parts/types_api_rpc.rs +622 -0
- package/crates/vault-daemon/src/lib.rs +54 -0
- package/crates/vault-daemon/src/persistence.rs +441 -0
- package/crates/vault-daemon/src/tests.rs +237 -0
- package/crates/vault-daemon/src/tests_parts/part1.rs +1224 -0
- package/crates/vault-daemon/src/tests_parts/part2.rs +1021 -0
- package/crates/vault-daemon/src/tests_parts/part3.rs +835 -0
- package/crates/vault-daemon/src/tests_parts/part4.rs +604 -0
- package/crates/vault-domain/Cargo.toml +20 -0
- package/crates/vault-domain/src/action.rs +849 -0
- package/crates/vault-domain/src/address.rs +51 -0
- package/crates/vault-domain/src/approval.rs +90 -0
- package/crates/vault-domain/src/constants.rs +4 -0
- package/crates/vault-domain/src/error.rs +54 -0
- package/crates/vault-domain/src/keys.rs +71 -0
- package/crates/vault-domain/src/lib.rs +42 -0
- package/crates/vault-domain/src/nonce.rs +102 -0
- package/crates/vault-domain/src/policy.rs +172 -0
- package/crates/vault-domain/src/request.rs +53 -0
- package/crates/vault-domain/src/scope.rs +24 -0
- package/crates/vault-domain/src/session.rs +50 -0
- package/crates/vault-domain/src/signature.rs +34 -0
- package/crates/vault-domain/src/tests.rs +651 -0
- package/crates/vault-domain/src/u128_as_decimal_string.rs +44 -0
- package/crates/vault-policy/Cargo.toml +17 -0
- package/crates/vault-policy/src/engine.rs +301 -0
- package/crates/vault-policy/src/error.rs +81 -0
- package/crates/vault-policy/src/lib.rs +17 -0
- package/crates/vault-policy/src/report.rs +34 -0
- package/crates/vault-policy/src/tests.rs +891 -0
- package/crates/vault-policy/src/tests_explain.rs +78 -0
- package/crates/vault-sdk-agent/Cargo.toml +21 -0
- package/crates/vault-sdk-agent/src/lib.rs +711 -0
- package/crates/vault-signer/Cargo.toml +25 -0
- package/crates/vault-signer/src/lib.rs +731 -0
- package/crates/vault-signer/tests/secure_enclave_acl.rs +54 -0
- package/crates/vault-transport-unix/Cargo.toml +24 -0
- package/crates/vault-transport-unix/src/lib.rs +1640 -0
- package/crates/vault-transport-xpc/Cargo.toml +25 -0
- package/crates/vault-transport-xpc/src/client_codec_api.rs +635 -0
- package/crates/vault-transport-xpc/src/lib.rs +680 -0
- package/crates/vault-transport-xpc/src/tests.rs +818 -0
- package/crates/vault-transport-xpc/tests/e2e_flow.rs +773 -0
- package/dist/cli.cjs +35088 -0
- package/dist/cli.cjs.map +1 -0
- package/package.json +49 -43
- package/packages/cache/.turbo/turbo-build.log +52 -0
- package/packages/cache/dist/chunk-2QFWMUXT.cjs +43 -0
- package/packages/cache/dist/chunk-2QFWMUXT.cjs.map +1 -0
- package/packages/cache/dist/chunk-4U63TZTQ.js +43 -0
- package/packages/cache/dist/chunk-4U63TZTQ.js.map +1 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs +404 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs.map +1 -0
- package/packages/cache/dist/chunk-FGJEEF5N.js +404 -0
- package/packages/cache/dist/chunk-FGJEEF5N.js.map +1 -0
- package/packages/cache/dist/chunk-UYNEHZHB.cjs +45 -0
- package/packages/cache/dist/chunk-UYNEHZHB.cjs.map +1 -0
- package/packages/cache/dist/chunk-VXVMPG3W.js +45 -0
- package/packages/cache/dist/chunk-VXVMPG3W.js.map +1 -0
- package/packages/cache/dist/client/index.cjs +11 -0
- package/packages/cache/dist/client/index.cjs.map +1 -0
- package/packages/cache/dist/client/index.d.cts +15 -0
- package/packages/cache/dist/client/index.d.ts +15 -0
- package/packages/cache/dist/client/index.js +11 -0
- package/packages/cache/dist/client/index.js.map +1 -0
- package/packages/cache/dist/errors/index.cjs +11 -0
- package/packages/cache/dist/errors/index.cjs.map +1 -0
- package/packages/cache/dist/errors/index.d.cts +26 -0
- package/packages/cache/dist/errors/index.d.ts +26 -0
- package/packages/cache/dist/errors/index.js +11 -0
- package/packages/cache/dist/errors/index.js.map +1 -0
- package/packages/cache/dist/index.cjs +29 -0
- package/packages/cache/dist/index.cjs.map +1 -0
- package/packages/cache/dist/index.d.cts +4 -0
- package/packages/cache/dist/index.d.ts +4 -0
- package/packages/cache/dist/index.js +29 -0
- package/packages/cache/dist/index.js.map +1 -0
- package/packages/cache/dist/service/index.cjs +15 -0
- package/packages/cache/dist/service/index.cjs.map +1 -0
- package/packages/cache/dist/service/index.d.cts +184 -0
- package/packages/cache/dist/service/index.d.ts +184 -0
- package/packages/cache/dist/service/index.js +15 -0
- package/packages/cache/dist/service/index.js.map +1 -0
- package/packages/cache/node_modules/.bin/jiti +17 -0
- package/packages/cache/node_modules/.bin/tsc +17 -0
- package/packages/cache/node_modules/.bin/tsserver +17 -0
- package/packages/cache/node_modules/.bin/tsup +17 -0
- package/packages/cache/node_modules/.bin/tsup-node +17 -0
- package/packages/cache/node_modules/.bin/tsx +17 -0
- package/packages/cache/node_modules/.bin/vitest +17 -0
- package/packages/cache/package.json +48 -0
- package/packages/cache/src/client/index.ts +56 -0
- package/packages/cache/src/errors/index.ts +53 -0
- package/packages/cache/src/index.ts +3 -0
- package/packages/cache/src/service/index.test.ts +263 -0
- package/packages/cache/src/service/index.ts +678 -0
- package/packages/cache/tsconfig.json +13 -0
- package/packages/cache/tsup.config.ts +13 -0
- package/packages/cache/vitest.config.ts +16 -0
- package/packages/config/.turbo/turbo-build.log +18 -0
- package/packages/config/dist/index.cjs +1037 -0
- package/packages/config/dist/index.cjs.map +1 -0
- package/packages/config/dist/index.d.ts +131 -0
- package/packages/config/node_modules/.bin/jiti +17 -0
- package/packages/config/node_modules/.bin/tsc +17 -0
- package/packages/config/node_modules/.bin/tsserver +17 -0
- package/packages/config/node_modules/.bin/tsup +17 -0
- package/packages/config/node_modules/.bin/tsup-node +17 -0
- package/packages/config/node_modules/.bin/tsx +17 -0
- package/packages/config/package.json +21 -0
- package/packages/config/src/index.js +1 -0
- package/packages/config/src/index.ts +1282 -0
- package/packages/config/tsconfig.json +4 -0
- package/packages/rpc/.turbo/turbo-build.log +32 -0
- package/packages/rpc/dist/_esm-BCLXDO2R.cjs +3660 -0
- package/packages/rpc/dist/_esm-BCLXDO2R.cjs.map +1 -0
- package/packages/rpc/dist/ccip-OWJLAW55.cjs +16 -0
- package/packages/rpc/dist/ccip-OWJLAW55.cjs.map +1 -0
- package/packages/rpc/dist/chunk-APQIFZ3B.cjs +6247 -0
- package/packages/rpc/dist/chunk-APQIFZ3B.cjs.map +1 -0
- package/packages/rpc/dist/chunk-CDO2GWRD.cjs +410 -0
- package/packages/rpc/dist/chunk-CDO2GWRD.cjs.map +1 -0
- package/packages/rpc/dist/chunk-QGTNTFJ7.cjs +2249 -0
- package/packages/rpc/dist/chunk-QGTNTFJ7.cjs.map +1 -0
- package/packages/rpc/dist/chunk-TZDTAHWR.cjs +44 -0
- package/packages/rpc/dist/chunk-TZDTAHWR.cjs.map +1 -0
- package/packages/rpc/dist/index.cjs +7342 -0
- package/packages/rpc/dist/index.cjs.map +1 -0
- package/packages/rpc/dist/index.d.ts +3857 -0
- package/packages/rpc/dist/secp256k1-WCNM675D.cjs +18 -0
- package/packages/rpc/dist/secp256k1-WCNM675D.cjs.map +1 -0
- package/packages/rpc/node_modules/.bin/jiti +17 -0
- package/packages/rpc/node_modules/.bin/tsc +17 -0
- package/packages/rpc/node_modules/.bin/tsserver +17 -0
- package/packages/rpc/node_modules/.bin/tsup +17 -0
- package/packages/rpc/node_modules/.bin/tsup-node +17 -0
- package/packages/rpc/node_modules/.bin/tsx +17 -0
- package/packages/rpc/package.json +25 -0
- package/packages/rpc/src/index.ts +206 -0
- package/packages/rpc/tsconfig.json +4 -0
- package/packages/typescript/base.json +36 -0
- package/packages/typescript/nextjs.json +17 -0
- package/packages/typescript/package.json +10 -0
- package/packages/ui/.turbo/turbo-build.log +44 -0
- package/packages/ui/dist/chunk-MOAFBKSA.js +11 -0
- package/packages/ui/dist/chunk-MOAFBKSA.js.map +1 -0
- package/packages/ui/dist/components/badge.d.ts +12 -0
- package/packages/ui/dist/components/badge.js +31 -0
- package/packages/ui/dist/components/badge.js.map +1 -0
- package/packages/ui/dist/components/button.d.ts +13 -0
- package/packages/ui/dist/components/button.js +40 -0
- package/packages/ui/dist/components/button.js.map +1 -0
- package/packages/ui/dist/components/card.d.ts +10 -0
- package/packages/ui/dist/components/card.js +39 -0
- package/packages/ui/dist/components/card.js.map +1 -0
- package/packages/ui/dist/components/input.d.ts +5 -0
- package/packages/ui/dist/components/input.js +28 -0
- package/packages/ui/dist/components/input.js.map +1 -0
- package/packages/ui/dist/components/label.d.ts +5 -0
- package/packages/ui/dist/components/label.js +13 -0
- package/packages/ui/dist/components/label.js.map +1 -0
- package/packages/ui/dist/components/separator.d.ts +5 -0
- package/packages/ui/dist/components/separator.js +13 -0
- package/packages/ui/dist/components/separator.js.map +1 -0
- package/packages/ui/dist/components/textarea.d.ts +5 -0
- package/packages/ui/dist/components/textarea.js +27 -0
- package/packages/ui/dist/components/textarea.js.map +1 -0
- package/packages/ui/dist/tailwind.d.ts +56 -0
- package/packages/ui/dist/tailwind.js +60 -0
- package/packages/ui/dist/tailwind.js.map +1 -0
- package/packages/ui/dist/utils/cn.d.ts +5 -0
- package/packages/ui/dist/utils/cn.js +7 -0
- package/packages/ui/dist/utils/cn.js.map +1 -0
- package/packages/ui/node_modules/.bin/jiti +17 -0
- package/packages/ui/node_modules/.bin/tsc +17 -0
- package/packages/ui/node_modules/.bin/tsserver +17 -0
- package/packages/ui/node_modules/.bin/tsup +17 -0
- package/packages/ui/node_modules/.bin/tsup-node +17 -0
- package/packages/ui/node_modules/.bin/tsx +17 -0
- package/packages/ui/package.json +69 -0
- package/packages/ui/src/components/badge.tsx +27 -0
- package/packages/ui/src/components/button.tsx +40 -0
- package/packages/ui/src/components/card.tsx +31 -0
- package/packages/ui/src/components/input.tsx +21 -0
- package/packages/ui/src/components/label.tsx +6 -0
- package/packages/ui/src/components/separator.tsx +6 -0
- package/packages/ui/src/components/textarea.tsx +20 -0
- package/packages/ui/src/globals.css +70 -0
- package/packages/ui/src/tailwind.ts +56 -0
- package/packages/ui/src/utils/cn.ts +6 -0
- package/packages/ui/tsconfig.json +20 -0
- package/packages/ui/tsup.config.ts +20 -0
- package/pnpm-workspace.yaml +4 -0
- package/scripts/install-rust-binaries.mjs +84 -0
- package/scripts/launchd/install-user-daemon.sh +358 -0
- package/scripts/launchd/run-vault-daemon.sh +5 -0
- package/scripts/launchd/run-wlfi-agent-daemon.sh +73 -0
- package/scripts/launchd/uninstall-user-daemon.sh +103 -0
- package/src/cli.ts +2121 -0
- package/src/lib/admin-guard.js +1 -0
- package/src/lib/admin-guard.ts +185 -0
- package/src/lib/admin-passthrough.ts +33 -0
- package/src/lib/admin-reset.ts +751 -0
- package/src/lib/admin-setup.ts +1612 -0
- package/src/lib/agent-auth-clear.js +1 -0
- package/src/lib/agent-auth-clear.ts +58 -0
- package/src/lib/agent-auth-forwarding.js +1 -0
- package/src/lib/agent-auth-forwarding.ts +149 -0
- package/src/lib/agent-auth-migrate.js +1 -0
- package/src/lib/agent-auth-migrate.ts +150 -0
- package/src/lib/agent-auth-revoke.ts +103 -0
- package/src/lib/agent-auth-rotate.ts +107 -0
- package/src/lib/agent-auth-token.js +1 -0
- package/src/lib/agent-auth-token.ts +25 -0
- package/src/lib/agent-auth.ts +89 -0
- package/src/lib/asset-broadcast.js +1 -0
- package/src/lib/asset-broadcast.ts +285 -0
- package/src/lib/bootstrap-artifacts.js +1 -0
- package/src/lib/bootstrap-artifacts.ts +205 -0
- package/src/lib/bootstrap-credentials.js +1 -0
- package/src/lib/bootstrap-credentials.ts +832 -0
- package/src/lib/config-amounts.js +1 -0
- package/src/lib/config-amounts.ts +189 -0
- package/src/lib/config-mutation.ts +27 -0
- package/src/lib/fs-trust.js +1 -0
- package/src/lib/fs-trust.ts +537 -0
- package/src/lib/keychain.js +1 -0
- package/src/lib/keychain.ts +225 -0
- package/src/lib/local-admin-access.ts +106 -0
- package/src/lib/network-selection.js +1 -0
- package/src/lib/network-selection.ts +71 -0
- package/src/lib/passthrough-security.js +1 -0
- package/src/lib/passthrough-security.ts +114 -0
- package/src/lib/rpc-guard.js +1 -0
- package/src/lib/rpc-guard.ts +7 -0
- package/src/lib/rust-spawn-options.js +1 -0
- package/src/lib/rust-spawn-options.ts +98 -0
- package/src/lib/rust.js +1 -0
- package/src/lib/rust.ts +143 -0
- package/src/lib/signed-tx.js +1 -0
- package/src/lib/signed-tx.ts +116 -0
- package/src/lib/status-repair-cli.ts +116 -0
- package/src/lib/sudo.js +1 -0
- package/src/lib/sudo.ts +172 -0
- package/src/lib/vault-password-forwarding.js +1 -0
- package/src/lib/vault-password-forwarding.ts +155 -0
- package/src/lib/wallet-profile.js +1 -0
- package/src/lib/wallet-profile.ts +332 -0
- package/src/lib/wallet-repair.js +1 -0
- package/src/lib/wallet-repair.ts +304 -0
- package/src/lib/wallet-setup.js +1 -0
- package/src/lib/wallet-setup.ts +1466 -0
- package/src/lib/wallet-status.js +1 -0
- package/src/lib/wallet-status.ts +640 -0
- package/tsconfig.base.json +17 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +25 -0
- package/turbo.json +41 -0
- package/LICENSE.md +0 -1
- package/dist/wlfa/index.cjs +0 -250
- package/dist/wlfa/index.d.cts +0 -1
- package/dist/wlfa/index.d.ts +0 -1
- package/dist/wlfa/index.js +0 -250
- package/dist/wlfc/index.cjs +0 -1894
- package/dist/wlfc/index.d.cts +0 -1
- package/dist/wlfc/index.d.ts +0 -1
- package/dist/wlfc/index.js +0 -1894
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import readline from 'node:readline';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import {
|
|
7
|
+
deleteConfigKey,
|
|
8
|
+
readConfig,
|
|
9
|
+
redactConfig,
|
|
10
|
+
resolveConfigPath,
|
|
11
|
+
resolveWlfiHome,
|
|
12
|
+
type WlfiConfig,
|
|
13
|
+
} from '../../packages/config/src/index.js';
|
|
14
|
+
import {
|
|
15
|
+
cleanupAutoGeneratedBootstrapArtifacts,
|
|
16
|
+
type CleanupBootstrapArtifactsResult,
|
|
17
|
+
} from './bootstrap-artifacts.js';
|
|
18
|
+
import {
|
|
19
|
+
AGENT_AUTH_TOKEN_KEYCHAIN_SERVICE,
|
|
20
|
+
DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
21
|
+
deleteAgentAuthTokenFromKeychain,
|
|
22
|
+
} from './keychain.js';
|
|
23
|
+
import { createSudoSession } from './sudo.js';
|
|
24
|
+
|
|
25
|
+
const DEFAULT_LAUNCH_DAEMON_LABEL = 'com.wlfi.agent.daemon';
|
|
26
|
+
const DEFAULT_MANAGED_DAEMON_SOCKET = '/Library/WLFI/run/daemon.sock';
|
|
27
|
+
const DEFAULT_MANAGED_STATE_FILE = '/var/db/wlfi-agent/daemon-state.enc';
|
|
28
|
+
const DEFAULT_LAUNCH_DAEMON_PLIST = `/Library/LaunchDaemons/${DEFAULT_LAUNCH_DAEMON_LABEL}.plist`;
|
|
29
|
+
const DEFAULT_MANAGED_ROOT_DIR = '/Library/WLFI';
|
|
30
|
+
const DEFAULT_MANAGED_STATE_DIR = '/var/db/wlfi-agent';
|
|
31
|
+
const DEFAULT_MANAGED_LOG_DIR = '/var/log/wlfi-agent';
|
|
32
|
+
const MAX_SECRET_STDIN_BYTES = 16 * 1024;
|
|
33
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
34
|
+
|
|
35
|
+
interface ProgressHandle {
|
|
36
|
+
succeed(message?: string): void;
|
|
37
|
+
fail(message?: string): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface AdminResetOptions {
|
|
41
|
+
yes?: boolean;
|
|
42
|
+
nonInteractive?: boolean;
|
|
43
|
+
deleteConfig?: boolean;
|
|
44
|
+
json?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface AdminUninstallOptions {
|
|
48
|
+
yes?: boolean;
|
|
49
|
+
nonInteractive?: boolean;
|
|
50
|
+
json?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CleanupLocalAdminResetStateResult {
|
|
54
|
+
agentKeyId: string | null;
|
|
55
|
+
keychain: {
|
|
56
|
+
removed: boolean;
|
|
57
|
+
service: string | null;
|
|
58
|
+
};
|
|
59
|
+
config: {
|
|
60
|
+
path: string;
|
|
61
|
+
existed: boolean;
|
|
62
|
+
deleted: boolean;
|
|
63
|
+
clearedAgentKeyId: boolean;
|
|
64
|
+
clearedLegacyAgentAuthToken: boolean;
|
|
65
|
+
value: Record<string, unknown> | null;
|
|
66
|
+
};
|
|
67
|
+
bootstrapArtifacts: {
|
|
68
|
+
attempted: boolean;
|
|
69
|
+
action: 'deleted';
|
|
70
|
+
fileCount: number;
|
|
71
|
+
cleanedCount: number;
|
|
72
|
+
skippedCount: number;
|
|
73
|
+
error: string | null;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface CleanupLocalAdminResetStateDeps {
|
|
78
|
+
platform?: NodeJS.Platform;
|
|
79
|
+
existsSync?: (targetPath: string) => boolean;
|
|
80
|
+
resolveConfigPath?: () => string;
|
|
81
|
+
resolveWlfiHome?: () => string;
|
|
82
|
+
readConfig?: () => WlfiConfig;
|
|
83
|
+
deleteConfigKey?: (key: keyof WlfiConfig) => WlfiConfig;
|
|
84
|
+
deleteAgentAuthToken?: (agentKeyId: string) => boolean;
|
|
85
|
+
unlinkSync?: (targetPath: string) => void;
|
|
86
|
+
cleanupBootstrapArtifacts?: (action: 'deleted') => CleanupBootstrapArtifactsResult;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface CleanupLocalAdminUninstallStateResult {
|
|
90
|
+
agentKeyId: string | null;
|
|
91
|
+
keychain: {
|
|
92
|
+
removed: boolean;
|
|
93
|
+
service: string | null;
|
|
94
|
+
};
|
|
95
|
+
config: {
|
|
96
|
+
path: string;
|
|
97
|
+
existed: boolean;
|
|
98
|
+
deleted: boolean;
|
|
99
|
+
};
|
|
100
|
+
wlfiHome: {
|
|
101
|
+
path: string;
|
|
102
|
+
existed: boolean;
|
|
103
|
+
deleted: boolean;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface CleanupLocalAdminUninstallStateDeps {
|
|
108
|
+
platform?: NodeJS.Platform;
|
|
109
|
+
existsSync?: (targetPath: string) => boolean;
|
|
110
|
+
resolveConfigPath?: () => string;
|
|
111
|
+
resolveWlfiHome?: () => string;
|
|
112
|
+
readConfig?: () => WlfiConfig;
|
|
113
|
+
deleteAgentAuthToken?: (agentKeyId: string) => boolean;
|
|
114
|
+
rmSync?: (targetPath: string, options: { recursive: boolean; force: boolean }) => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function renderError(error: unknown): string {
|
|
118
|
+
return error instanceof Error ? error.message : String(error);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function validateSecret(value: string, label: string): string {
|
|
122
|
+
if (Buffer.byteLength(value, 'utf8') > MAX_SECRET_STDIN_BYTES) {
|
|
123
|
+
throw new Error(`${label} must not exceed ${MAX_SECRET_STDIN_BYTES} bytes`);
|
|
124
|
+
}
|
|
125
|
+
if (!value.trim()) {
|
|
126
|
+
throw new Error(`${label} must not be empty or whitespace`);
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function promptHidden(query: string, label: string): Promise<string> {
|
|
132
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
133
|
+
throw new Error(`${label} is required; rerun on a local TTY`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const rl = readline.createInterface({
|
|
137
|
+
input: process.stdin,
|
|
138
|
+
output: process.stdout,
|
|
139
|
+
terminal: true,
|
|
140
|
+
}) as readline.Interface & { stdoutMuted?: boolean; _writeToOutput?: (value: string) => void };
|
|
141
|
+
|
|
142
|
+
rl.stdoutMuted = true;
|
|
143
|
+
rl._writeToOutput = (value: string) => {
|
|
144
|
+
if (value.includes(query)) {
|
|
145
|
+
(rl as unknown as { output: NodeJS.WritableStream }).output.write(value);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (!rl.stdoutMuted) {
|
|
149
|
+
(rl as unknown as { output: NodeJS.WritableStream }).output.write(value);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const answer = await new Promise<string>((resolve) => {
|
|
154
|
+
rl.question(query, resolve);
|
|
155
|
+
});
|
|
156
|
+
rl.close();
|
|
157
|
+
process.stdout.write('\n');
|
|
158
|
+
return validateSecret(answer, label);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function promptVisible(query: string): Promise<string> {
|
|
162
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
163
|
+
throw new Error('admin reset requires --yes in non-interactive environments');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const rl = readline.createInterface({
|
|
167
|
+
input: process.stdin,
|
|
168
|
+
output: process.stdout,
|
|
169
|
+
terminal: true,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const answer = await new Promise<string>((resolve) => {
|
|
173
|
+
rl.question(query, resolve);
|
|
174
|
+
});
|
|
175
|
+
rl.close();
|
|
176
|
+
return answer.trim();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const sudoSession = createSudoSession({
|
|
180
|
+
promptPassword: async () =>
|
|
181
|
+
await promptHidden(
|
|
182
|
+
'Root password (input hidden; required to uninstall the root daemon and delete its state): ',
|
|
183
|
+
'root password',
|
|
184
|
+
),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
function createProgress(message: string, enabled = true): ProgressHandle {
|
|
188
|
+
if (!enabled) {
|
|
189
|
+
return {
|
|
190
|
+
succeed() {},
|
|
191
|
+
fail() {},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!process.stderr.isTTY) {
|
|
196
|
+
process.stderr.write(`==> ${message}\n`);
|
|
197
|
+
return {
|
|
198
|
+
succeed(finalMessage = message) {
|
|
199
|
+
process.stderr.write(`✓ ${finalMessage}\n`);
|
|
200
|
+
},
|
|
201
|
+
fail(finalMessage = `${message} failed`) {
|
|
202
|
+
process.stderr.write(`✗ ${finalMessage}\n`);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let frameIndex = 0;
|
|
208
|
+
const render = (prefix: string, currentMessage: string) => {
|
|
209
|
+
process.stderr.write(`\r\u001b[2K${prefix} ${currentMessage}`);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
render(SPINNER_FRAMES[frameIndex], message);
|
|
213
|
+
const timer = setInterval(() => {
|
|
214
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
215
|
+
render(SPINNER_FRAMES[frameIndex], message);
|
|
216
|
+
}, 80);
|
|
217
|
+
|
|
218
|
+
const stop = (prefix: string, finalMessage: string) => {
|
|
219
|
+
clearInterval(timer);
|
|
220
|
+
render(prefix, finalMessage);
|
|
221
|
+
process.stderr.write('\n');
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
succeed(finalMessage = message) {
|
|
226
|
+
stop('✓', finalMessage);
|
|
227
|
+
},
|
|
228
|
+
fail(finalMessage = `${message} failed`) {
|
|
229
|
+
stop('✗', finalMessage);
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function print(payload: unknown, asJson: boolean | undefined): void {
|
|
235
|
+
if (asJson) {
|
|
236
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (typeof payload === 'string') {
|
|
240
|
+
console.log(payload);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
console.dir(payload, { depth: null, colors: process.stdout.isTTY });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function printResetSummary(result: {
|
|
247
|
+
daemon: {
|
|
248
|
+
label: string;
|
|
249
|
+
daemonSocket: string;
|
|
250
|
+
stateFile: string;
|
|
251
|
+
};
|
|
252
|
+
local: CleanupLocalAdminResetStateResult;
|
|
253
|
+
}): void {
|
|
254
|
+
const lines = [
|
|
255
|
+
'reset complete',
|
|
256
|
+
`managed daemon removed: ${result.daemon.label}`,
|
|
257
|
+
`managed state deleted: ${result.daemon.stateFile}`,
|
|
258
|
+
`managed socket removed: ${result.daemon.daemonSocket}`,
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
if (result.local.agentKeyId) {
|
|
262
|
+
lines.push(`old agent key cleared: ${result.local.agentKeyId}`);
|
|
263
|
+
} else {
|
|
264
|
+
lines.push('old agent key cleared: no configured agent key was found');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (result.local.config.deleted) {
|
|
268
|
+
lines.push(`config deleted: ${result.local.config.path}`);
|
|
269
|
+
} else if (result.local.config.existed) {
|
|
270
|
+
lines.push(`config kept: ${result.local.config.path}`);
|
|
271
|
+
} else {
|
|
272
|
+
lines.push(`config not found: ${result.local.config.path}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (result.local.bootstrapArtifacts.error) {
|
|
276
|
+
lines.push(`bootstrap artifact cleanup warning: ${result.local.bootstrapArtifacts.error}`);
|
|
277
|
+
} else if (result.local.bootstrapArtifacts.attempted) {
|
|
278
|
+
lines.push(
|
|
279
|
+
`bootstrap artifacts deleted: ${result.local.bootstrapArtifacts.cleanedCount}/${result.local.bootstrapArtifacts.fileCount}`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
lines.push('next: run `wlfi-agent admin setup` to create a new wallet');
|
|
284
|
+
console.log(lines.join('\n'));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function resolveLaunchDaemonUninstallScriptPath(): string {
|
|
288
|
+
const candidates = [
|
|
289
|
+
path.resolve(__dirname, '../scripts/launchd/uninstall-user-daemon.sh'),
|
|
290
|
+
path.resolve(__dirname, '../../scripts/launchd/uninstall-user-daemon.sh'),
|
|
291
|
+
path.resolve(process.cwd(), 'scripts/launchd/uninstall-user-daemon.sh'),
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
for (const candidate of candidates) {
|
|
295
|
+
if (fs.existsSync(candidate)) {
|
|
296
|
+
return candidate;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return candidates[0];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function cleanupLocalAdminResetState(
|
|
304
|
+
options: { deleteConfig?: boolean } = {},
|
|
305
|
+
deps: CleanupLocalAdminResetStateDeps = {},
|
|
306
|
+
): CleanupLocalAdminResetStateResult {
|
|
307
|
+
const platform = deps.platform ?? process.platform;
|
|
308
|
+
const existsSync = deps.existsSync ?? fs.existsSync;
|
|
309
|
+
const resolveConfigPathImpl = deps.resolveConfigPath ?? resolveConfigPath;
|
|
310
|
+
const resolveWlfiHomeImpl = deps.resolveWlfiHome ?? resolveWlfiHome;
|
|
311
|
+
const readConfigImpl = deps.readConfig ?? readConfig;
|
|
312
|
+
const deleteConfigKeyImpl = deps.deleteConfigKey ?? deleteConfigKey;
|
|
313
|
+
const deleteAgentAuthTokenImpl = deps.deleteAgentAuthToken ?? deleteAgentAuthTokenFromKeychain;
|
|
314
|
+
const unlinkSyncImpl = deps.unlinkSync ?? fs.unlinkSync;
|
|
315
|
+
const cleanupBootstrapArtifactsImpl =
|
|
316
|
+
deps.cleanupBootstrapArtifacts ?? cleanupAutoGeneratedBootstrapArtifacts;
|
|
317
|
+
|
|
318
|
+
const configPath = resolveConfigPathImpl();
|
|
319
|
+
const wlfiHome = resolveWlfiHomeImpl();
|
|
320
|
+
const configExists = existsSync(configPath);
|
|
321
|
+
const currentConfig = configExists ? readConfigImpl() : null;
|
|
322
|
+
const agentKeyId = currentConfig?.agentKeyId ?? null;
|
|
323
|
+
const clearedAgentKeyId = currentConfig?.agentKeyId !== undefined;
|
|
324
|
+
const clearedLegacyAgentAuthToken = currentConfig?.agentAuthToken !== undefined;
|
|
325
|
+
const keychainRemoved = agentKeyId ? deleteAgentAuthTokenImpl(agentKeyId) : false;
|
|
326
|
+
|
|
327
|
+
let configDeleted = false;
|
|
328
|
+
let configValue: Record<string, unknown> | null = configExists ? redactConfig(currentConfig ?? {}) : null;
|
|
329
|
+
if (options.deleteConfig) {
|
|
330
|
+
if (configExists) {
|
|
331
|
+
unlinkSyncImpl(configPath);
|
|
332
|
+
configDeleted = true;
|
|
333
|
+
configValue = null;
|
|
334
|
+
}
|
|
335
|
+
} else if (configExists) {
|
|
336
|
+
if (clearedAgentKeyId) {
|
|
337
|
+
deleteConfigKeyImpl('agentKeyId');
|
|
338
|
+
}
|
|
339
|
+
if (clearedLegacyAgentAuthToken) {
|
|
340
|
+
deleteConfigKeyImpl('agentAuthToken');
|
|
341
|
+
}
|
|
342
|
+
configValue = redactConfig(readConfigImpl());
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let bootstrapArtifacts: CleanupLocalAdminResetStateResult['bootstrapArtifacts'];
|
|
346
|
+
if (!existsSync(wlfiHome)) {
|
|
347
|
+
bootstrapArtifacts = {
|
|
348
|
+
attempted: false,
|
|
349
|
+
action: 'deleted',
|
|
350
|
+
fileCount: 0,
|
|
351
|
+
cleanedCount: 0,
|
|
352
|
+
skippedCount: 0,
|
|
353
|
+
error: null,
|
|
354
|
+
};
|
|
355
|
+
} else {
|
|
356
|
+
try {
|
|
357
|
+
const cleanup = cleanupBootstrapArtifactsImpl('deleted');
|
|
358
|
+
bootstrapArtifacts = {
|
|
359
|
+
attempted: true,
|
|
360
|
+
action: 'deleted',
|
|
361
|
+
fileCount: cleanup.files.length,
|
|
362
|
+
cleanedCount: cleanup.files.filter((file) => file.cleanup === 'deleted').length,
|
|
363
|
+
skippedCount: cleanup.files.filter((file) => file.cleanup === 'skipped').length,
|
|
364
|
+
error: null,
|
|
365
|
+
};
|
|
366
|
+
} catch (error) {
|
|
367
|
+
bootstrapArtifacts = {
|
|
368
|
+
attempted: true,
|
|
369
|
+
action: 'deleted',
|
|
370
|
+
fileCount: 0,
|
|
371
|
+
cleanedCount: 0,
|
|
372
|
+
skippedCount: 0,
|
|
373
|
+
error: renderError(error),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
agentKeyId,
|
|
380
|
+
keychain: {
|
|
381
|
+
removed: keychainRemoved,
|
|
382
|
+
service: platform === 'darwin' ? AGENT_AUTH_TOKEN_KEYCHAIN_SERVICE : null,
|
|
383
|
+
},
|
|
384
|
+
config: {
|
|
385
|
+
path: configPath,
|
|
386
|
+
existed: configExists,
|
|
387
|
+
deleted: configDeleted,
|
|
388
|
+
clearedAgentKeyId,
|
|
389
|
+
clearedLegacyAgentAuthToken,
|
|
390
|
+
value: configValue,
|
|
391
|
+
},
|
|
392
|
+
bootstrapArtifacts,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function isPathInside(parentPath: string, childPath: string): boolean {
|
|
397
|
+
const relative = path.relative(path.resolve(parentPath), path.resolve(childPath));
|
|
398
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function cleanupLocalAdminUninstallState(
|
|
402
|
+
deps: CleanupLocalAdminUninstallStateDeps = {},
|
|
403
|
+
): CleanupLocalAdminUninstallStateResult {
|
|
404
|
+
const platform = deps.platform ?? process.platform;
|
|
405
|
+
const existsSync = deps.existsSync ?? fs.existsSync;
|
|
406
|
+
const resolveConfigPathImpl = deps.resolveConfigPath ?? resolveConfigPath;
|
|
407
|
+
const resolveWlfiHomeImpl = deps.resolveWlfiHome ?? resolveWlfiHome;
|
|
408
|
+
const readConfigImpl = deps.readConfig ?? readConfig;
|
|
409
|
+
const deleteAgentAuthTokenImpl = deps.deleteAgentAuthToken ?? deleteAgentAuthTokenFromKeychain;
|
|
410
|
+
const rmSyncImpl = deps.rmSync ?? fs.rmSync;
|
|
411
|
+
|
|
412
|
+
const configPath = resolveConfigPathImpl();
|
|
413
|
+
const wlfiHome = resolveWlfiHomeImpl();
|
|
414
|
+
const configExists = existsSync(configPath);
|
|
415
|
+
const wlfiHomeExists = existsSync(wlfiHome);
|
|
416
|
+
const currentConfig = configExists ? readConfigImpl() : null;
|
|
417
|
+
const agentKeyId = currentConfig?.agentKeyId ?? null;
|
|
418
|
+
const keychainRemoved = agentKeyId ? deleteAgentAuthTokenImpl(agentKeyId) : false;
|
|
419
|
+
|
|
420
|
+
let configDeleted = false;
|
|
421
|
+
if (configExists && (!wlfiHomeExists || !isPathInside(wlfiHome, configPath))) {
|
|
422
|
+
rmSyncImpl(configPath, { recursive: true, force: true });
|
|
423
|
+
configDeleted = true;
|
|
424
|
+
} else if (configExists && wlfiHomeExists) {
|
|
425
|
+
configDeleted = true;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
let wlfiHomeDeleted = false;
|
|
429
|
+
if (wlfiHomeExists) {
|
|
430
|
+
rmSyncImpl(wlfiHome, { recursive: true, force: true });
|
|
431
|
+
wlfiHomeDeleted = true;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
agentKeyId,
|
|
436
|
+
keychain: {
|
|
437
|
+
removed: keychainRemoved,
|
|
438
|
+
service: platform === 'darwin' ? AGENT_AUTH_TOKEN_KEYCHAIN_SERVICE : null,
|
|
439
|
+
},
|
|
440
|
+
config: {
|
|
441
|
+
path: configPath,
|
|
442
|
+
existed: configExists,
|
|
443
|
+
deleted: configDeleted,
|
|
444
|
+
},
|
|
445
|
+
wlfiHome: {
|
|
446
|
+
path: wlfiHome,
|
|
447
|
+
existed: wlfiHomeExists,
|
|
448
|
+
deleted: wlfiHomeDeleted,
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function confirmReset(options: AdminResetOptions): Promise<void> {
|
|
454
|
+
if (options.yes) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (options.nonInteractive) {
|
|
458
|
+
throw new Error('`wlfi-agent admin reset` requires --yes in non-interactive mode');
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
process.stderr.write(
|
|
462
|
+
'warning: admin reset permanently deletes the managed daemon state and the current wallet; if you do not have an external backup, the old address is lost forever.\n',
|
|
463
|
+
);
|
|
464
|
+
const confirmation = await promptVisible(
|
|
465
|
+
'Type RESET to delete the managed daemon state and local wallet credentials: ',
|
|
466
|
+
);
|
|
467
|
+
if (confirmation !== 'RESET') {
|
|
468
|
+
throw new Error('admin reset aborted');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
473
|
+
await confirmReset(options);
|
|
474
|
+
const showProgress = !options.json;
|
|
475
|
+
const keychainAccount = os.userInfo().username;
|
|
476
|
+
|
|
477
|
+
if (!options.json && typeof process.geteuid === 'function' && process.geteuid() !== 0) {
|
|
478
|
+
process.stderr.write(
|
|
479
|
+
'Root password required: reset must uninstall the root LaunchDaemon and delete the root-managed daemon state.\n',
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
await sudoSession.prime();
|
|
483
|
+
|
|
484
|
+
const uninstallProgress = createProgress('Uninstalling managed daemon', showProgress);
|
|
485
|
+
let uninstallResult;
|
|
486
|
+
try {
|
|
487
|
+
uninstallResult = await sudoSession.run([
|
|
488
|
+
resolveLaunchDaemonUninstallScriptPath(),
|
|
489
|
+
'--label',
|
|
490
|
+
DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
491
|
+
'--keychain-service',
|
|
492
|
+
DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
493
|
+
'--keychain-account',
|
|
494
|
+
keychainAccount,
|
|
495
|
+
'--delete-keychain-password',
|
|
496
|
+
]);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
uninstallProgress.fail();
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
501
|
+
if (uninstallResult.code !== 0) {
|
|
502
|
+
uninstallProgress.fail();
|
|
503
|
+
throw new Error(
|
|
504
|
+
uninstallResult.stderr.trim()
|
|
505
|
+
|| uninstallResult.stdout.trim()
|
|
506
|
+
|| `failed to uninstall managed daemon (exit code ${uninstallResult.code})`,
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
uninstallProgress.succeed('Managed daemon uninstalled');
|
|
510
|
+
|
|
511
|
+
const stateProgress = createProgress('Deleting root-managed daemon state', showProgress);
|
|
512
|
+
let deleteRootArtifactsResult;
|
|
513
|
+
try {
|
|
514
|
+
deleteRootArtifactsResult = await sudoSession.run([
|
|
515
|
+
'/bin/rm',
|
|
516
|
+
'-f',
|
|
517
|
+
DEFAULT_MANAGED_STATE_FILE,
|
|
518
|
+
DEFAULT_MANAGED_DAEMON_SOCKET,
|
|
519
|
+
]);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
stateProgress.fail();
|
|
522
|
+
throw error;
|
|
523
|
+
}
|
|
524
|
+
if (deleteRootArtifactsResult.code !== 0) {
|
|
525
|
+
stateProgress.fail();
|
|
526
|
+
throw new Error(
|
|
527
|
+
deleteRootArtifactsResult.stderr.trim()
|
|
528
|
+
|| deleteRootArtifactsResult.stdout.trim()
|
|
529
|
+
|| `failed to delete managed daemon state (exit code ${deleteRootArtifactsResult.code})`,
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
stateProgress.succeed('Root-managed daemon state deleted');
|
|
533
|
+
|
|
534
|
+
const localProgress = createProgress('Removing local wallet credentials', showProgress);
|
|
535
|
+
const local = cleanupLocalAdminResetState({ deleteConfig: options.deleteConfig });
|
|
536
|
+
localProgress.succeed('Local wallet credentials removed');
|
|
537
|
+
|
|
538
|
+
const result = {
|
|
539
|
+
command: 'reset',
|
|
540
|
+
daemon: {
|
|
541
|
+
autostart: false,
|
|
542
|
+
label: DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
543
|
+
launchdDomain: 'system',
|
|
544
|
+
plist: DEFAULT_LAUNCH_DAEMON_PLIST,
|
|
545
|
+
daemonSocket: DEFAULT_MANAGED_DAEMON_SOCKET,
|
|
546
|
+
stateFile: DEFAULT_MANAGED_STATE_FILE,
|
|
547
|
+
systemKeychainPasswordService: DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
548
|
+
systemKeychainPasswordAccount: keychainAccount,
|
|
549
|
+
},
|
|
550
|
+
local,
|
|
551
|
+
nextStep: 'Run `wlfi-agent admin setup` to create a new wallet and re-install the managed daemon.',
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
if (options.json) {
|
|
555
|
+
print(result, true);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
printResetSummary({
|
|
560
|
+
daemon: {
|
|
561
|
+
label: result.daemon.label,
|
|
562
|
+
daemonSocket: result.daemon.daemonSocket,
|
|
563
|
+
stateFile: result.daemon.stateFile,
|
|
564
|
+
},
|
|
565
|
+
local,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function confirmUninstall(options: AdminUninstallOptions): Promise<void> {
|
|
570
|
+
if (options.yes) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (options.nonInteractive) {
|
|
574
|
+
throw new Error('`wlfi-agent admin uninstall` requires --yes in non-interactive mode');
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
process.stderr.write(
|
|
578
|
+
'warning: admin uninstall permanently removes the managed daemon, root-managed state, local wallet config, local binaries, and logs.\n',
|
|
579
|
+
);
|
|
580
|
+
const confirmation = await promptVisible(
|
|
581
|
+
'Type UNINSTALL to delete all WLFI managed files and local credentials: ',
|
|
582
|
+
);
|
|
583
|
+
if (confirmation !== 'UNINSTALL') {
|
|
584
|
+
throw new Error('admin uninstall aborted');
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function printUninstallSummary(result: {
|
|
589
|
+
daemon: {
|
|
590
|
+
label: string;
|
|
591
|
+
daemonRoot: string;
|
|
592
|
+
stateDir: string;
|
|
593
|
+
logDir: string;
|
|
594
|
+
};
|
|
595
|
+
local: CleanupLocalAdminUninstallStateResult;
|
|
596
|
+
}): void {
|
|
597
|
+
const lines = [
|
|
598
|
+
'uninstall complete',
|
|
599
|
+
`managed daemon removed: ${result.daemon.label}`,
|
|
600
|
+
`managed daemon files removed: ${result.daemon.daemonRoot}`,
|
|
601
|
+
`managed state directory removed: ${result.daemon.stateDir}`,
|
|
602
|
+
`managed log directory removed: ${result.daemon.logDir}`,
|
|
603
|
+
result.local.agentKeyId
|
|
604
|
+
? `old agent key cleared: ${result.local.agentKeyId}`
|
|
605
|
+
: 'old agent key cleared: no configured agent key was found',
|
|
606
|
+
result.local.wlfiHome.existed
|
|
607
|
+
? `local WLFI home removed: ${result.local.wlfiHome.path}`
|
|
608
|
+
: `local WLFI home not found: ${result.local.wlfiHome.path}`,
|
|
609
|
+
result.local.config.existed
|
|
610
|
+
? `config removed: ${result.local.config.path}`
|
|
611
|
+
: `config not found: ${result.local.config.path}`,
|
|
612
|
+
'next: reinstall with `wlfi-agent admin setup` only if you want a fresh managed wallet again',
|
|
613
|
+
];
|
|
614
|
+
console.log(lines.join('\n'));
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
async function runAdminUninstall(options: AdminUninstallOptions): Promise<void> {
|
|
618
|
+
await confirmUninstall(options);
|
|
619
|
+
const showProgress = !options.json;
|
|
620
|
+
const keychainAccount = os.userInfo().username;
|
|
621
|
+
|
|
622
|
+
if (!options.json && typeof process.geteuid === 'function' && process.geteuid() !== 0) {
|
|
623
|
+
process.stderr.write(
|
|
624
|
+
'Root password required: uninstall must remove the root LaunchDaemon and all managed root-owned files.\n',
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
await sudoSession.prime();
|
|
628
|
+
|
|
629
|
+
const uninstallProgress = createProgress('Uninstalling managed daemon', showProgress);
|
|
630
|
+
let uninstallResult;
|
|
631
|
+
try {
|
|
632
|
+
uninstallResult = await sudoSession.run([
|
|
633
|
+
resolveLaunchDaemonUninstallScriptPath(),
|
|
634
|
+
'--label',
|
|
635
|
+
DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
636
|
+
'--keychain-service',
|
|
637
|
+
DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
638
|
+
'--keychain-account',
|
|
639
|
+
keychainAccount,
|
|
640
|
+
'--delete-keychain-password',
|
|
641
|
+
]);
|
|
642
|
+
} catch (error) {
|
|
643
|
+
uninstallProgress.fail();
|
|
644
|
+
throw error;
|
|
645
|
+
}
|
|
646
|
+
if (uninstallResult.code !== 0) {
|
|
647
|
+
uninstallProgress.fail();
|
|
648
|
+
throw new Error(
|
|
649
|
+
uninstallResult.stderr.trim()
|
|
650
|
+
|| uninstallResult.stdout.trim()
|
|
651
|
+
|| `failed to uninstall managed daemon (exit code ${uninstallResult.code})`,
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
uninstallProgress.succeed('Managed daemon uninstalled');
|
|
655
|
+
|
|
656
|
+
const rootProgress = createProgress('Removing managed root-owned files', showProgress);
|
|
657
|
+
let deleteRootArtifactsResult;
|
|
658
|
+
try {
|
|
659
|
+
deleteRootArtifactsResult = await sudoSession.run([
|
|
660
|
+
'/bin/rm',
|
|
661
|
+
'-rf',
|
|
662
|
+
DEFAULT_MANAGED_ROOT_DIR,
|
|
663
|
+
DEFAULT_MANAGED_STATE_DIR,
|
|
664
|
+
DEFAULT_MANAGED_LOG_DIR,
|
|
665
|
+
]);
|
|
666
|
+
} catch (error) {
|
|
667
|
+
rootProgress.fail();
|
|
668
|
+
throw error;
|
|
669
|
+
}
|
|
670
|
+
if (deleteRootArtifactsResult.code !== 0) {
|
|
671
|
+
rootProgress.fail();
|
|
672
|
+
throw new Error(
|
|
673
|
+
deleteRootArtifactsResult.stderr.trim()
|
|
674
|
+
|| deleteRootArtifactsResult.stdout.trim()
|
|
675
|
+
|| `failed to delete managed root-owned files (exit code ${deleteRootArtifactsResult.code})`,
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
rootProgress.succeed('Managed root-owned files removed');
|
|
679
|
+
|
|
680
|
+
const localProgress = createProgress('Removing local WLFI files and credentials', showProgress);
|
|
681
|
+
const local = cleanupLocalAdminUninstallState();
|
|
682
|
+
localProgress.succeed('Local WLFI files and credentials removed');
|
|
683
|
+
|
|
684
|
+
const result = {
|
|
685
|
+
command: 'uninstall',
|
|
686
|
+
daemon: {
|
|
687
|
+
autostart: false,
|
|
688
|
+
label: DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
689
|
+
launchdDomain: 'system',
|
|
690
|
+
plist: DEFAULT_LAUNCH_DAEMON_PLIST,
|
|
691
|
+
daemonSocket: DEFAULT_MANAGED_DAEMON_SOCKET,
|
|
692
|
+
stateFile: DEFAULT_MANAGED_STATE_FILE,
|
|
693
|
+
daemonRoot: DEFAULT_MANAGED_ROOT_DIR,
|
|
694
|
+
stateDir: DEFAULT_MANAGED_STATE_DIR,
|
|
695
|
+
logDir: DEFAULT_MANAGED_LOG_DIR,
|
|
696
|
+
systemKeychainPasswordService: DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
697
|
+
systemKeychainPasswordAccount: keychainAccount,
|
|
698
|
+
},
|
|
699
|
+
local,
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
if (options.json) {
|
|
703
|
+
print(result, true);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
printUninstallSummary({
|
|
708
|
+
daemon: {
|
|
709
|
+
label: result.daemon.label,
|
|
710
|
+
daemonRoot: result.daemon.daemonRoot,
|
|
711
|
+
stateDir: result.daemon.stateDir,
|
|
712
|
+
logDir: result.daemon.logDir,
|
|
713
|
+
},
|
|
714
|
+
local,
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export async function runAdminResetCli(argv: string[]): Promise<void> {
|
|
719
|
+
const program = new Command();
|
|
720
|
+
program
|
|
721
|
+
.name('wlfi-agent admin reset')
|
|
722
|
+
.description(
|
|
723
|
+
'Delete the root-managed daemon state and local wallet credentials so a fresh wallet can be set up',
|
|
724
|
+
)
|
|
725
|
+
.option('-y, --yes', 'Skip the destructive confirmation prompt', false)
|
|
726
|
+
.option('--non-interactive', 'Disable confirmation prompts; requires --yes', false)
|
|
727
|
+
.option(
|
|
728
|
+
'--delete-config',
|
|
729
|
+
'Delete ~/.wlfi_agent/config.json instead of only clearing wallet-specific credentials',
|
|
730
|
+
false,
|
|
731
|
+
)
|
|
732
|
+
.option('--json', 'Print JSON output', false)
|
|
733
|
+
.action(runAdminReset);
|
|
734
|
+
|
|
735
|
+
await program.parseAsync(argv, { from: 'user' });
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
export async function runAdminUninstallCli(argv: string[]): Promise<void> {
|
|
739
|
+
const program = new Command();
|
|
740
|
+
program
|
|
741
|
+
.name('wlfi-agent admin uninstall')
|
|
742
|
+
.description(
|
|
743
|
+
'Fully remove the managed daemon, root-owned state, local wallet config, local binaries, and logs',
|
|
744
|
+
)
|
|
745
|
+
.option('-y, --yes', 'Skip the destructive confirmation prompt', false)
|
|
746
|
+
.option('--non-interactive', 'Disable confirmation prompts; requires --yes', false)
|
|
747
|
+
.option('--json', 'Print JSON output', false)
|
|
748
|
+
.action(runAdminUninstall);
|
|
749
|
+
|
|
750
|
+
await program.parseAsync(argv, { from: 'user' });
|
|
751
|
+
}
|