@wlfi-agent/cli 1.4.16 → 1.4.18

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.
Files changed (97) hide show
  1. package/Cargo.lock +26 -20
  2. package/Cargo.toml +1 -1
  3. package/README.md +61 -28
  4. package/crates/vault-cli-admin/src/io_utils.rs +149 -1
  5. package/crates/vault-cli-admin/src/main.rs +639 -16
  6. package/crates/vault-cli-admin/src/shared_config.rs +18 -18
  7. package/crates/vault-cli-admin/src/tui/token_rpc.rs +190 -3
  8. package/crates/vault-cli-admin/src/tui/utils.rs +59 -0
  9. package/crates/vault-cli-admin/src/tui.rs +1205 -120
  10. package/crates/vault-cli-agent/Cargo.toml +1 -0
  11. package/crates/vault-cli-agent/src/io_utils.rs +163 -2
  12. package/crates/vault-cli-agent/src/main.rs +648 -32
  13. package/crates/vault-cli-daemon/Cargo.toml +4 -0
  14. package/crates/vault-cli-daemon/src/main.rs +617 -67
  15. package/crates/vault-cli-daemon/src/relay_sync.rs +776 -4
  16. package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +5 -0
  17. package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +32 -1
  18. package/crates/vault-daemon/src/persistence.rs +637 -100
  19. package/crates/vault-daemon/src/tests.rs +1013 -3
  20. package/crates/vault-daemon/src/tests_parts/part2.rs +99 -0
  21. package/crates/vault-daemon/src/tests_parts/part4.rs +11 -7
  22. package/crates/vault-domain/src/nonce.rs +4 -0
  23. package/crates/vault-domain/src/tests.rs +616 -0
  24. package/crates/vault-policy/src/engine.rs +55 -32
  25. package/crates/vault-policy/src/tests.rs +195 -0
  26. package/crates/vault-sdk-agent/src/lib.rs +415 -22
  27. package/crates/vault-signer/Cargo.toml +3 -0
  28. package/crates/vault-signer/src/lib.rs +266 -40
  29. package/crates/vault-transport-unix/src/lib.rs +653 -5
  30. package/crates/vault-transport-xpc/src/tests.rs +531 -3
  31. package/crates/vault-transport-xpc/tests/e2e_flow.rs +3 -0
  32. package/dist/cli.cjs +663 -190
  33. package/dist/cli.cjs.map +1 -1
  34. package/package.json +5 -2
  35. package/packages/cache/.turbo/turbo-build.log +53 -52
  36. package/packages/cache/coverage/clover.xml +529 -394
  37. package/packages/cache/coverage/coverage-final.json +2 -2
  38. package/packages/cache/coverage/index.html +21 -21
  39. package/packages/cache/coverage/src/client/index.html +1 -1
  40. package/packages/cache/coverage/src/client/index.ts.html +1 -1
  41. package/packages/cache/coverage/src/errors/index.html +1 -1
  42. package/packages/cache/coverage/src/errors/index.ts.html +12 -12
  43. package/packages/cache/coverage/src/index.html +1 -1
  44. package/packages/cache/coverage/src/index.ts.html +1 -1
  45. package/packages/cache/coverage/src/service/index.html +21 -21
  46. package/packages/cache/coverage/src/service/index.ts.html +769 -313
  47. package/packages/cache/dist/{chunk-QNK6GOTI.js → chunk-KC53LH5Z.js} +35 -2
  48. package/packages/cache/dist/chunk-KC53LH5Z.js.map +1 -0
  49. package/packages/cache/dist/{chunk-QF4XKEIA.cjs → chunk-UVU7VFE3.cjs} +35 -2
  50. package/packages/cache/dist/chunk-UVU7VFE3.cjs.map +1 -0
  51. package/packages/cache/dist/index.cjs +2 -2
  52. package/packages/cache/dist/index.js +1 -1
  53. package/packages/cache/dist/service/index.cjs +2 -2
  54. package/packages/cache/dist/service/index.js +1 -1
  55. package/packages/cache/node_modules/.bin/tsc +2 -2
  56. package/packages/cache/node_modules/.bin/tsserver +2 -2
  57. package/packages/cache/node_modules/.bin/tsup +2 -2
  58. package/packages/cache/node_modules/.bin/tsup-node +2 -2
  59. package/packages/cache/node_modules/.bin/vitest +4 -4
  60. package/packages/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  61. package/packages/cache/src/service/index.test.ts +165 -19
  62. package/packages/cache/src/service/index.ts +38 -1
  63. package/packages/config/.turbo/turbo-build.log +18 -17
  64. package/packages/config/dist/index.cjs +0 -17
  65. package/packages/config/dist/index.cjs.map +1 -1
  66. package/packages/config/src/index.ts +0 -17
  67. package/packages/rpc/.turbo/turbo-build.log +32 -31
  68. package/packages/rpc/dist/index.cjs +0 -17
  69. package/packages/rpc/dist/index.cjs.map +1 -1
  70. package/packages/rpc/src/index.js +1 -0
  71. package/packages/ui/.turbo/turbo-build.log +44 -43
  72. package/packages/ui/dist/components/badge.d.ts +1 -1
  73. package/packages/ui/dist/components/button.d.ts +1 -1
  74. package/packages/ui/node_modules/.bin/tsc +2 -2
  75. package/packages/ui/node_modules/.bin/tsserver +2 -2
  76. package/packages/ui/node_modules/.bin/tsup +2 -2
  77. package/packages/ui/node_modules/.bin/tsup-node +2 -2
  78. package/scripts/install-cli-launcher.mjs +37 -0
  79. package/scripts/install-rust-binaries.mjs +112 -0
  80. package/scripts/run-tests-isolated.mjs +210 -0
  81. package/src/cli.ts +310 -50
  82. package/src/lib/admin-reset.ts +15 -30
  83. package/src/lib/admin-setup.ts +246 -55
  84. package/src/lib/agent-auth-migrate.ts +5 -1
  85. package/src/lib/asset-broadcast.ts +15 -4
  86. package/src/lib/config-amounts.ts +6 -4
  87. package/src/lib/hidden-tty-prompt.js +1 -0
  88. package/src/lib/hidden-tty-prompt.ts +105 -0
  89. package/src/lib/keychain.ts +1 -0
  90. package/src/lib/local-admin-access.ts +4 -29
  91. package/src/lib/rust.ts +129 -33
  92. package/src/lib/signed-tx.ts +1 -0
  93. package/src/lib/sudo.ts +15 -5
  94. package/src/lib/wallet-profile.ts +3 -0
  95. package/src/lib/wallet-setup.ts +52 -0
  96. package/packages/cache/dist/chunk-QF4XKEIA.cjs.map +0 -1
  97. package/packages/cache/dist/chunk-QNK6GOTI.js.map +0 -1
