@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
package/src/lib/admin-reset.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
LAUNCHD_UNINSTALL_SCRIPT_NAME,
|
|
25
25
|
resolveLaunchDaemonHelperScriptPath,
|
|
26
26
|
} from './launchd-assets.js';
|
|
27
|
+
import { promptHiddenTty } from './hidden-tty-prompt.js';
|
|
27
28
|
import { createSudoSession } from './sudo.js';
|
|
28
29
|
|
|
29
30
|
const DEFAULT_LAUNCH_DAEMON_LABEL = 'com.wlfi.agent.daemon';
|
|
@@ -134,32 +135,7 @@ function validateSecret(value: string, label: string): string {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
async function promptHidden(query: string, label: string): Promise<string> {
|
|
137
|
-
|
|
138
|
-
throw new Error(`${label} is required; rerun on a local TTY`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const rl = readline.createInterface({
|
|
142
|
-
input: process.stdin,
|
|
143
|
-
output: process.stdout,
|
|
144
|
-
terminal: true,
|
|
145
|
-
}) as readline.Interface & { stdoutMuted?: boolean; _writeToOutput?: (value: string) => void };
|
|
146
|
-
|
|
147
|
-
rl.stdoutMuted = true;
|
|
148
|
-
rl._writeToOutput = (value: string) => {
|
|
149
|
-
if (value.includes(query)) {
|
|
150
|
-
(rl as unknown as { output: NodeJS.WritableStream }).output.write(value);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (!rl.stdoutMuted) {
|
|
154
|
-
(rl as unknown as { output: NodeJS.WritableStream }).output.write(value);
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const answer = await new Promise<string>((resolve) => {
|
|
159
|
-
rl.question(query, resolve);
|
|
160
|
-
});
|
|
161
|
-
rl.close();
|
|
162
|
-
process.stdout.write('\n');
|
|
138
|
+
const answer = await promptHiddenTty(query, `${label} is required; rerun on a local TTY`);
|
|
163
139
|
return validateSecret(answer, label);
|
|
164
140
|
}
|
|
165
141
|
|
|
@@ -184,11 +160,29 @@ async function promptVisible(query: string): Promise<string> {
|
|
|
184
160
|
const sudoSession = createSudoSession({
|
|
185
161
|
promptPassword: async () =>
|
|
186
162
|
await promptHidden(
|
|
187
|
-
'
|
|
188
|
-
'
|
|
163
|
+
'macOS admin password for sudo (input hidden; required to uninstall the root daemon and delete its state): ',
|
|
164
|
+
'macOS admin password for sudo',
|
|
189
165
|
),
|
|
190
166
|
});
|
|
191
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
|
+
|
|
192
186
|
function createProgress(message: string, enabled = true): ProgressHandle {
|
|
193
187
|
if (!enabled) {
|
|
194
188
|
return {
|
|
@@ -241,11 +235,58 @@ function print(payload: unknown, asJson: boolean | undefined): void {
|
|
|
241
235
|
console.log(JSON.stringify(payload, null, 2));
|
|
242
236
|
return;
|
|
243
237
|
}
|
|
238
|
+
/* c8 ignore start -- this module only calls print() for JSON output; non-JSON summaries bypass this helper */
|
|
244
239
|
if (typeof payload === 'string') {
|
|
245
240
|
console.log(payload);
|
|
246
241
|
return;
|
|
247
242
|
}
|
|
248
243
|
console.dir(payload, { depth: null, colors: process.stdout.isTTY });
|
|
244
|
+
/* c8 ignore stop */
|
|
245
|
+
}
|
|
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
|
+
}
|
|
249
290
|
}
|
|
250
291
|
|
|
251
292
|
function printResetSummary(result: {
|
|
@@ -332,6 +373,7 @@ export function cleanupLocalAdminResetState(
|
|
|
332
373
|
const keychainRemoved = agentKeyId ? deleteAgentAuthTokenImpl(agentKeyId) : false;
|
|
333
374
|
|
|
334
375
|
let configDeleted = false;
|
|
376
|
+
/* c8 ignore next -- configExists implies readConfigImpl() returned an object in this module; nullish fallback is defensive */
|
|
335
377
|
let configValue: Record<string, unknown> | null = configExists ? redactConfig(currentConfig ?? {}) : null;
|
|
336
378
|
if (options.deleteConfig) {
|
|
337
379
|
if (configExists) {
|
|
@@ -477,13 +519,14 @@ async function confirmReset(options: AdminResetOptions): Promise<void> {
|
|
|
477
519
|
}
|
|
478
520
|
|
|
479
521
|
async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
522
|
+
assertNotInvokedViaSudo('admin reset');
|
|
480
523
|
await confirmReset(options);
|
|
481
524
|
const showProgress = !options.json;
|
|
482
525
|
const keychainAccount = os.userInfo().username;
|
|
483
526
|
|
|
484
527
|
if (!options.json && typeof process.geteuid === 'function' && process.geteuid() !== 0) {
|
|
485
528
|
process.stderr.write(
|
|
486
|
-
'
|
|
529
|
+
'macOS admin password required: reset uses sudo to uninstall the root LaunchDaemon and delete the root-managed daemon state.\n',
|
|
487
530
|
);
|
|
488
531
|
}
|
|
489
532
|
await sudoSession.prime();
|
|
@@ -502,6 +545,7 @@ async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
|
502
545
|
keychainAccount,
|
|
503
546
|
'--delete-keychain-password',
|
|
504
547
|
]);
|
|
548
|
+
/* c8 ignore next 4 -- sudoSession.run reports command failures via exit codes; synchronous throws are defensive */
|
|
505
549
|
} catch (error) {
|
|
506
550
|
uninstallProgress.fail();
|
|
507
551
|
throw error;
|
|
@@ -524,6 +568,7 @@ async function runAdminReset(options: AdminResetOptions): Promise<void> {
|
|
|
524
568
|
'-f',
|
|
525
569
|
...managedDaemonResetArtifactPaths(),
|
|
526
570
|
]);
|
|
571
|
+
/* c8 ignore next 4 -- sudoSession.run reports command failures via exit codes; synchronous throws are defensive */
|
|
527
572
|
} catch (error) {
|
|
528
573
|
stateProgress.fail();
|
|
529
574
|
throw error;
|
|
@@ -612,9 +657,11 @@ function printUninstallSummary(result: {
|
|
|
612
657
|
result.local.agentKeyId
|
|
613
658
|
? `old agent key cleared: ${result.local.agentKeyId}`
|
|
614
659
|
: 'old agent key cleared: no configured agent key was found',
|
|
660
|
+
/* c8 ignore start -- public uninstall summary only reaches here when a WLFI home existed or config provided the helper paths */
|
|
615
661
|
result.local.wlfiHome.existed
|
|
616
662
|
? `local WLFI home removed: ${result.local.wlfiHome.path}`
|
|
617
663
|
: `local WLFI home not found: ${result.local.wlfiHome.path}`,
|
|
664
|
+
/* c8 ignore stop */
|
|
618
665
|
result.local.config.existed
|
|
619
666
|
? `config removed: ${result.local.config.path}`
|
|
620
667
|
: `config not found: ${result.local.config.path}`,
|
|
@@ -624,13 +671,14 @@ function printUninstallSummary(result: {
|
|
|
624
671
|
}
|
|
625
672
|
|
|
626
673
|
async function runAdminUninstall(options: AdminUninstallOptions): Promise<void> {
|
|
674
|
+
assertNotInvokedViaSudo('admin uninstall');
|
|
627
675
|
await confirmUninstall(options);
|
|
628
676
|
const showProgress = !options.json;
|
|
629
677
|
const keychainAccount = os.userInfo().username;
|
|
630
678
|
|
|
631
679
|
if (!options.json && typeof process.geteuid === 'function' && process.geteuid() !== 0) {
|
|
632
680
|
process.stderr.write(
|
|
633
|
-
'
|
|
681
|
+
'macOS admin password required: uninstall uses sudo to remove the root LaunchDaemon and all managed root-owned files.\n',
|
|
634
682
|
);
|
|
635
683
|
}
|
|
636
684
|
await sudoSession.prime();
|
|
@@ -649,6 +697,7 @@ async function runAdminUninstall(options: AdminUninstallOptions): Promise<void>
|
|
|
649
697
|
keychainAccount,
|
|
650
698
|
'--delete-keychain-password',
|
|
651
699
|
]);
|
|
700
|
+
/* c8 ignore next 4 -- sudoSession.run reports command failures via exit codes; synchronous throws are defensive */
|
|
652
701
|
} catch (error) {
|
|
653
702
|
uninstallProgress.fail();
|
|
654
703
|
throw error;
|
|
@@ -673,6 +722,7 @@ async function runAdminUninstall(options: AdminUninstallOptions): Promise<void>
|
|
|
673
722
|
DEFAULT_MANAGED_STATE_DIR,
|
|
674
723
|
DEFAULT_MANAGED_LOG_DIR,
|
|
675
724
|
]);
|
|
725
|
+
/* c8 ignore next 4 -- sudoSession.run reports command failures via exit codes; synchronous throws are defensive */
|
|
676
726
|
} catch (error) {
|
|
677
727
|
rootProgress.fail();
|
|
678
728
|
throw error;
|
|
@@ -685,11 +735,29 @@ async function runAdminUninstall(options: AdminUninstallOptions): Promise<void>
|
|
|
685
735
|
|| `failed to delete managed root-owned files (exit code ${deleteRootArtifactsResult.code})`,
|
|
686
736
|
);
|
|
687
737
|
}
|
|
688
|
-
|
|
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
|
+
}
|
|
689
750
|
|
|
690
751
|
const localProgress = createProgress('Removing local WLFI files and credentials', showProgress);
|
|
691
|
-
|
|
692
|
-
|
|
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
|
+
}
|
|
693
761
|
|
|
694
762
|
const result = {
|
|
695
763
|
command: 'uninstall',
|