@gengjiawen/os-init 1.7.0 → 1.8.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.0](https://github.com/gengjiawen/os-init/compare/v1.7.1...v1.8.0) (2025-11-10)
4
+
5
+
6
+ ### Features
7
+
8
+ * bump andoid tool version ([e54ef83](https://github.com/gengjiawen/os-init/commit/e54ef835b9a4baa03f89cfca651b0d032364734b))
9
+ * refine default sdk home ([690d4ef](https://github.com/gengjiawen/os-init/commit/690d4efdeec1b96da2296e1275cfa2898382fea2))
10
+
11
+ ## [1.7.1](https://github.com/gengjiawen/os-init/compare/v1.7.0...v1.7.1) (2025-11-09)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * android CI ([#12](https://github.com/gengjiawen/os-init/issues/12)) ([535b011](https://github.com/gengjiawen/os-init/commit/535b0115b513902ceb6fd9ef98bcb8338ac9972e))
17
+
3
18
  ## [1.7.0](https://github.com/gengjiawen/os-init/compare/v1.6.0...v1.7.0) (2025-11-09)
4
19
 
5
20
 
package/README.md CHANGED
@@ -2,41 +2,10 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/%40gengjiawen%2Fos-init)](https://www.npmjs.com/package/@gengjiawen/os-init)
4
4
 
5
- A CLI tool to quickly configure AI development tools and environments.
5
+ A CLI tool to quickly configure development tools and environments.
6
6
 
7
7
  ## Usage
8
8
 
9
- ### Configure Claude Code
10
-
11
- ```bash
12
- pnpx @gengjiawen/os-init set-cc <API_KEY>
13
- ```
14
-
15
- Configures Claude Code Router with your API key. This command will:
16
- - Write `~/.claude-code-router/config.json`
17
- - Write `~/.claude/settings.json`
18
- - Install global tools: `@anthropic-ai/claude-code`, `@musistudio/claude-code-router`
19
-
20
- ### Configure Codex CLI
21
-
22
- ```bash
23
- pnpx @gengjiawen/os-init set-codex <API_KEY>
24
- ```
25
-
26
- Configures Codex CLI with your API key. This command will:
27
- - Write `~/.codex/config.toml`
28
- - Write `~/.codex/auth.json`
29
- - Install global tool: `@openai/codex`
30
-
31
- ### Configure Raycast AI
32
-
33
- ```bash
34
- pnpx @gengjiawen/os-init set-raycast-ai <API_KEY>
35
- ```
36
-
37
- Configures Raycast AI providers with your API key. This command will:
38
- - Write `~/.config/raycast/ai/providers.yaml`
39
-
40
9
  ### Setup Dev Environment
41
10
 
42
11
  ```bash
@@ -44,12 +13,14 @@ pnpx @gengjiawen/os-init set-dev <SSH_PUBLIC_KEY>
44
13
  ```
45
14
 
46
15
  Sets up a Docker-based development environment with SSH access. This command will:
16
+
47
17
  - Copy `dev-setup` directory to your current directory (or specify with `-t, --target <dir>`)
48
18
  - Configure SSH public key in Dockerfile
49
19
  - Automatically run `docker-compose build && docker-compose up -d` (if docker-compose is available)
50
20
  - Display SSH connection command with your local IP address
51
21
 
52
22
  Example:
23
+
53
24
  ```bash
54
25
  pnpx @gengjiawen/os-init set-dev "ssh-rsa AAAAB3NzaC1yc2..."
55
26
  ```
@@ -57,10 +28,11 @@ pnpx @gengjiawen/os-init set-dev "ssh-rsa AAAAB3NzaC1yc2..."
57
28
  ### Setup Android Development Environment
58
29
 
59
30
  ```bash
60
- pnpx @gengjiawen/os-init set-android [options]
31
+ pnpx @gengjiawen/os-init set-android
61
32
  ```
62
33
 
63
34
  Sets up a complete Android development environment on macOS and Linux. This command will:
35
+
64
36
  - Install Android SDK to `~/Android` (or custom path with `--android-home <path>`)
65
37
  - Download and install Android SDK Command-line Tools, Platform Tools, Build Tools, CMake, and NDK
66
38
  - Automatically accept Android SDK licenses
@@ -69,10 +41,8 @@ Sets up a complete Android development environment on macOS and Linux. This comm
69
41
  - No sudo access required
70
42
 
71
43
  Example:
72
- ```bash
73
- # Basic installation
74
- pnpx @gengjiawen/os-init set-android
75
44
 
45
+ ```bash
76
46
  # Custom installation path
77
47
  pnpx @gengjiawen/os-init set-android --android-home ~/my-android-sdk
78
48
 
@@ -82,4 +52,38 @@ pnpx @gengjiawen/os-init set-android --skip-env-vars
82
52
 
83
53
  ---
84
54
 
55
+ ### Configure Claude Code
56
+
57
+ ```bash
58
+ pnpx @gengjiawen/os-init set-cc <API_KEY>
59
+ ```
60
+
61
+ Configures Claude Code Router with your API key. This command will:
62
+
63
+ - Write `~/.claude-code-router/config.json`
64
+ - Write `~/.claude/settings.json`
65
+ - Install global tools: `@anthropic-ai/claude-code`, `@musistudio/claude-code-router`
66
+
67
+ ### Configure Codex CLI
68
+
69
+ ```bash
70
+ pnpx @gengjiawen/os-init set-codex <API_KEY>
71
+ ```
72
+
73
+ Configures Codex CLI with your API key. This command will:
74
+
75
+ - Write `~/.codex/config.toml`
76
+ - Write `~/.codex/auth.json`
77
+ - Install global tool: `@openai/codex`
78
+
79
+ ### Configure Raycast AI
80
+
81
+ ```bash
82
+ pnpx @gengjiawen/os-init set-raycast-ai <API_KEY>
83
+ ```
84
+
85
+ Configures Raycast AI providers with your API key. This command will:
86
+
87
+ - Write `~/.config/raycast/ai/providers.yaml`
88
+
85
89
  Project generated by [gengjiawen/ts-scaffold](https://github.com/gengjiawen/ts-scaffold)
@@ -5,16 +5,34 @@ const fs = require("fs");
5
5
  const path = require("path");
6
6
  const os = require("os");
7
7
  const execa_1 = require("execa");
8
- const unzip_url_1 = require("@compilets/unzip-url");
8
+ const unzip_url_1 = require("@gengjiawen/unzip-url");
9
+ const fish_shell_utils_1 = require("./fish-shell-utils");
9
10
  const ANDROID_CONFIG = {
10
11
  sdkVersion: '11076708',
11
12
  platformVersion: 'android-36',
12
- buildToolsVersion: '36.0.0',
13
- cmakeVersion: '3.30.5',
13
+ buildToolsVersion: '36.1.0',
14
+ cmakeVersion: '4.1.2',
14
15
  ndkVersion: '29.0.14206865',
15
16
  };
16
17
  function getDefaultAndroidHome() {
17
- return path.join(os.homedir(), 'Android');
18
+ const { ANDROID_SDK_ROOT, ANDROID_HOME, LOCALAPPDATA } = process.env;
19
+ if (ANDROID_SDK_ROOT && ANDROID_SDK_ROOT.trim())
20
+ return ANDROID_SDK_ROOT;
21
+ if (ANDROID_HOME && ANDROID_HOME.trim())
22
+ return ANDROID_HOME;
23
+ const home = os.homedir();
24
+ switch (process.platform) {
25
+ case 'darwin':
26
+ return path.join(home, 'Library', 'Android', 'sdk');
27
+ case 'linux':
28
+ return path.join(home, 'Android', 'Sdk');
29
+ case 'win32':
30
+ return LOCALAPPDATA
31
+ ? path.join(LOCALAPPDATA, 'Android', 'Sdk')
32
+ : path.join(home, 'AppData', 'Local', 'Android', 'Sdk');
33
+ default:
34
+ return path.join(home, 'Android', 'Sdk');
35
+ }
18
36
  }
19
37
  function getSdkDownloadUrl(sdkVersion) {
20
38
  const platform = os.platform();
@@ -30,11 +48,12 @@ function getSdkDownloadUrl(sdkVersion) {
30
48
  }
31
49
  function getAndroidEnvVars(androidHome, ndkVersion) {
32
50
  return `
33
- # Android development environment
51
+ # ===== Android development environment - START (2025-11-09) =====
34
52
  export ANDROID_HOME=${androidHome}
35
53
  export ANDROID_SDK_ROOT=\${ANDROID_HOME}
36
54
  export ANDROID_NDK_HOME=\${ANDROID_HOME}/ndk/${ndkVersion}
37
- export PATH=\${ANDROID_HOME}/cmdline-tools/latest/bin:\${ANDROID_HOME}/emulator:\${ANDROID_HOME}/platform-tools:\${ANDROID_HOME}/tools:\${ANDROID_HOME}/tools/bin:\${PATH}
55
+ export PATH=\${ANDROID_HOME}/cmdline-tools/bin:\${ANDROID_HOME}/cmdline-tools/latest/bin:\${ANDROID_HOME}/emulator:\${ANDROID_HOME}/platform-tools:\${ANDROID_HOME}/tools:\${ANDROID_HOME}/tools/bin:\${PATH}
56
+ # ===== Android development environment - END =====
38
57
  `;
39
58
  }
40
59
  function getShellRcFile() {
@@ -58,12 +77,20 @@ function hasAndroidEnvVars(rcFile) {
58
77
  return content.includes('ANDROID_HOME');
59
78
  }
60
79
  function appendEnvVarsToShellConfig(rcFile, envVars) {
61
- const dir = path.dirname(rcFile);
62
- if (!fs.existsSync(dir)) {
63
- fs.mkdirSync(dir, { recursive: true });
80
+ const shell = process.env.SHELL || '';
81
+ const homeDir = os.homedir();
82
+ const bashrcFile = path.join(homeDir, '.bashrc');
83
+ if (!fs.existsSync(bashrcFile) ||
84
+ !fs.readFileSync(bashrcFile, 'utf-8').includes('ANDROID_HOME')) {
85
+ fs.appendFileSync(bashrcFile, envVars);
86
+ console.log(`Environment variables added to: ${bashrcFile}`);
87
+ }
88
+ else {
89
+ console.log(`Environment variables already exist in: ${bashrcFile}`);
90
+ }
91
+ if (shell.includes('fish')) {
92
+ (0, fish_shell_utils_1.appendFishImportScript)();
64
93
  }
65
- fs.appendFileSync(rcFile, envVars);
66
- console.log(`Environment variables added to: ${rcFile}`);
67
94
  }
68
95
  async function setupAndroidEnvironment(options) {
69
96
  const androidHome = options?.androidHome || getDefaultAndroidHome();
@@ -85,32 +112,34 @@ async function setupAndroidEnvironment(options) {
85
112
  }
86
113
  console.log('\nDownloading and setting up Android SDK...');
87
114
  const sdkUrl = getSdkDownloadUrl(sdkVersion);
115
+ const cmdlineToolsPath = path.join(androidHome, 'cmdline-tools');
116
+ const latestPath = path.join(cmdlineToolsPath, 'latest');
117
+ const latestBin = path.join(latestPath, 'bin');
88
118
  try {
89
- const cmdlineToolsPath = path.join(androidHome, 'cmdline-tools');
90
- if (!fs.existsSync(cmdlineToolsPath)) {
91
- fs.mkdirSync(cmdlineToolsPath, { recursive: true });
92
- }
93
- console.log(`Downloading and extracting from: ${sdkUrl}`);
94
- await (0, unzip_url_1.unzip)(sdkUrl, cmdlineToolsPath);
95
- const tempToolsPath = path.join(cmdlineToolsPath, 'cmdline-tools');
96
- const latestToolsPath = path.join(cmdlineToolsPath, 'latest');
97
- if (fs.existsSync(tempToolsPath)) {
98
- fs.renameSync(tempToolsPath, latestToolsPath);
119
+ if (!fs.existsSync(latestBin)) {
120
+ console.log(`Downloading and extracting from: ${sdkUrl} to ${androidHome}`);
121
+ await (0, unzip_url_1.unzip)(sdkUrl, cmdlineToolsPath);
122
+ const tmp_toolchain = path.join(cmdlineToolsPath, 'cmdline-tools');
123
+ fs.renameSync(tmp_toolchain, latestPath);
124
+ console.log('Android SDK extracted successfully');
99
125
  }
100
- console.log('Android SDK extracted successfully');
101
126
  }
102
127
  catch (error) {
103
128
  throw new Error(`Failed to download/extract Android SDK: ${error instanceof Error ? error.message : String(error)}`);
104
129
  }
130
+ const sdkmanagerBinary = path.join(latestBin, 'sdkmanager');
105
131
  const sdkmanagerEnv = {
106
132
  ...process.env,
107
133
  ANDROID_HOME: androidHome,
108
134
  ANDROID_SDK_ROOT: androidHome,
109
- PATH: `${androidHome}/cmdline-tools/latest/bin:${process.env.PATH}`,
135
+ PATH: `${latestBin}:${process.env.PATH}`,
110
136
  };
111
137
  console.log('\nAccepting Android SDK licenses...');
112
138
  try {
113
- await (0, execa_1.execa)('bash', ['-c', 'yes | sdkmanager --licenses'], {
139
+ await (0, execa_1.execa)('bash', [
140
+ '-c',
141
+ `yes | "${sdkmanagerBinary}" --sdk_root=${androidHome} --licenses`,
142
+ ], {
114
143
  env: sdkmanagerEnv,
115
144
  stdio: 'inherit',
116
145
  });
@@ -127,8 +156,7 @@ async function setupAndroidEnvironment(options) {
127
156
  `ndk;${ndkVersion}`,
128
157
  ];
129
158
  try {
130
- const componentsList = components.join(' ');
131
- await (0, execa_1.execa)('bash', ['-c', `yes | sdkmanager ${componentsList}`], {
159
+ await (0, execa_1.execa)(sdkmanagerBinary, [`--sdk_root=${androidHome}`, ...components], {
132
160
  env: sdkmanagerEnv,
133
161
  stdio: 'inherit',
134
162
  });
@@ -0,0 +1,2 @@
1
+ export declare function getFishImportBashExports(): string;
2
+ export declare function appendFishImportScript(): void;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFishImportBashExports = getFishImportBashExports;
4
+ exports.appendFishImportScript = appendFishImportScript;
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const os = require("os");
8
+ function getFishImportBashExports() {
9
+ return `
10
+ # ===== Import environment variables from .bashrc - START (2025-11-09) =====
11
+ egrep "^export " ~/.bashrc | while read e
12
+ set var (echo $e | sed -E "s/^export ([A-Za-z_0-9]+)=(.*)\\$/\\1/")
13
+ set value (echo $e | sed -E "s/^export ([A-Za-z_0-9]+)=(.*)\\$/\\2/")
14
+
15
+ # remove surrounding quotes if existing
16
+ set value (echo $value | sed -E "s/^\\"(.*)\\"\\$/\\1/")
17
+
18
+ # convert Bash \${VAR} to fish {\$VAR} before eval
19
+ set value (printf '%s' "$value" | sed -E 's/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/{$\\1}/g')
20
+
21
+ if test $var = "PATH"
22
+ # replace ":" by spaces. this is how PATH looks for Fish
23
+ set value (echo $value | sed -E "s/:/ /g")
24
+
25
+ # use eval because we need to expand the value
26
+ eval set -xg $var $value
27
+
28
+ continue
29
+ end
30
+
31
+ # evaluate variables. we can use eval because we most likely just used "$var"
32
+ set value (eval echo $value)
33
+
34
+ #echo "set -xg '$var' '$value' (via '$e')"
35
+ set -xg $var $value
36
+ end
37
+ # ===== Import environment variables from .bashrc - END =====
38
+ `;
39
+ }
40
+ function appendFishImportScript() {
41
+ const homeDir = os.homedir();
42
+ const fishConfigPath = path.join(homeDir, '.config', 'fish', 'config.fish');
43
+ if (!fs.existsSync(fishConfigPath)) {
44
+ console.log('Fish shell config not found, skipping fish setup');
45
+ return;
46
+ }
47
+ const configDir = path.dirname(fishConfigPath);
48
+ if (!fs.existsSync(configDir)) {
49
+ fs.mkdirSync(configDir, { recursive: true });
50
+ }
51
+ const fishContent = fs.existsSync(fishConfigPath)
52
+ ? fs.readFileSync(fishConfigPath, 'utf-8')
53
+ : '';
54
+ if (!fishContent.includes('Import environment variables from .bashrc - START')) {
55
+ const fishScript = getFishImportBashExports();
56
+ fs.appendFileSync(fishConfigPath, fishScript);
57
+ console.log(`Fish import script added to: ${fishConfigPath}`);
58
+ }
59
+ else {
60
+ console.log(`Fish import script already exists in: ${fishConfigPath}`);
61
+ }
62
+ }
@@ -2,20 +2,39 @@ import * as fs from 'fs'
2
2
  import * as path from 'path'
3
3
  import * as os from 'os'
4
4
  import { execa } from 'execa'
5
- import { unzip } from '@compilets/unzip-url'
5
+ import { unzip } from '@gengjiawen/unzip-url'
6
+ import { appendFishImportScript } from './fish-shell-utils'
6
7
 
7
8
  /** Android SDK configuration */
8
9
  const ANDROID_CONFIG = {
9
10
  sdkVersion: '11076708',
10
11
  platformVersion: 'android-36',
11
- buildToolsVersion: '36.0.0',
12
- cmakeVersion: '3.30.5',
12
+ buildToolsVersion: '36.1.0',
13
+ cmakeVersion: '4.1.2',
13
14
  ndkVersion: '29.0.14206865',
14
15
  } as const
15
16
 
16
17
  /** Get default Android home directory */
17
18
  function getDefaultAndroidHome(): string {
18
- return path.join(os.homedir(), 'Android')
19
+ const { ANDROID_SDK_ROOT, ANDROID_HOME, LOCALAPPDATA } = process.env
20
+
21
+ if (ANDROID_SDK_ROOT && ANDROID_SDK_ROOT.trim()) return ANDROID_SDK_ROOT
22
+ if (ANDROID_HOME && ANDROID_HOME.trim()) return ANDROID_HOME
23
+
24
+ const home = os.homedir()
25
+ switch (process.platform) {
26
+ case 'darwin':
27
+ return path.join(home, 'Library', 'Android', 'sdk')
28
+ case 'linux':
29
+ return path.join(home, 'Android', 'Sdk')
30
+ case 'win32':
31
+ return LOCALAPPDATA
32
+ ? path.join(LOCALAPPDATA, 'Android', 'Sdk')
33
+ : path.join(home, 'AppData', 'Local', 'Android', 'Sdk')
34
+ default:
35
+ // Reasonable fallback for other POSIX-like environments
36
+ return path.join(home, 'Android', 'Sdk')
37
+ }
19
38
  }
20
39
 
21
40
  /** Get SDK download URL based on platform */
@@ -35,11 +54,12 @@ function getSdkDownloadUrl(sdkVersion: string): string {
35
54
  /** Get Android environment variables */
36
55
  function getAndroidEnvVars(androidHome: string, ndkVersion: string): string {
37
56
  return `
38
- # Android development environment
57
+ # ===== Android development environment - START (2025-11-09) =====
39
58
  export ANDROID_HOME=${androidHome}
40
59
  export ANDROID_SDK_ROOT=\${ANDROID_HOME}
41
60
  export ANDROID_NDK_HOME=\${ANDROID_HOME}/ndk/${ndkVersion}
42
- export PATH=\${ANDROID_HOME}/cmdline-tools/latest/bin:\${ANDROID_HOME}/emulator:\${ANDROID_HOME}/platform-tools:\${ANDROID_HOME}/tools:\${ANDROID_HOME}/tools/bin:\${PATH}
61
+ export PATH=\${ANDROID_HOME}/cmdline-tools/bin:\${ANDROID_HOME}/cmdline-tools/latest/bin:\${ANDROID_HOME}/emulator:\${ANDROID_HOME}/platform-tools:\${ANDROID_HOME}/tools:\${ANDROID_HOME}/tools/bin:\${PATH}
62
+ # ===== Android development environment - END =====
43
63
  `
44
64
  }
45
65
 
@@ -68,13 +88,25 @@ function hasAndroidEnvVars(rcFile: string): boolean {
68
88
 
69
89
  /** Append Android environment variables to shell config */
70
90
  function appendEnvVarsToShellConfig(rcFile: string, envVars: string): void {
71
- const dir = path.dirname(rcFile)
72
- if (!fs.existsSync(dir)) {
73
- fs.mkdirSync(dir, { recursive: true })
91
+ const shell = process.env.SHELL || ''
92
+ const homeDir = os.homedir()
93
+ const bashrcFile = path.join(homeDir, '.bashrc')
94
+
95
+ // Write to bashrc
96
+ if (
97
+ !fs.existsSync(bashrcFile) ||
98
+ !fs.readFileSync(bashrcFile, 'utf-8').includes('ANDROID_HOME')
99
+ ) {
100
+ fs.appendFileSync(bashrcFile, envVars)
101
+ console.log(`Environment variables added to: ${bashrcFile}`)
102
+ } else {
103
+ console.log(`Environment variables already exist in: ${bashrcFile}`)
74
104
  }
75
105
 
76
- fs.appendFileSync(rcFile, envVars)
77
- console.log(`Environment variables added to: ${rcFile}`)
106
+ // For fish shell, always write to bashrc first, then add import script to fish config
107
+ if (shell.includes('fish')) {
108
+ appendFishImportScript()
109
+ }
78
110
  }
79
111
 
80
112
  /** Setup Android development environment */
@@ -123,46 +155,49 @@ export async function setupAndroidEnvironment(options?: {
123
155
  console.log('\nDownloading and setting up Android SDK...')
124
156
  const sdkUrl = getSdkDownloadUrl(sdkVersion)
125
157
 
126
- try {
127
- // Create cmdline-tools directory
128
- const cmdlineToolsPath = path.join(androidHome, 'cmdline-tools')
129
- if (!fs.existsSync(cmdlineToolsPath)) {
130
- fs.mkdirSync(cmdlineToolsPath, { recursive: true })
131
- }
158
+ const cmdlineToolsPath = path.join(androidHome, 'cmdline-tools')
159
+ const latestPath = path.join(cmdlineToolsPath, 'latest')
160
+ const latestBin = path.join(latestPath, 'bin')
132
161
 
162
+ try {
133
163
  // Download and extract SDK using unzip-url
134
- console.log(`Downloading and extracting from: ${sdkUrl}`)
135
- await unzip(sdkUrl, cmdlineToolsPath)
136
-
137
- // Move cmdline-tools to latest
138
- const tempToolsPath = path.join(cmdlineToolsPath, 'cmdline-tools')
139
- const latestToolsPath = path.join(cmdlineToolsPath, 'latest')
140
- if (fs.existsSync(tempToolsPath)) {
141
- fs.renameSync(tempToolsPath, latestToolsPath)
164
+ if (!fs.existsSync(latestBin)) {
165
+ console.log(
166
+ `Downloading and extracting from: ${sdkUrl} to ${androidHome}`
167
+ )
168
+ await unzip(sdkUrl, cmdlineToolsPath)
169
+ const tmp_toolchain = path.join(cmdlineToolsPath, 'cmdline-tools')
170
+ fs.renameSync(tmp_toolchain, latestPath)
171
+ console.log('Android SDK extracted successfully')
142
172
  }
143
-
144
- console.log('Android SDK extracted successfully')
145
173
  } catch (error) {
146
174
  throw new Error(
147
175
  `Failed to download/extract Android SDK: ${error instanceof Error ? error.message : String(error)}`
148
176
  )
149
177
  }
178
+ const sdkmanagerBinary = path.join(latestBin, 'sdkmanager')
150
179
 
151
- // Set environment variables for sdkmanager
152
180
  const sdkmanagerEnv = {
153
181
  ...process.env,
154
182
  ANDROID_HOME: androidHome,
155
183
  ANDROID_SDK_ROOT: androidHome,
156
- PATH: `${androidHome}/cmdline-tools/latest/bin:${process.env.PATH}`,
184
+ PATH: `${latestBin}:${process.env.PATH}`,
157
185
  }
158
186
 
159
187
  // Accept licenses
160
188
  console.log('\nAccepting Android SDK licenses...')
161
189
  try {
162
- await execa('bash', ['-c', 'yes | sdkmanager --licenses'], {
163
- env: sdkmanagerEnv,
164
- stdio: 'inherit',
165
- })
190
+ await execa(
191
+ 'bash',
192
+ [
193
+ '-c',
194
+ `yes | "${sdkmanagerBinary}" --sdk_root=${androidHome} --licenses`,
195
+ ],
196
+ {
197
+ env: sdkmanagerEnv,
198
+ stdio: 'inherit',
199
+ }
200
+ )
166
201
  } catch (error) {
167
202
  console.warn(
168
203
  'Warning: License acceptance may have failed, but continuing...'
@@ -180,11 +215,14 @@ export async function setupAndroidEnvironment(options?: {
180
215
  ]
181
216
 
182
217
  try {
183
- const componentsList = components.join(' ')
184
- await execa('bash', ['-c', `yes | sdkmanager ${componentsList}`], {
185
- env: sdkmanagerEnv,
186
- stdio: 'inherit',
187
- })
218
+ await execa(
219
+ sdkmanagerBinary,
220
+ [`--sdk_root=${androidHome}`, ...components],
221
+ {
222
+ env: sdkmanagerEnv,
223
+ stdio: 'inherit',
224
+ }
225
+ )
188
226
  console.log('Android SDK components installed successfully')
189
227
  } catch (error) {
190
228
  throw new Error(
@@ -0,0 +1,75 @@
1
+ import * as fs from 'fs'
2
+ import * as path from 'path'
3
+ import * as os from 'os'
4
+
5
+ /** Get Fish shell script to import bash exports */
6
+ export function getFishImportBashExports(): string {
7
+ return `
8
+ # ===== Import environment variables from .bashrc - START (2025-11-09) =====
9
+ egrep "^export " ~/.bashrc | while read e
10
+ set var (echo $e | sed -E "s/^export ([A-Za-z_0-9]+)=(.*)\\$/\\1/")
11
+ set value (echo $e | sed -E "s/^export ([A-Za-z_0-9]+)=(.*)\\$/\\2/")
12
+
13
+ # remove surrounding quotes if existing
14
+ set value (echo $value | sed -E "s/^\\"(.*)\\"\\$/\\1/")
15
+
16
+ # convert Bash \${VAR} to fish {\$VAR} before eval
17
+ set value (printf '%s' "$value" | sed -E 's/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/{$\\1}/g')
18
+
19
+ if test $var = "PATH"
20
+ # replace ":" by spaces. this is how PATH looks for Fish
21
+ set value (echo $value | sed -E "s/:/ /g")
22
+
23
+ # use eval because we need to expand the value
24
+ eval set -xg $var $value
25
+
26
+ continue
27
+ end
28
+
29
+ # evaluate variables. we can use eval because we most likely just used "$var"
30
+ set value (eval echo $value)
31
+
32
+ #echo "set -xg '$var' '$value' (via '$e')"
33
+ set -xg $var $value
34
+ end
35
+ # ===== Import environment variables from .bashrc - END =====
36
+ `
37
+ }
38
+
39
+ /**
40
+ * Append bash import script to Fish shell config file
41
+ * This function adds the import script to fish config.fish to automatically
42
+ * source environment variables from .bashrc
43
+ * It detects fish shell by checking if ~/.config/fish/config.fish exists
44
+ */
45
+ export function appendFishImportScript(): void {
46
+ const homeDir = os.homedir()
47
+ const fishConfigPath = path.join(homeDir, '.config', 'fish', 'config.fish')
48
+
49
+ // Detect if fish shell exists by checking config file
50
+ if (!fs.existsSync(fishConfigPath)) {
51
+ console.log('Fish shell config not found, skipping fish setup')
52
+ return
53
+ }
54
+
55
+ // Ensure fish config directory exists
56
+ const configDir = path.dirname(fishConfigPath)
57
+ if (!fs.existsSync(configDir)) {
58
+ fs.mkdirSync(configDir, { recursive: true })
59
+ }
60
+
61
+ // Check if import script already exists
62
+ const fishContent = fs.existsSync(fishConfigPath)
63
+ ? fs.readFileSync(fishConfigPath, 'utf-8')
64
+ : ''
65
+
66
+ if (
67
+ !fishContent.includes('Import environment variables from .bashrc - START')
68
+ ) {
69
+ const fishScript = getFishImportBashExports()
70
+ fs.appendFileSync(fishConfigPath, fishScript)
71
+ console.log(`Fish import script added to: ${fishConfigPath}`)
72
+ } else {
73
+ console.log(`Fish import script already exists in: ${fishConfigPath}`)
74
+ }
75
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gengjiawen/os-init",
3
3
  "private": false,
4
- "version": "1.7.0",
4
+ "version": "1.8.0",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "postbuild": "cpy '**/*' '!**/*.ts' ../build/ --cwd=libs --parents"
20
20
  },
21
21
  "dependencies": {
22
- "@compilets/unzip-url": "^1.0.0",
22
+ "@gengjiawen/unzip-url": "^1.0.0",
23
23
  "commander": "^12.1.0",
24
24
  "ip": "^2.0.1",
25
25
  "execa": "^8.0.1"