@longleash/cli 0.1.0-rc.6 → 0.1.0-rc.8

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/bin/longleash.mjs CHANGED
@@ -1816,8 +1816,8 @@ StartLimitBurst=5
1816
1816
  [Service]
1817
1817
  Type=simple
1818
1818
  ExecStart=${systemdQuote(paths.wrapper)} run
1819
- EnvironmentFile=${systemdQuote(paths.environment)}
1820
- WorkingDirectory=${systemdQuote(home)}
1819
+ EnvironmentFile=${systemdPath(paths.environment)}
1820
+ WorkingDirectory=${systemdPath(home)}
1821
1821
  UMask=0077
1822
1822
  Restart=on-failure
1823
1823
  RestartSec=5s
@@ -1879,7 +1879,7 @@ function startService(context = {}) {
1879
1879
  assertServiceInstallation(resolved);
1880
1880
  if (resolved.platform === "darwin") {
1881
1881
  if (launchdLoaded(resolved)) requireSuccess(resolved, "/bin/launchctl", ["kickstart", "-k", serviceTarget(resolved)]);
1882
- else requireSuccess(resolved, "/bin/launchctl", ["bootstrap", launchDomain(resolved), resolved.paths.definition]);
1882
+ else bootstrapLaunchAgent(resolved);
1883
1883
  } else {
1884
1884
  requireSuccess(resolved, "systemctl", ["--user", "start", SYSTEMD_UNIT]);
1885
1885
  }
@@ -1904,7 +1904,7 @@ function restartService(context = {}) {
1904
1904
  assertServiceInstallation(resolved);
1905
1905
  if (resolved.platform === "darwin") {
1906
1906
  if (launchdLoaded(resolved)) requireSuccess(resolved, "/bin/launchctl", ["kickstart", "-k", serviceTarget(resolved)]);
1907
- else requireSuccess(resolved, "/bin/launchctl", ["bootstrap", launchDomain(resolved), resolved.paths.definition]);
1907
+ else bootstrapLaunchAgent(resolved);
1908
1908
  } else {
1909
1909
  requireSuccess(resolved, "systemctl", ["--user", "restart", SYSTEMD_UNIT]);
1910
1910
  }
@@ -1960,27 +1960,52 @@ function installLaunchAgent(context) {
1960
1960
  writeManagedAtomically(context.paths.definition, content, 384, (temporary) => {
1961
1961
  requireSuccess(context, "/usr/bin/plutil", ["-lint", temporary]);
1962
1962
  });
1963
- requireSuccess(context, "/bin/launchctl", ["bootstrap", launchDomain(context), context.paths.definition]);
1963
+ bootstrapLaunchAgent(context);
1964
1964
  } catch (error) {
1965
1965
  restore(context.paths.definition, previous);
1966
1966
  if (previous !== null && wasLoaded) run(context, "/bin/launchctl", ["bootstrap", launchDomain(context), context.paths.definition]);
1967
1967
  throw error;
1968
1968
  }
1969
1969
  }
1970
+ function bootstrapLaunchAgent(context) {
1971
+ const args = ["bootstrap", launchDomain(context), context.paths.definition];
1972
+ const first = run(context, "/bin/launchctl", args);
1973
+ if (first.status === 0 || launchdLoaded(context)) return;
1974
+ run(context, "/bin/launchctl", ["bootout", serviceTarget(context)]);
1975
+ const retry = run(context, "/bin/launchctl", args);
1976
+ if (retry.status === 0 || launchdLoaded(context)) return;
1977
+ const failure = commandError("/bin/launchctl", args, retry);
1978
+ const firstDetail = (first.stderr || first.stdout).trim().replace(/\s+/g, " ").slice(0, 240);
1979
+ throw new Error(
1980
+ `${failure.message} launchd still rejected the managed job after one stale-state recovery attempt${firstDetail ? ` (first response: ${firstDetail})` : ""}. Run \`longleash service logs\` for daemon output.`
1981
+ );
1982
+ }
1970
1983
  function installSystemdService(context) {
1971
1984
  if (!context.paths.environment) throw new Error("The Linux service environment path is missing.");
1972
1985
  const previousUnit = snapshot(context.paths.definition);
1973
1986
  const previousEnvironment = snapshot(context.paths.environment);
1987
+ const wasEnabled = run(context, "systemctl", ["--user", "is-enabled", "--quiet", SYSTEMD_UNIT]).status === 0;
1988
+ const wasActive = run(context, "systemctl", ["--user", "is-active", "--quiet", SYSTEMD_UNIT]).status === 0;
1974
1989
  try {
1975
1990
  writeManagedAtomically(context.paths.environment, renderSystemdEnvironment(context.paths, context.env), 384);
1976
1991
  writeManagedAtomically(context.paths.definition, renderSystemdUnit(context.paths, context.home), 384);
1977
1992
  requireSuccess(context, "systemctl", ["--user", "daemon-reload"]);
1978
- requireSuccess(context, "systemctl", ["--user", "enable", "--now", SYSTEMD_UNIT]);
1993
+ if (wasActive) {
1994
+ if (!wasEnabled) requireSuccess(context, "systemctl", ["--user", "enable", SYSTEMD_UNIT]);
1995
+ requireSuccess(context, "systemctl", ["--user", "restart", SYSTEMD_UNIT]);
1996
+ } else {
1997
+ requireSuccess(context, "systemctl", ["--user", "enable", "--now", SYSTEMD_UNIT]);
1998
+ }
1979
1999
  } catch (error) {
1980
2000
  restore(context.paths.environment, previousEnvironment);
1981
2001
  restore(context.paths.definition, previousUnit);
1982
2002
  run(context, "systemctl", ["--user", "daemon-reload"]);
1983
- if (previousUnit !== null) run(context, "systemctl", ["--user", "enable", "--now", SYSTEMD_UNIT]);
2003
+ if (previousUnit !== null) {
2004
+ if (wasEnabled) run(context, "systemctl", ["--user", "enable", SYSTEMD_UNIT]);
2005
+ else run(context, "systemctl", ["--user", "disable", SYSTEMD_UNIT]);
2006
+ if (wasActive) run(context, "systemctl", ["--user", "restart", SYSTEMD_UNIT]);
2007
+ else run(context, "systemctl", ["--user", "stop", SYSTEMD_UNIT]);
2008
+ }
1984
2009
  throw error;
1985
2010
  }
1986
2011
  }
@@ -2102,6 +2127,12 @@ function systemdQuote(value) {
2102
2127
  if (/[\0\r\n]/.test(value)) throw new Error("Service paths may not contain control characters.");
2103
2128
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/%/g, "%%")}"`;
2104
2129
  }
2130
+ function systemdPath(value) {
2131
+ if (/[\u0000-\u0008\u000a-\u001f\u007f-\u009f]/.test(value)) {
2132
+ throw new Error("Service paths may not contain control characters.");
2133
+ }
2134
+ return value.replace(/%/g, "%%").replace(/\\/g, "\\x5c").replace(/ /g, "\\x20").replace(/\t/g, "\\x09").replace(/"/g, "\\x22").replace(/'/g, "\\x27");
2135
+ }
2105
2136
  function environmentQuote(value) {
2106
2137
  if (/[\0\r\n]/.test(value)) throw new Error("Service environment values may not contain control characters.");
2107
2138
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@longleash/cli",
3
- "version": "0.1.0-rc.6",
3
+ "version": "0.1.0-rc.8",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@longleash/cli",
9
- "version": "0.1.0-rc.6",
9
+ "version": "0.1.0-rc.8",
10
10
  "license": "MIT",
11
11
  "os": [
12
12
  "darwin",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longleash/cli",
3
- "version": "0.1.0-rc.6",
3
+ "version": "0.1.0-rc.8",
4
4
  "description": "Securely control Claude Code and Codex sessions on your laptop from your phone.",
5
5
  "license": "MIT",
6
6
  "type": "module",