@wlfi-agent/cli 1.4.15 → 1.4.17
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 +22 -20
- package/Cargo.toml +2 -2
- package/README.md +10 -2
- package/crates/vault-cli-admin/src/main.rs +21 -2
- package/crates/vault-cli-admin/src/tui.rs +634 -129
- package/crates/vault-cli-daemon/Cargo.toml +1 -0
- package/crates/vault-cli-daemon/src/bin/wlfi-agent-system-keychain.rs +122 -8
- package/crates/vault-cli-daemon/src/main.rs +24 -4
- package/crates/vault-cli-daemon/src/relay_sync.rs +155 -35
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +23 -18
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +6 -0
- package/crates/vault-daemon/src/daemon_parts/types_api_rpc.rs +6 -0
- package/crates/vault-daemon/src/tests.rs +2 -2
- package/crates/vault-daemon/src/tests_parts/part4.rs +110 -0
- package/crates/vault-transport-unix/src/lib.rs +22 -3
- package/crates/vault-transport-xpc/src/lib.rs +20 -2
- package/dist/cli.cjs +20842 -25552
- package/dist/cli.cjs.map +1 -1
- package/package.json +5 -3
- package/packages/cache/.turbo/turbo-build.log +53 -52
- package/packages/cache/coverage/base.css +224 -0
- package/packages/cache/coverage/block-navigation.js +87 -0
- package/packages/cache/coverage/clover.xml +585 -0
- package/packages/cache/coverage/coverage-final.json +5 -0
- package/packages/cache/coverage/favicon.png +0 -0
- package/packages/cache/coverage/index.html +161 -0
- package/packages/cache/coverage/prettify.css +1 -0
- package/packages/cache/coverage/prettify.js +2 -0
- package/packages/cache/coverage/sort-arrow-sprite.png +0 -0
- package/packages/cache/coverage/sorter.js +210 -0
- package/packages/cache/coverage/src/client/index.html +116 -0
- package/packages/cache/coverage/src/client/index.ts.html +253 -0
- package/packages/cache/coverage/src/errors/index.html +116 -0
- package/packages/cache/coverage/src/errors/index.ts.html +244 -0
- package/packages/cache/coverage/src/index.html +116 -0
- package/packages/cache/coverage/src/index.ts.html +94 -0
- package/packages/cache/coverage/src/service/index.html +116 -0
- package/packages/cache/coverage/src/service/index.ts.html +2212 -0
- package/packages/cache/dist/{chunk-ALQ6H7KG.cjs → chunk-QF4XKEIA.cjs} +189 -45
- package/packages/cache/dist/chunk-QF4XKEIA.cjs.map +1 -0
- package/packages/cache/dist/{chunk-FGJEEF5N.js → chunk-QNK6GOTI.js} +182 -38
- package/packages/cache/dist/chunk-QNK6GOTI.js.map +1 -0
- package/packages/cache/dist/index.cjs +2 -2
- package/packages/cache/dist/index.js +1 -1
- package/packages/cache/dist/service/index.cjs +2 -2
- package/packages/cache/dist/service/index.d.cts +2 -0
- package/packages/cache/dist/service/index.d.ts +2 -0
- package/packages/cache/dist/service/index.js +1 -1
- package/packages/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/packages/cache/src/service/index.test.ts +575 -0
- package/packages/cache/src/service/index.ts +234 -51
- package/packages/config/.turbo/turbo-build.log +2 -2
- package/packages/config/node_modules/.bin/tsc +2 -2
- package/packages/config/node_modules/.bin/tsserver +2 -2
- package/packages/config/node_modules/.bin/tsup +2 -2
- package/packages/config/node_modules/.bin/tsup-node +2 -2
- package/packages/rpc/.turbo/turbo-build.log +11 -11
- package/packages/rpc/node_modules/.bin/tsc +2 -2
- package/packages/rpc/node_modules/.bin/tsserver +2 -2
- package/packages/rpc/node_modules/.bin/tsup +2 -2
- package/packages/rpc/node_modules/.bin/tsup-node +2 -2
- package/packages/ui/.turbo/turbo-build.log +13 -13
- package/packages/ui/dist/components/badge.d.ts +1 -1
- package/packages/ui/dist/components/button.d.ts +1 -1
- package/scripts/install-rust-binaries.mjs +229 -58
- package/src/cli.ts +51 -39
- package/src/lib/admin-passthrough.js +1 -0
- package/src/lib/admin-reset.js +1 -0
- package/src/lib/admin-reset.ts +26 -16
- package/src/lib/admin-setup.js +1 -0
- package/src/lib/admin-setup.ts +32 -20
- package/src/lib/agent-auth-revoke.js +1 -0
- package/src/lib/agent-auth-rotate.js +1 -0
- package/src/lib/agent-auth.js +1 -0
- package/src/lib/config-mutation.js +1 -0
- package/src/lib/launchd-assets.js +1 -0
- package/src/lib/launchd-assets.ts +29 -0
- package/src/lib/local-admin-access.js +1 -0
- package/src/lib/rust.ts +1 -1
- package/src/lib/status-repair-cli.js +1 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs.map +0 -1
- package/packages/cache/dist/chunk-FGJEEF5N.js.map +0 -1
|
@@ -2,83 +2,254 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
-
|
|
6
|
-
if (process.env.WLFI_SKIP_RUST_INSTALL === '1') {
|
|
7
|
-
process.exit(0);
|
|
8
|
-
}
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
9
6
|
|
|
10
7
|
const repoRoot = new URL('..', import.meta.url).pathname;
|
|
11
|
-
const wlfiHome = process.env.WLFI_HOME?.trim() || path.join(os.homedir(), '.wlfi_agent');
|
|
12
|
-
const binDir = path.join(wlfiHome, 'bin');
|
|
13
8
|
const extension = process.platform === 'win32' ? '.exe' : '';
|
|
9
|
+
const MIN_RUST_VERSION = {
|
|
10
|
+
major: 1,
|
|
11
|
+
minor: 87,
|
|
12
|
+
patch: 0,
|
|
13
|
+
};
|
|
14
14
|
const rustBins = [
|
|
15
15
|
'wlfi-agent-daemon',
|
|
16
16
|
'wlfi-agent-admin',
|
|
17
17
|
'wlfi-agent-agent',
|
|
18
|
-
'wlfi-agent-system-keychain'
|
|
18
|
+
'wlfi-agent-system-keychain',
|
|
19
19
|
];
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const RERUN_INSTRUCTIONS =
|
|
21
|
+
'After installing prerequisites, rerun `npm install -g @wlfi-agent/cli`.';
|
|
22
|
+
|
|
23
|
+
function decodeOutput(value) {
|
|
24
|
+
if (!value) {
|
|
25
|
+
return '';
|
|
24
26
|
}
|
|
25
|
-
|
|
27
|
+
return value.toString().trim();
|
|
28
|
+
}
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (cargo.status !== 0) {
|
|
30
|
-
console.warn('[wlfi-agent] cargo was not found; skipping Rust binary installation');
|
|
31
|
-
console.warn('[wlfi-agent] install Rust from https://rustup.rs and rerun `npm run install:rust-binaries`.');
|
|
32
|
-
process.exit(0);
|
|
30
|
+
function formatMinimumRustVersion() {
|
|
31
|
+
return `${MIN_RUST_VERSION.major}.${MIN_RUST_VERSION.minor}.${MIN_RUST_VERSION.patch}`;
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'wlfi-agent-agent'
|
|
46
|
-
],
|
|
47
|
-
{ cwd: repoRoot, stdio: 'inherit' }
|
|
48
|
-
);
|
|
49
|
-
if (build.status !== 0) {
|
|
50
|
-
process.exit(build.status ?? 1);
|
|
34
|
+
function parseRustVersion(output) {
|
|
35
|
+
const match = output.match(/\b(\d+)\.(\d+)\.(\d+)(?:[-+][^\s]+)?\b/);
|
|
36
|
+
if (!match) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
major: Number(match[1]),
|
|
41
|
+
minor: Number(match[2]),
|
|
42
|
+
patch: Number(match[3]),
|
|
43
|
+
};
|
|
51
44
|
}
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
46
|
+
function compareVersions(left, right) {
|
|
47
|
+
if (left.major !== right.major) {
|
|
48
|
+
return left.major - right.major;
|
|
49
|
+
}
|
|
50
|
+
if (left.minor !== right.minor) {
|
|
51
|
+
return left.minor - right.minor;
|
|
59
52
|
}
|
|
53
|
+
return left.patch - right.patch;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function runCheck(spawnSyncImpl, command, args, options = {}) {
|
|
57
|
+
return spawnSyncImpl(command, args, {
|
|
58
|
+
cwd: repoRoot,
|
|
59
|
+
stdio: 'pipe',
|
|
60
|
+
...options,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resolveWlfiPaths(env) {
|
|
65
|
+
const wlfiHome = env.WLFI_HOME?.trim() || path.join(os.homedir(), '.wlfi_agent');
|
|
66
|
+
return {
|
|
67
|
+
wlfiHome,
|
|
68
|
+
binDir: path.join(wlfiHome, 'bin'),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveHelperScripts(binDir) {
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
source: path.join(repoRoot, 'scripts', 'launchd', 'run-wlfi-agent-daemon.sh'),
|
|
76
|
+
destination: path.join(binDir, 'run-wlfi-agent-daemon.sh'),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
source: path.join(repoRoot, 'scripts', 'launchd', 'install-user-daemon.sh'),
|
|
80
|
+
destination: path.join(binDir, 'install-user-daemon.sh'),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
source: path.join(repoRoot, 'scripts', 'launchd', 'uninstall-user-daemon.sh'),
|
|
84
|
+
destination: path.join(binDir, 'uninstall-user-daemon.sh'),
|
|
85
|
+
},
|
|
86
|
+
];
|
|
60
87
|
}
|
|
61
88
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
89
|
+
function checkCargoAvailable(spawnSyncImpl) {
|
|
90
|
+
const result = runCheck(spawnSyncImpl, 'cargo', ['--version']);
|
|
91
|
+
if (result.status === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const detail = decodeOutput(result.stderr) || decodeOutput(result.stdout);
|
|
96
|
+
const lines = [
|
|
97
|
+
'[wlfi-agent] Rust toolchain was not found on PATH.',
|
|
98
|
+
'[wlfi-agent] Install Rust from https://rustup.rs.',
|
|
99
|
+
];
|
|
100
|
+
if (detail) {
|
|
101
|
+
lines.push(`[wlfi-agent] cargo check output: ${detail}`);
|
|
102
|
+
}
|
|
103
|
+
lines.push(`[wlfi-agent] ${RERUN_INSTRUCTIONS}`);
|
|
104
|
+
throw new Error(lines.join('\n'));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function checkRustcVersion(spawnSyncImpl) {
|
|
108
|
+
const result = runCheck(spawnSyncImpl, 'rustc', ['--version']);
|
|
109
|
+
if (result.status !== 0) {
|
|
110
|
+
const detail = decodeOutput(result.stderr) || decodeOutput(result.stdout);
|
|
111
|
+
const lines = [
|
|
112
|
+
'[wlfi-agent] Rust compiler was not found on PATH.',
|
|
113
|
+
`[wlfi-agent] Install Rust ${formatMinimumRustVersion()} or newer from https://rustup.rs.`,
|
|
114
|
+
];
|
|
115
|
+
if (detail) {
|
|
116
|
+
lines.push(`[wlfi-agent] rustc check output: ${detail}`);
|
|
117
|
+
}
|
|
118
|
+
lines.push(`[wlfi-agent] ${RERUN_INSTRUCTIONS}`);
|
|
119
|
+
throw new Error(lines.join('\n'));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const versionOutput = decodeOutput(result.stdout) || decodeOutput(result.stderr);
|
|
123
|
+
const parsedVersion = parseRustVersion(versionOutput);
|
|
124
|
+
if (!parsedVersion) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`[wlfi-agent] Unable to determine the installed Rust compiler version from: ${versionOutput || '<empty output>'}`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (compareVersions(parsedVersion, MIN_RUST_VERSION) < 0) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`[wlfi-agent] Rust ${formatMinimumRustVersion()} or newer is required; found ${versionOutput}.\n` +
|
|
133
|
+
`[wlfi-agent] Update Rust with \`rustup update\`.\n` +
|
|
134
|
+
`[wlfi-agent] ${RERUN_INSTRUCTIONS}`,
|
|
135
|
+
);
|
|
66
136
|
}
|
|
67
137
|
}
|
|
68
138
|
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
139
|
+
function checkMacOsToolchainAvailable(spawnSyncImpl, platform) {
|
|
140
|
+
if (platform !== 'darwin') {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const result = runCheck(spawnSyncImpl, 'xcrun', ['--sdk', 'macosx', '--find', 'clang']);
|
|
145
|
+
if (result.status === 0) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const detail = decodeOutput(result.stderr) || decodeOutput(result.stdout);
|
|
150
|
+
const lines = [
|
|
151
|
+
'[wlfi-agent] macOS Command Line Tools were not found or are not configured.',
|
|
152
|
+
'[wlfi-agent] Install them with `xcode-select --install`.',
|
|
153
|
+
];
|
|
154
|
+
if (detail) {
|
|
155
|
+
lines.push(`[wlfi-agent] xcrun check output: ${detail}`);
|
|
156
|
+
}
|
|
157
|
+
lines.push(`[wlfi-agent] ${RERUN_INSTRUCTIONS}`);
|
|
158
|
+
throw new Error(lines.join('\n'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function verifyRustInstallPrerequisites({
|
|
162
|
+
spawnSyncImpl = spawnSync,
|
|
163
|
+
platform = process.platform,
|
|
164
|
+
} = {}) {
|
|
165
|
+
checkCargoAvailable(spawnSyncImpl);
|
|
166
|
+
checkRustcVersion(spawnSyncImpl);
|
|
167
|
+
checkMacOsToolchainAvailable(spawnSyncImpl, platform);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function installRustBinaries({
|
|
171
|
+
spawnSyncImpl = spawnSync,
|
|
172
|
+
env = process.env,
|
|
173
|
+
platform = process.platform,
|
|
174
|
+
} = {}) {
|
|
175
|
+
if (env.WLFI_SKIP_RUST_INSTALL === '1') {
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
verifyRustInstallPrerequisites({ spawnSyncImpl, platform });
|
|
180
|
+
|
|
181
|
+
const { wlfiHome, binDir } = resolveWlfiPaths(env);
|
|
182
|
+
const helperScripts = resolveHelperScripts(binDir);
|
|
183
|
+
|
|
184
|
+
fs.mkdirSync(binDir, { recursive: true, mode: 0o700 });
|
|
185
|
+
const build = spawnSyncImpl(
|
|
186
|
+
'cargo',
|
|
187
|
+
[
|
|
188
|
+
'build',
|
|
189
|
+
'--locked',
|
|
190
|
+
'--release',
|
|
191
|
+
'-p',
|
|
192
|
+
'wlfi-agent-daemon',
|
|
193
|
+
'-p',
|
|
194
|
+
'wlfi-agent-admin',
|
|
195
|
+
'-p',
|
|
196
|
+
'wlfi-agent-agent',
|
|
197
|
+
],
|
|
198
|
+
{ cwd: repoRoot, stdio: 'inherit' },
|
|
199
|
+
);
|
|
200
|
+
if (build.status !== 0) {
|
|
201
|
+
return build.status ?? 1;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const binary of rustBins) {
|
|
205
|
+
const source = path.join(repoRoot, 'target', 'release', binary + extension);
|
|
206
|
+
const destination = path.join(binDir, binary + extension);
|
|
207
|
+
fs.copyFileSync(source, destination);
|
|
208
|
+
if (platform !== 'win32') {
|
|
209
|
+
fs.chmodSync(destination, 0o755);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const script of helperScripts) {
|
|
214
|
+
fs.copyFileSync(script.source, script.destination);
|
|
215
|
+
if (platform !== 'win32') {
|
|
216
|
+
fs.chmodSync(script.destination, 0o755);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const configPath = path.join(wlfiHome, 'config.json');
|
|
221
|
+
if (!fs.existsSync(configPath)) {
|
|
222
|
+
fs.writeFileSync(
|
|
223
|
+
configPath,
|
|
224
|
+
JSON.stringify(
|
|
225
|
+
{
|
|
226
|
+
daemonSocket: path.join(wlfiHome, 'daemon.sock'),
|
|
227
|
+
stateFile: path.join(wlfiHome, 'daemon-state.enc'),
|
|
228
|
+
rustBinDir: binDir,
|
|
229
|
+
},
|
|
230
|
+
null,
|
|
231
|
+
2,
|
|
232
|
+
) + '\n',
|
|
233
|
+
{ mode: 0o600 },
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isDirectExecution() {
|
|
241
|
+
return (
|
|
242
|
+
Boolean(process.argv[1]) &&
|
|
243
|
+
import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
|
|
83
244
|
);
|
|
84
245
|
}
|
|
246
|
+
|
|
247
|
+
if (isDirectExecution()) {
|
|
248
|
+
try {
|
|
249
|
+
process.exitCode = installRustBinaries();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
252
|
+
process.stderr.write(`${message}\n`);
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
}
|
|
255
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,41 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
deleteConfigKey,
|
|
5
|
-
ensureWlfiHome,
|
|
6
|
-
listBuiltinChains,
|
|
7
|
-
listBuiltinTokens,
|
|
8
|
-
listConfiguredChains,
|
|
9
|
-
listConfiguredTokens,
|
|
10
|
-
readConfig,
|
|
11
|
-
redactConfig,
|
|
12
|
-
removeChainProfile,
|
|
13
|
-
removeTokenChainProfile,
|
|
14
|
-
removeTokenProfile,
|
|
15
|
-
resolveChainProfile,
|
|
16
|
-
resolveConfigPath,
|
|
17
|
-
resolveTokenProfile,
|
|
18
|
-
saveChainProfile,
|
|
19
|
-
saveTokenChainProfile,
|
|
20
|
-
switchActiveChain,
|
|
21
|
-
type TokenChainProfile,
|
|
22
|
-
type WlfiConfig,
|
|
23
|
-
writeConfig,
|
|
24
|
-
} from '@wlfi-agent/config';
|
|
25
|
-
import {
|
|
26
|
-
broadcastRawTransaction,
|
|
27
|
-
estimateFees,
|
|
28
|
-
estimateGas,
|
|
29
|
-
getAccountSnapshot,
|
|
30
|
-
getChainInfo,
|
|
31
|
-
getCodeAtAddress,
|
|
32
|
-
getLatestBlockNumber,
|
|
33
|
-
getNativeBalance,
|
|
34
|
-
getNonce,
|
|
35
|
-
getTokenBalance,
|
|
36
|
-
getTransactionByHash,
|
|
37
|
-
getTransactionReceiptByHash,
|
|
38
|
-
} from '@wlfi-agent/rpc';
|
|
1
|
+
import * as configPackage from '../packages/config/src/index.js';
|
|
2
|
+
import * as rpcPackage from '../packages/rpc/src/index.ts';
|
|
3
|
+
import type { ChainProfile, TokenChainProfile, WlfiConfig } from '../packages/config/src/index.js';
|
|
39
4
|
import { Command, Option } from 'commander';
|
|
40
5
|
import { type Address, type Hex, isAddress, isHex } from 'viem';
|
|
41
6
|
import { assertSafeRpcUrl } from '../packages/config/src/index.js';
|
|
@@ -118,6 +83,49 @@ import {
|
|
|
118
83
|
walletProfileFromBootstrapSummary,
|
|
119
84
|
} from './lib/wallet-profile.js';
|
|
120
85
|
|
|
86
|
+
const configExports = (
|
|
87
|
+
'default' in configPackage ? configPackage.default : configPackage
|
|
88
|
+
) as typeof import('../packages/config/src/index.js');
|
|
89
|
+
const {
|
|
90
|
+
defaultDaemonSocketPath,
|
|
91
|
+
deleteConfigKey,
|
|
92
|
+
ensureWlfiHome,
|
|
93
|
+
listBuiltinChains,
|
|
94
|
+
listBuiltinTokens,
|
|
95
|
+
listConfiguredChains,
|
|
96
|
+
listConfiguredTokens,
|
|
97
|
+
readConfig,
|
|
98
|
+
redactConfig,
|
|
99
|
+
removeChainProfile,
|
|
100
|
+
removeTokenChainProfile,
|
|
101
|
+
removeTokenProfile,
|
|
102
|
+
resolveChainProfile,
|
|
103
|
+
resolveConfigPath,
|
|
104
|
+
resolveTokenProfile,
|
|
105
|
+
saveChainProfile,
|
|
106
|
+
saveTokenChainProfile,
|
|
107
|
+
switchActiveChain,
|
|
108
|
+
writeConfig,
|
|
109
|
+
} = configExports;
|
|
110
|
+
|
|
111
|
+
const rpcExports = ('default' in rpcPackage ? rpcPackage.default : rpcPackage) as typeof import(
|
|
112
|
+
'../packages/rpc/src/index.ts'
|
|
113
|
+
);
|
|
114
|
+
const {
|
|
115
|
+
broadcastRawTransaction,
|
|
116
|
+
estimateFees,
|
|
117
|
+
estimateGas,
|
|
118
|
+
getAccountSnapshot,
|
|
119
|
+
getChainInfo,
|
|
120
|
+
getCodeAtAddress,
|
|
121
|
+
getLatestBlockNumber,
|
|
122
|
+
getNativeBalance,
|
|
123
|
+
getNonce,
|
|
124
|
+
getTokenBalance,
|
|
125
|
+
getTransactionByHash,
|
|
126
|
+
getTransactionReceiptByHash,
|
|
127
|
+
} = rpcExports;
|
|
128
|
+
|
|
121
129
|
interface RustBroadcastOutput {
|
|
122
130
|
command: string;
|
|
123
131
|
network: string;
|
|
@@ -302,7 +310,11 @@ async function readTrimmedStdin(label: string): Promise<string> {
|
|
|
302
310
|
}
|
|
303
311
|
|
|
304
312
|
function formatJson(payload: unknown) {
|
|
305
|
-
return JSON.stringify(
|
|
313
|
+
return JSON.stringify(
|
|
314
|
+
payload,
|
|
315
|
+
(_key, value) => (typeof value === 'bigint' ? value.toString() : value),
|
|
316
|
+
2,
|
|
317
|
+
);
|
|
306
318
|
}
|
|
307
319
|
|
|
308
320
|
function stringifyOptionalValue(value: { toString(): string } | null | undefined): string | null {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './admin-passthrough.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './admin-reset.ts';
|
package/src/lib/admin-reset.ts
CHANGED
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
21
21
|
deleteAgentAuthTokenFromKeychain,
|
|
22
22
|
} from './keychain.js';
|
|
23
|
+
import {
|
|
24
|
+
LAUNCHD_UNINSTALL_SCRIPT_NAME,
|
|
25
|
+
resolveLaunchDaemonHelperScriptPath,
|
|
26
|
+
} from './launchd-assets.js';
|
|
23
27
|
import { createSudoSession } from './sudo.js';
|
|
24
28
|
|
|
25
29
|
const DEFAULT_LAUNCH_DAEMON_LABEL = 'com.wlfi.agent.daemon';
|
|
@@ -29,6 +33,7 @@ const DEFAULT_LAUNCH_DAEMON_PLIST = `/Library/LaunchDaemons/${DEFAULT_LAUNCH_DAE
|
|
|
29
33
|
const DEFAULT_MANAGED_ROOT_DIR = '/Library/WLFI';
|
|
30
34
|
const DEFAULT_MANAGED_STATE_DIR = '/var/db/wlfi-agent';
|
|
31
35
|
const DEFAULT_MANAGED_LOG_DIR = '/var/log/wlfi-agent';
|
|
36
|
+
const DEFAULT_MANAGED_RELAY_DAEMON_TOKEN_FILE = `${DEFAULT_MANAGED_STATE_DIR}/relay-daemon-token`;
|
|
32
37
|
const MAX_SECRET_STDIN_BYTES = 16 * 1024;
|
|
33
38
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
34
39
|
|
|
@@ -247,6 +252,7 @@ function printResetSummary(result: {
|
|
|
247
252
|
daemon: {
|
|
248
253
|
label: string;
|
|
249
254
|
daemonSocket: string;
|
|
255
|
+
relayDaemonTokenFile: string;
|
|
250
256
|
stateFile: string;
|
|
251
257
|
};
|
|
252
258
|
local: CleanupLocalAdminResetStateResult;
|
|
@@ -256,6 +262,7 @@ function printResetSummary(result: {
|
|
|
256
262
|
`managed daemon removed: ${result.daemon.label}`,
|
|
257
263
|
`managed state deleted: ${result.daemon.stateFile}`,
|
|
258
264
|
`managed socket removed: ${result.daemon.daemonSocket}`,
|
|
265
|
+
`managed relay token removed: ${result.daemon.relayDaemonTokenFile}`,
|
|
259
266
|
];
|
|
260
267
|
|
|
261
268
|
if (result.local.agentKeyId) {
|
|
@@ -284,20 +291,20 @@ function printResetSummary(result: {
|
|
|
284
291
|
console.log(lines.join('\n'));
|
|
285
292
|
}
|
|
286
293
|
|
|
287
|
-
function resolveLaunchDaemonUninstallScriptPath(): string {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
path.resolve(__dirname, '../../scripts/launchd/uninstall-user-daemon.sh'),
|
|
291
|
-
path.resolve(process.cwd(), 'scripts/launchd/uninstall-user-daemon.sh'),
|
|
292
|
-
];
|
|
294
|
+
function resolveLaunchDaemonUninstallScriptPath(config?: WlfiConfig): string {
|
|
295
|
+
return resolveLaunchDaemonHelperScriptPath(LAUNCHD_UNINSTALL_SCRIPT_NAME, config);
|
|
296
|
+
}
|
|
293
297
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
}
|
|
298
|
+
function readConfigIfPresent(): WlfiConfig | undefined {
|
|
299
|
+
return fs.existsSync(resolveConfigPath()) ? readConfig() : undefined;
|
|
300
|
+
}
|
|
299
301
|
|
|
300
|
-
|
|
302
|
+
export function managedDaemonResetArtifactPaths(): string[] {
|
|
303
|
+
return [
|
|
304
|
+
DEFAULT_MANAGED_STATE_FILE,
|
|
305
|
+
DEFAULT_MANAGED_DAEMON_SOCKET,
|
|
306
|
+
DEFAULT_MANAGED_RELAY_DAEMON_TOKEN_FILE,
|
|
307
|
+
];
|
|
301
308
|
}
|
|
302
309
|
|
|
303
310
|
export function cleanupLocalAdminResetState(
|
|
@@ -480,12 +487,13 @@ async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
|
480
487
|
);
|
|
481
488
|
}
|
|
482
489
|
await sudoSession.prime();
|
|
490
|
+
const currentConfig = readConfigIfPresent();
|
|
483
491
|
|
|
484
492
|
const uninstallProgress = createProgress('Uninstalling managed daemon', showProgress);
|
|
485
493
|
let uninstallResult;
|
|
486
494
|
try {
|
|
487
495
|
uninstallResult = await sudoSession.run([
|
|
488
|
-
resolveLaunchDaemonUninstallScriptPath(),
|
|
496
|
+
resolveLaunchDaemonUninstallScriptPath(currentConfig),
|
|
489
497
|
'--label',
|
|
490
498
|
DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
491
499
|
'--keychain-service',
|
|
@@ -514,8 +522,7 @@ async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
|
514
522
|
deleteRootArtifactsResult = await sudoSession.run([
|
|
515
523
|
'/bin/rm',
|
|
516
524
|
'-f',
|
|
517
|
-
|
|
518
|
-
DEFAULT_MANAGED_DAEMON_SOCKET,
|
|
525
|
+
...managedDaemonResetArtifactPaths(),
|
|
519
526
|
]);
|
|
520
527
|
} catch (error) {
|
|
521
528
|
stateProgress.fail();
|
|
@@ -543,6 +550,7 @@ async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
|
543
550
|
launchdDomain: 'system',
|
|
544
551
|
plist: DEFAULT_LAUNCH_DAEMON_PLIST,
|
|
545
552
|
daemonSocket: DEFAULT_MANAGED_DAEMON_SOCKET,
|
|
553
|
+
relayDaemonTokenFile: DEFAULT_MANAGED_RELAY_DAEMON_TOKEN_FILE,
|
|
546
554
|
stateFile: DEFAULT_MANAGED_STATE_FILE,
|
|
547
555
|
systemKeychainPasswordService: DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
548
556
|
systemKeychainPasswordAccount: keychainAccount,
|
|
@@ -560,6 +568,7 @@ async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
|
560
568
|
daemon: {
|
|
561
569
|
label: result.daemon.label,
|
|
562
570
|
daemonSocket: result.daemon.daemonSocket,
|
|
571
|
+
relayDaemonTokenFile: result.daemon.relayDaemonTokenFile,
|
|
563
572
|
stateFile: result.daemon.stateFile,
|
|
564
573
|
},
|
|
565
574
|
local,
|
|
@@ -625,12 +634,13 @@ async function runAdminUninstall(options: AdminUninstallOptions): Promise<void>
|
|
|
625
634
|
);
|
|
626
635
|
}
|
|
627
636
|
await sudoSession.prime();
|
|
637
|
+
const currentConfig = readConfigIfPresent();
|
|
628
638
|
|
|
629
639
|
const uninstallProgress = createProgress('Uninstalling managed daemon', showProgress);
|
|
630
640
|
let uninstallResult;
|
|
631
641
|
try {
|
|
632
642
|
uninstallResult = await sudoSession.run([
|
|
633
|
-
resolveLaunchDaemonUninstallScriptPath(),
|
|
643
|
+
resolveLaunchDaemonUninstallScriptPath(currentConfig),
|
|
634
644
|
'--label',
|
|
635
645
|
DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
636
646
|
'--keychain-service',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './admin-setup.ts';
|
package/src/lib/admin-setup.ts
CHANGED
|
@@ -21,6 +21,11 @@ import {
|
|
|
21
21
|
assertTrustedRootPlannedPrivateFilePath,
|
|
22
22
|
} from './fs-trust.js';
|
|
23
23
|
import { DAEMON_PASSWORD_KEYCHAIN_SERVICE } from './keychain.js';
|
|
24
|
+
import {
|
|
25
|
+
LAUNCHD_INSTALL_SCRIPT_NAME,
|
|
26
|
+
LAUNCHD_RUNNER_SCRIPT_NAME,
|
|
27
|
+
resolveLaunchDaemonHelperScriptPath,
|
|
28
|
+
} from './launchd-assets.js';
|
|
24
29
|
import { resolveCliNetworkProfile } from './network-selection.js';
|
|
25
30
|
import { passthroughRustBinary, RustBinaryExitError, runRustBinary } from './rust.js';
|
|
26
31
|
import { createSudoSession } from './sudo.js';
|
|
@@ -639,7 +644,7 @@ function resolveRustBinDir(config: WlfiConfig): string {
|
|
|
639
644
|
function resolveSourceLaunchDaemonPaths(config: WlfiConfig): LaunchDaemonAssetPaths {
|
|
640
645
|
const rustBinDir = resolveRustBinDir(config);
|
|
641
646
|
return {
|
|
642
|
-
runnerPath: path.join(rustBinDir,
|
|
647
|
+
runnerPath: path.join(rustBinDir, LAUNCHD_RUNNER_SCRIPT_NAME),
|
|
643
648
|
daemonBin: path.join(
|
|
644
649
|
rustBinDir,
|
|
645
650
|
`wlfi-agent-daemon${process.platform === 'win32' ? '.exe' : ''}`,
|
|
@@ -662,20 +667,8 @@ export function resolveManagedLaunchDaemonPaths(): LaunchDaemonAssetPaths {
|
|
|
662
667
|
};
|
|
663
668
|
}
|
|
664
669
|
|
|
665
|
-
function resolveLaunchDaemonInstallScriptPath(): string {
|
|
666
|
-
|
|
667
|
-
path.resolve(__dirname, '../scripts/launchd/install-user-daemon.sh'),
|
|
668
|
-
path.resolve(__dirname, '../../scripts/launchd/install-user-daemon.sh'),
|
|
669
|
-
path.resolve(process.cwd(), 'scripts/launchd/install-user-daemon.sh'),
|
|
670
|
-
];
|
|
671
|
-
|
|
672
|
-
for (const candidate of candidates) {
|
|
673
|
-
if (fs.existsSync(candidate)) {
|
|
674
|
-
return candidate;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return candidates[0];
|
|
670
|
+
function resolveLaunchDaemonInstallScriptPath(config: WlfiConfig): string {
|
|
671
|
+
return resolveLaunchDaemonHelperScriptPath(LAUNCHD_INSTALL_SCRIPT_NAME, config);
|
|
679
672
|
}
|
|
680
673
|
|
|
681
674
|
function filesHaveIdenticalContents(leftPath: string, rightPath: string): boolean {
|
|
@@ -718,23 +711,24 @@ export function assertManagedDaemonInstallPreconditions(
|
|
|
718
711
|
deps.assertTrustedRootPlannedDaemonSocketPath ?? assertTrustedRootPlannedDaemonSocketPath;
|
|
719
712
|
const trustStateFilePath =
|
|
720
713
|
deps.assertTrustedRootPlannedPrivateFilePath ?? assertTrustedRootPlannedPrivateFilePath;
|
|
721
|
-
const installScript =
|
|
714
|
+
const installScript =
|
|
715
|
+
(deps.resolveInstallScriptPath ?? (() => resolveLaunchDaemonInstallScriptPath(config)))();
|
|
722
716
|
const sourcePaths = resolveSourceLaunchDaemonPaths(config);
|
|
723
717
|
const managedPaths = resolveManagedLaunchDaemonPaths();
|
|
724
718
|
|
|
725
719
|
if (!existsSync(sourcePaths.runnerPath)) {
|
|
726
720
|
throw new Error(
|
|
727
|
-
`daemon runner is not installed at ${sourcePaths.runnerPath};
|
|
721
|
+
`daemon runner is not installed at ${sourcePaths.runnerPath}; reinstall @wlfi-agent/cli`,
|
|
728
722
|
);
|
|
729
723
|
}
|
|
730
724
|
if (!existsSync(sourcePaths.daemonBin)) {
|
|
731
725
|
throw new Error(
|
|
732
|
-
`daemon binary is not installed at ${sourcePaths.daemonBin};
|
|
726
|
+
`daemon binary is not installed at ${sourcePaths.daemonBin}; reinstall @wlfi-agent/cli`,
|
|
733
727
|
);
|
|
734
728
|
}
|
|
735
729
|
if (!existsSync(sourcePaths.keychainHelperBin)) {
|
|
736
730
|
throw new Error(
|
|
737
|
-
`daemon keychain helper is not installed at ${sourcePaths.keychainHelperBin};
|
|
731
|
+
`daemon keychain helper is not installed at ${sourcePaths.keychainHelperBin}; reinstall @wlfi-agent/cli`,
|
|
738
732
|
);
|
|
739
733
|
}
|
|
740
734
|
if (!existsSync(installScript)) {
|
|
@@ -927,6 +921,17 @@ function plistContainsValue(plistContents: string, value: string): boolean {
|
|
|
927
921
|
return plistContents.includes(`<string>${value}</string>`);
|
|
928
922
|
}
|
|
929
923
|
|
|
924
|
+
export function launchDaemonPlistValue(
|
|
925
|
+
plistContents: string,
|
|
926
|
+
key: string,
|
|
927
|
+
): string | null {
|
|
928
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
929
|
+
const match = plistContents.match(
|
|
930
|
+
new RegExp(`<key>${escapedKey}</key>\\s*<string>([^<]+)</string>`, 'u'),
|
|
931
|
+
);
|
|
932
|
+
return match?.[1] ?? null;
|
|
933
|
+
}
|
|
934
|
+
|
|
930
935
|
function resolveInstalledLaunchDaemonPaths(
|
|
931
936
|
config: WlfiConfig,
|
|
932
937
|
plistContents: string,
|
|
@@ -961,6 +966,7 @@ function isManagedDaemonInstallCurrent(
|
|
|
961
966
|
const sourcePaths = resolveSourceLaunchDaemonPaths(config);
|
|
962
967
|
const keychainAccount = os.userInfo().username;
|
|
963
968
|
const expectedAdminUid = String(process.getuid?.() ?? process.geteuid?.() ?? 0);
|
|
969
|
+
const expectedAgentUid = String(process.getuid?.() ?? process.geteuid?.() ?? 0);
|
|
964
970
|
|
|
965
971
|
if (!fs.existsSync(DEFAULT_LAUNCH_DAEMON_PLIST)) {
|
|
966
972
|
return false;
|
|
@@ -1003,6 +1009,13 @@ function isManagedDaemonInstallCurrent(
|
|
|
1003
1009
|
return false;
|
|
1004
1010
|
}
|
|
1005
1011
|
|
|
1012
|
+
if (
|
|
1013
|
+
launchDaemonPlistValue(plistContents, 'WLFI_ALLOW_ADMIN_EUID') !== expectedAdminUid ||
|
|
1014
|
+
launchDaemonPlistValue(plistContents, 'WLFI_ALLOW_AGENT_EUID') !== expectedAgentUid
|
|
1015
|
+
) {
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1006
1019
|
return [
|
|
1007
1020
|
DEFAULT_LAUNCH_DAEMON_LABEL,
|
|
1008
1021
|
installedPaths.runnerPath,
|
|
@@ -1013,7 +1026,6 @@ function isManagedDaemonInstallCurrent(
|
|
|
1013
1026
|
DAEMON_PASSWORD_KEYCHAIN_SERVICE,
|
|
1014
1027
|
keychainAccount,
|
|
1015
1028
|
DEFAULT_SIGNER_BACKEND,
|
|
1016
|
-
expectedAdminUid,
|
|
1017
1029
|
].every((value) => plistContainsValue(plistContents, value));
|
|
1018
1030
|
}
|
|
1019
1031
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-auth-revoke.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-auth-rotate.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-auth.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './config-mutation.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './launchd-assets.ts';
|