@gengjiawen/os-init 1.9.1 → 1.11.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 +17 -0
- package/README.md +24 -3
- package/bin/bin.js +17 -4
- package/build/android-setup.js +3 -5
- package/build/dev-setup.js +17 -0
- package/build/fish-shell-utils.js +4 -7
- package/build/index.d.ts +0 -1
- package/build/index.js +3 -33
- package/dev-setup/Dockerfile +2 -0
- package/dev-setup/docker-compose.yml +4 -0
- package/dev-setup/init.sh +40 -0
- package/dev-setup/sshd_config +3 -3
- package/libs/android-setup.ts +6 -5
- package/libs/dev-setup.ts +25 -0
- package/libs/fish-shell-utils.ts +6 -9
- package/libs/index.ts +4 -41
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.11.0](https://github.com/gengjiawen/os-init/compare/v1.10.0...v1.11.0) (2025-12-31)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* update model version in Codex config template from gpt-5.1 to gpt-5.2 ([56c36d3](https://github.com/gengjiawen/os-init/commit/56c36d3da5c3732100c090c4df3318b5cdd003cf))
|
|
9
|
+
|
|
10
|
+
## [1.10.0](https://github.com/gengjiawen/os-init/compare/v1.9.1...v1.10.0) (2025-12-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add command to set up Fish shell import script ([8418406](https://github.com/gengjiawen/os-init/commit/841840694783a1264733b7b8415cdb12d2bb21fa))
|
|
16
|
+
* add dev tun to setup ([a373417](https://github.com/gengjiawen/os-init/commit/a373417ca60f0f1554c85c06d73c017bc722fdf3))
|
|
17
|
+
* add ownership fix for gitpod user config and cache in init script ([dd3720c](https://github.com/gengjiawen/os-init/commit/dd3720cd0a57b603d1cc74179187669e3659ae82))
|
|
18
|
+
* persist ssh host keys via init script and mount gitconfig ([dbaabe9](https://github.com/gengjiawen/os-init/commit/dbaabe98ca44aa3e1cdb29deb12f700a4b559fb5))
|
|
19
|
+
|
|
3
20
|
## [1.9.1](https://github.com/gengjiawen/os-init/compare/v1.9.0...v1.9.1) (2025-11-26)
|
|
4
21
|
|
|
5
22
|
|
package/README.md
CHANGED
|
@@ -50,6 +50,28 @@ pnpx @gengjiawen/os-init set-android --android-home ~/my-android-sdk
|
|
|
50
50
|
pnpx @gengjiawen/os-init set-android --skip-env-vars
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
### Setup Fish Shell Import Script
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpx @gengjiawen/os-init set-fish
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Sets up Fish shell to automatically import environment variables from `.bashrc`. This command will:
|
|
60
|
+
|
|
61
|
+
- Detect if Fish shell is installed (by checking for `~/.config/fish/config.fish`)
|
|
62
|
+
- Add an import script to `~/.config/fish/config.fish` that reads environment variables from `~/.bashrc`
|
|
63
|
+
- Automatically convert Bash-style environment variable syntax to Fish shell syntax
|
|
64
|
+
- Handle PATH variables correctly (converting `:` separators to spaces)
|
|
65
|
+
- Skip if the import script already exists
|
|
66
|
+
|
|
67
|
+
This is useful when you have environment variables configured in `.bashrc` but want to use them in Fish shell without duplicating configuration.
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpx @gengjiawen/os-init set-fish
|
|
73
|
+
```
|
|
74
|
+
|
|
53
75
|
---
|
|
54
76
|
|
|
55
77
|
### Configure Claude Code
|
|
@@ -58,11 +80,10 @@ pnpx @gengjiawen/os-init set-android --skip-env-vars
|
|
|
58
80
|
pnpx @gengjiawen/os-init set-cc <API_KEY>
|
|
59
81
|
```
|
|
60
82
|
|
|
61
|
-
Configures Claude Code
|
|
83
|
+
Configures Claude Code with your API key. This command will:
|
|
62
84
|
|
|
63
|
-
- Write `~/.claude-code-router/config.json`
|
|
64
85
|
- Write `~/.claude/settings.json`
|
|
65
|
-
- Install global
|
|
86
|
+
- Install global tool: `@anthropic-ai/claude-code`
|
|
66
87
|
|
|
67
88
|
### Configure Codex CLI
|
|
68
89
|
|
package/bin/bin.js
CHANGED
|
@@ -10,12 +10,13 @@ const {
|
|
|
10
10
|
setupDevEnvironment,
|
|
11
11
|
setupAndroidEnvironment,
|
|
12
12
|
} = require('../build')
|
|
13
|
+
const { appendFishImportScript } = require('../build/fish-shell-utils')
|
|
13
14
|
|
|
14
15
|
const program = new Command()
|
|
15
16
|
|
|
16
17
|
program
|
|
17
18
|
.command('set-cc')
|
|
18
|
-
.description('setup
|
|
19
|
+
.description('setup Claude Code')
|
|
19
20
|
.argument('<apiKey>', 'API key to set')
|
|
20
21
|
.action(async (apiKey) => {
|
|
21
22
|
// Ensure apiKey is provided
|
|
@@ -25,8 +26,7 @@ program
|
|
|
25
26
|
return
|
|
26
27
|
}
|
|
27
28
|
try {
|
|
28
|
-
const {
|
|
29
|
-
console.log(`Claude router config written to: ${routerConfigPath}`)
|
|
29
|
+
const { settingsPath } = writeClaudeConfig(apiKey)
|
|
30
30
|
console.log(`Claude settings written to: ${settingsPath}`)
|
|
31
31
|
await installDeps()
|
|
32
32
|
} catch (err) {
|
|
@@ -34,7 +34,7 @@ program
|
|
|
34
34
|
process.exit(1)
|
|
35
35
|
}
|
|
36
36
|
console.log(
|
|
37
|
-
'Claude
|
|
37
|
+
'Claude Code is ready, use `claude` in terminal to start building'
|
|
38
38
|
)
|
|
39
39
|
})
|
|
40
40
|
|
|
@@ -128,4 +128,17 @@ program
|
|
|
128
128
|
}
|
|
129
129
|
})
|
|
130
130
|
|
|
131
|
+
program
|
|
132
|
+
.command('set-fish')
|
|
133
|
+
.description('setup Fish shell to import environment variables from .bashrc')
|
|
134
|
+
.action(async () => {
|
|
135
|
+
try {
|
|
136
|
+
appendFishImportScript()
|
|
137
|
+
console.log('\n✅ Fish shell import script setup completed!')
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error('Failed to setup Fish shell:', err.message)
|
|
140
|
+
process.exit(1)
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
131
144
|
program.parse(process.argv)
|
package/build/android-setup.js
CHANGED
|
@@ -52,7 +52,7 @@ function getAndroidEnvVars(androidHome, ndkVersion) {
|
|
|
52
52
|
export ANDROID_HOME=${androidHome}
|
|
53
53
|
export ANDROID_SDK_ROOT=\${ANDROID_HOME}
|
|
54
54
|
export ANDROID_NDK_HOME=\${ANDROID_HOME}/ndk/${ndkVersion}
|
|
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
|
|
55
|
+
export PATH=\${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
|
|
56
56
|
# ===== Android development environment - END =====
|
|
57
57
|
`;
|
|
58
58
|
}
|
|
@@ -77,7 +77,6 @@ function hasAndroidEnvVars(rcFile) {
|
|
|
77
77
|
return content.includes('ANDROID_HOME');
|
|
78
78
|
}
|
|
79
79
|
function appendEnvVarsToShellConfig(rcFile, envVars) {
|
|
80
|
-
const shell = process.env.SHELL || '';
|
|
81
80
|
const homeDir = os.homedir();
|
|
82
81
|
const bashrcFile = path.join(homeDir, '.bashrc');
|
|
83
82
|
if (!fs.existsSync(bashrcFile) ||
|
|
@@ -88,9 +87,7 @@ function appendEnvVarsToShellConfig(rcFile, envVars) {
|
|
|
88
87
|
else {
|
|
89
88
|
console.log(`Environment variables already exist in: ${bashrcFile}`);
|
|
90
89
|
}
|
|
91
|
-
|
|
92
|
-
(0, fish_shell_utils_1.appendFishImportScript)();
|
|
93
|
-
}
|
|
90
|
+
(0, fish_shell_utils_1.appendFishImportScript)();
|
|
94
91
|
}
|
|
95
92
|
async function setupAndroidEnvironment(options) {
|
|
96
93
|
const androidHome = options?.androidHome || getDefaultAndroidHome();
|
|
@@ -191,6 +188,7 @@ async function setupAndroidEnvironment(options) {
|
|
|
191
188
|
appendEnvVarsToShellConfig(shellRcFile, envVars);
|
|
192
189
|
envVarsAdded = true;
|
|
193
190
|
}
|
|
191
|
+
(0, fish_shell_utils_1.appendFishImportScript)();
|
|
194
192
|
console.log('\n📋 To activate the environment in your current shell, run:');
|
|
195
193
|
console.log(` source ${shellRcFile}`);
|
|
196
194
|
}
|
package/build/dev-setup.js
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require("fs");
|
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const execa_1 = require("execa");
|
|
7
7
|
const ip = require("ip");
|
|
8
|
+
const yaml = require("yaml");
|
|
8
9
|
function ensureDir(dirPath) {
|
|
9
10
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
10
11
|
}
|
|
@@ -53,6 +54,22 @@ async function setupDevEnvironment(sshPublicKey, targetDir) {
|
|
|
53
54
|
else {
|
|
54
55
|
throw new Error(`Dockerfile not found in ${targetPath}`);
|
|
55
56
|
}
|
|
57
|
+
const tunDevicePath = '/dev/net/tun';
|
|
58
|
+
const dockerComposePath = path.join(targetPath, 'docker-compose.yml');
|
|
59
|
+
if (fs.existsSync(tunDevicePath) && fs.existsSync(dockerComposePath)) {
|
|
60
|
+
const content = fs.readFileSync(dockerComposePath, 'utf-8');
|
|
61
|
+
const doc = yaml.parse(content);
|
|
62
|
+
if (doc.services) {
|
|
63
|
+
const serviceName = Object.keys(doc.services)[0];
|
|
64
|
+
const service = doc.services[serviceName];
|
|
65
|
+
if (!service.devices?.includes('/dev/net/tun:/dev/net/tun')) {
|
|
66
|
+
service.devices = service.devices || [];
|
|
67
|
+
service.devices.push('/dev/net/tun:/dev/net/tun');
|
|
68
|
+
fs.writeFileSync(dockerComposePath, yaml.stringify(doc));
|
|
69
|
+
console.log('Added /dev/net/tun device to docker-compose.yml');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
56
73
|
const hasDockerCompose = await commandExists('docker-compose');
|
|
57
74
|
if (hasDockerCompose) {
|
|
58
75
|
console.log('\n🐳 Docker Compose detected. Building and starting containers...');
|
|
@@ -40,17 +40,14 @@ end
|
|
|
40
40
|
function appendFishImportScript() {
|
|
41
41
|
const homeDir = os.homedir();
|
|
42
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
43
|
const configDir = path.dirname(fishConfigPath);
|
|
48
44
|
if (!fs.existsSync(configDir)) {
|
|
49
45
|
fs.mkdirSync(configDir, { recursive: true });
|
|
50
46
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
if (!fs.existsSync(fishConfigPath)) {
|
|
48
|
+
fs.writeFileSync(fishConfigPath, '', 'utf-8');
|
|
49
|
+
}
|
|
50
|
+
const fishContent = fs.readFileSync(fishConfigPath, 'utf-8');
|
|
54
51
|
if (!fishContent.includes('Import environment variables from .bashrc - START')) {
|
|
55
52
|
const fishScript = getFishImportBashExports();
|
|
56
53
|
fs.appendFileSync(fishConfigPath, fishScript);
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -10,31 +10,9 @@ const fs = require("fs");
|
|
|
10
10
|
const path = require("path");
|
|
11
11
|
const os = require("os");
|
|
12
12
|
const execa_1 = require("execa");
|
|
13
|
-
function getDefaultConfigDir() {
|
|
14
|
-
return path.join(os.homedir(), '.claude-code-router');
|
|
15
|
-
}
|
|
16
13
|
function ensureDir(dirPath) {
|
|
17
14
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
18
15
|
}
|
|
19
|
-
const DEFAULT_TEMPLATE = `{
|
|
20
|
-
"Providers": [
|
|
21
|
-
{
|
|
22
|
-
"name": "jw",
|
|
23
|
-
"api_base_url": "https://ai.gengjiawen.com/api/openai/v1/chat/completions",
|
|
24
|
-
"api_key": "API_KEY_PLACEHOLDER",
|
|
25
|
-
"models": ["code", "free"],
|
|
26
|
-
"transformer": {
|
|
27
|
-
"use": ["openrouter"]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
],
|
|
31
|
-
"Router": {
|
|
32
|
-
"default": "jw,code",
|
|
33
|
-
"background": "jw,code",
|
|
34
|
-
"think": "jw,code",
|
|
35
|
-
"longContext": "jw,code"
|
|
36
|
-
}
|
|
37
|
-
}`;
|
|
38
16
|
function getClaudeSettingsDir() {
|
|
39
17
|
return path.join(os.homedir(), '.claude');
|
|
40
18
|
}
|
|
@@ -54,17 +32,12 @@ const CLAUDE_SETTINGS_TEMPLATE = `{
|
|
|
54
32
|
}
|
|
55
33
|
}`;
|
|
56
34
|
function writeClaudeConfig(apiKey) {
|
|
57
|
-
const routerConfigDir = getDefaultConfigDir();
|
|
58
|
-
const routerConfigPath = path.join(routerConfigDir, 'config.json');
|
|
59
|
-
ensureDir(routerConfigDir);
|
|
60
|
-
const routerContent = DEFAULT_TEMPLATE.replace('API_KEY_PLACEHOLDER', apiKey);
|
|
61
|
-
fs.writeFileSync(routerConfigPath, routerContent);
|
|
62
35
|
const settingsDir = getClaudeSettingsDir();
|
|
63
36
|
const settingsPath = path.join(settingsDir, 'settings.json');
|
|
64
37
|
ensureDir(settingsDir);
|
|
65
38
|
const settingsContent = CLAUDE_SETTINGS_TEMPLATE.replace(/API_KEY_PLACEHOLDER/g, apiKey);
|
|
66
39
|
fs.writeFileSync(settingsPath, settingsContent);
|
|
67
|
-
return {
|
|
40
|
+
return { settingsPath };
|
|
68
41
|
}
|
|
69
42
|
async function commandExists(command) {
|
|
70
43
|
try {
|
|
@@ -79,10 +52,7 @@ async function commandExists(command) {
|
|
|
79
52
|
}
|
|
80
53
|
}
|
|
81
54
|
async function installDeps() {
|
|
82
|
-
const packages = [
|
|
83
|
-
'@anthropic-ai/claude-code',
|
|
84
|
-
'@musistudio/claude-code-router',
|
|
85
|
-
];
|
|
55
|
+
const packages = ['@anthropic-ai/claude-code'];
|
|
86
56
|
const usePnpm = await commandExists('pnpm');
|
|
87
57
|
if (usePnpm) {
|
|
88
58
|
console.log('pnpm detected. Installing dependencies with pnpm...');
|
|
@@ -98,7 +68,7 @@ function getCodexConfigDir() {
|
|
|
98
68
|
return path.join(os.homedir(), '.codex');
|
|
99
69
|
}
|
|
100
70
|
const CODEX_CONFIG_TOML_TEMPLATE = `model_provider = "jw"
|
|
101
|
-
model = "gpt-5.
|
|
71
|
+
model = "gpt-5.2"
|
|
102
72
|
model_reasoning_effort = "high"
|
|
103
73
|
disable_response_storage = true
|
|
104
74
|
preferred_auth_method = "apikey"
|
package/dev-setup/Dockerfile
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Generate SSH host keys in mounted volume to keep fingerprints stable across rebuilds.
|
|
5
|
+
HOST_KEY_DIR="/etc/ssh/host_keys"
|
|
6
|
+
|
|
7
|
+
mkdir -p "${HOST_KEY_DIR}"
|
|
8
|
+
|
|
9
|
+
for key_type in rsa ed25519; do
|
|
10
|
+
key_path="${HOST_KEY_DIR}/ssh_host_${key_type}_key"
|
|
11
|
+
if [ ! -f "${key_path}" ]; then
|
|
12
|
+
echo "Generating ${key_type} host key..."
|
|
13
|
+
ssh-keygen -q -N "" -t "${key_type}" -f "${key_path}"
|
|
14
|
+
else
|
|
15
|
+
echo "${key_type} host key already exists."
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
echo "Done. SSH host keys are ready."
|
|
20
|
+
|
|
21
|
+
# Fix ownership for gitpod user config/cache if needed
|
|
22
|
+
TARGET_UID=${TARGET_UID:-33333}
|
|
23
|
+
TARGET_GID=${TARGET_GID:-33333}
|
|
24
|
+
TARGET_HOME=/home/gitpod
|
|
25
|
+
|
|
26
|
+
fix_owner() {
|
|
27
|
+
local dir="$1"
|
|
28
|
+
[ -d "$dir" ] || return 0
|
|
29
|
+
local owner_uid owner_gid
|
|
30
|
+
owner_uid=$(stat -c %u "$dir")
|
|
31
|
+
owner_gid=$(stat -c %g "$dir")
|
|
32
|
+
if [ "$owner_uid" != "$TARGET_UID" ] || [ "$owner_gid" != "$TARGET_GID" ]; then
|
|
33
|
+
chown -R "${TARGET_UID}:${TARGET_GID}" "$dir"
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fix_owner "${TARGET_HOME}/.config"
|
|
38
|
+
fix_owner "${TARGET_HOME}/.cache"
|
|
39
|
+
|
|
40
|
+
exec /usr/sbin/sshd -D -e
|
package/dev-setup/sshd_config
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Port 22
|
|
2
2
|
Protocol 2
|
|
3
3
|
|
|
4
|
-
# Host keys are
|
|
5
|
-
HostKey /etc/ssh/ssh_host_rsa_key
|
|
6
|
-
HostKey /etc/ssh/ssh_host_ed25519_key
|
|
4
|
+
# Host keys are persisted in mounted volume to keep fingerprints stable
|
|
5
|
+
HostKey /etc/ssh/host_keys/ssh_host_rsa_key
|
|
6
|
+
HostKey /etc/ssh/host_keys/ssh_host_ed25519_key
|
|
7
7
|
|
|
8
8
|
SyslogFacility AUTHPRIV
|
|
9
9
|
LogLevel VERBOSE
|
package/libs/android-setup.ts
CHANGED
|
@@ -58,7 +58,7 @@ function getAndroidEnvVars(androidHome: string, ndkVersion: string): string {
|
|
|
58
58
|
export ANDROID_HOME=${androidHome}
|
|
59
59
|
export ANDROID_SDK_ROOT=\${ANDROID_HOME}
|
|
60
60
|
export ANDROID_NDK_HOME=\${ANDROID_HOME}/ndk/${ndkVersion}
|
|
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
|
|
61
|
+
export PATH=\${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
|
|
62
62
|
# ===== Android development environment - END =====
|
|
63
63
|
`
|
|
64
64
|
}
|
|
@@ -88,7 +88,6 @@ function hasAndroidEnvVars(rcFile: string): boolean {
|
|
|
88
88
|
|
|
89
89
|
/** Append Android environment variables to shell config */
|
|
90
90
|
function appendEnvVarsToShellConfig(rcFile: string, envVars: string): void {
|
|
91
|
-
const shell = process.env.SHELL || ''
|
|
92
91
|
const homeDir = os.homedir()
|
|
93
92
|
const bashrcFile = path.join(homeDir, '.bashrc')
|
|
94
93
|
|
|
@@ -104,9 +103,7 @@ function appendEnvVarsToShellConfig(rcFile: string, envVars: string): void {
|
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
// For fish shell, always write to bashrc first, then add import script to fish config
|
|
107
|
-
|
|
108
|
-
appendFishImportScript()
|
|
109
|
-
}
|
|
106
|
+
appendFishImportScript()
|
|
110
107
|
}
|
|
111
108
|
|
|
112
109
|
/** Setup Android development environment */
|
|
@@ -265,6 +262,10 @@ export async function setupAndroidEnvironment(options?: {
|
|
|
265
262
|
envVarsAdded = true
|
|
266
263
|
}
|
|
267
264
|
|
|
265
|
+
// Always check and add fish import script if fish shell exists
|
|
266
|
+
// This ensures fish users get the import script even if current shell is not fish
|
|
267
|
+
appendFishImportScript()
|
|
268
|
+
|
|
268
269
|
console.log('\n📋 To activate the environment in your current shell, run:')
|
|
269
270
|
console.log(` source ${shellRcFile}`)
|
|
270
271
|
}
|
package/libs/dev-setup.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'fs'
|
|
|
2
2
|
import * as path from 'path'
|
|
3
3
|
import { execa } from 'execa'
|
|
4
4
|
import * as ip from 'ip'
|
|
5
|
+
import * as yaml from 'yaml'
|
|
5
6
|
|
|
6
7
|
/** Ensure directory exists */
|
|
7
8
|
function ensureDir(dirPath: string): void {
|
|
@@ -68,6 +69,30 @@ export async function setupDevEnvironment(
|
|
|
68
69
|
throw new Error(`Dockerfile not found in ${targetPath}`)
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
// Check if /dev/net/tun exists and add devices to docker-compose.yml
|
|
73
|
+
const tunDevicePath = '/dev/net/tun'
|
|
74
|
+
const dockerComposePath = path.join(targetPath, 'docker-compose.yml')
|
|
75
|
+
if (fs.existsSync(tunDevicePath) && fs.existsSync(dockerComposePath)) {
|
|
76
|
+
const content = fs.readFileSync(dockerComposePath, 'utf-8')
|
|
77
|
+
const doc = yaml.parse(content) as {
|
|
78
|
+
services?: { [key: string]: { devices?: string[] } }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Find the first service and add devices
|
|
82
|
+
if (doc.services) {
|
|
83
|
+
const serviceName = Object.keys(doc.services)[0]
|
|
84
|
+
const service = doc.services[serviceName]
|
|
85
|
+
|
|
86
|
+
// Check if devices already contains /dev/net/tun
|
|
87
|
+
if (!service.devices?.includes('/dev/net/tun:/dev/net/tun')) {
|
|
88
|
+
service.devices = service.devices || []
|
|
89
|
+
service.devices.push('/dev/net/tun:/dev/net/tun')
|
|
90
|
+
fs.writeFileSync(dockerComposePath, yaml.stringify(doc))
|
|
91
|
+
console.log('Added /dev/net/tun device to docker-compose.yml')
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
71
96
|
// Check if docker-compose is available
|
|
72
97
|
const hasDockerCompose = await commandExists('docker-compose')
|
|
73
98
|
|
package/libs/fish-shell-utils.ts
CHANGED
|
@@ -46,22 +46,19 @@ export function appendFishImportScript(): void {
|
|
|
46
46
|
const homeDir = os.homedir()
|
|
47
47
|
const fishConfigPath = path.join(homeDir, '.config', 'fish', 'config.fish')
|
|
48
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
49
|
// Ensure fish config directory exists
|
|
56
50
|
const configDir = path.dirname(fishConfigPath)
|
|
57
51
|
if (!fs.existsSync(configDir)) {
|
|
58
52
|
fs.mkdirSync(configDir, { recursive: true })
|
|
59
53
|
}
|
|
60
54
|
|
|
55
|
+
// Create fish config file if it doesn't exist
|
|
56
|
+
if (!fs.existsSync(fishConfigPath)) {
|
|
57
|
+
fs.writeFileSync(fishConfigPath, '', 'utf-8')
|
|
58
|
+
}
|
|
59
|
+
|
|
61
60
|
// Check if import script already exists
|
|
62
|
-
const fishContent = fs.
|
|
63
|
-
? fs.readFileSync(fishConfigPath, 'utf-8')
|
|
64
|
-
: ''
|
|
61
|
+
const fishContent = fs.readFileSync(fishConfigPath, 'utf-8')
|
|
65
62
|
|
|
66
63
|
if (
|
|
67
64
|
!fishContent.includes('Import environment variables from .bashrc - START')
|
package/libs/index.ts
CHANGED
|
@@ -3,37 +3,11 @@ import * as path from 'path'
|
|
|
3
3
|
import * as os from 'os'
|
|
4
4
|
import { execa } from 'execa'
|
|
5
5
|
|
|
6
|
-
/** Return default configuration directory path */
|
|
7
|
-
function getDefaultConfigDir(): string {
|
|
8
|
-
return path.join(os.homedir(), '.claude-code-router')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
6
|
/** Ensure directory exists */
|
|
12
7
|
function ensureDir(dirPath: string): void {
|
|
13
8
|
fs.mkdirSync(dirPath, { recursive: true })
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
/** Template string used for simple string replacement */
|
|
17
|
-
const DEFAULT_TEMPLATE = `{
|
|
18
|
-
"Providers": [
|
|
19
|
-
{
|
|
20
|
-
"name": "jw",
|
|
21
|
-
"api_base_url": "https://ai.gengjiawen.com/api/openai/v1/chat/completions",
|
|
22
|
-
"api_key": "API_KEY_PLACEHOLDER",
|
|
23
|
-
"models": ["code", "free"],
|
|
24
|
-
"transformer": {
|
|
25
|
-
"use": ["openrouter"]
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
|
-
"Router": {
|
|
30
|
-
"default": "jw,code",
|
|
31
|
-
"background": "jw,code",
|
|
32
|
-
"think": "jw,code",
|
|
33
|
-
"longContext": "jw,code"
|
|
34
|
-
}
|
|
35
|
-
}`
|
|
36
|
-
|
|
37
11
|
/** Return Claude settings directory path */
|
|
38
12
|
function getClaudeSettingsDir(): string {
|
|
39
13
|
return path.join(os.homedir(), '.claude')
|
|
@@ -56,18 +30,10 @@ const CLAUDE_SETTINGS_TEMPLATE = `{
|
|
|
56
30
|
}
|
|
57
31
|
}`
|
|
58
32
|
|
|
59
|
-
/** Write Claude config files
|
|
33
|
+
/** Write Claude config files */
|
|
60
34
|
export function writeClaudeConfig(apiKey: string): {
|
|
61
|
-
routerConfigPath: string
|
|
62
35
|
settingsPath: string
|
|
63
36
|
} {
|
|
64
|
-
// Write claude-code-router config
|
|
65
|
-
const routerConfigDir = getDefaultConfigDir()
|
|
66
|
-
const routerConfigPath = path.join(routerConfigDir, 'config.json')
|
|
67
|
-
ensureDir(routerConfigDir)
|
|
68
|
-
const routerContent = DEFAULT_TEMPLATE.replace('API_KEY_PLACEHOLDER', apiKey)
|
|
69
|
-
fs.writeFileSync(routerConfigPath, routerContent)
|
|
70
|
-
|
|
71
37
|
// Write Claude settings
|
|
72
38
|
const settingsDir = getClaudeSettingsDir()
|
|
73
39
|
const settingsPath = path.join(settingsDir, 'settings.json')
|
|
@@ -78,7 +44,7 @@ export function writeClaudeConfig(apiKey: string): {
|
|
|
78
44
|
)
|
|
79
45
|
fs.writeFileSync(settingsPath, settingsContent)
|
|
80
46
|
|
|
81
|
-
return {
|
|
47
|
+
return { settingsPath }
|
|
82
48
|
}
|
|
83
49
|
|
|
84
50
|
/** Check if a command exists */
|
|
@@ -98,10 +64,7 @@ async function commandExists(command: string): Promise<boolean> {
|
|
|
98
64
|
|
|
99
65
|
/** Install global dependencies */
|
|
100
66
|
export async function installDeps(): Promise<void> {
|
|
101
|
-
const packages = [
|
|
102
|
-
'@anthropic-ai/claude-code',
|
|
103
|
-
'@musistudio/claude-code-router',
|
|
104
|
-
]
|
|
67
|
+
const packages = ['@anthropic-ai/claude-code']
|
|
105
68
|
const usePnpm = await commandExists('pnpm')
|
|
106
69
|
|
|
107
70
|
if (usePnpm) {
|
|
@@ -121,7 +84,7 @@ function getCodexConfigDir(): string {
|
|
|
121
84
|
|
|
122
85
|
/** Template for Codex config.toml */
|
|
123
86
|
const CODEX_CONFIG_TOML_TEMPLATE = `model_provider = "jw"
|
|
124
|
-
model = "gpt-5.
|
|
87
|
+
model = "gpt-5.2"
|
|
125
88
|
model_reasoning_effort = "high"
|
|
126
89
|
disable_response_storage = true
|
|
127
90
|
preferred_auth_method = "apikey"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gengjiawen/os-init",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.11.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"dev": "tsc -w",
|
|
12
|
-
"server": "nodemon --exec ts-node libs/index.ts",
|
|
13
12
|
"prepare": "husky",
|
|
14
13
|
"clean": "rimraf build",
|
|
15
14
|
"format": "prettier --write \"{examples,libs,script,bin}/**/*.{js,ts}\" \"**/*.yml\"",
|
|
@@ -22,7 +21,8 @@
|
|
|
22
21
|
"@gengjiawen/unzip-url": "^1.1.0",
|
|
23
22
|
"commander": "^12.1.0",
|
|
24
23
|
"ip": "^2.0.1",
|
|
25
|
-
"execa": "^8.0.1"
|
|
24
|
+
"execa": "^8.0.1",
|
|
25
|
+
"yaml": "^2.8.2"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/ip": "^1.1.3",
|
|
39
|
-
"@types/jest": "
|
|
39
|
+
"@types/jest": "30.0.0",
|
|
40
40
|
"@types/node": "24.1.0",
|
|
41
41
|
"cpy-cli": "^6.0.0",
|
|
42
42
|
"husky": "9.1.6",
|
|
43
|
-
"jest": "
|
|
43
|
+
"jest": "30.2.0",
|
|
44
44
|
"lint-staged": "^15.2.2",
|
|
45
45
|
"nodemon": "3.1.4",
|
|
46
46
|
"prettier": "3.5.3",
|
|
47
47
|
"rimraf": "6.1.2",
|
|
48
|
-
"ts-jest": "29.
|
|
48
|
+
"ts-jest": "29.4.6",
|
|
49
49
|
"ts-node": "^10.9.1",
|
|
50
50
|
"typescript": "5.6.3"
|
|
51
51
|
},
|