@gengjiawen/os-init 1.4.0 → 1.5.1

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.
@@ -6,6 +6,7 @@ on:
6
6
  workflow_dispatch:
7
7
 
8
8
  permissions:
9
+ id-token: write # Required for OIDC
9
10
  contents: write
10
11
  issues: write
11
12
  pull-requests: write
@@ -44,7 +45,5 @@ jobs:
44
45
  if: ${{ steps.release.outputs.release_created }}
45
46
 
46
47
  - name: Publish
47
- env:
48
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
49
48
  run: npm publish
50
49
  if: ${{ steps.release.outputs.release_created }}
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.5.1](https://github.com/gengjiawen/os-init/compare/v1.5.0...v1.5.1) (2025-10-25)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * add 'ip' dependency to package.json ([195b0d0](https://github.com/gengjiawen/os-init/commit/195b0d06b158962d57060336629e0666b726b9d9))
9
+ * add repository information to package.json ([441f752](https://github.com/gengjiawen/os-init/commit/441f7525a44af7a96b0f2916e2228439e89bb1d7))
10
+
11
+ ## [1.5.0](https://github.com/gengjiawen/os-init/compare/v1.4.0...v1.5.0) (2025-10-25)
12
+
13
+
14
+ ### Features
15
+
16
+ * add dev environment setup command and update dependencies ([3bd05ae](https://github.com/gengjiawen/os-init/commit/3bd05aec3ba20c55e0b2323fb0da390eb4de539d))
17
+
3
18
  ## [1.4.0](https://github.com/gengjiawen/os-init/compare/v1.3.2...v1.4.0) (2025-10-25)
4
19
 
5
20
 
package/README.md CHANGED
@@ -1,16 +1,57 @@
1
1
  ## os-init CLI
2
2
 
3
- What it does
3
+ A CLI tool to quickly configure AI development tools and environments.
4
4
 
5
- - Configure Claude Code Router with your API key (writes `~/.claude-code-router/config.json`).
6
- - Configure Codex CLI with your API key (writes `~/.codex/config.toml` and `~/.codex/auth.json`).
7
- - Configure Raycast AI with your API key (writes `~/.config/raycast/ai/providers.yaml`).
8
- - Install global tools: `@anthropic-ai/claude-code`, `@musistudio/claude-code-router`, `@openai/codex`.
5
+ ## Usage
9
6
 
10
- Usage
7
+ ### Configure Claude Code
11
8
 
12
- - `pnpx @gengjiawen/os-init set-cc <API_KEY>`
13
- - `pnpx @gengjiawen/os-init set-codex <API_KEY>`
14
- - `pnpx @gengjiawen/os-init set-raycast-ai <API_KEY>`
9
+ ```bash
10
+ pnpx @gengjiawen/os-init set-cc <API_KEY>
11
+ ```
12
+
13
+ Configures Claude Code Router with your API key. This command will:
14
+ - Write `~/.claude-code-router/config.json`
15
+ - Write `~/.claude/settings.json`
16
+ - Install global tools: `@anthropic-ai/claude-code`, `@musistudio/claude-code-router`
17
+
18
+ ### Configure Codex CLI
19
+
20
+ ```bash
21
+ pnpx @gengjiawen/os-init set-codex <API_KEY>
22
+ ```
23
+
24
+ Configures Codex CLI with your API key. This command will:
25
+ - Write `~/.codex/config.toml`
26
+ - Write `~/.codex/auth.json`
27
+ - Install global tool: `@openai/codex`
28
+
29
+ ### Configure Raycast AI
30
+
31
+ ```bash
32
+ pnpx @gengjiawen/os-init set-raycast-ai <API_KEY>
33
+ ```
34
+
35
+ Configures Raycast AI providers with your API key. This command will:
36
+ - Write `~/.config/raycast/ai/providers.yaml`
37
+
38
+ ### Setup Dev Environment
39
+
40
+ ```bash
41
+ pnpx @gengjiawen/os-init set-dev <SSH_PUBLIC_KEY>
42
+ ```
43
+
44
+ Sets up a Docker-based development environment with SSH access. This command will:
45
+ - Copy `dev-setup` directory to your current directory (or specify with `-t, --target <dir>`)
46
+ - Configure SSH public key in Dockerfile
47
+ - Automatically run `docker-compose build && docker-compose up -d` (if docker-compose is available)
48
+ - Display SSH connection command with your local IP address
49
+
50
+ Example:
51
+ ```bash
52
+ pnpx @gengjiawen/os-init set-dev "ssh-rsa AAAAB3NzaC1yc2..."
53
+ ```
54
+
55
+ ---
15
56
 
16
57
  Project generated by [gengjiawen/ts-scaffold](https://github.com/gengjiawen/ts-scaffold)
package/bin/bin.js CHANGED
@@ -7,6 +7,7 @@ const {
7
7
  writeCodexConfig,
8
8
  installCodexDeps,
9
9
  writeRaycastConfig,
10
+ setupDevEnvironment,
10
11
  } = require('../build')
11
12
 
12
13
  const program = new Command()
@@ -78,4 +79,27 @@ program
78
79
  console.log('Raycast AI is ready to use')
79
80
  })
80
81
 
82
+ program
83
+ .command('set-dev')
84
+ .description('setup dev environment with SSH access')
85
+ .argument('<sshPublicKey>', 'SSH public key to set')
86
+ .option('-t, --target <dir>', 'Target directory for dev-setup')
87
+ .action(async (sshPublicKey, options) => {
88
+ if (!sshPublicKey || String(sshPublicKey).trim().length === 0) {
89
+ console.error('Missing required argument: <sshPublicKey>')
90
+ program.help({ error: true })
91
+ return
92
+ }
93
+ try {
94
+ const { targetPath } = await setupDevEnvironment(
95
+ sshPublicKey,
96
+ options.target
97
+ )
98
+ console.log(`\n✅ Dev environment setup completed at: ${targetPath}`)
99
+ } catch (err) {
100
+ console.error('Failed to setup dev environment:', err.message)
101
+ process.exit(1)
102
+ }
103
+ })
104
+
81
105
  program.parse(process.argv)
@@ -0,0 +1,3 @@
1
+ export declare function setupDevEnvironment(sshPublicKey: string, targetDir?: string): Promise<{
2
+ targetPath: string;
3
+ }>;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupDevEnvironment = setupDevEnvironment;
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const execa_1 = require("execa");
7
+ const ip = require("ip");
8
+ function ensureDir(dirPath) {
9
+ fs.mkdirSync(dirPath, { recursive: true });
10
+ }
11
+ function copyDirSync(src, dest) {
12
+ ensureDir(dest);
13
+ const entries = fs.readdirSync(src, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ const srcPath = path.join(src, entry.name);
16
+ const destPath = path.join(dest, entry.name);
17
+ if (entry.isDirectory()) {
18
+ copyDirSync(srcPath, destPath);
19
+ }
20
+ else {
21
+ fs.copyFileSync(srcPath, destPath);
22
+ }
23
+ }
24
+ }
25
+ async function commandExists(command) {
26
+ try {
27
+ const { failed } = await (0, execa_1.execa)(command, ['--version'], {
28
+ stdio: 'ignore',
29
+ reject: false,
30
+ });
31
+ return !failed;
32
+ }
33
+ catch (error) {
34
+ return false;
35
+ }
36
+ }
37
+ async function setupDevEnvironment(sshPublicKey, targetDir) {
38
+ const sourceDir = path.join(__dirname, '..', 'dev-setup');
39
+ const defaultTargetDir = path.join(process.cwd(), 'dev-setup');
40
+ const targetPath = targetDir || defaultTargetDir;
41
+ if (!fs.existsSync(sourceDir)) {
42
+ throw new Error(`Source directory not found: ${sourceDir}`);
43
+ }
44
+ console.log(`Copying dev-setup to: ${targetPath}`);
45
+ copyDirSync(sourceDir, targetPath);
46
+ const dockerfilePath = path.join(targetPath, 'Dockerfile.txt');
47
+ if (fs.existsSync(dockerfilePath)) {
48
+ let content = fs.readFileSync(dockerfilePath, 'utf-8');
49
+ content = content.replace('ssh-public-key-placeholder', sshPublicKey);
50
+ fs.writeFileSync(dockerfilePath, content);
51
+ console.log('SSH public key has been configured in Dockerfile.txt');
52
+ }
53
+ else {
54
+ throw new Error(`Dockerfile.txt not found in ${targetPath}`);
55
+ }
56
+ const hasDockerCompose = await commandExists('docker-compose');
57
+ if (hasDockerCompose) {
58
+ console.log('\n🐳 Docker Compose detected. Building and starting containers...');
59
+ try {
60
+ console.log('Running: docker-compose build');
61
+ await (0, execa_1.execa)('docker-compose', ['build'], {
62
+ cwd: targetPath,
63
+ stdio: 'inherit',
64
+ });
65
+ console.log('Running: docker-compose up -d');
66
+ await (0, execa_1.execa)('docker-compose', ['up', '-d'], {
67
+ cwd: targetPath,
68
+ stdio: 'inherit',
69
+ });
70
+ console.log('\n✅ Dev environment is up and running!');
71
+ }
72
+ catch (error) {
73
+ const errorMessage = error instanceof Error ? error.message : String(error);
74
+ console.error('\n⚠️ Failed to start containers:', errorMessage);
75
+ console.log('\nYou can manually start the containers with:');
76
+ console.log(` cd ${targetPath}`);
77
+ console.log(' docker-compose build && docker-compose up -d');
78
+ }
79
+ }
80
+ else {
81
+ console.log('\n⚠️ Docker Compose not found.');
82
+ console.log('\nTo start the dev environment, run:');
83
+ console.log(` cd ${targetPath}`);
84
+ console.log(' docker-compose build && docker-compose up -d');
85
+ }
86
+ const localIp = ip.address();
87
+ console.log('\n📡 SSH Connection:');
88
+ console.log(` ssh gitpod@${localIp} -p 2222`);
89
+ console.log('\nOr use localhost if connecting from the same machine:');
90
+ console.log(' ssh gitpod@localhost -p 2222');
91
+ return { targetPath };
92
+ }
package/build/index.d.ts CHANGED
@@ -11,3 +11,4 @@ export declare function installCodexDeps(): Promise<void>;
11
11
  export declare function writeRaycastConfig(apiKey: string): {
12
12
  configPath: string;
13
13
  };
14
+ export { setupDevEnvironment } from './dev-setup';
package/build/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupDevEnvironment = void 0;
3
4
  exports.writeClaudeConfig = writeClaudeConfig;
4
5
  exports.installDeps = installDeps;
5
6
  exports.writeCodexConfig = writeCodexConfig;
@@ -162,3 +163,5 @@ function writeRaycastConfig(apiKey) {
162
163
  fs.writeFileSync(configPath, content);
163
164
  return { configPath };
164
165
  }
166
+ var dev_setup_1 = require("./dev-setup");
167
+ Object.defineProperty(exports, "setupDevEnvironment", { enumerable: true, get: function () { return dev_setup_1.setupDevEnvironment; } });
@@ -0,0 +1,19 @@
1
+ FROM gengjiawen/node-build
2
+
3
+ # SSHD configuration
4
+ COPY sshd_config /etc/ssh/sshd_config
5
+
6
+ ENV SSH_PUBLIC_KEY="ssh-public-key-placeholder"
7
+ RUN set -eux; \
8
+ if id -u gitpod >/dev/null 2>&1; then \
9
+ userhome="$(getent passwd gitpod | cut -d: -f6)"; \
10
+ install -o gitpod -g gitpod -m 700 -d "$userhome/.ssh"; \
11
+ printf '%s\n' "$SSH_PUBLIC_KEY" > "$userhome/.ssh/authorized_keys"; \
12
+ chown gitpod:gitpod "$userhome/.ssh/authorized_keys"; \
13
+ chmod 600 "$userhome/.ssh/authorized_keys"; \
14
+ fi
15
+
16
+ # Run sshd in foreground
17
+ USER root
18
+ EXPOSE 22
19
+ CMD ["/usr/sbin/sshd", "-D", "-e"]
@@ -0,0 +1,15 @@
1
+ version: '3'
2
+
3
+ services:
4
+ node-core-dev:
5
+ build: ./
6
+ container_name: 'node-dev'
7
+ restart: always
8
+ tty: true
9
+ ports:
10
+ - '2222:22'
11
+ cap_add:
12
+ - SYS_PTRACE
13
+ volumes:
14
+ - .:/pwd
15
+ - $HOME/.local:/root/.local
@@ -0,0 +1,31 @@
1
+ Port 22
2
+ Protocol 2
3
+
4
+ # Host keys are generated at runtime with `ssh-keygen -A`
5
+ HostKey /etc/ssh/ssh_host_rsa_key
6
+ HostKey /etc/ssh/ssh_host_ed25519_key
7
+
8
+ SyslogFacility AUTHPRIV
9
+ LogLevel VERBOSE
10
+
11
+ # Disallow root login; use non-root user (e.g., gitpod)
12
+ PermitRootLogin no
13
+ PasswordAuthentication no
14
+ KbdInteractiveAuthentication no
15
+ ChallengeResponseAuthentication no
16
+ PubkeyAuthentication yes
17
+
18
+ # Use per-user authorized_keys in each home directory
19
+ AuthorizedKeysFile .ssh/authorized_keys
20
+
21
+ PermitEmptyPasswords no
22
+ X11Forwarding no
23
+ AllowAgentForwarding yes
24
+ AllowTcpForwarding yes
25
+ ClientAliveInterval 120
26
+ ClientAliveCountMax 3
27
+ PrintMotd no
28
+ AcceptEnv LANG LC_*
29
+
30
+ # SFTP Subsystem
31
+ Subsystem sftp /usr/lib/openssh/sftp-server
@@ -0,0 +1,118 @@
1
+ import * as fs from 'fs'
2
+ import * as path from 'path'
3
+ import { execa } from 'execa'
4
+ import * as ip from 'ip'
5
+
6
+ /** Ensure directory exists */
7
+ function ensureDir(dirPath: string): void {
8
+ fs.mkdirSync(dirPath, { recursive: true })
9
+ }
10
+
11
+ /** Copy directory recursively */
12
+ function copyDirSync(src: string, dest: string): void {
13
+ ensureDir(dest)
14
+ const entries = fs.readdirSync(src, { withFileTypes: true })
15
+
16
+ for (const entry of entries) {
17
+ const srcPath = path.join(src, entry.name)
18
+ const destPath = path.join(dest, entry.name)
19
+
20
+ if (entry.isDirectory()) {
21
+ copyDirSync(srcPath, destPath)
22
+ } else {
23
+ fs.copyFileSync(srcPath, destPath)
24
+ }
25
+ }
26
+ }
27
+
28
+ /** Check if a command exists */
29
+ async function commandExists(command: string): Promise<boolean> {
30
+ try {
31
+ const { failed } = await execa(command, ['--version'], {
32
+ stdio: 'ignore',
33
+ reject: false,
34
+ })
35
+ return !failed
36
+ } catch (error) {
37
+ return false
38
+ }
39
+ }
40
+
41
+ /** Setup dev environment by copying dev-setup directory and replacing SSH public key */
42
+ export async function setupDevEnvironment(
43
+ sshPublicKey: string,
44
+ targetDir?: string
45
+ ): Promise<{ targetPath: string }> {
46
+ // Determine source and target paths
47
+ const sourceDir = path.join(__dirname, '..', 'dev-setup')
48
+ const defaultTargetDir = path.join(process.cwd(), 'dev-setup')
49
+ const targetPath = targetDir || defaultTargetDir
50
+
51
+ // Check if source directory exists
52
+ if (!fs.existsSync(sourceDir)) {
53
+ throw new Error(`Source directory not found: ${sourceDir}`)
54
+ }
55
+
56
+ // Copy dev-setup directory
57
+ console.log(`Copying dev-setup to: ${targetPath}`)
58
+ copyDirSync(sourceDir, targetPath)
59
+
60
+ // Replace SSH_PUBLIC_KEY placeholder in Dockerfile.txt
61
+ const dockerfilePath = path.join(targetPath, 'Dockerfile.txt')
62
+ if (fs.existsSync(dockerfilePath)) {
63
+ let content = fs.readFileSync(dockerfilePath, 'utf-8')
64
+ content = content.replace('ssh-public-key-placeholder', sshPublicKey)
65
+ fs.writeFileSync(dockerfilePath, content)
66
+ console.log('SSH public key has been configured in Dockerfile.txt')
67
+ } else {
68
+ throw new Error(`Dockerfile.txt not found in ${targetPath}`)
69
+ }
70
+
71
+ // Check if docker-compose is available
72
+ const hasDockerCompose = await commandExists('docker-compose')
73
+
74
+ if (hasDockerCompose) {
75
+ console.log(
76
+ '\n🐳 Docker Compose detected. Building and starting containers...'
77
+ )
78
+
79
+ try {
80
+ // Run docker-compose build
81
+ console.log('Running: docker-compose build')
82
+ await execa('docker-compose', ['build'], {
83
+ cwd: targetPath,
84
+ stdio: 'inherit',
85
+ })
86
+
87
+ // Run docker-compose up -d
88
+ console.log('Running: docker-compose up -d')
89
+ await execa('docker-compose', ['up', '-d'], {
90
+ cwd: targetPath,
91
+ stdio: 'inherit',
92
+ })
93
+
94
+ console.log('\n✅ Dev environment is up and running!')
95
+ } catch (error) {
96
+ const errorMessage =
97
+ error instanceof Error ? error.message : String(error)
98
+ console.error('\n⚠️ Failed to start containers:', errorMessage)
99
+ console.log('\nYou can manually start the containers with:')
100
+ console.log(` cd ${targetPath}`)
101
+ console.log(' docker-compose build && docker-compose up -d')
102
+ }
103
+ } else {
104
+ console.log('\n⚠️ Docker Compose not found.')
105
+ console.log('\nTo start the dev environment, run:')
106
+ console.log(` cd ${targetPath}`)
107
+ console.log(' docker-compose build && docker-compose up -d')
108
+ }
109
+
110
+ // Display SSH connection instructions
111
+ const localIp = ip.address()
112
+ console.log('\n📡 SSH Connection:')
113
+ console.log(` ssh gitpod@${localIp} -p 2222`)
114
+ console.log('\nOr use localhost if connecting from the same machine:')
115
+ console.log(' ssh gitpod@localhost -p 2222')
116
+
117
+ return { targetPath }
118
+ }
package/libs/index.ts CHANGED
@@ -207,3 +207,6 @@ export function writeRaycastConfig(apiKey: string): { configPath: string } {
207
207
 
208
208
  return { configPath }
209
209
  }
210
+
211
+ // Re-export dev-setup functionality
212
+ export { setupDevEnvironment } from './dev-setup'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gengjiawen/os-init",
3
3
  "private": false,
4
- "version": "1.4.0",
4
+ "version": "1.5.1",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -20,15 +20,21 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "commander": "^12.1.0",
23
+ "ip": "^2.0.1",
23
24
  "execa": "^8.0.1"
24
25
  },
25
26
  "publishConfig": {
26
27
  "access": "public"
27
28
  },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/gengjiawen/os-init"
32
+ },
28
33
  "engines": {
29
34
  "node": ">22.12.0"
30
35
  },
31
36
  "devDependencies": {
37
+ "@types/ip": "^1.1.3",
32
38
  "@types/jest": "29.5.12",
33
39
  "@types/node": "24.1.0",
34
40
  "cpy-cli": "^4.2.0",