mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-22 13:35:08 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
293ded3e26 | ||
|
|
b0db826c9a |
Binary file not shown.
|
Before Width: | Height: | Size: 366 KiB After Width: | Height: | Size: 222 KiB |
@@ -3,13 +3,11 @@
|
|||||||
import { DatabaseSync } from "node:sqlite";
|
import { DatabaseSync } from "node:sqlite";
|
||||||
import { randomBytes, createHash } from "node:crypto";
|
import { randomBytes, createHash } from "node:crypto";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { mkdirSync, chmodSync } from "node:fs";
|
import { mkdirSync } from "node:fs";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
|
|
||||||
const OCP_DIR = join(homedir(), ".ocp");
|
const OCP_DIR = join(homedir(), ".ocp");
|
||||||
mkdirSync(OCP_DIR, { recursive: true, mode: 0o700 });
|
mkdirSync(OCP_DIR, { recursive: true });
|
||||||
// Tighten the directory mode in case it already existed with broader permissions.
|
|
||||||
try { chmodSync(OCP_DIR, 0o700); } catch { /* ignore EPERM on pre-existing dirs */ }
|
|
||||||
const DB_PATH = join(OCP_DIR, "ocp.db");
|
const DB_PATH = join(OCP_DIR, "ocp.db");
|
||||||
|
|
||||||
let db;
|
let db;
|
||||||
@@ -20,8 +18,6 @@ export function getDb() {
|
|||||||
db.exec("PRAGMA journal_mode = WAL");
|
db.exec("PRAGMA journal_mode = WAL");
|
||||||
db.exec("PRAGMA foreign_keys = ON");
|
db.exec("PRAGMA foreign_keys = ON");
|
||||||
initSchema();
|
initSchema();
|
||||||
// Tighten mode on the DB file (0600) after creation / first open.
|
|
||||||
try { chmodSync(DB_PATH, 0o600); } catch { /* ignore — same-user access still works */ }
|
|
||||||
}
|
}
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,21 @@ set -euo pipefail
|
|||||||
|
|
||||||
PROXY="http://127.0.0.1:3456"
|
PROXY="http://127.0.0.1:3456"
|
||||||
|
|
||||||
# Auth args for multi-key mode: reads from OCP_ADMIN_KEY env or ~/.ocp/admin-key file
|
# Auth header for multi-key mode: reads from OCP_ADMIN_KEY env or ~/.ocp/admin-key file
|
||||||
# Using a bash array preserves word boundaries — no eval needed.
|
_AUTH_HEADER=""
|
||||||
_AUTH_ARGS=()
|
|
||||||
if [[ -n "${OCP_ADMIN_KEY:-}" ]]; then
|
if [[ -n "${OCP_ADMIN_KEY:-}" ]]; then
|
||||||
_AUTH_ARGS=(-H "Authorization: Bearer $OCP_ADMIN_KEY")
|
_AUTH_HEADER="-H \"Authorization: Bearer $OCP_ADMIN_KEY\""
|
||||||
elif [[ -f "$HOME/.ocp/admin-key" ]]; then
|
elif [[ -f "$HOME/.ocp/admin-key" ]]; then
|
||||||
_AUTH_ARGS=(-H "Authorization: Bearer $(cat "$HOME/.ocp/admin-key")")
|
_AUTH_HEADER="-H \"Authorization: Bearer $(cat "$HOME/.ocp/admin-key")\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Wrapper: curl with optional auth
|
# Wrapper: curl with optional auth
|
||||||
_curl() {
|
_curl() {
|
||||||
curl "${_AUTH_ARGS[@]}" "$@"
|
if [[ -n "$_AUTH_HEADER" ]]; then
|
||||||
|
eval curl "$_AUTH_HEADER" "$@"
|
||||||
|
else
|
||||||
|
curl "$@"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_json() { python3 -m json.tool 2>/dev/null || cat; }
|
_json() { python3 -m json.tool 2>/dev/null || cat; }
|
||||||
|
|||||||
+3
-13
@@ -582,19 +582,11 @@ print(k if k else '')
|
|||||||
if [[ "${SHELL:-}" == */fish ]]; then
|
if [[ "${SHELL:-}" == */fish ]]; then
|
||||||
echo " Note: fish shell detected. Writing to ~/.bashrc — add to fish config manually."
|
echo " Note: fish shell detected. Writing to ~/.bashrc — add to fish config manually."
|
||||||
rc_files+=("$HOME/.bashrc")
|
rc_files+=("$HOME/.bashrc")
|
||||||
elif $is_mac; then
|
|
||||||
# macOS: default shell since Catalina (2019) is zsh.
|
|
||||||
# Always write ~/.zshrc (create if absent — zsh tolerates an empty file).
|
|
||||||
# Only write ~/.bashrc if it already exists (don't surprise users with new files).
|
|
||||||
[[ -f "$HOME/.bashrc" ]] && rc_files+=("$HOME/.bashrc")
|
|
||||||
# zshrc: always include on macOS; create the file if it doesn't exist yet
|
|
||||||
[[ -f "$HOME/.zshrc" ]] || touch "$HOME/.zshrc"
|
|
||||||
rc_files+=("$HOME/.zshrc")
|
|
||||||
else
|
else
|
||||||
# Linux / other: write to whichever rc files already exist or match current shell
|
# Always write both on macOS (default shell is zsh but some tools source bashrc)
|
||||||
[[ -f "$HOME/.bashrc" || "${SHELL:-}" == */bash ]] && rc_files+=("$HOME/.bashrc")
|
[[ -f "$HOME/.bashrc" || "${SHELL:-}" == */bash ]] && rc_files+=("$HOME/.bashrc")
|
||||||
[[ -f "$HOME/.zshrc" || "${SHELL:-}" == */zsh ]] && rc_files+=("$HOME/.zshrc")
|
[[ -f "$HOME/.zshrc" || "${SHELL:-}" == */zsh ]] && rc_files+=("$HOME/.zshrc")
|
||||||
# If neither exists, fall back to creating one for the current shell
|
# If neither exists, create for current shell
|
||||||
[[ ${#rc_files[@]} -eq 0 ]] && rc_files+=("$HOME/.${SHELL##*/}rc")
|
[[ ${#rc_files[@]} -eq 0 ]] && rc_files+=("$HOME/.${SHELL##*/}rc")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -713,9 +705,7 @@ PYEOF
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Done. Reload your shell to apply:"
|
echo " Done. Reload your shell to apply:"
|
||||||
for rc_file in "${rc_files[@]}"; do
|
echo " source $rc_file"
|
||||||
echo " source $rc_file"
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
+4
-86
@@ -30,7 +30,7 @@
|
|||||||
import { createServer } from "node:http";
|
import { createServer } from "node:http";
|
||||||
import { spawn, execFileSync } from "node:child_process";
|
import { spawn, execFileSync } from "node:child_process";
|
||||||
import { randomUUID, timingSafeEqual } from "node:crypto";
|
import { randomUUID, timingSafeEqual } from "node:crypto";
|
||||||
import { readFileSync, readdirSync, accessSync, existsSync, constants, chmodSync, statSync } from "node:fs";
|
import { readFileSync, accessSync, existsSync, constants } from "node:fs";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
@@ -41,48 +41,8 @@ const _pkg = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8"));
|
|||||||
const modelsConfig = JSON.parse(readFileSync(join(__dirname, "models.json"), "utf8"));
|
const modelsConfig = JSON.parse(readFileSync(join(__dirname, "models.json"), "utf8"));
|
||||||
|
|
||||||
// ── Resolve claude binary ───────────────────────────────────────────────
|
// ── Resolve claude binary ───────────────────────────────────────────────
|
||||||
// Priority: CLAUDE_BIN env > well-known paths > nvm/fnm/asdf user-local
|
// Priority: CLAUDE_BIN env > well-known paths > which lookup
|
||||||
// installs > which lookup. Fail-fast if not found — never start with an
|
// Fail-fast if not found — never start with an unresolvable binary.
|
||||||
// unresolvable binary.
|
|
||||||
function _listVersionDirs(parent) {
|
|
||||||
try { return readdirSync(parent); } catch { return []; }
|
|
||||||
}
|
|
||||||
function _collectNodeManagerCandidates(home) {
|
|
||||||
if (!home) return [];
|
|
||||||
const out = [];
|
|
||||||
|
|
||||||
// nvm: $HOME/.nvm/versions/node/<version>/bin/claude
|
|
||||||
const nvmRoot = join(home, ".nvm/versions/node");
|
|
||||||
for (const v of _listVersionDirs(nvmRoot)) {
|
|
||||||
out.push(join(nvmRoot, v, "bin/claude"));
|
|
||||||
}
|
|
||||||
// nvm default alias: resolve $HOME/.nvm/aliases/default if it points to a version
|
|
||||||
try {
|
|
||||||
const aliasFile = join(home, ".nvm/aliases/default");
|
|
||||||
const aliasVer = readFileSync(aliasFile, "utf8").trim();
|
|
||||||
if (aliasVer) {
|
|
||||||
const direct = join(nvmRoot, aliasVer, "bin/claude");
|
|
||||||
if (!out.includes(direct)) out.unshift(direct);
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
// fnm: $HOME/.fnm/node-versions/<version>/installation/bin/claude
|
|
||||||
const fnmRoot = join(home, ".fnm/node-versions");
|
|
||||||
for (const v of _listVersionDirs(fnmRoot)) {
|
|
||||||
out.push(join(fnmRoot, v, "installation/bin/claude"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// asdf: $HOME/.asdf/installs/nodejs/<version>/bin/claude
|
|
||||||
const asdfRoot = join(home, ".asdf/installs/nodejs");
|
|
||||||
for (const v of _listVersionDirs(asdfRoot)) {
|
|
||||||
out.push(join(asdfRoot, v, "bin/claude"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// npm prefix-relocated: $HOME/.npm-global/bin/claude
|
|
||||||
out.push(join(home, ".npm-global/bin/claude"));
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
function resolveClaude() {
|
function resolveClaude() {
|
||||||
if (process.env.CLAUDE_BIN) {
|
if (process.env.CLAUDE_BIN) {
|
||||||
try {
|
try {
|
||||||
@@ -94,13 +54,11 @@ function resolveClaude() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const home = process.env.HOME || "";
|
|
||||||
const candidates = [
|
const candidates = [
|
||||||
"/opt/homebrew/bin/claude",
|
"/opt/homebrew/bin/claude",
|
||||||
"/usr/local/bin/claude",
|
"/usr/local/bin/claude",
|
||||||
"/usr/bin/claude",
|
"/usr/bin/claude",
|
||||||
join(home, ".local/bin/claude"),
|
join(process.env.HOME || "", ".local/bin/claude"),
|
||||||
..._collectNodeManagerCandidates(home),
|
|
||||||
];
|
];
|
||||||
for (const p of candidates) {
|
for (const p of candidates) {
|
||||||
try { accessSync(p, constants.X_OK); console.warn(`[init] CLAUDE_BIN not set, resolved to ${p}`); return p; } catch {}
|
try { accessSync(p, constants.X_OK); console.warn(`[init] CLAUDE_BIN not set, resolved to ${p}`); return p; } catch {}
|
||||||
@@ -114,8 +72,6 @@ function resolveClaude() {
|
|||||||
console.error(
|
console.error(
|
||||||
"FATAL: claude binary not found.\n" +
|
"FATAL: claude binary not found.\n" +
|
||||||
" Set CLAUDE_BIN=/path/to/claude or ensure claude is in PATH.\n" +
|
" Set CLAUDE_BIN=/path/to/claude or ensure claude is in PATH.\n" +
|
||||||
" Hint: if you use nvm/fnm/asdf, set CLAUDE_BIN to the absolute path\n" +
|
|
||||||
" shown by `which claude` in your interactive shell.\n" +
|
|
||||||
" Checked: " + candidates.join(", ")
|
" Checked: " + candidates.join(", ")
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -167,44 +123,6 @@ function logEvent(level, event, data = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Startup file-mode reconciliation ───────────────────────────────────
|
|
||||||
// Idempotently tightens OCP credential-bearing files to 700/600 so that
|
|
||||||
// existing installs (created before this fix) are hardened on next restart.
|
|
||||||
// Wrapped in try/catch — chmod failure must never crash startup.
|
|
||||||
// Does NOT touch systemd units or launchd plists; those are managed by setup.mjs.
|
|
||||||
function _tightenFileModesIfPossible() {
|
|
||||||
const ocpDir = join(homedir(), ".ocp");
|
|
||||||
const targets = [
|
|
||||||
{ path: ocpDir, mode: 0o700, label: "~/.ocp (dir)" },
|
|
||||||
{ path: join(ocpDir, "admin-key"), mode: 0o600, label: "~/.ocp/admin-key" },
|
|
||||||
{ path: join(ocpDir, "ocp.db"), mode: 0o600, label: "~/.ocp/ocp.db" },
|
|
||||||
];
|
|
||||||
let tightened = 0;
|
|
||||||
let alreadyOk = 0;
|
|
||||||
for (const { path, mode, label } of targets) {
|
|
||||||
try {
|
|
||||||
const st = statSync(path);
|
|
||||||
const current = st.mode & 0o777;
|
|
||||||
if (current !== mode) {
|
|
||||||
chmodSync(path, mode);
|
|
||||||
tightened++;
|
|
||||||
} else {
|
|
||||||
alreadyOk++;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== "ENOENT") {
|
|
||||||
// File exists but chmod failed (e.g. EPERM) — log and move on
|
|
||||||
logEvent("warn", "file_mode_tighten_failed", { path: label, error: e.message });
|
|
||||||
}
|
|
||||||
// ENOENT is fine — file doesn't exist yet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tightened > 0) {
|
|
||||||
logEvent("info", "file_modes_tightened", { tightened, alreadyOk });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_tightenFileModesIfPossible();
|
|
||||||
|
|
||||||
// ── Circuit breaker (DISABLED) ──────────────────────────────────────────
|
// ── Circuit breaker (DISABLED) ──────────────────────────────────────────
|
||||||
// Disabled: CLI proxy has its own retry logic, and the breaker was causing
|
// Disabled: CLI proxy has its own retry logic, and the breaker was causing
|
||||||
// cascading failures — once API got briefly slow, ALL agents lost connectivity
|
// cascading failures — once API got briefly slow, ALL agents lost connectivity
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* 4. Creates start.sh for easy launch
|
* 4. Creates start.sh for easy launch
|
||||||
* 5. Optionally starts the proxy
|
* 5. Optionally starts the proxy
|
||||||
*/
|
*/
|
||||||
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, chmodSync } from "node:fs";
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync } from "node:fs";
|
||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { join, dirname } from "node:path";
|
import { join, dirname } from "node:path";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
@@ -39,29 +39,6 @@ const PROVIDER_NAME = opt("provider-name", "claude-local");
|
|||||||
const BIND_ADDRESS = opt("bind", "127.0.0.1");
|
const BIND_ADDRESS = opt("bind", "127.0.0.1");
|
||||||
const AUTH_MODE_CONFIG = opt("auth-mode", "none");
|
const AUTH_MODE_CONFIG = opt("auth-mode", "none");
|
||||||
|
|
||||||
// ── Service-env injection: CLAUDE_BIN, OCP_ADMIN_KEY, PROXY_ANONYMOUS_KEY ──
|
|
||||||
// These are read from the user's shell env at install time and written into
|
|
||||||
// the service unit (plist / systemd) so the daemon picks them up on boot.
|
|
||||||
|
|
||||||
// CLAUDE_BIN — detect at install time; omit if not found (server.mjs fallback)
|
|
||||||
let CLAUDE_BIN_INJECT = null;
|
|
||||||
if (process.env.CLAUDE_BIN) {
|
|
||||||
CLAUDE_BIN_INJECT = process.env.CLAUDE_BIN;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const detected = execSync("which claude 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
||||||
if (detected && existsSync(detected)) {
|
|
||||||
CLAUDE_BIN_INJECT = detected;
|
|
||||||
}
|
|
||||||
} catch { /* which not available or claude not on PATH — omit */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
// OCP_ADMIN_KEY — omit entirely when empty/unset; don't write empty string
|
|
||||||
const OCP_ADMIN_KEY_INJECT = process.env.OCP_ADMIN_KEY || null;
|
|
||||||
|
|
||||||
// PROXY_ANONYMOUS_KEY — same pattern
|
|
||||||
const PROXY_ANON_KEY_INJECT = process.env.PROXY_ANONYMOUS_KEY || null;
|
|
||||||
|
|
||||||
// ── Models: derived from models.json (single source of truth) ──────────
|
// ── Models: derived from models.json (single source of truth) ──────────
|
||||||
const modelsConfig = JSON.parse(readFileSync(join(__dirname, "models.json"), "utf-8"));
|
const modelsConfig = JSON.parse(readFileSync(join(__dirname, "models.json"), "utf-8"));
|
||||||
|
|
||||||
@@ -309,29 +286,6 @@ banner.push(`╚═════════════════════
|
|||||||
console.log("\n" + banner.join("\n") + "\n");
|
console.log("\n" + banner.join("\n") + "\n");
|
||||||
|
|
||||||
// ── Step 7: Install auto-start on boot ──────────────────────────────────
|
// ── Step 7: Install auto-start on boot ──────────────────────────────────
|
||||||
|
|
||||||
// Log service-env injection plan (shown in both dry-run and live mode)
|
|
||||||
console.log("\n🔧 Service unit env vars to inject:\n");
|
|
||||||
if (CLAUDE_BIN_INJECT) {
|
|
||||||
log(`CLAUDE_BIN: ${CLAUDE_BIN_INJECT}`);
|
|
||||||
} else {
|
|
||||||
log(`CLAUDE_BIN: (not found — server.mjs will auto-detect at runtime)`);
|
|
||||||
}
|
|
||||||
if (OCP_ADMIN_KEY_INJECT) {
|
|
||||||
log(`OCP_ADMIN_KEY: injected (length: ${OCP_ADMIN_KEY_INJECT.length})`);
|
|
||||||
} else {
|
|
||||||
log(`OCP_ADMIN_KEY: (unset — admin endpoints disabled)`);
|
|
||||||
}
|
|
||||||
if (PROXY_ANON_KEY_INJECT) {
|
|
||||||
log(`PROXY_ANONYMOUS_KEY: injected (set)`);
|
|
||||||
} else {
|
|
||||||
log(`PROXY_ANONYMOUS_KEY: (unset — anonymous access disabled)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DRY_RUN) {
|
|
||||||
console.log("\n [dry-run] would write service unit with above env vars\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DRY_RUN) {
|
if (!DRY_RUN) {
|
||||||
console.log("\n🔄 Installing auto-start on login...\n");
|
console.log("\n🔄 Installing auto-start on login...\n");
|
||||||
|
|
||||||
@@ -404,13 +358,7 @@ if (!DRY_RUN) {
|
|||||||
<key>CLAUDE_BIND</key>
|
<key>CLAUDE_BIND</key>
|
||||||
<string>${BIND_ADDRESS}</string>
|
<string>${BIND_ADDRESS}</string>
|
||||||
<key>CLAUDE_AUTH_MODE</key>
|
<key>CLAUDE_AUTH_MODE</key>
|
||||||
<string>${AUTH_MODE_CONFIG}</string>${CLAUDE_BIN_INJECT ? `
|
<string>${AUTH_MODE_CONFIG}</string>
|
||||||
<key>CLAUDE_BIN</key>
|
|
||||||
<string>${CLAUDE_BIN_INJECT}</string>` : ""}${OCP_ADMIN_KEY_INJECT ? `
|
|
||||||
<key>OCP_ADMIN_KEY</key>
|
|
||||||
<string>${OCP_ADMIN_KEY_INJECT}</string>` : ""}${PROXY_ANON_KEY_INJECT ? `
|
|
||||||
<key>PROXY_ANONYMOUS_KEY</key>
|
|
||||||
<string>${PROXY_ANON_KEY_INJECT}</string>` : ""}
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
<true/>
|
<true/>
|
||||||
@@ -425,8 +373,7 @@ if (!DRY_RUN) {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
writeFileSync(plistPath, plistXml);
|
writeFileSync(plistPath, plistXml);
|
||||||
chmodSync(plistPath, 0o600);
|
log(`Plist written: ${plistPath}`);
|
||||||
log(`Plist written: ${plistPath} (mode 600)`);
|
|
||||||
|
|
||||||
// Bootout first (in case it was already loaded) then bootstrap
|
// Bootout first (in case it was already loaded) then bootstrap
|
||||||
try { execSync(`launchctl bootout gui/$(id -u) "${plistPath}" 2>/dev/null`); } catch { /* ignore */ }
|
try { execSync(`launchctl bootout gui/$(id -u) "${plistPath}" 2>/dev/null`); } catch { /* ignore */ }
|
||||||
@@ -449,7 +396,7 @@ After=network.target
|
|||||||
ExecStart=${nodeBin} ${serverPath}
|
ExecStart=${nodeBin} ${serverPath}
|
||||||
Environment=CLAUDE_PROXY_PORT=${PORT}
|
Environment=CLAUDE_PROXY_PORT=${PORT}
|
||||||
Environment=CLAUDE_BIND=${BIND_ADDRESS}
|
Environment=CLAUDE_BIND=${BIND_ADDRESS}
|
||||||
Environment=CLAUDE_AUTH_MODE=${AUTH_MODE_CONFIG}${CLAUDE_BIN_INJECT ? `\nEnvironment=CLAUDE_BIN=${CLAUDE_BIN_INJECT}` : ""}${OCP_ADMIN_KEY_INJECT ? `\nEnvironment=OCP_ADMIN_KEY=${OCP_ADMIN_KEY_INJECT}` : ""}${PROXY_ANON_KEY_INJECT ? `\nEnvironment=PROXY_ANONYMOUS_KEY=${PROXY_ANON_KEY_INJECT}` : ""}
|
Environment=CLAUDE_AUTH_MODE=${AUTH_MODE_CONFIG}
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
StandardOutput=append:${logPath}
|
StandardOutput=append:${logPath}
|
||||||
@@ -460,8 +407,7 @@ WantedBy=default.target
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
writeFileSync(servicePath, serviceUnit);
|
writeFileSync(servicePath, serviceUnit);
|
||||||
chmodSync(servicePath, 0o600);
|
log(`Service file written: ${servicePath}`);
|
||||||
log(`Service file written: ${servicePath} (mode 600)`);
|
|
||||||
|
|
||||||
execSync(`systemctl --user daemon-reload`);
|
execSync(`systemctl --user daemon-reload`);
|
||||||
execSync(`systemctl --user enable ocp-proxy`);
|
execSync(`systemctl --user enable ocp-proxy`);
|
||||||
|
|||||||
Reference in New Issue
Block a user