@@ -0,0 +1 @@
1
+ export * from './index.ts';
@@ -1,43 +1,44 @@
1
-
2
- > @wlfi-agent/ui@0.0.0 build /Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/packages/ui
3
- > tsup
4
-
5
- CLI Building entry: src/tailwind.ts, src/components/badge.tsx, src/components/button.tsx, src/components/card.tsx, src/components/input.tsx, src/components/label.tsx, src/components/separator.tsx, src/components/textarea.tsx, src/utils/cn.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Using tsup config: /Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/packages/ui/tsup.config.ts
9
- CLI Target: es2022
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/tailwind.js 1.59 KB
13
- ESM dist/utils/cn.js 92.00 B
14
- ESM dist/components/label.js 327.00 B
15
- ESM dist/components/card.js 1.22 KB
16
- ESM dist/components/button.js 1.43 KB
17
- ESM dist/components/input.js 840.00 B
18
- ESM dist/components/badge.js 971.00 B
19
- ESM dist/components/textarea.js 785.00 B
20
- ESM dist/components/separator.js 349.00 B
21
- ESM dist/chunk-MOAFBKSA.js 209.00 B
22
- ESM dist/utils/cn.js.map 71.00 B
23
- ESM dist/tailwind.js.map 2.39 KB
24
- ESM dist/components/label.js.map 502.00 B
25
- ESM dist/components/card.js.map 2.10 KB
26
- ESM dist/components/input.js.map 1.12 KB
27
- ESM dist/components/button.js.map 2.14 KB
28
- ESM dist/components/textarea.js.map 1.05 KB
29
- ESM dist/components/badge.js.map 1.50 KB
30
- ESM dist/chunk-MOAFBKSA.js.map 368.00 B
31
- ESM dist/components/separator.js.map 519.00 B
32
- ESM ⚡️ Build success in 31ms
33
- DTS Build start
34
- DTS ⚡️ Build success in 688ms
35
- DTS dist/tailwind.d.ts 1.49 KB
36
- DTS dist/components/badge.d.ts 612.00 B
37
- DTS dist/components/button.d.ts 715.00 B
38
- DTS dist/components/card.d.ts 791.00 B
39
- DTS dist/components/input.d.ts 191.00 B
40
- DTS dist/components/label.d.ts 165.00 B
41
- DTS dist/components/separator.d.ts 166.00 B
42
- DTS dist/components/textarea.d.ts 206.00 B
43
- DTS dist/utils/cn.d.ts 106.00 B
1
+
2
+ 
3
+ > @wlfi-agent/ui@0.0.0 build /Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/packages/ui
4
+ > tsup
5
+
6
+ CLI Building entry: src/tailwind.ts, src/components/badge.tsx, src/components/button.tsx, src/components/card.tsx, src/components/input.tsx, src/components/label.tsx, src/components/separator.tsx, src/components/textarea.tsx, src/utils/cn.ts
7
+ CLI Using tsconfig: tsconfig.json
8
+ CLI tsup v8.5.1
9
+ CLI Using tsup config: /Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/packages/ui/tsup.config.ts
10
+ CLI Target: es2022
11
+ CLI Cleaning output folder
12
+ ESM Build start
13
+ ESM dist/utils/cn.js 92.00 B
14
+ ESM dist/components/badge.js 971.00 B
15
+ ESM dist/tailwind.js 1.59 KB
16
+ ESM dist/components/button.js 1.43 KB
17
+ ESM dist/components/input.js 840.00 B
18
+ ESM dist/components/label.js 327.00 B
19
+ ESM dist/components/textarea.js 785.00 B
20
+ ESM dist/components/card.js 1.22 KB
21
+ ESM dist/components/separator.js 349.00 B
22
+ ESM dist/chunk-MOAFBKSA.js 209.00 B
23
+ ESM dist/tailwind.js.map 2.39 KB
24
+ ESM dist/utils/cn.js.map 71.00 B
25
+ ESM dist/components/badge.js.map 1.50 KB
26
+ ESM dist/components/input.js.map 1.12 KB
27
+ ESM dist/components/button.js.map 2.14 KB
28
+ ESM dist/components/separator.js.map 519.00 B
29
+ ESM dist/components/label.js.map 502.00 B
30
+ ESM dist/components/textarea.js.map 1.05 KB
31
+ ESM dist/components/card.js.map 2.10 KB
32
+ ESM dist/chunk-MOAFBKSA.js.map 368.00 B
33
+ ESM ⚡️ Build success in 13ms
34
+ DTS Build start
35
+ DTS ⚡️ Build success in 792ms
36
+ DTS dist/tailwind.d.ts 1.49 KB
37
+ DTS dist/components/badge.d.ts 612.00 B
38
+ DTS dist/components/button.d.ts 715.00 B
39
+ DTS dist/components/card.d.ts 791.00 B
40
+ DTS dist/components/input.d.ts 191.00 B
41
+ DTS dist/components/label.d.ts 165.00 B
42
+ DTS dist/components/separator.d.ts 166.00 B
43
+ DTS dist/components/textarea.d.ts 206.00 B
44
+ DTS dist/utils/cn.d.ts 106.00 B
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import * as React from 'react';
4
4
 
