@gengjiawen/os-init 1.3.2 → 1.5.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/.github/workflows/release-please.yml +1 -2
- package/CHANGELOG.md +19 -0
- package/README.md +50 -7
- package/bin/bin.js +48 -1
- package/build/dev-setup.d.ts +3 -0
- package/build/dev-setup.js +92 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +36 -0
- package/dev-setup/Dockerfile.txt +19 -0
- package/dev-setup/docker-compose.yml +15 -0
- package/dev-setup/sshd_config.txt +31 -0
- package/libs/dev-setup.ts +118 -0
- package/libs/index.ts +46 -0
- package/package.json +7 -1
- package/pnpm-lock.yaml +3804 -0
|
@@ -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,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.0](https://github.com/gengjiawen/os-init/compare/v1.4.0...v1.5.0) (2025-10-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add dev environment setup command and update dependencies ([3bd05ae](https://github.com/gengjiawen/os-init/commit/3bd05aec3ba20c55e0b2323fb0da390eb4de539d))
|
|
9
|
+
|
|
10
|
+
## [1.4.0](https://github.com/gengjiawen/os-init/compare/v1.3.2...v1.4.0) (2025-10-25)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add Raycast AI configuration setup and command ([b5d64d4](https://github.com/gengjiawen/os-init/commit/b5d64d4fa65dddf739b7b3b6e50ce3f94346538a))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* format ([ad7cc79](https://github.com/gengjiawen/os-init/commit/ad7cc791efb1437c2eaae916fe3ba43bc545b355))
|
|
21
|
+
|
|
3
22
|
## [1.3.2](https://github.com/gengjiawen/os-init/compare/v1.3.1...v1.3.2) (2025-10-19)
|
|
4
23
|
|
|
5
24
|
|
package/README.md
CHANGED
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
## os-init CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A CLI tool to quickly configure AI development tools and environments.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Configure Codex CLI with your API key (writes `~/.codex/config.toml` and `~/.codex/auth.json`).
|
|
7
|
-
- Install global tools: `@anthropic-ai/claude-code`, `@musistudio/claude-code-router`, `@openai/codex`.
|
|
5
|
+
## Usage
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
### Configure Claude Code
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
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
|
+
---
|
|
13
56
|
|
|
14
57
|
Project generated by [gengjiawen/ts-scaffold](https://github.com/gengjiawen/ts-scaffold)
|
package/bin/bin.js
CHANGED
|
@@ -6,6 +6,8 @@ const {
|
|
|
6
6
|
installDeps,
|
|
7
7
|
writeCodexConfig,
|
|
8
8
|
installCodexDeps,
|
|
9
|
+
writeRaycastConfig,
|
|
10
|
+
setupDevEnvironment,
|
|
9
11
|
} = require('../build')
|
|
10
12
|
|
|
11
13
|
const program = new Command()
|
|
@@ -30,7 +32,9 @@ program
|
|
|
30
32
|
console.error('Failed to complete setup:', err.message)
|
|
31
33
|
process.exit(1)
|
|
32
34
|
}
|
|
33
|
-
console.log(
|
|
35
|
+
console.log(
|
|
36
|
+
'Claude code is ready, use `claude` in terminal to start building'
|
|
37
|
+
)
|
|
34
38
|
})
|
|
35
39
|
|
|
36
40
|
program
|
|
@@ -55,4 +59,47 @@ program
|
|
|
55
59
|
console.log('Codex is ready. use `codex` in terminal to start building')
|
|
56
60
|
})
|
|
57
61
|
|
|
62
|
+
program
|
|
63
|
+
.command('set-raycast-ai')
|
|
64
|
+
.description('setup Raycast AI providers config')
|
|
65
|
+
.argument('<apiKey>', 'API key to set for Raycast AI')
|
|
66
|
+
.action(async (apiKey) => {
|
|
67
|
+
if (!apiKey || String(apiKey).trim().length === 0) {
|
|
68
|
+
console.error('Missing required argument: <apiKey>')
|
|
69
|
+
program.help({ error: true })
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const { configPath } = writeRaycastConfig(apiKey)
|
|
74
|
+
console.log(`Raycast AI config written to: ${configPath}`)
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error('Failed to setup Raycast AI:', err.message)
|
|
77
|
+
process.exit(1)
|
|
78
|
+
}
|
|
79
|
+
console.log('Raycast AI is ready to use')
|
|
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
|
+
|
|
58
105
|
program.parse(process.argv)
|
|
@@ -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
|
@@ -8,3 +8,7 @@ export declare function writeCodexConfig(apiKey: string): {
|
|
|
8
8
|
authPath: string;
|
|
9
9
|
};
|
|
10
10
|
export declare function installCodexDeps(): Promise<void>;
|
|
11
|
+
export declare function writeRaycastConfig(apiKey: string): {
|
|
12
|
+
configPath: string;
|
|
13
|
+
};
|
|
14
|
+
export { setupDevEnvironment } from './dev-setup';
|
package/build/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
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;
|
|
6
7
|
exports.installCodexDeps = installCodexDeps;
|
|
8
|
+
exports.writeRaycastConfig = writeRaycastConfig;
|
|
7
9
|
const fs = require("fs");
|
|
8
10
|
const path = require("path");
|
|
9
11
|
const os = require("os");
|
|
@@ -129,3 +131,37 @@ async function installCodexDeps() {
|
|
|
129
131
|
}
|
|
130
132
|
console.log('Codex dependency installed successfully.');
|
|
131
133
|
}
|
|
134
|
+
function getRaycastAIConfigDir() {
|
|
135
|
+
return path.join(os.homedir(), '.config', 'raycast', 'ai');
|
|
136
|
+
}
|
|
137
|
+
const RAYCAST_PROVIDERS_YAML_TEMPLATE = `providers:
|
|
138
|
+
- id: my_provider
|
|
139
|
+
name: gengjiawen AI
|
|
140
|
+
base_url: https://ai.gengjiawen.com/api/openai/v1/
|
|
141
|
+
api_keys:
|
|
142
|
+
openai: API_KEY_PLACEHOLDER
|
|
143
|
+
models:
|
|
144
|
+
- id: sota
|
|
145
|
+
name: "sota"
|
|
146
|
+
context: 200000
|
|
147
|
+
provider: openai
|
|
148
|
+
abilities:
|
|
149
|
+
temperature:
|
|
150
|
+
supported: true
|
|
151
|
+
vision:
|
|
152
|
+
supported: true
|
|
153
|
+
system_message:
|
|
154
|
+
supported: true
|
|
155
|
+
tools:
|
|
156
|
+
supported: true
|
|
157
|
+
`;
|
|
158
|
+
function writeRaycastConfig(apiKey) {
|
|
159
|
+
const configDir = getRaycastAIConfigDir();
|
|
160
|
+
ensureDir(configDir);
|
|
161
|
+
const configPath = path.join(configDir, 'providers.yaml');
|
|
162
|
+
const content = RAYCAST_PROVIDERS_YAML_TEMPLATE.replace('API_KEY_PLACEHOLDER', apiKey);
|
|
163
|
+
fs.writeFileSync(configPath, content);
|
|
164
|
+
return { configPath };
|
|
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,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
|
@@ -164,3 +164,49 @@ export async function installCodexDeps(): Promise<void> {
|
|
|
164
164
|
}
|
|
165
165
|
console.log('Codex dependency installed successfully.')
|
|
166
166
|
}
|
|
167
|
+
|
|
168
|
+
/** Return Raycast AI configuration directory path */
|
|
169
|
+
function getRaycastAIConfigDir(): string {
|
|
170
|
+
return path.join(os.homedir(), '.config', 'raycast', 'ai')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Template for Raycast AI providers.yaml */
|
|
174
|
+
const RAYCAST_PROVIDERS_YAML_TEMPLATE = `providers:
|
|
175
|
+
- id: my_provider
|
|
176
|
+
name: gengjiawen AI
|
|
177
|
+
base_url: https://ai.gengjiawen.com/api/openai/v1/
|
|
178
|
+
api_keys:
|
|
179
|
+
openai: API_KEY_PLACEHOLDER
|
|
180
|
+
models:
|
|
181
|
+
- id: sota
|
|
182
|
+
name: "sota"
|
|
183
|
+
context: 200000
|
|
184
|
+
provider: openai
|
|
185
|
+
abilities:
|
|
186
|
+
temperature:
|
|
187
|
+
supported: true
|
|
188
|
+
vision:
|
|
189
|
+
supported: true
|
|
190
|
+
system_message:
|
|
191
|
+
supported: true
|
|
192
|
+
tools:
|
|
193
|
+
supported: true
|
|
194
|
+
`
|
|
195
|
+
|
|
196
|
+
/** Write Raycast AI providers.yaml */
|
|
197
|
+
export function writeRaycastConfig(apiKey: string): { configPath: string } {
|
|
198
|
+
const configDir = getRaycastAIConfigDir()
|
|
199
|
+
ensureDir(configDir)
|
|
200
|
+
|
|
201
|
+
const configPath = path.join(configDir, 'providers.yaml')
|
|
202
|
+
const content = RAYCAST_PROVIDERS_YAML_TEMPLATE.replace(
|
|
203
|
+
'API_KEY_PLACEHOLDER',
|
|
204
|
+
apiKey
|
|
205
|
+
)
|
|
206
|
+
fs.writeFileSync(configPath, content)
|
|
207
|
+
|
|
208
|
+
return { configPath }
|
|
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
|
+
"version": "1.5.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
@@ -25,14 +25,20 @@
|
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/gengjiawen/os-init"
|
|
31
|
+
},
|
|
28
32
|
"engines": {
|
|
29
33
|
"node": ">22.12.0"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
36
|
+
"@types/ip": "^1.1.3",
|
|
32
37
|
"@types/jest": "29.5.12",
|
|
33
38
|
"@types/node": "24.1.0",
|
|
34
39
|
"cpy-cli": "^4.2.0",
|
|
35
40
|
"husky": "9.1.6",
|
|
41
|
+
"ip": "^2.0.1",
|
|
36
42
|
"jest": "29.7.0",
|
|
37
43
|
"lint-staged": "^15.2.2",
|
|
38
44
|
"nodemon": "3.1.4",
|