fix: honor gateway service override labels
This commit is contained in:
@@ -6,6 +6,9 @@
|
|||||||
- Cron: accept ISO timestamps for one-shot schedules (UTC) and allow optional delete-after-run; wired into CLI + macOS editor.
|
- Cron: accept ISO timestamps for one-shot schedules (UTC) and allow optional delete-after-run; wired into CLI + macOS editor.
|
||||||
- Gateway: add Tailscale binary discovery, custom bind mode, and probe auth retry for password changes. (#740 — thanks @jeffersonwarrior)
|
- Gateway: add Tailscale binary discovery, custom bind mode, and probe auth retry for password changes. (#740 — thanks @jeffersonwarrior)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Gateway: honor `CLAWDBOT_LAUNCHD_LABEL` / `CLAWDBOT_SYSTEMD_UNIT` overrides when checking or restarting the daemon.
|
||||||
|
|
||||||
## 2026.1.12-4
|
## 2026.1.12-4
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ pnpm gateway:watch
|
|||||||
|
|
||||||
## Multiple gateways (same host)
|
## Multiple gateways (same host)
|
||||||
|
|
||||||
|
Usually unnecessary: one Gateway can serve multiple messaging channels and agents. Use multiple Gateways only for redundancy or strict isolation (ex: rescue bot).
|
||||||
|
|
||||||
Supported if you isolate state + config and use unique ports.
|
Supported if you isolate state + config and use unique ports.
|
||||||
|
|
||||||
Service names are profile-aware:
|
Service names are profile-aware:
|
||||||
@@ -96,6 +98,12 @@ Checklist per instance:
|
|||||||
- unique `agents.defaults.workspace`
|
- unique `agents.defaults.workspace`
|
||||||
- separate WhatsApp numbers (if using WA)
|
- separate WhatsApp numbers (if using WA)
|
||||||
|
|
||||||
|
Daemon install per profile:
|
||||||
|
```bash
|
||||||
|
clawdbot --profile main daemon install
|
||||||
|
clawdbot --profile rescue daemon install
|
||||||
|
```
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```bash
|
```bash
|
||||||
CLAWDBOT_CONFIG_PATH=~/.clawdbot/a.json CLAWDBOT_STATE_DIR=~/.clawdbot-a clawdbot gateway --port 19001
|
CLAWDBOT_CONFIG_PATH=~/.clawdbot/a.json CLAWDBOT_STATE_DIR=~/.clawdbot-a clawdbot gateway --port 19001
|
||||||
|
|||||||
@@ -829,6 +829,8 @@ Fix:
|
|||||||
|
|
||||||
### Can I run multiple Gateways on the same host?
|
### Can I run multiple Gateways on the same host?
|
||||||
|
|
||||||
|
Usually no — one Gateway can run multiple messaging channels and agents. Use multiple Gateways only when you need redundancy (ex: rescue bot) or hard isolation.
|
||||||
|
|
||||||
Yes, but you must isolate:
|
Yes, but you must isolate:
|
||||||
|
|
||||||
- `CLAWDBOT_CONFIG_PATH` (per‑instance config)
|
- `CLAWDBOT_CONFIG_PATH` (per‑instance config)
|
||||||
@@ -836,9 +838,12 @@ Yes, but you must isolate:
|
|||||||
- `agents.defaults.workspace` (workspace isolation)
|
- `agents.defaults.workspace` (workspace isolation)
|
||||||
- `gateway.port` (unique ports)
|
- `gateway.port` (unique ports)
|
||||||
|
|
||||||
There are convenience CLI flags like `--dev` and `--profile <name>` that shift state dirs and ports.
|
Quick setup (recommended):
|
||||||
When using profiles, service names are suffixed (`com.clawdbot.<profile>`, `clawdbot-gateway-<profile>.service`,
|
- Use `clawdbot --profile <name> …` per instance (auto-creates `~/.clawdbot-<name>`).
|
||||||
`Clawdbot Gateway (<profile>)`).
|
- Set a unique `gateway.port` in each profile config (or pass `--port` for manual runs).
|
||||||
|
- Install a per-profile daemon: `clawdbot --profile <name> daemon install`.
|
||||||
|
|
||||||
|
Profiles also suffix service names (`com.clawdbot.<profile>`, `clawdbot-gateway-<profile>.service`, `Clawdbot Gateway (<profile>)`).
|
||||||
|
|
||||||
## Logging and debugging
|
## Logging and debugging
|
||||||
|
|
||||||
|
|||||||
@@ -367,7 +367,10 @@ async function gatherDaemonStatus(opts: {
|
|||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const [loaded, command, runtime] = await Promise.all([
|
const [loaded, command, runtime] = await Promise.all([
|
||||||
service
|
service
|
||||||
.isLoaded({ profile: process.env.CLAWDBOT_PROFILE })
|
.isLoaded({
|
||||||
|
env: process.env,
|
||||||
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
|
})
|
||||||
.catch(() => false),
|
.catch(() => false),
|
||||||
service.readCommand(process.env).catch(() => null),
|
service.readCommand(process.env).catch(() => null),
|
||||||
service.readRuntime(process.env).catch(() => undefined),
|
service.readRuntime(process.env).catch(() => undefined),
|
||||||
@@ -899,7 +902,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
|||||||
const profile = process.env.CLAWDBOT_PROFILE;
|
const profile = process.env.CLAWDBOT_PROFILE;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile });
|
loaded = await service.isLoaded({ env: process.env, profile });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
@@ -980,7 +983,7 @@ export async function runDaemonStart() {
|
|||||||
const profile = process.env.CLAWDBOT_PROFILE;
|
const profile = process.env.CLAWDBOT_PROFILE;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile });
|
loaded = await service.isLoaded({ env: process.env, profile });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
@@ -994,7 +997,11 @@ export async function runDaemonStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await service.restart({ profile, stdout: process.stdout });
|
await service.restart({
|
||||||
|
env: process.env,
|
||||||
|
profile,
|
||||||
|
stdout: process.stdout,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway start failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway start failed: ${String(err)}`);
|
||||||
for (const hint of renderGatewayServiceStartHints()) {
|
for (const hint of renderGatewayServiceStartHints()) {
|
||||||
@@ -1009,7 +1016,7 @@ export async function runDaemonStop() {
|
|||||||
const profile = process.env.CLAWDBOT_PROFILE;
|
const profile = process.env.CLAWDBOT_PROFILE;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile });
|
loaded = await service.isLoaded({ env: process.env, profile });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
@@ -1020,7 +1027,7 @@ export async function runDaemonStop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await service.stop({ profile, stdout: process.stdout });
|
await service.stop({ env: process.env, profile, stdout: process.stdout });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway stop failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway stop failed: ${String(err)}`);
|
||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
@@ -1037,7 +1044,7 @@ export async function runDaemonRestart(): Promise<boolean> {
|
|||||||
const profile = process.env.CLAWDBOT_PROFILE;
|
const profile = process.env.CLAWDBOT_PROFILE;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile });
|
loaded = await service.isLoaded({ env: process.env, profile });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway service check failed: ${String(err)}`);
|
||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
@@ -1051,7 +1058,11 @@ export async function runDaemonRestart(): Promise<boolean> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await service.restart({ profile, stdout: process.stdout });
|
await service.restart({
|
||||||
|
env: process.env,
|
||||||
|
profile,
|
||||||
|
stdout: process.stdout,
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.error(`Gateway restart failed: ${String(err)}`);
|
defaultRuntime.error(`Gateway restart failed: ${String(err)}`);
|
||||||
|
|||||||
@@ -399,7 +399,10 @@ async function maybeExplainGatewayServiceStop() {
|
|||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
let loaded: boolean | null = null;
|
let loaded: boolean | null = null;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile: process.env.CLAWDBOT_PROFILE });
|
loaded = await service.isLoaded({
|
||||||
|
env: process.env,
|
||||||
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
loaded = null;
|
loaded = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ async function maybeInstallDaemon(params: {
|
|||||||
}) {
|
}) {
|
||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const loaded = await service.isLoaded({
|
const loaded = await service.isLoaded({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
});
|
});
|
||||||
let shouldCheckLinger = false;
|
let shouldCheckLinger = false;
|
||||||
@@ -492,6 +493,7 @@ async function maybeInstallDaemon(params: {
|
|||||||
);
|
);
|
||||||
if (action === "restart") {
|
if (action === "restart") {
|
||||||
await service.restart({
|
await service.restart({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ export async function maybeMigrateLegacyGatewayService(
|
|||||||
|
|
||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const loaded = await service.isLoaded({
|
const loaded = await service.isLoaded({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
});
|
});
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
|
|||||||
@@ -450,6 +450,7 @@ export async function doctorCommand(
|
|||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({
|
loaded = await service.isLoaded({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
@@ -575,6 +576,7 @@ export async function doctorCommand(
|
|||||||
if (!healthOk) {
|
if (!healthOk) {
|
||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const loaded = await service.isLoaded({
|
const loaded = await service.isLoaded({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
});
|
});
|
||||||
let serviceRuntime:
|
let serviceRuntime:
|
||||||
@@ -676,6 +678,7 @@ export async function doctorCommand(
|
|||||||
});
|
});
|
||||||
if (start) {
|
if (start) {
|
||||||
await service.restart({
|
await service.restart({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
});
|
});
|
||||||
@@ -698,6 +701,7 @@ export async function doctorCommand(
|
|||||||
});
|
});
|
||||||
if (restart) {
|
if (restart) {
|
||||||
await service.restart({
|
await service.restart({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ async function stopGatewayIfRunning(runtime: RuntimeEnv) {
|
|||||||
const profile = process.env.CLAWDBOT_PROFILE;
|
const profile = process.env.CLAWDBOT_PROFILE;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile });
|
loaded = await service.isLoaded({ env: process.env, profile });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
runtime.error(`Gateway service check failed: ${String(err)}`);
|
runtime.error(`Gateway service check failed: ${String(err)}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!loaded) return;
|
if (!loaded) return;
|
||||||
try {
|
try {
|
||||||
await service.stop({ profile, stdout: process.stdout });
|
await service.stop({ env: process.env, profile, stdout: process.stdout });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
runtime.error(`Gateway stop failed: ${String(err)}`);
|
runtime.error(`Gateway stop failed: ${String(err)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,10 @@ export async function statusAllCommand(
|
|||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const [loaded, runtimeInfo, command] = await Promise.all([
|
const [loaded, runtimeInfo, command] = await Promise.all([
|
||||||
service
|
service
|
||||||
.isLoaded({ profile: process.env.CLAWDBOT_PROFILE })
|
.isLoaded({
|
||||||
|
env: process.env,
|
||||||
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
|
})
|
||||||
.catch(() => false),
|
.catch(() => false),
|
||||||
service.readRuntime(process.env).catch(() => undefined),
|
service.readRuntime(process.env).catch(() => undefined),
|
||||||
service.readCommand(process.env).catch(() => null),
|
service.readCommand(process.env).catch(() => null),
|
||||||
|
|||||||
@@ -336,7 +336,10 @@ async function getDaemonStatusSummary(): Promise<{
|
|||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const [loaded, runtime, command] = await Promise.all([
|
const [loaded, runtime, command] = await Promise.all([
|
||||||
service
|
service
|
||||||
.isLoaded({ profile: process.env.CLAWDBOT_PROFILE })
|
.isLoaded({
|
||||||
|
env: process.env,
|
||||||
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
|
})
|
||||||
.catch(() => false),
|
.catch(() => false),
|
||||||
service.readRuntime(process.env).catch(() => undefined),
|
service.readRuntime(process.env).catch(() => undefined),
|
||||||
service.readCommand(process.env).catch(() => null),
|
service.readCommand(process.env).catch(() => null),
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ async function stopAndUninstallService(runtime: RuntimeEnv): Promise<boolean> {
|
|||||||
const profile = process.env.CLAWDBOT_PROFILE;
|
const profile = process.env.CLAWDBOT_PROFILE;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
try {
|
try {
|
||||||
loaded = await service.isLoaded({ profile });
|
loaded = await service.isLoaded({ env: process.env, profile });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
runtime.error(`Gateway service check failed: ${String(err)}`);
|
runtime.error(`Gateway service check failed: ${String(err)}`);
|
||||||
return false;
|
return false;
|
||||||
@@ -80,7 +80,7 @@ async function stopAndUninstallService(runtime: RuntimeEnv): Promise<boolean> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await service.stop({ profile, stdout: process.stdout });
|
await service.stop({ env: process.env, profile, stdout: process.stdout });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
runtime.error(`Gateway stop failed: ${String(err)}`);
|
runtime.error(`Gateway stop failed: ${String(err)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ const formatLine = (label: string, value: string) => {
|
|||||||
const rich = isRich();
|
const rich = isRich();
|
||||||
return `${colorize(rich, theme.muted, `${label}:`)} ${colorize(rich, theme.command, value)}`;
|
return `${colorize(rich, theme.muted, `${label}:`)} ${colorize(rich, theme.command, value)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function resolveLaunchAgentLabel(params?: {
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
profile?: string;
|
||||||
|
}): string {
|
||||||
|
const envLabel = params?.env?.CLAWDBOT_LAUNCHD_LABEL?.trim();
|
||||||
|
if (envLabel) return envLabel;
|
||||||
|
return resolveGatewayLaunchAgentLabel(params?.profile);
|
||||||
|
}
|
||||||
function resolveHomeDir(env: Record<string, string | undefined>): string {
|
function resolveHomeDir(env: Record<string, string | undefined>): string {
|
||||||
const home = env.HOME?.trim() || env.USERPROFILE?.trim();
|
const home = env.HOME?.trim() || env.USERPROFILE?.trim();
|
||||||
if (!home) throw new Error("Missing HOME");
|
if (!home) throw new Error("Missing HOME");
|
||||||
@@ -284,9 +293,12 @@ export function parseLaunchctlPrint(output: string): LaunchctlPrintInfo {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isLaunchAgentLoaded(profile?: string): Promise<boolean> {
|
export async function isLaunchAgentLoaded(params?: {
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
profile?: string;
|
||||||
|
}): Promise<boolean> {
|
||||||
const domain = resolveGuiDomain();
|
const domain = resolveGuiDomain();
|
||||||
const label = resolveGatewayLaunchAgentLabel(profile);
|
const label = resolveLaunchAgentLabel(params);
|
||||||
const res = await execLaunchctl(["print", `${domain}/${label}`]);
|
const res = await execLaunchctl(["print", `${domain}/${label}`]);
|
||||||
return res.code === 0;
|
return res.code === 0;
|
||||||
}
|
}
|
||||||
@@ -461,13 +473,15 @@ function isLaunchctlNotLoaded(res: {
|
|||||||
|
|
||||||
export async function stopLaunchAgent({
|
export async function stopLaunchAgent({
|
||||||
stdout,
|
stdout,
|
||||||
|
env,
|
||||||
profile,
|
profile,
|
||||||
}: {
|
}: {
|
||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const domain = resolveGuiDomain();
|
const domain = resolveGuiDomain();
|
||||||
const label = resolveGatewayLaunchAgentLabel(profile);
|
const label = resolveLaunchAgentLabel({ env, profile });
|
||||||
const res = await execLaunchctl(["bootout", `${domain}/${label}`]);
|
const res = await execLaunchctl(["bootout", `${domain}/${label}`]);
|
||||||
if (res.code !== 0 && !isLaunchctlNotLoaded(res)) {
|
if (res.code !== 0 && !isLaunchctlNotLoaded(res)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -548,13 +562,15 @@ export async function installLaunchAgent({
|
|||||||
|
|
||||||
export async function restartLaunchAgent({
|
export async function restartLaunchAgent({
|
||||||
stdout,
|
stdout,
|
||||||
|
env,
|
||||||
profile,
|
profile,
|
||||||
}: {
|
}: {
|
||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const domain = resolveGuiDomain();
|
const domain = resolveGuiDomain();
|
||||||
const label = resolveGatewayLaunchAgentLabel(profile);
|
const label = resolveLaunchAgentLabel({ env, profile });
|
||||||
const res = await execLaunchctl(["kickstart", "-k", `${domain}/${label}`]);
|
const res = await execLaunchctl(["kickstart", "-k", `${domain}/${label}`]);
|
||||||
if (res.code !== 0) {
|
if (res.code !== 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@@ -45,14 +45,19 @@ export type GatewayService = {
|
|||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
stop: (args: {
|
stop: (args: {
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
restart: (args: {
|
restart: (args: {
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
isLoaded: (args: { profile?: string }) => Promise<boolean>;
|
isLoaded: (args: {
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
profile?: string;
|
||||||
|
}) => Promise<boolean>;
|
||||||
readCommand: (env: Record<string, string | undefined>) => Promise<{
|
readCommand: (env: Record<string, string | undefined>) => Promise<{
|
||||||
programArguments: string[];
|
programArguments: string[];
|
||||||
workingDirectory?: string;
|
workingDirectory?: string;
|
||||||
@@ -77,15 +82,21 @@ export function resolveGatewayService(): GatewayService {
|
|||||||
await uninstallLaunchAgent(args);
|
await uninstallLaunchAgent(args);
|
||||||
},
|
},
|
||||||
stop: async (args) => {
|
stop: async (args) => {
|
||||||
await stopLaunchAgent({ stdout: args.stdout, profile: args.profile });
|
await stopLaunchAgent({
|
||||||
|
stdout: args.stdout,
|
||||||
|
profile: args.profile,
|
||||||
|
env: args.env,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
restart: async (args) => {
|
restart: async (args) => {
|
||||||
await restartLaunchAgent({
|
await restartLaunchAgent({
|
||||||
stdout: args.stdout,
|
stdout: args.stdout,
|
||||||
profile: args.profile,
|
profile: args.profile,
|
||||||
|
env: args.env,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isLoaded: async (args) => isLaunchAgentLoaded(args.profile),
|
isLoaded: async (args) =>
|
||||||
|
isLaunchAgentLoaded({ profile: args.profile, env: args.env }),
|
||||||
readCommand: readLaunchAgentProgramArguments,
|
readCommand: readLaunchAgentProgramArguments,
|
||||||
readRuntime: readLaunchAgentRuntime,
|
readRuntime: readLaunchAgentRuntime,
|
||||||
};
|
};
|
||||||
@@ -106,15 +117,18 @@ export function resolveGatewayService(): GatewayService {
|
|||||||
await stopSystemdService({
|
await stopSystemdService({
|
||||||
stdout: args.stdout,
|
stdout: args.stdout,
|
||||||
profile: args.profile,
|
profile: args.profile,
|
||||||
|
env: args.env,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
restart: async (args) => {
|
restart: async (args) => {
|
||||||
await restartSystemdService({
|
await restartSystemdService({
|
||||||
stdout: args.stdout,
|
stdout: args.stdout,
|
||||||
profile: args.profile,
|
profile: args.profile,
|
||||||
|
env: args.env,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isLoaded: async (args) => isSystemdServiceEnabled(args.profile),
|
isLoaded: async (args) =>
|
||||||
|
isSystemdServiceEnabled({ profile: args.profile, env: args.env }),
|
||||||
readCommand: readSystemdServiceExecStart,
|
readCommand: readSystemdServiceExecStart,
|
||||||
readRuntime: async (env) => await readSystemdServiceRuntime(env),
|
readRuntime: async (env) => await readSystemdServiceRuntime(env),
|
||||||
};
|
};
|
||||||
@@ -132,7 +146,10 @@ export function resolveGatewayService(): GatewayService {
|
|||||||
await uninstallScheduledTask(args);
|
await uninstallScheduledTask(args);
|
||||||
},
|
},
|
||||||
stop: async (args) => {
|
stop: async (args) => {
|
||||||
await stopScheduledTask({ stdout: args.stdout, profile: args.profile });
|
await stopScheduledTask({
|
||||||
|
stdout: args.stdout,
|
||||||
|
profile: args.profile,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
restart: async (args) => {
|
restart: async (args) => {
|
||||||
await restartScheduledTask({
|
await restartScheduledTask({
|
||||||
|
|||||||
@@ -46,6 +46,14 @@ function resolveSystemdServiceName(
|
|||||||
return resolveGatewaySystemdServiceName(env.CLAWDBOT_PROFILE);
|
return resolveGatewaySystemdServiceName(env.CLAWDBOT_PROFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveSystemdServiceNameFromParams(params?: {
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
profile?: string;
|
||||||
|
}): string {
|
||||||
|
if (params?.env) return resolveSystemdServiceName(params.env);
|
||||||
|
return resolveGatewaySystemdServiceName(params?.profile);
|
||||||
|
}
|
||||||
|
|
||||||
function resolveSystemdUnitPath(
|
function resolveSystemdUnitPath(
|
||||||
env: Record<string, string | undefined>,
|
env: Record<string, string | undefined>,
|
||||||
): string {
|
): string {
|
||||||
@@ -466,13 +474,15 @@ export async function uninstallSystemdService({
|
|||||||
|
|
||||||
export async function stopSystemdService({
|
export async function stopSystemdService({
|
||||||
stdout,
|
stdout,
|
||||||
|
env,
|
||||||
profile,
|
profile,
|
||||||
}: {
|
}: {
|
||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
await assertSystemdAvailable();
|
await assertSystemdAvailable();
|
||||||
const serviceName = resolveGatewaySystemdServiceName(profile);
|
const serviceName = resolveSystemdServiceNameFromParams({ env, profile });
|
||||||
const unitName = `${serviceName}.service`;
|
const unitName = `${serviceName}.service`;
|
||||||
const res = await execSystemctl(["--user", "stop", unitName]);
|
const res = await execSystemctl(["--user", "stop", unitName]);
|
||||||
if (res.code !== 0) {
|
if (res.code !== 0) {
|
||||||
@@ -485,13 +495,15 @@ export async function stopSystemdService({
|
|||||||
|
|
||||||
export async function restartSystemdService({
|
export async function restartSystemdService({
|
||||||
stdout,
|
stdout,
|
||||||
|
env,
|
||||||
profile,
|
profile,
|
||||||
}: {
|
}: {
|
||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
await assertSystemdAvailable();
|
await assertSystemdAvailable();
|
||||||
const serviceName = resolveGatewaySystemdServiceName(profile);
|
const serviceName = resolveSystemdServiceNameFromParams({ env, profile });
|
||||||
const unitName = `${serviceName}.service`;
|
const unitName = `${serviceName}.service`;
|
||||||
const res = await execSystemctl(["--user", "restart", unitName]);
|
const res = await execSystemctl(["--user", "restart", unitName]);
|
||||||
if (res.code !== 0) {
|
if (res.code !== 0) {
|
||||||
@@ -502,11 +514,12 @@ export async function restartSystemdService({
|
|||||||
stdout.write(`${formatLine("Restarted systemd service", unitName)}\n`);
|
stdout.write(`${formatLine("Restarted systemd service", unitName)}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isSystemdServiceEnabled(
|
export async function isSystemdServiceEnabled(params?: {
|
||||||
profile?: string,
|
env?: Record<string, string | undefined>;
|
||||||
): Promise<boolean> {
|
profile?: string;
|
||||||
|
}): Promise<boolean> {
|
||||||
await assertSystemdAvailable();
|
await assertSystemdAvailable();
|
||||||
const serviceName = resolveGatewaySystemdServiceName(profile);
|
const serviceName = resolveSystemdServiceNameFromParams(params);
|
||||||
const unitName = `${serviceName}.service`;
|
const unitName = `${serviceName}.service`;
|
||||||
const res = await execSystemctl(["--user", "is-enabled", unitName]);
|
const res = await execSystemctl(["--user", "is-enabled", unitName]);
|
||||||
return res.code === 0;
|
return res.code === 0;
|
||||||
|
|||||||
@@ -697,6 +697,7 @@ export async function runOnboardingWizard(
|
|||||||
}
|
}
|
||||||
const service = resolveGatewayService();
|
const service = resolveGatewayService();
|
||||||
const loaded = await service.isLoaded({
|
const loaded = await service.isLoaded({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
});
|
});
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
@@ -710,6 +711,7 @@ export async function runOnboardingWizard(
|
|||||||
})) as "restart" | "reinstall" | "skip";
|
})) as "restart" | "reinstall" | "skip";
|
||||||
if (action === "restart") {
|
if (action === "restart") {
|
||||||
await service.restart({
|
await service.restart({
|
||||||
|
env: process.env,
|
||||||
profile: process.env.CLAWDBOT_PROFILE,
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
});
|
});
|
||||||
@@ -721,8 +723,10 @@ export async function runOnboardingWizard(
|
|||||||
if (
|
if (
|
||||||
!loaded ||
|
!loaded ||
|
||||||
(loaded &&
|
(loaded &&
|
||||||
(await service.isLoaded({ profile: process.env.CLAWDBOT_PROFILE })) ===
|
(await service.isLoaded({
|
||||||
false)
|
env: process.env,
|
||||||
|
profile: process.env.CLAWDBOT_PROFILE,
|
||||||
|
})) === false)
|
||||||
) {
|
) {
|
||||||
const devMode =
|
const devMode =
|
||||||
process.argv[1]?.includes(`${path.sep}src${path.sep}`) &&
|
process.argv[1]?.includes(`${path.sep}src${path.sep}`) &&
|
||||||
|
|||||||
Reference in New Issue
Block a user