5
5
  declare const badgeVariants: (props?: ({
6
- variant?: "default" | "secondary" | "success" | "warning" | "destructive" | null | undefined;
6
+ variant?: "default" | "secondary" | "destructive" | "success" | "warning" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
9
9
  }
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import * as React from 'react';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
6
+ variant?: "default" | "secondary" | "outline" | "ghost" | "destructive" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -11,7 +11,7 @@ else
11
11
  export NODE_PATH="/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/node_modules:$NODE_PATH"
12
12
  fi
13
13
  if [ -x "$basedir/node" ]; then
14
- exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc" "$@"
14
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
15
15
  else
16
- exec node "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc" "$@"
16
+ exec node "$basedir/../typescript/bin/tsc" "$@"
17
17
  fi
@@ -11,7 +11,7 @@ else
11
11
  export NODE_PATH="/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/node_modules:$NODE_PATH"
12
12
  fi
13
13
  if [ -x "$basedir/node" ]; then
14
- exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsserver" "$@"
14
+ exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
15
15
  else
16
- exec node "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsserver" "$@"
16
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
17
17
  fi
@@ -11,7 +11,7 @@ else
11
11
  export NODE_PATH="/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/node_modules:$NODE_PATH"
12
12
  fi
13
13
  if [ -x "$basedir/node" ]; then
14
- exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/cli-default.js" "$@"
14
+ exec "$basedir/node" "$basedir/../tsup/dist/cli-default.js" "$@"
15
15
  else
16
- exec node "$basedir/../../../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/cli-default.js" "$@"
16
+ exec node "$basedir/../tsup/dist/cli-default.js" "$@"
17
17
  fi
@@ -11,7 +11,7 @@ else
11
11
  export NODE_PATH="/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules:/Users/hanzhi/Documents/WLFI/wlfi-agent-sdk/node_modules/.pnpm/node_modules:$NODE_PATH"
12
12
  fi
13
13
  if [ -x "$basedir/node" ]; then
14
- exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/cli-node.js" "$@"
14
+ exec "$basedir/node" "$basedir/../tsup/dist/cli-node.js" "$@"
15
15
  else
16
- exec node "$basedir/../../../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/cli-node.js" "$@"
16
+ exec node "$basedir/../tsup/dist/cli-node.js" "$@"
17
17
  fi
@@ -0,0 +1,37 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { installCliLauncher } from './install-rust-binaries.mjs';
5
+
6
+ function resolveWlfiHome(env) {
7
+ return env.WLFI_HOME?.trim() || path.join(os.homedir(), '.wlfi_agent');
8
+ }
9
+
10
+ function installLocalCliLauncher({
11
+ env = process.env,
12
+ platform = process.platform,
13
+ cliEntrypoint,
14
+ } = {}) {
15
+ const wlfiHome = resolveWlfiHome(env);
16
+ const binDir = path.join(wlfiHome, 'bin');
17
+ return installCliLauncher({ binDir, platform, cliEntrypoint });
18
+ }
19
+
20
+ function isDirectExecution() {
21
+ return (
22
+ Boolean(process.argv[1]) &&
23
+ import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
24
+ );
25
+ }
26
+
27
+ if (isDirectExecution()) {
28
+ try {
29
+ installLocalCliLauncher();
30
+ } catch (error) {
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ process.stderr.write(`${message}\n`);
33
+ process.exitCode = 1;
34
+ }
35
+ }
36
+
37
+ export { installLocalCliLauncher };
@@ -6,6 +6,11 @@ import { pathToFileURL } from 'node:url';
6
6
 
7
7
  const repoRoot = new URL('..', import.meta.url).pathname;
8
8
  const extension = process.platform === 'win32' ? '.exe' : '';
9
+ const MIN_RUST_VERSION = {
10
+ major: 1,
11
+ minor: 87,
12
+ patch: 0,
13
+ };
9
14
  const rustBins = [
10
15
  'wlfi-agent-daemon',
11
16
  'wlfi-agent-admin',
@@ -22,6 +27,32 @@ function decodeOutput(value) {
22
27
  return value.toString().trim();
23
28
  }
24
29
 
30
+ function formatMinimumRustVersion() {
31
+ return `${MIN_RUST_VERSION.major}.${MIN_RUST_VERSION.minor}.${MIN_RUST_VERSION.patch}`;
32
+ }
33
+
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
+ };
44
+ }
45
+
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;
52
+ }
53
+ return left.patch - right.patch;
54
+ }
55
+
25
56
  function runCheck(spawnSyncImpl, command, args, options = {}) {
26
57
  return spawnSyncImpl(command, args, {
27
58
  cwd: repoRoot,
@@ -55,6 +86,53 @@ function resolveHelperScripts(binDir) {
55
86
  ];
56
87
  }
57
88
 
89
+ function resolveCliEntrypoint() {
90
+ return path.join(repoRoot, 'dist', 'cli.cjs');
91
+ }
92
+
93
+ function escapePosixShellArgument(value) {
94
+ return `'${value.replace(/'/g, `'\\''`)}'`;
95
+ }
96
+
97
+ export function installCliLauncher({
98
+ binDir,
99
+ cliEntrypoint = resolveCliEntrypoint(),
100
+ platform = process.platform,
101
+ allowMissingEntrypoint = false,
102
+ } = {}) {
103
+ if (!binDir) {
104
+ throw new Error('[wlfi-agent] binDir is required to install the CLI launcher.');
105
+ }
106
+
107
+ fs.mkdirSync(binDir, { recursive: true, mode: 0o700 });
108
+
109
+ if (!fs.existsSync(cliEntrypoint)) {
110
+ if (allowMissingEntrypoint) {
111
+ return false;
112
+ }
113
+ throw new Error(
114
+ `[wlfi-agent] CLI entrypoint was not found at ${cliEntrypoint}. Run \`npm run build\` first.`,
115
+ );
116
+ }
117
+
118
+ if (platform === 'win32') {
119
+ const destination = path.join(binDir, 'wlfi-agent.cmd');
120
+ const escaped = cliEntrypoint.replace(/"/g, '""');
121
+ fs.writeFileSync(destination, `@echo off\r\nnode "${escaped}" %*\r\n`, {
122
+ mode: 0o755,
123
+ });
124
+ return true;
125
+ }
126
+
127
+ const destination = path.join(binDir, 'wlfi-agent');
128
+ const script =
129
+ '#!/bin/sh\n' +
130
+ `exec node ${escapePosixShellArgument(cliEntrypoint)} "$@"\n`;
131
+ fs.writeFileSync(destination, script, { mode: 0o755 });
132
+ fs.chmodSync(destination, 0o755);
133
+ return true;
134
+ }
135
+
58
136
  function checkCargoAvailable(spawnSyncImpl) {
59
137
  const result = runCheck(spawnSyncImpl, 'cargo', ['--version']);
60
138
  if (result.status === 0) {
@@ -73,6 +151,38 @@ function checkCargoAvailable(spawnSyncImpl) {
73
151
  throw new Error(lines.join('\n'));
74
152
  }
75
153
 
154
+ function checkRustcVersion(spawnSyncImpl) {
155
+ const result = runCheck(spawnSyncImpl, 'rustc', ['--version']);
156
+ if (result.status !== 0) {
157
+ const detail = decodeOutput(result.stderr) || decodeOutput(result.stdout);
158
+ const lines = [
159
+ '[wlfi-agent] Rust compiler was not found on PATH.',
160
+ `[wlfi-agent] Install Rust ${formatMinimumRustVersion()} or newer from https://rustup.rs.`,
161
+ ];
162
+ if (detail) {
163
+ lines.push(`[wlfi-agent] rustc check output: ${detail}`);
164
+ }
165
+ lines.push(`[wlfi-agent] ${RERUN_INSTRUCTIONS}`);
166
+ throw new Error(lines.join('\n'));
167
+ }
168
+
169
+ const versionOutput = decodeOutput(result.stdout) || decodeOutput(result.stderr);
170
+ const parsedVersion = parseRustVersion(versionOutput);
171
+ if (!parsedVersion) {
172
+ throw new Error(
173
+ `[wlfi-agent] Unable to determine the installed Rust compiler version from: ${versionOutput || '<empty output>'}`,
174
+ );
175
+ }
176
+
177
+ if (compareVersions(parsedVersion, MIN_RUST_VERSION) < 0) {
178
+ throw new Error(
179
+ `[wlfi-agent] Rust ${formatMinimumRustVersion()} or newer is required; found ${versionOutput}.\n` +
180
+ `[wlfi-agent] Update Rust with \`rustup update\`.\n` +
181
+ `[wlfi-agent] ${RERUN_INSTRUCTIONS}`,
182
+ );
183
+ }
184
+ }
185
+
76
186
  function checkMacOsToolchainAvailable(spawnSyncImpl, platform) {
77
187
  if (platform !== 'darwin') {
78
188
  return;
@@ -100,6 +210,7 @@ export function verifyRustInstallPrerequisites({
100
210
  platform = process.platform,
101
211
  } = {}) {
102
212
  checkCargoAvailable(spawnSyncImpl);
213
+ checkRustcVersion(spawnSyncImpl);
103
214
  checkMacOsToolchainAvailable(spawnSyncImpl, platform);
104
215
  }
105
216
 
@@ -122,6 +233,7 @@ export function installRustBinaries({
122
233
  'cargo',
123
234
  [
124
235
  'build',
236
+ '--locked',
125
237
  '--release',
126
238
  '-p',
127
239
  'wlfi-agent-daemon',
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'node:child_process';
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+
8
+ function fail(message) {
9
+ console.error(message);
10
+ process.exit(1);
11
+ }
12
+
13
+ const forwardedArgs = process.argv.slice(2);
14
+ const commandArgs =
15
+ forwardedArgs[0] === '--' ? forwardedArgs.slice(1) : forwardedArgs;
16
+
17
+ if (commandArgs.length === 0) {
18
+ fail(
19
+ 'usage: node ./scripts/run-tests-isolated.mjs -- <command> [args...]',
20
+ );
21
+ }
22
+
23
+ const hostHome = process.env.HOME;
24
+ const sandboxBaseDir = hostHome ? path.join(hostHome, '.wt') : os.tmpdir();
25
+ fs.mkdirSync(sandboxBaseDir, { recursive: true, mode: 0o700 });
26
+ const sandboxRoot = fs.mkdtempSync(path.join(sandboxBaseDir, 't-'));
27
+ const homeDir = path.join(sandboxRoot, 'h');
28
+ const wlfiHome = path.join(sandboxRoot, 'w');
29
+ const tmpDir = path.join(sandboxRoot, 't');
30
+ const binDir = path.join(sandboxRoot, 'b');
31
+ const fakeKeychainRoot = path.join(sandboxRoot, 'k');
32
+ const keepSandbox = process.env.WLFI_TEST_KEEP_SANDBOX === '1';
33
+ const cargoHome =
34
+ process.env.CARGO_HOME ?? (hostHome ? path.join(hostHome, '.cargo') : undefined);
35
+ const rustupHome =
36
+ process.env.RUSTUP_HOME ?? (hostHome ? path.join(hostHome, '.rustup') : undefined);
37
+ const xdgCacheHome = path.join(sandboxRoot, 'c');
38
+ const xdgConfigHome = path.join(sandboxRoot, 'g');
39
+
40
+ for (const dir of [
41
+ homeDir,
42
+ wlfiHome,
43
+ tmpDir,
44
+ binDir,
45
+ fakeKeychainRoot,
46
+ xdgCacheHome,
47
+ xdgConfigHome,
48
+ ]) {
49
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
50
+ }
51
+
52
+ function writeExecutable(targetPath, contents) {
53
+ fs.writeFileSync(targetPath, contents, { mode: 0o755 });
54
+ }
55
+
56
+ writeExecutable(
57
+ path.join(binDir, 'security'),
58
+ `#!/usr/bin/env node
59
+ import fs from 'node:fs';
60
+ import path from 'node:path';
61
+
62
+ const root = process.env.WLFI_TEST_FAKE_KEYCHAIN_ROOT;
63
+ if (!root) {
64
+ console.error('WLFI_TEST_FAKE_KEYCHAIN_ROOT is required');
65
+ process.exit(70);
66
+ }
67
+
68
+ const dbPath = path.join(root, 'keychain.json');
69
+ const [command, ...args] = process.argv.slice(2);
70
+
71
+ function loadDb() {
72
+ try {
73
+ return JSON.parse(fs.readFileSync(dbPath, 'utf8'));
74
+ } catch (error) {
75
+ if (error && typeof error === 'object' && error.code === 'ENOENT') {
76
+ return {};
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+
82
+ function saveDb(db) {
83
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2) + '\\n', { mode: 0o600 });
84
+ }
85
+
86
+ function readFlag(flag) {
87
+ const index = args.indexOf(flag);
88
+ if (index === -1 || index === args.length - 1) {
89
+ console.error('isolated security shim requires ' + flag);
90
+ process.exit(64);
91
+ }
92
+ return args[index + 1];
93
+ }
94
+
95
+ const service = readFlag('-s');
96
+ const account = readFlag('-a');
97
+ const key = service + '\\u0000' + account;
98
+ const db = loadDb();
99
+
100
+ switch (command) {
101
+ case 'add-generic-password': {
102
+ const secretHex = readFlag('-X');
103
+ db[key] = Buffer.from(secretHex, 'hex').toString('utf8');
104
+ saveDb(db);
105
+ process.exit(0);
106
+ }
107
+ case 'find-generic-password': {
108
+ if (!(key in db)) {
109
+ console.error('The specified item could not be found in the keychain.');
110
+ process.exit(44);
111
+ }
112
+ process.stdout.write(String(db[key]));
113
+ process.exit(0);
114
+ }
115
+ case 'delete-generic-password': {
116
+ if (!(key in db)) {
117
+ console.error('The specified item could not be found in the keychain.');
118
+ process.exit(44);
119
+ }
120
+ delete db[key];
121
+ saveDb(db);
122
+ process.exit(0);
123
+ }
124
+ default:
125
+ console.error('isolated security shim does not implement: ' + command);
126
+ process.exit(64);
127
+ }
128
+ `,
129
+ );
130
+
131
+ for (const commandName of ['sudo', 'launchctl']) {
132
+ writeExecutable(
133
+ path.join(binDir, commandName),
134
+ `#!/bin/sh
135
+ echo "isolated test harness blocked real ${commandName}; inject a test double if this path is expected" >&2
136
+ exit 99
137
+ `,
138
+ );
139
+ }
140
+
141
+ const child = spawn(commandArgs[0], commandArgs.slice(1), {
142
+ stdio: 'inherit',
143
+ env: {
144
+ ...process.env,
145
+ HOME: homeDir,
146
+ WLFI_HOME: wlfiHome,
147
+ TMPDIR: tmpDir,
148
+ XDG_CACHE_HOME: xdgCacheHome,
149
+ XDG_CONFIG_HOME: xdgConfigHome,
150
+ PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ''}`,
151
+ WLFI_TEST_FAKE_KEYCHAIN_ROOT: fakeKeychainRoot,
152
+ WLFI_TEST_SANDBOX_ROOT: sandboxRoot,
153
+ WLFI_TEST_ISOLATED: '1',
154
+ ...(cargoHome ? { CARGO_HOME: cargoHome } : {}),
155
+ ...(rustupHome ? { RUSTUP_HOME: rustupHome } : {}),
156
+ },
157
+ });
158
+
159
+ let cleanedUp = false;
160
+
161
+ function cleanup() {
162
+ if (cleanedUp) {
163
+ return;
164
+ }
165
+ cleanedUp = true;
166
+
167
+ if (keepSandbox) {
168
+ console.error(`kept test sandbox: ${sandboxRoot}`);
169
+ return;
170
+ }
171
+
172
+ fs.rmSync(sandboxRoot, { recursive: true, force: true });
173
+ try {
174
+ fs.rmdirSync(sandboxBaseDir);
175
+ } catch (error) {
176
+ if (!error || typeof error !== 'object') {
177
+ throw error;
178
+ }
179
+ if (!('code' in error) || (error.code !== 'ENOENT' && error.code !== 'ENOTEMPTY')) {
180
+ throw error;
181
+ }
182
+ }
183
+ }
184
+
185
+ const signalExitCodes = {
186
+ SIGHUP: 129,
187
+ SIGINT: 130,
188
+ SIGTERM: 143,
189
+ };
190
+
191
+ for (const signal of Object.keys(signalExitCodes)) {
192
+ process.on(signal, () => {
193
+ if (!child.killed) {
194
+ child.kill(signal);
195
+ }
196
+ });
197
+ }
198
+
199
+ child.on('error', (error) => {
200
+ cleanup();
201
+ fail(`failed to start isolated test command: ${error.message}`);
202
+ });
203
+
204
+ child.on('exit', (code, signal) => {
205
+ cleanup();
206
+ if (signal) {
207
+ process.exit(signalExitCodes[signal] ?? 1);
208
+ }
209
+ process.exit(code ?? 1);
210
+ });