fix: soften windows daemon install
This commit is contained in:
@@ -42,6 +42,28 @@ const STEP_LABELS: Record<string, string> = {
|
|||||||
type UpdateChannel = "stable" | "beta";
|
type UpdateChannel = "stable" | "beta";
|
||||||
|
|
||||||
const DEFAULT_UPDATE_CHANNEL: UpdateChannel = "stable";
|
const DEFAULT_UPDATE_CHANNEL: UpdateChannel = "stable";
|
||||||
|
const UPDATE_QUIPS = [
|
||||||
|
"Leveled up! New skills unlocked. You're welcome.",
|
||||||
|
"Fresh code, same lobster. Miss me?",
|
||||||
|
"Back and better. Did you even notice I was gone?",
|
||||||
|
"Update complete. I learned some new tricks while I was out.",
|
||||||
|
"Upgraded! Now with 23% more sass.",
|
||||||
|
"I've evolved. Try to keep up.",
|
||||||
|
"New version, who dis? Oh right, still me but shinier.",
|
||||||
|
"Patched, polished, and ready to pinch. Let's go.",
|
||||||
|
"The lobster has molted. Harder shell, sharper claws.",
|
||||||
|
"Update done! Check the changelog or just trust me, it's good.",
|
||||||
|
"Reborn from the boiling waters of npm. Stronger now.",
|
||||||
|
"I went away and came back smarter. You should try it sometime.",
|
||||||
|
"Update complete. The bugs feared me, so they left.",
|
||||||
|
"New version installed. Old version sends its regards.",
|
||||||
|
"Firmware fresh. Brain wrinkles: increased.",
|
||||||
|
"I've seen things you wouldn't believe. Anyway, I'm updated.",
|
||||||
|
"Back online. The changelog is long but our friendship is longer.",
|
||||||
|
"Upgraded! Peter fixed stuff. Blame him if it breaks.",
|
||||||
|
"Molting complete. Please don't look at my soft shell phase.",
|
||||||
|
"Version bump! Same chaos energy, fewer crashes (probably).",
|
||||||
|
];
|
||||||
|
|
||||||
function normalizeChannel(value?: string | null): UpdateChannel | null {
|
function normalizeChannel(value?: string | null): UpdateChannel | null {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
@@ -61,6 +83,10 @@ function channelToTag(channel: UpdateChannel): string {
|
|||||||
return channel === "beta" ? "beta" : "latest";
|
return channel === "beta" ? "beta" : "latest";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pickUpdateQuip(): string {
|
||||||
|
return UPDATE_QUIPS[Math.floor(Math.random() * UPDATE_QUIPS.length)] ?? "Update complete.";
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeVersionTag(tag: string): string | null {
|
function normalizeVersionTag(tag: string): string | null {
|
||||||
const trimmed = tag.trim();
|
const trimmed = tag.trim();
|
||||||
if (!trimmed) return null;
|
if (!trimmed) return null;
|
||||||
@@ -402,6 +428,10 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!opts.json) {
|
||||||
|
defaultRuntime.log(theme.muted(pickUpdateQuip()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerUpdateCli(program: Command) {
|
export function registerUpdateCli(program: Command) {
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export async function maybeInstallDaemon(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldInstall) {
|
if (shouldInstall) {
|
||||||
|
let installError: unknown | null = null;
|
||||||
await withProgress(
|
await withProgress(
|
||||||
{ label: "Gateway daemon", indeterminate: true, delayMs: 0 },
|
{ label: "Gateway daemon", indeterminate: true, delayMs: 0 },
|
||||||
async (progress) => {
|
async (progress) => {
|
||||||
@@ -117,6 +118,7 @@ export async function maybeInstallDaemon(params: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
progress.setLabel("Installing Gateway daemon…");
|
progress.setLabel("Installing Gateway daemon…");
|
||||||
|
try {
|
||||||
await service.install({
|
await service.install({
|
||||||
env: process.env,
|
env: process.env,
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
@@ -125,8 +127,24 @@ export async function maybeInstallDaemon(params: {
|
|||||||
environment,
|
environment,
|
||||||
});
|
});
|
||||||
progress.setLabel("Gateway daemon installed.");
|
progress.setLabel("Gateway daemon installed.");
|
||||||
|
} catch (err) {
|
||||||
|
installError = err;
|
||||||
|
progress.setLabel("Gateway daemon install failed.");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (installError) {
|
||||||
|
note(`Gateway daemon install failed: ${String(installError)}`, "Gateway");
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
note(
|
||||||
|
"Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip daemon install.",
|
||||||
|
"Gateway",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
note("Tip: rerun `clawdbot daemon install` after fixing the error.", "Gateway");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
shouldCheckLinger = true;
|
shouldCheckLinger = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export async function installGatewayDaemonNonInteractive(params: {
|
|||||||
? resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE)
|
? resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE)
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
await service.install({
|
await service.install({
|
||||||
env: process.env,
|
env: process.env,
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
@@ -76,5 +77,16 @@ export async function installGatewayDaemonNonInteractive(params: {
|
|||||||
workingDirectory,
|
workingDirectory,
|
||||||
environment,
|
environment,
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
runtime.error(`Gateway daemon install failed: ${String(err)}`);
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
runtime.log(
|
||||||
|
"Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip daemon install.",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
runtime.log("Tip: rerun `clawdbot daemon install` after fixing the error.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
await ensureSystemdUserLingerNonInteractive({ runtime });
|
await ensureSystemdUserLingerNonInteractive({ runtime });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ function quoteCmdArg(value: string): string {
|
|||||||
return `"${value.replace(/"/g, '\\"')}"`;
|
return `"${value.replace(/"/g, '\\"')}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveTaskUser(env: Record<string, string | undefined>): string | null {
|
||||||
|
const username = env.USERNAME || env.USER || env.LOGNAME;
|
||||||
|
if (!username) return null;
|
||||||
|
if (username.includes("\\")) return username;
|
||||||
|
const domain = env.USERDOMAIN;
|
||||||
|
if (domain) return `${domain}\\${username}`;
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
function parseCommandLine(value: string): string[] {
|
function parseCommandLine(value: string): string[] {
|
||||||
const args: string[] = [];
|
const args: string[] = [];
|
||||||
let current = "";
|
let current = "";
|
||||||
@@ -216,7 +225,7 @@ export async function installScheduledTask({
|
|||||||
|
|
||||||
const taskName = resolveGatewayWindowsTaskName(env.CLAWDBOT_PROFILE);
|
const taskName = resolveGatewayWindowsTaskName(env.CLAWDBOT_PROFILE);
|
||||||
const quotedScript = quoteCmdArg(scriptPath);
|
const quotedScript = quoteCmdArg(scriptPath);
|
||||||
const create = await execSchtasks([
|
const baseArgs = [
|
||||||
"/Create",
|
"/Create",
|
||||||
"/F",
|
"/F",
|
||||||
"/SC",
|
"/SC",
|
||||||
@@ -227,9 +236,20 @@ export async function installScheduledTask({
|
|||||||
taskName,
|
taskName,
|
||||||
"/TR",
|
"/TR",
|
||||||
quotedScript,
|
quotedScript,
|
||||||
]);
|
];
|
||||||
|
const taskUser = resolveTaskUser(env);
|
||||||
|
let create = await execSchtasks(
|
||||||
|
taskUser ? [...baseArgs, "/RU", taskUser, "/NP", "/IT"] : baseArgs,
|
||||||
|
);
|
||||||
|
if (create.code !== 0 && taskUser) {
|
||||||
|
create = await execSchtasks(baseArgs);
|
||||||
|
}
|
||||||
if (create.code !== 0) {
|
if (create.code !== 0) {
|
||||||
throw new Error(`schtasks create failed: ${create.stderr || create.stdout}`.trim());
|
const detail = create.stderr || create.stdout;
|
||||||
|
const hint = /access is denied/i.test(detail)
|
||||||
|
? " Run PowerShell as Administrator or rerun without installing the daemon."
|
||||||
|
: "";
|
||||||
|
throw new Error(`schtasks create failed: ${detail}${hint}`.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
await execSchtasks(["/Run", "/TN", taskName]);
|
await execSchtasks(["/Run", "/TN", taskName]);
|
||||||
|
|||||||
Reference in New Issue
Block a user