@openape/cli 0.1.4 → 0.2.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.
|
@@ -66,18 +66,14 @@ audit_log = "${auditLog}"
|
|
|
66
66
|
ensureDir(auditDir, 0o755);
|
|
67
67
|
console.log(`✅ Audit log dir: ${auditDir}`);
|
|
68
68
|
}
|
|
69
|
-
//
|
|
69
|
+
// Install proxy via npm/bun (no git needed)
|
|
70
70
|
ensureDir(INSTALL_DIR, 0o755);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
execSync(`cd ${INSTALL_DIR} &&
|
|
71
|
+
console.log('Installing @openape/proxy via bun...');
|
|
72
|
+
if (!existsSync(`${INSTALL_DIR}/package.json`)) {
|
|
73
|
+
execSync(`cd ${INSTALL_DIR} && bun init -y`, { stdio: 'pipe' });
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
execSync(`git clone https://github.com/openape-ai/proxy.git ${INSTALL_DIR}`, { stdio: 'inherit' });
|
|
78
|
-
}
|
|
79
|
-
console.log('Installing dependencies...');
|
|
80
|
-
execSync(`cd ${INSTALL_DIR} && bun install`, { stdio: 'inherit' });
|
|
75
|
+
execSync(`cd ${INSTALL_DIR} && bun add @openape/proxy`, { stdio: 'inherit' });
|
|
76
|
+
console.log(`✅ Proxy installed: ${INSTALL_DIR}`);
|
|
81
77
|
// System service
|
|
82
78
|
if (isMacOS()) {
|
|
83
79
|
setupLaunchd();
|
|
@@ -86,7 +82,7 @@ audit_log = "${auditLog}"
|
|
|
86
82
|
setupSystemd();
|
|
87
83
|
}
|
|
88
84
|
else {
|
|
89
|
-
console.log(`\n⚠️ Manual start: cd ${INSTALL_DIR} &&
|
|
85
|
+
console.log(`\n⚠️ Manual start: cd ${INSTALL_DIR} && bunx openape-proxy --config ${CONFIG_PATH}`);
|
|
90
86
|
}
|
|
91
87
|
console.log('\n🎉 OpenApe Proxy installed!\n');
|
|
92
88
|
console.log(` Config: ${CONFIG_PATH}`);
|
|
@@ -107,7 +103,7 @@ function setupLaunchd() {
|
|
|
107
103
|
<array>
|
|
108
104
|
<string>${bunPath}</string>
|
|
109
105
|
<string>run</string>
|
|
110
|
-
<string>${INSTALL_DIR}/src/index.ts</string>
|
|
106
|
+
<string>${INSTALL_DIR}/node_modules/@openape/proxy/src/index.ts</string>
|
|
111
107
|
<string>--config</string>
|
|
112
108
|
<string>${CONFIG_PATH}</string>
|
|
113
109
|
</array>
|
|
@@ -139,7 +135,7 @@ After=network.target
|
|
|
139
135
|
|
|
140
136
|
[Service]
|
|
141
137
|
Type=simple
|
|
142
|
-
ExecStart=${bunPath} run ${INSTALL_DIR}/src/index.ts --config ${CONFIG_PATH}
|
|
138
|
+
ExecStart=${bunPath} run ${INSTALL_DIR}/node_modules/@openape/proxy/src/index.ts --config ${CONFIG_PATH}
|
|
143
139
|
WorkingDirectory=${INSTALL_DIR}
|
|
144
140
|
Restart=on-failure
|
|
145
141
|
RestartSec=5
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
+
import { arch, platform } from 'node:os';
|
|
3
4
|
import { requireRoot, ensureDir, writeSecureFile, run, parseFlags, requireFlag } from '../utils.js';
|
|
4
5
|
const CONFIG_DIR = '/etc/apes';
|
|
5
6
|
const CONFIG_PATH = `${CONFIG_DIR}/config.toml`;
|
|
6
7
|
const BIN_PATH = '/usr/local/bin/apes';
|
|
8
|
+
const REPO = 'openape-ai/sudo';
|
|
7
9
|
export async function installSudo(args) {
|
|
8
10
|
requireRoot();
|
|
9
11
|
const flags = parseFlags(args);
|
|
@@ -16,32 +18,12 @@ export async function installSudo(args) {
|
|
|
16
18
|
if (existsSync(BIN_PATH) && !force) {
|
|
17
19
|
throw new Error(`apes already installed at ${BIN_PATH}. Use --force to reinstall.`);
|
|
18
20
|
}
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
throw new Error('Rust toolchain required to build apes. Install it: https://rustup.rs\n' +
|
|
25
|
-
'Pre-built binaries will be available in a future release.');
|
|
26
|
-
}
|
|
27
|
-
// Build from source
|
|
28
|
-
const buildDir = '/tmp/openape-sudo-build';
|
|
29
|
-
if (existsSync(`${buildDir}/.git`)) {
|
|
30
|
-
console.log('Updating source...');
|
|
31
|
-
execSync(`cd ${buildDir} && git pull`, { stdio: 'inherit' });
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
console.log('Downloading source...');
|
|
35
|
-
execSync(`rm -rf ${buildDir}`, { stdio: 'ignore' });
|
|
36
|
-
execSync(`git clone https://github.com/openape-ai/sudo.git ${buildDir}`, { stdio: 'inherit' });
|
|
21
|
+
// Try GitHub Release first, fall back to source build
|
|
22
|
+
const installed = await tryGitHubRelease();
|
|
23
|
+
if (!installed) {
|
|
24
|
+
console.log('No pre-built binary available. Building from source...');
|
|
25
|
+
buildFromSource();
|
|
37
26
|
}
|
|
38
|
-
console.log('Building apes (release mode)...');
|
|
39
|
-
execSync(`cd ${buildDir} && cargo build --release`, { stdio: 'inherit' });
|
|
40
|
-
// Install binary with setuid
|
|
41
|
-
execSync(`cp ${buildDir}/target/release/apes ${BIN_PATH}`);
|
|
42
|
-
execSync(`chown root:wheel ${BIN_PATH}`);
|
|
43
|
-
execSync(`chmod u+s ${BIN_PATH}`);
|
|
44
|
-
console.log(`✅ Binary: ${BIN_PATH} (setuid root)`);
|
|
45
27
|
// Config
|
|
46
28
|
if (existsSync(CONFIG_PATH) && !force) {
|
|
47
29
|
console.log(`ℹ️ Config preserved: ${CONFIG_PATH}`);
|
|
@@ -71,3 +53,77 @@ timeout_secs = ${pollTimeout}
|
|
|
71
53
|
console.log(' 2. Admin approves enrollment');
|
|
72
54
|
console.log(' 3. Use: apes --key ~/.ssh/id_ed25519 --reason "why" -- <command>\n');
|
|
73
55
|
}
|
|
56
|
+
function getAssetName() {
|
|
57
|
+
const os = platform();
|
|
58
|
+
const cpu = arch();
|
|
59
|
+
const osMap = { darwin: 'apple-darwin', linux: 'unknown-linux-gnu' };
|
|
60
|
+
const archMap = { arm64: 'aarch64', x64: 'x86_64' };
|
|
61
|
+
const osStr = osMap[os];
|
|
62
|
+
const archStr = archMap[cpu];
|
|
63
|
+
if (!osStr || !archStr) {
|
|
64
|
+
throw new Error(`Unsupported platform: ${os}-${cpu}`);
|
|
65
|
+
}
|
|
66
|
+
return `apes-${archStr}-${osStr}`;
|
|
67
|
+
}
|
|
68
|
+
async function tryGitHubRelease() {
|
|
69
|
+
try {
|
|
70
|
+
// Check if gh CLI is available
|
|
71
|
+
run('which gh');
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
console.log('GitHub CLI (gh) not found — skipping release download.');
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const assetName = getAssetName();
|
|
79
|
+
console.log(`Looking for pre-built binary: ${assetName}...`);
|
|
80
|
+
// Download latest release asset
|
|
81
|
+
const tmpBin = '/tmp/apes-download';
|
|
82
|
+
execSync(`gh release download --repo ${REPO} --pattern "${assetName}" --output ${tmpBin} --clobber`, {
|
|
83
|
+
stdio: 'pipe',
|
|
84
|
+
timeout: 30000,
|
|
85
|
+
});
|
|
86
|
+
if (!existsSync(tmpBin)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
// Install
|
|
90
|
+
execSync(`cp ${tmpBin} ${BIN_PATH}`);
|
|
91
|
+
execSync(`chown root:wheel ${BIN_PATH}`);
|
|
92
|
+
execSync(`chmod u+s ${BIN_PATH}`);
|
|
93
|
+
execSync(`rm -f ${tmpBin}`);
|
|
94
|
+
console.log(`✅ Binary (pre-built): ${BIN_PATH} (setuid root)`);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
console.log('No pre-built binary found for this platform.');
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function buildFromSource() {
|
|
103
|
+
// Check Rust toolchain
|
|
104
|
+
try {
|
|
105
|
+
run('cargo --version');
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
throw new Error('Rust toolchain required to build apes. Install it: https://rustup.rs\n' +
|
|
109
|
+
'Or create a GitHub Release with pre-built binaries.');
|
|
110
|
+
}
|
|
111
|
+
const buildDir = '/tmp/openape-sudo-build';
|
|
112
|
+
if (existsSync(`${buildDir}/.git`)) {
|
|
113
|
+
console.log('Updating source...');
|
|
114
|
+
execSync(`git config --global --add safe.directory ${buildDir}`, { stdio: 'ignore' });
|
|
115
|
+
execSync(`cd ${buildDir} && git pull`, { stdio: 'inherit' });
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log('Downloading source...');
|
|
119
|
+
execSync(`rm -rf ${buildDir}`, { stdio: 'ignore' });
|
|
120
|
+
execSync(`git clone https://github.com/${REPO}.git ${buildDir}`, { stdio: 'inherit' });
|
|
121
|
+
}
|
|
122
|
+
console.log('Building apes (release mode)...');
|
|
123
|
+
execSync(`cd ${buildDir} && cargo build --release`, { stdio: 'inherit' });
|
|
124
|
+
// Install binary with setuid
|
|
125
|
+
execSync(`cp ${buildDir}/target/release/apes ${BIN_PATH}`);
|
|
126
|
+
execSync(`chown root:wheel ${BIN_PATH}`);
|
|
127
|
+
execSync(`chmod u+s ${BIN_PATH}`);
|
|
128
|
+
console.log(`✅ Binary (built): ${BIN_PATH} (setuid root)`);
|
|
129
|
+
}
|
package/package.json
CHANGED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
import { writeFileSync } from 'node:fs';
|
|
4
|
-
import { parseArgs } from 'node:util';
|
|
5
|
-
import { ask, closePrompt, isMacOS } from '../utils.js';
|
|
6
|
-
const CONFIG_DIR = '/etc/openape-proxy';
|
|
7
|
-
const CONFIG_PATH = `${CONFIG_DIR}/config.toml`;
|
|
8
|
-
const INSTALL_DIR = '/opt/openape-proxy';
|
|
9
|
-
const LAUNCHD_LABEL = 'ai.openape.proxy';
|
|
10
|
-
const LAUNCHD_PLIST = `/Library/LaunchDaemons/${LAUNCHD_LABEL}.plist`;
|
|
11
|
-
function apes(reason, cmd) {
|
|
12
|
-
const keyPaths = [
|
|
13
|
-
`${process.env.HOME}/.ssh/id_ed25519`,
|
|
14
|
-
`${process.env.HOME}/.ssh/id_ecdsa`,
|
|
15
|
-
`${process.env.HOME}/.ssh/id_rsa`,
|
|
16
|
-
];
|
|
17
|
-
const keyPath = keyPaths.find(p => existsSync(p));
|
|
18
|
-
if (!keyPath) {
|
|
19
|
-
throw new Error('No SSH key found. apes needs --key to authenticate.');
|
|
20
|
-
}
|
|
21
|
-
console.log(`\n🔐 Requesting grant: ${reason}`);
|
|
22
|
-
execSync(`apes --key ${keyPath} --reason "${reason}" -- ${cmd}`, {
|
|
23
|
-
stdio: 'inherit',
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
export async function installProxyApes() {
|
|
27
|
-
// Check apes is installed
|
|
28
|
-
try {
|
|
29
|
-
execSync('which apes', { stdio: 'ignore' });
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
throw new Error('apes is not installed. Run: sudo npx @openape/cli install-sudo');
|
|
33
|
-
}
|
|
34
|
-
// Parse flags for non-interactive mode
|
|
35
|
-
const { values: flags } = parseArgs({
|
|
36
|
-
args: process.argv.slice(3),
|
|
37
|
-
options: {
|
|
38
|
-
'idp-url': { type: 'string' },
|
|
39
|
-
'agent-email': { type: 'string' },
|
|
40
|
-
'listen': { type: 'string' },
|
|
41
|
-
'default-action': { type: 'string' },
|
|
42
|
-
'audit-log': { type: 'string' },
|
|
43
|
-
'via-apes': { type: 'boolean' },
|
|
44
|
-
},
|
|
45
|
-
strict: false,
|
|
46
|
-
});
|
|
47
|
-
const interactive = !flags['agent-email'];
|
|
48
|
-
console.log('\n🐾 OpenApe Proxy Installer (via apes)\n');
|
|
49
|
-
console.log('Each privileged step requires approval from your admin.\n');
|
|
50
|
-
let idpUrl, agentEmail, listen, defaultAction, auditLog;
|
|
51
|
-
if (interactive) {
|
|
52
|
-
idpUrl = await ask('IdP URL', 'https://id.test.openape.at');
|
|
53
|
-
agentEmail = await ask('Agent email');
|
|
54
|
-
listen = await ask('Listen address', '127.0.0.1:9090');
|
|
55
|
-
defaultAction = await ask('Default action (block/request/request-async)', 'block');
|
|
56
|
-
auditLog = await ask('Audit log path', '/var/log/openape-proxy/audit.log');
|
|
57
|
-
closePrompt();
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
idpUrl = flags['idp-url'] || 'https://id.test.openape.at';
|
|
61
|
-
agentEmail = flags['agent-email'];
|
|
62
|
-
listen = flags['listen'] || '127.0.0.1:9090';
|
|
63
|
-
defaultAction = flags['default-action'] || 'block';
|
|
64
|
-
auditLog = flags['audit-log'] || '/var/log/openape-proxy/audit.log';
|
|
65
|
-
console.log(` IdP: ${idpUrl}`);
|
|
66
|
-
console.log(` Agent: ${agentEmail}`);
|
|
67
|
-
console.log(` Listen: ${listen}`);
|
|
68
|
-
console.log(` Default: ${defaultAction}`);
|
|
69
|
-
console.log(` Audit: ${auditLog}`);
|
|
70
|
-
}
|
|
71
|
-
// Generate config locally in /tmp
|
|
72
|
-
const config = `# OpenApe Proxy Configuration
|
|
73
|
-
# Generated by: openape install-proxy --via-apes
|
|
74
|
-
|
|
75
|
-
[proxy]
|
|
76
|
-
listen = "${listen}"
|
|
77
|
-
idp_url = "${idpUrl}"
|
|
78
|
-
agent_email = "${agentEmail}"
|
|
79
|
-
default_action = "${defaultAction}"
|
|
80
|
-
audit_log = "${auditLog}"
|
|
81
|
-
|
|
82
|
-
# Add your rules below:
|
|
83
|
-
|
|
84
|
-
# [[allow]]
|
|
85
|
-
# domain = "api.github.com"
|
|
86
|
-
# methods = ["GET"]
|
|
87
|
-
# note = "GitHub read-only"
|
|
88
|
-
|
|
89
|
-
# [[deny]]
|
|
90
|
-
# domain = "*.malware.example.com"
|
|
91
|
-
# note = "Blocked"
|
|
92
|
-
|
|
93
|
-
# [[grant_required]]
|
|
94
|
-
# domain = "api.github.com"
|
|
95
|
-
# methods = ["POST", "PUT", "DELETE"]
|
|
96
|
-
# grant_type = "allow_once"
|
|
97
|
-
# note = "Needs approval"
|
|
98
|
-
`;
|
|
99
|
-
const tmpConfig = '/tmp/openape-proxy-config.toml';
|
|
100
|
-
writeFileSync(tmpConfig, config);
|
|
101
|
-
console.log(`\n📝 Config prepared at ${tmpConfig}`);
|
|
102
|
-
// Step 1: Create config directory
|
|
103
|
-
apes('Create OpenApe Proxy config directory', `mkdir -p ${CONFIG_DIR}`);
|
|
104
|
-
apes('Set permissions on config directory', `chmod 700 ${CONFIG_DIR}`);
|
|
105
|
-
// Step 2: Copy config
|
|
106
|
-
apes('Install proxy config', `cp ${tmpConfig} ${CONFIG_PATH}`);
|
|
107
|
-
apes('Secure proxy config (root-only)', `chmod 600 ${CONFIG_PATH}`);
|
|
108
|
-
// Step 3: Create audit log directory
|
|
109
|
-
const auditDir = auditLog.substring(0, auditLog.lastIndexOf('/'));
|
|
110
|
-
if (auditDir) {
|
|
111
|
-
apes('Create audit log directory', `mkdir -p ${auditDir}`);
|
|
112
|
-
}
|
|
113
|
-
// Step 4: Clone/update proxy source
|
|
114
|
-
if (existsSync(`${INSTALL_DIR}/package.json`)) {
|
|
115
|
-
apes('Update proxy source', `git -C ${INSTALL_DIR} pull`);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
apes('Download proxy source', `git clone https://github.com/openape-ai/proxy.git ${INSTALL_DIR}`);
|
|
119
|
-
}
|
|
120
|
-
// Step 5: Install dependencies
|
|
121
|
-
const bunPath = execSync('which bun', { encoding: 'utf-8' }).trim();
|
|
122
|
-
apes('Install proxy dependencies', `${bunPath} install --cwd ${INSTALL_DIR}`);
|
|
123
|
-
// Step 6: Set up system service
|
|
124
|
-
if (isMacOS()) {
|
|
125
|
-
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
126
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
127
|
-
<plist version="1.0">
|
|
128
|
-
<dict>
|
|
129
|
-
<key>Label</key>
|
|
130
|
-
<string>${LAUNCHD_LABEL}</string>
|
|
131
|
-
<key>ProgramArguments</key>
|
|
132
|
-
<array>
|
|
133
|
-
<string>${bunPath}</string>
|
|
134
|
-
<string>run</string>
|
|
135
|
-
<string>${INSTALL_DIR}/src/index.ts</string>
|
|
136
|
-
<string>--config</string>
|
|
137
|
-
<string>${CONFIG_PATH}</string>
|
|
138
|
-
</array>
|
|
139
|
-
<key>RunAtLoad</key>
|
|
140
|
-
<true/>
|
|
141
|
-
<key>KeepAlive</key>
|
|
142
|
-
<true/>
|
|
143
|
-
<key>StandardOutPath</key>
|
|
144
|
-
<string>/var/log/openape-proxy/stdout.log</string>
|
|
145
|
-
<key>StandardErrorPath</key>
|
|
146
|
-
<string>/var/log/openape-proxy/stderr.log</string>
|
|
147
|
-
<key>WorkingDirectory</key>
|
|
148
|
-
<string>${INSTALL_DIR}</string>
|
|
149
|
-
</dict>
|
|
150
|
-
</plist>`;
|
|
151
|
-
const tmpPlist = '/tmp/openape-proxy-launchd.plist';
|
|
152
|
-
writeFileSync(tmpPlist, plist);
|
|
153
|
-
apes('Install launchd service', `cp ${tmpPlist} ${LAUNCHD_PLIST}`);
|
|
154
|
-
apes('Load proxy service', `launchctl load ${LAUNCHD_PLIST}`);
|
|
155
|
-
}
|
|
156
|
-
console.log('\n🎉 OpenApe Proxy installed via apes!\n');
|
|
157
|
-
console.log(` Config: ${CONFIG_PATH}`);
|
|
158
|
-
console.log(` Proxy: ${INSTALL_DIR}`);
|
|
159
|
-
console.log(` Audit log: ${auditLog}`);
|
|
160
|
-
console.log(` Listening: ${listen}`);
|
|
161
|
-
console.log('\n Every step was approved by your admin. 🔐\n');
|
|
162
|
-
}
|