get-opus 0.1.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.
- package/package.json +28 -0
- package/src/config.ts +34 -0
- package/src/download.ts +17 -0
- package/src/index.ts +3 -0
- package/src/install.ts +48 -0
- package/src/platform.ts +26 -0
- package/src/service.ts +29 -0
- package/templates/com.opus.agent.plist +21 -0
- package/templates/opus.service +16 -0
- package/tsconfig.json +13 -0
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "get-opus",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Installer for Opus AI Agent",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"opus": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@clack/prompts": "^0.7.0",
|
|
15
|
+
"chalk": "^5.3.0",
|
|
16
|
+
"commander": "^11.1.0",
|
|
17
|
+
"execa": "^8.0.1",
|
|
18
|
+
"fs-extra": "^11.2.0",
|
|
19
|
+
"nanoid": "^5.0.4",
|
|
20
|
+
"node-fetch": "^3.0.0",
|
|
21
|
+
"toml": "^3.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/fs-extra": "^11.0.4",
|
|
25
|
+
"@types/node": "^22.0.0",
|
|
26
|
+
"typescript": "^5.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
|
|
5
|
+
export interface Config {
|
|
6
|
+
port: number;
|
|
7
|
+
dbPath: string;
|
|
8
|
+
driver: string;
|
|
9
|
+
jwtSecret: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function writeConfig(config: Config) {
|
|
13
|
+
const opusDir = path.join(process.env.HOME || process.env.USERPROFILE || '', '.opus');
|
|
14
|
+
await fs.ensureDir(opusDir);
|
|
15
|
+
const configFile = path.join(opusDir, 'config.toml');
|
|
16
|
+
|
|
17
|
+
const content = `
|
|
18
|
+
[server]
|
|
19
|
+
port = ${config.port}
|
|
20
|
+
|
|
21
|
+
[database]
|
|
22
|
+
driver = "${config.driver}"
|
|
23
|
+
dsn = "${config.dbPath}"
|
|
24
|
+
|
|
25
|
+
[auth]
|
|
26
|
+
secret = "${config.jwtSecret}"
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
await fs.writeFile(configFile, content.trim());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function generateJwtSecret(): string {
|
|
33
|
+
return nanoid(32);
|
|
34
|
+
}
|
package/src/download.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export async function downloadBinary(binaryName: string, destination: string) {
|
|
6
|
+
const url = `https://github.com/kilip/opus/releases/latest/download/${binaryName}`;
|
|
7
|
+
const response = await fetch(url);
|
|
8
|
+
if (!response.ok) throw new Error(`Failed to download ${binaryName}: ${response.statusText}`);
|
|
9
|
+
|
|
10
|
+
await fs.ensureDir(path.dirname(destination));
|
|
11
|
+
const fileStream = fs.createWriteStream(destination, { mode: 0o755 });
|
|
12
|
+
await new Promise<void>((resolve, reject) => {
|
|
13
|
+
response.body!.pipe(fileStream);
|
|
14
|
+
response.body!.on('error', reject);
|
|
15
|
+
fileStream.on('finish', () => resolve());
|
|
16
|
+
});
|
|
17
|
+
}
|
package/src/index.ts
ADDED
package/src/install.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { intro, outro, select, text, confirm, spinner } from '@clack/prompts';
|
|
2
|
+
import color from 'picocolors';
|
|
3
|
+
import { detectPlatform } from './platform';
|
|
4
|
+
import { downloadBinary } from './download';
|
|
5
|
+
import { writeConfig, generateJwtSecret } from './config';
|
|
6
|
+
import { registerService } from './service';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
|
|
10
|
+
export async function runInstaller() {
|
|
11
|
+
intro(color.bgCyan(color.black(' Opus AI Installation Wizard ')));
|
|
12
|
+
|
|
13
|
+
// 1. Detect Platform
|
|
14
|
+
const s1 = spinner();
|
|
15
|
+
s1.start('[1/5] Detecting platform...');
|
|
16
|
+
const platformInfo = detectPlatform();
|
|
17
|
+
s1.stop(`[1/5] Detected: ${platformInfo.os}/${platformInfo.arch}`);
|
|
18
|
+
|
|
19
|
+
// 2. Download Binary
|
|
20
|
+
const s2 = spinner();
|
|
21
|
+
s2.start('[2/5] Downloading binary...');
|
|
22
|
+
const dest = os.platform() === 'win32' ? 'C:\ProgramData\Opus\opus.exe' : '/usr/local/bin/opus';
|
|
23
|
+
await downloadBinary(platformInfo.binaryName, dest);
|
|
24
|
+
s2.stop('[2/5] Binary downloaded');
|
|
25
|
+
|
|
26
|
+
// 3. Configure
|
|
27
|
+
const s3 = spinner();
|
|
28
|
+
s3.start('[3/5] Configuring...');
|
|
29
|
+
const port = await text({ message: 'Server port:', initialValue: '8080' });
|
|
30
|
+
const dbPath = await text({ message: 'Database path:', initialValue: path.join(os.homedir(), '.opus', 'opus.db') });
|
|
31
|
+
const jwtSecret = await text({ message: 'JWT Secret (blank to auto-generate):' });
|
|
32
|
+
|
|
33
|
+
await writeConfig({
|
|
34
|
+
port: parseInt(port as string),
|
|
35
|
+
dbPath: dbPath as string,
|
|
36
|
+
driver: 'sqlite',
|
|
37
|
+
jwtSecret: (jwtSecret as string) || generateJwtSecret()
|
|
38
|
+
});
|
|
39
|
+
s3.stop('[3/5] Configured');
|
|
40
|
+
|
|
41
|
+
// 4. Register Service
|
|
42
|
+
const s4 = spinner();
|
|
43
|
+
s4.start('[4/5] Installing system service...');
|
|
44
|
+
await registerService();
|
|
45
|
+
s4.stop('[4/5] Service installed');
|
|
46
|
+
|
|
47
|
+
outro(color.green('✓ Opus is installed and running!'));
|
|
48
|
+
}
|
package/src/platform.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
|
|
3
|
+
export interface PlatformInfo {
|
|
4
|
+
os: string;
|
|
5
|
+
arch: string;
|
|
6
|
+
binaryName: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function detectPlatform(): PlatformInfo {
|
|
10
|
+
const osType = os.platform(); // 'linux', 'darwin', 'win32'
|
|
11
|
+
const archType = os.arch(); // 'x64', 'arm64'
|
|
12
|
+
|
|
13
|
+
let binaryName = 'opus';
|
|
14
|
+
|
|
15
|
+
if (osType === 'linux') {
|
|
16
|
+
binaryName = archType === 'arm64' ? 'opus-linux-arm64' : 'opus-linux-amd64';
|
|
17
|
+
} else if (osType === 'darwin') {
|
|
18
|
+
binaryName = archType === 'arm64' ? 'opus-darwin-arm64' : 'opus-darwin-amd64';
|
|
19
|
+
} else if (osType === 'win32') {
|
|
20
|
+
binaryName = 'opus-windows-amd64.exe';
|
|
21
|
+
} else {
|
|
22
|
+
throw new Error(`Unsupported platform: ${osType}-${archType}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { os: osType, arch: archType, binaryName };
|
|
26
|
+
}
|
package/src/service.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
|
|
6
|
+
export async function registerService() {
|
|
7
|
+
const osType = os.platform();
|
|
8
|
+
const user = os.userInfo().username;
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
|
|
11
|
+
if (osType === 'linux') {
|
|
12
|
+
const servicePath = '/etc/systemd/system/opus.service';
|
|
13
|
+
let template = await fs.readFile(path.join(__dirname, '../templates/opus.service'), 'utf8');
|
|
14
|
+
template = template.replace(/{{USER}}/g, user);
|
|
15
|
+
|
|
16
|
+
await fs.writeFile(servicePath, template);
|
|
17
|
+
await execa('systemctl', ['daemon-reload']);
|
|
18
|
+
await execa('systemctl', ['enable', '--now', 'opus']);
|
|
19
|
+
} else if (osType === 'darwin') {
|
|
20
|
+
const plistPath = path.join(home, 'Library/LaunchAgents/com.opus.agent.plist');
|
|
21
|
+
let template = await fs.readFile(path.join(__dirname, '../templates/com.opus.agent.plist'), 'utf8');
|
|
22
|
+
template = template.replace(/{{HOME}}/g, home);
|
|
23
|
+
|
|
24
|
+
await fs.writeFile(plistPath, template);
|
|
25
|
+
await execa('launchctl', ['load', plistPath]);
|
|
26
|
+
} else if (osType === 'win32') {
|
|
27
|
+
await execa('sc.exe', ['create', 'Opus', 'binPath=', 'C:\ProgramData\Opus\opus.exe start']);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>Label</key>
|
|
6
|
+
<string>com.opus.agent</string>
|
|
7
|
+
<key>ProgramArguments</key>
|
|
8
|
+
<array>
|
|
9
|
+
<string>/usr/local/bin/opus</string>
|
|
10
|
+
<string>start</string>
|
|
11
|
+
</array>
|
|
12
|
+
<key>RunAtLoad</key>
|
|
13
|
+
<true/>
|
|
14
|
+
<key>KeepAlive</key>
|
|
15
|
+
<true/>
|
|
16
|
+
<key>StandardOutPath</key>
|
|
17
|
+
<string>{{HOME}}/.opus/logs/opus.log</string>
|
|
18
|
+
<key>StandardErrorPath</key>
|
|
19
|
+
<string>{{HOME}}/.opus/logs/opus.error.log</string>
|
|
20
|
+
</dict>
|
|
21
|
+
</plist>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Opus AI Agent
|
|
3
|
+
Documentation=https://github.com/kilip/opus/opus
|
|
4
|
+
After=network.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=simple
|
|
8
|
+
ExecStart=/usr/local/bin/opus start
|
|
9
|
+
Restart=always
|
|
10
|
+
RestartSec=5
|
|
11
|
+
User={{USER}}
|
|
12
|
+
Environment=HOME=/home/{{USER}}
|
|
13
|
+
Environment=OPUS_SERVER_ENV=production
|
|
14
|
+
|
|
15
|
+
[Install]
|
|
16
|
+
WantedBy=multi-user.target
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*"]
|
|
13
|
+
}
|