@wlfi-agent/cli 1.4.17 → 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/Cargo.lock +5 -0
- package/README.md +61 -28
- package/crates/vault-cli-admin/src/io_utils.rs +149 -1
- package/crates/vault-cli-admin/src/main.rs +639 -16
- package/crates/vault-cli-admin/src/shared_config.rs +18 -18
- package/crates/vault-cli-admin/src/tui/token_rpc.rs +190 -3
- package/crates/vault-cli-admin/src/tui/utils.rs +59 -0
- package/crates/vault-cli-admin/src/tui.rs +1205 -120
- package/crates/vault-cli-agent/Cargo.toml +1 -0
- package/crates/vault-cli-agent/src/io_utils.rs +163 -2
- package/crates/vault-cli-agent/src/main.rs +648 -32
- package/crates/vault-cli-daemon/Cargo.toml +4 -0
- package/crates/vault-cli-daemon/src/main.rs +617 -67
- package/crates/vault-cli-daemon/src/relay_sync.rs +776 -4
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +5 -0
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +32 -1
- package/crates/vault-daemon/src/persistence.rs +637 -100
- package/crates/vault-daemon/src/tests.rs +1013 -3
- package/crates/vault-daemon/src/tests_parts/part2.rs +99 -0
- package/crates/vault-daemon/src/tests_parts/part4.rs +11 -7
- package/crates/vault-domain/src/nonce.rs +4 -0
- package/crates/vault-domain/src/tests.rs +616 -0
- package/crates/vault-policy/src/engine.rs +55 -32
- package/crates/vault-policy/src/tests.rs +195 -0
- package/crates/vault-sdk-agent/src/lib.rs +415 -22
- package/crates/vault-signer/Cargo.toml +3 -0
- package/crates/vault-signer/src/lib.rs +266 -40
- package/crates/vault-transport-unix/src/lib.rs +653 -5
- package/crates/vault-transport-xpc/src/tests.rs +531 -3
- package/crates/vault-transport-xpc/tests/e2e_flow.rs +3 -0
- package/dist/cli.cjs +756 -194
- package/dist/cli.cjs.map +1 -1
- package/package.json +5 -2
- package/packages/cache/.turbo/turbo-build.log +20 -20
- package/packages/cache/coverage/clover.xml +529 -394
- package/packages/cache/coverage/coverage-final.json +2 -2
- package/packages/cache/coverage/index.html +21 -21
- package/packages/cache/coverage/src/client/index.html +1 -1
- package/packages/cache/coverage/src/client/index.ts.html +1 -1
- package/packages/cache/coverage/src/errors/index.html +1 -1
- package/packages/cache/coverage/src/errors/index.ts.html +12 -12
- package/packages/cache/coverage/src/index.html +1 -1
- package/packages/cache/coverage/src/index.ts.html +1 -1
- package/packages/cache/coverage/src/service/index.html +21 -21
- package/packages/cache/coverage/src/service/index.ts.html +769 -313
- package/packages/cache/dist/{chunk-QNK6GOTI.js → chunk-KC53LH5Z.js} +35 -2
- package/packages/cache/dist/chunk-KC53LH5Z.js.map +1 -0
- package/packages/cache/dist/{chunk-QF4XKEIA.cjs → chunk-UVU7VFE3.cjs} +35 -2
- package/packages/cache/dist/chunk-UVU7VFE3.cjs.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.js +1 -1
- package/packages/cache/node_modules/.bin/tsc +2 -2
- package/packages/cache/node_modules/.bin/tsserver +2 -2
- package/packages/cache/node_modules/.bin/tsup +2 -2
- package/packages/cache/node_modules/.bin/tsup-node +2 -2
- package/packages/cache/node_modules/.bin/vitest +4 -4
- package/packages/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/packages/cache/src/service/index.test.ts +165 -19
- package/packages/cache/src/service/index.ts +38 -1
- package/packages/config/.turbo/turbo-build.log +4 -4
- package/packages/config/dist/index.cjs +0 -17
- package/packages/config/dist/index.cjs.map +1 -1
- package/packages/config/src/index.ts +0 -17
- package/packages/rpc/.turbo/turbo-build.log +11 -11
- package/packages/rpc/dist/index.cjs +0 -17
- package/packages/rpc/dist/index.cjs.map +1 -1
- package/packages/rpc/src/index.js +1 -0
- package/packages/ui/node_modules/.bin/tsc +2 -2
- package/packages/ui/node_modules/.bin/tsserver +2 -2
- package/packages/ui/node_modules/.bin/tsup +2 -2
- package/packages/ui/node_modules/.bin/tsup-node +2 -2
- package/scripts/install-cli-launcher.mjs +37 -0
- package/scripts/install-rust-binaries.mjs +47 -0
- package/scripts/run-tests-isolated.mjs +210 -0
- package/src/cli.ts +310 -50
- package/src/lib/admin-reset.ts +101 -33
- package/src/lib/admin-setup.ts +285 -55
- package/src/lib/agent-auth-migrate.ts +5 -1
- package/src/lib/asset-broadcast.ts +15 -4
- package/src/lib/config-amounts.ts +6 -4
- package/src/lib/hidden-tty-prompt.js +1 -0
- package/src/lib/hidden-tty-prompt.ts +105 -0
- package/src/lib/keychain.ts +1 -0
- package/src/lib/local-admin-access.ts +4 -29
- package/src/lib/rust.ts +129 -33
- package/src/lib/signed-tx.ts +1 -0
- package/src/lib/sudo.ts +15 -5
- package/src/lib/wallet-profile.ts +3 -0
- package/src/lib/wallet-setup.ts +52 -0
- package/packages/cache/dist/chunk-QF4XKEIA.cjs.map +0 -1
- package/packages/cache/dist/chunk-QNK6GOTI.js.map +0 -1
|
@@ -80,13 +80,24 @@ fn read_secret_from_reader(mut reader: impl Read, label: &str) -> Result<String>
|
|
|
80
80
|
#[cfg(test)]
|
|
81
81
|
mod tests {
|
|
82
82
|
use super::{
|
|
83
|
-
|
|
83
|
+
emit_output, ensure_output_parent, is_symlink_path, print_agent_output,
|
|
84
|
+
read_secret_from_reader, resolve_agent_auth_token, resolve_output_target,
|
|
85
|
+
temporary_output_path, validate_secret, write_output_file,
|
|
84
86
|
};
|
|
87
|
+
use crate::{AgentCommandOutput, OutputFormat, OutputTarget};
|
|
85
88
|
use std::fs;
|
|
86
|
-
use std::io::Cursor;
|
|
89
|
+
use std::io::{Cursor, Read};
|
|
87
90
|
use std::path::PathBuf;
|
|
88
91
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
89
92
|
|
|
93
|
+
struct FailingReader;
|
|
94
|
+
|
|
95
|
+
impl Read for FailingReader {
|
|
96
|
+
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
|
|
97
|
+
Err(std::io::Error::other("boom"))
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
fn temp_path(prefix: &str) -> PathBuf {
|
|
91
102
|
std::env::temp_dir().join(format!(
|
|
92
103
|
"{prefix}-{}-{}",
|
|
@@ -105,6 +116,25 @@ mod tests {
|
|
|
105
116
|
assert!(err.to_string().contains("must not exceed"));
|
|
106
117
|
}
|
|
107
118
|
|
|
119
|
+
#[test]
|
|
120
|
+
fn read_secret_from_reader_trims_newlines_and_rejects_blank_values() {
|
|
121
|
+
let trimmed = read_secret_from_reader(Cursor::new("agent-token\r\n"), "agent auth token")
|
|
122
|
+
.expect("trimmed token");
|
|
123
|
+
assert_eq!(trimmed, "agent-token");
|
|
124
|
+
|
|
125
|
+
let err = read_secret_from_reader(Cursor::new(" \n"), "agent auth token")
|
|
126
|
+
.expect_err("must fail");
|
|
127
|
+
assert!(err.to_string().contains("must not be empty or whitespace"));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#[test]
|
|
131
|
+
fn read_secret_from_reader_propagates_io_errors() {
|
|
132
|
+
let err = read_secret_from_reader(FailingReader, "agent auth token").expect_err("must fail");
|
|
133
|
+
assert!(err
|
|
134
|
+
.to_string()
|
|
135
|
+
.contains("failed to read agent auth token from stdin"));
|
|
136
|
+
}
|
|
137
|
+
|
|
108
138
|
#[test]
|
|
109
139
|
fn validate_secret_rejects_oversized_non_stdin_secret() {
|
|
110
140
|
let err = validate_secret("a".repeat((16 * 1024) + 1), "argument or environment")
|
|
@@ -112,6 +142,22 @@ mod tests {
|
|
|
112
142
|
assert!(err.to_string().contains("must not exceed"));
|
|
113
143
|
}
|
|
114
144
|
|
|
145
|
+
#[test]
|
|
146
|
+
fn validate_secret_rejects_whitespace_only() {
|
|
147
|
+
let err = validate_secret(" \t ".to_string(), "environment").expect_err("must fail");
|
|
148
|
+
assert!(err.to_string().contains("must not be empty or whitespace"));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#[test]
|
|
152
|
+
fn resolve_agent_auth_token_covers_env_and_non_interactive_paths() {
|
|
153
|
+
let token = resolve_agent_auth_token(None, Some("secret".to_string()), false, false)
|
|
154
|
+
.expect("environment token");
|
|
155
|
+
assert_eq!(token, "secret");
|
|
156
|
+
|
|
157
|
+
let err = resolve_agent_auth_token(None, None, false, true).expect_err("must fail");
|
|
158
|
+
assert!(err.to_string().contains("agent auth token is required in non-interactive mode"));
|
|
159
|
+
}
|
|
160
|
+
|
|
115
161
|
#[test]
|
|
116
162
|
#[cfg(unix)]
|
|
117
163
|
fn ensure_output_parent_rejects_symlinked_parent_directory() {
|
|
@@ -199,6 +245,36 @@ mod tests {
|
|
|
199
245
|
fs::remove_dir_all(&root).expect("cleanup temp tree");
|
|
200
246
|
}
|
|
201
247
|
|
|
248
|
+
#[test]
|
|
249
|
+
fn ensure_output_parent_rejects_directory_output_path() {
|
|
250
|
+
let root = temp_path("vault-cli-agent-output-directory");
|
|
251
|
+
fs::create_dir_all(&root).expect("create root directory");
|
|
252
|
+
|
|
253
|
+
let err = ensure_output_parent(&root).expect_err("must reject directory path");
|
|
254
|
+
assert!(err.to_string().contains("is a directory; provide a file path"));
|
|
255
|
+
|
|
256
|
+
fs::remove_dir_all(&root).expect("cleanup temp tree");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#[test]
|
|
260
|
+
#[cfg(unix)]
|
|
261
|
+
fn ensure_output_parent_rejects_symlinked_output_path() {
|
|
262
|
+
use std::os::unix::fs::symlink;
|
|
263
|
+
|
|
264
|
+
let root = temp_path("vault-cli-agent-output-path-symlink");
|
|
265
|
+
fs::create_dir_all(&root).expect("create root directory");
|
|
266
|
+
let target = root.join("target.json");
|
|
267
|
+
let link = root.join("link.json");
|
|
268
|
+
fs::write(&target, "seed\n").expect("seed target");
|
|
269
|
+
symlink(&target, &link).expect("symlink output path");
|
|
270
|
+
|
|
271
|
+
let err = ensure_output_parent(&link).expect_err("must reject symlink path");
|
|
272
|
+
assert!(err.to_string().contains("must not be a symlink"));
|
|
273
|
+
assert!(is_symlink_path(&link).expect("symlink metadata"));
|
|
274
|
+
|
|
275
|
+
fs::remove_dir_all(&root).expect("cleanup temp tree");
|
|
276
|
+
}
|
|
277
|
+
|
|
202
278
|
#[test]
|
|
203
279
|
#[cfg(unix)]
|
|
204
280
|
fn write_output_file_overwrite_replaces_existing_hard_link_instead_of_mutating_shared_inode() {
|
|
@@ -229,6 +305,91 @@ mod tests {
|
|
|
229
305
|
|
|
230
306
|
fs::remove_dir_all(&root).expect("cleanup temp tree");
|
|
231
307
|
}
|
|
308
|
+
|
|
309
|
+
#[test]
|
|
310
|
+
fn resolve_output_target_preserves_file_paths() {
|
|
311
|
+
let path = temp_path("vault-cli-agent-output-target");
|
|
312
|
+
let target = resolve_output_target(Some(path.clone()), true).expect("target");
|
|
313
|
+
match target {
|
|
314
|
+
OutputTarget::File { path: actual, overwrite } => {
|
|
315
|
+
assert_eq!(actual, path);
|
|
316
|
+
assert!(overwrite);
|
|
317
|
+
}
|
|
318
|
+
OutputTarget::Stdout => panic!("expected file target"),
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#[test]
|
|
323
|
+
fn emit_output_and_print_agent_output_write_expected_content() {
|
|
324
|
+
let output_path = temp_path("vault-cli-agent-emit-output");
|
|
325
|
+
let agent_output_path = temp_path("vault-cli-agent-render-output");
|
|
326
|
+
|
|
327
|
+
emit_output(
|
|
328
|
+
"hello",
|
|
329
|
+
&OutputTarget::File {
|
|
330
|
+
path: output_path.clone(),
|
|
331
|
+
overwrite: false,
|
|
332
|
+
},
|
|
333
|
+
)
|
|
334
|
+
.expect("emit file output");
|
|
335
|
+
assert_eq!(fs::read_to_string(&output_path).expect("read output"), "hello\n");
|
|
336
|
+
|
|
337
|
+
let output = AgentCommandOutput {
|
|
338
|
+
command: "broadcast".to_string(),
|
|
339
|
+
network: "1".to_string(),
|
|
340
|
+
asset: "native_eth".to_string(),
|
|
341
|
+
counterparty: "0x2000000000000000000000000000000000000000".to_string(),
|
|
342
|
+
amount_wei: "7".to_string(),
|
|
343
|
+
estimated_max_gas_spend_wei: Some("21000".to_string()),
|
|
344
|
+
tx_type: Some("0x02".to_string()),
|
|
345
|
+
delegation_enabled: Some(false),
|
|
346
|
+
signature_hex: "0xdead".to_string(),
|
|
347
|
+
r_hex: Some("0x01".to_string()),
|
|
348
|
+
s_hex: Some("0x02".to_string()),
|
|
349
|
+
v: Some(1),
|
|
350
|
+
raw_tx_hex: Some("0xbeef".to_string()),
|
|
351
|
+
tx_hash_hex: Some("0xcafe".to_string()),
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
print_agent_output(
|
|
355
|
+
&output,
|
|
356
|
+
OutputFormat::Text,
|
|
357
|
+
&OutputTarget::File {
|
|
358
|
+
path: agent_output_path.clone(),
|
|
359
|
+
overwrite: false,
|
|
360
|
+
},
|
|
361
|
+
)
|
|
362
|
+
.expect("text render");
|
|
363
|
+
let rendered = fs::read_to_string(&agent_output_path).expect("read rendered");
|
|
364
|
+
assert!(rendered.contains("Command: broadcast"));
|
|
365
|
+
assert!(rendered.contains("Estimated Max Gas Spend (wei): 21000"));
|
|
366
|
+
assert!(rendered.contains("Delegation Enabled: false"));
|
|
367
|
+
assert!(rendered.contains("Tx Hash: 0xcafe"));
|
|
368
|
+
|
|
369
|
+
print_agent_output(
|
|
370
|
+
&output,
|
|
371
|
+
OutputFormat::Json,
|
|
372
|
+
&OutputTarget::File {
|
|
373
|
+
path: agent_output_path.clone(),
|
|
374
|
+
overwrite: true,
|
|
375
|
+
},
|
|
376
|
+
)
|
|
377
|
+
.expect("json render");
|
|
378
|
+
let rendered = fs::read_to_string(&agent_output_path).expect("read rendered");
|
|
379
|
+
assert!(rendered.contains("\"command\": \"broadcast\""));
|
|
380
|
+
assert!(rendered.contains("\"tx_hash_hex\": \"0xcafe\""));
|
|
381
|
+
|
|
382
|
+
fs::remove_file(&output_path).expect("cleanup output");
|
|
383
|
+
fs::remove_file(&agent_output_path).expect("cleanup rendered");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
#[test]
|
|
387
|
+
fn temporary_output_path_stays_in_parent_directory() {
|
|
388
|
+
let output = temp_path("vault-cli-agent-temp-output");
|
|
389
|
+
let temp = temporary_output_path(&output);
|
|
390
|
+
assert_eq!(temp.parent(), output.parent());
|
|
391
|
+
assert!(temp.file_name().expect("file name").to_string_lossy().contains(".tmp-"));
|
|
392
|
+
}
|
|
232
393
|
}
|
|
233
394
|
|
|
234
395
|
pub(crate) fn resolve_output_format(
|