find tea by absolute path, not by name
PM2 hands the sidecar a login-less PATH that excludes ~/.local/bin, which is exactly where the platform installs a release binary. So `tea whoami` failed with "the token may be wrong, or tea is not installed" on a machine where tea was sitting in the owner's home and working perfectly from a shell. Third time today the same trap: the probe in os-deps, then the agent's env, now this. Anything the platform installs to ~/.local/bin has to be found deliberately, because nothing the platform spawns inherits a PATH that includes it. Resolved per call rather than cached, so an install that adds it mid-run is picked up instead of a cached "missing" outliving the thing that fixed it.
This commit is contained in:
+16
-2
@@ -35,6 +35,20 @@ export type TeaLogin = { name: string; url: string; user: string | null; hasToke
|
||||
/** The owner IS this process, so no escalation — and no sudo prompt on a machine that has no sudo. */
|
||||
const isOwnerUser = (osUser: string | null): boolean => !osUser || osUser === process.env.USER;
|
||||
|
||||
/**
|
||||
* `tea`, by absolute path.
|
||||
*
|
||||
* NOT by bare name. PM2 hands this sidecar a login-less PATH that does not include `~/.local/bin`, which
|
||||
* is precisely where the platform installs a release binary — so `tea whoami` failed with "not
|
||||
* installed" on a machine where `tea` was sitting in the owner's home and working perfectly from a
|
||||
* shell. The same trap caught the agent's PATH an hour earlier.
|
||||
*
|
||||
* Resolved once per call rather than cached: an install can add it while this process is running, and a
|
||||
* cached "missing" would outlive the thing that fixed it.
|
||||
*/
|
||||
const teaBinary = (): string =>
|
||||
Bun.which('tea', { PATH: `${join(homedir(), '.local', 'bin')}:${process.env.PATH ?? ''}` }) ?? 'tea';
|
||||
|
||||
const configPathFor = (home: string): string => join(home, '.config', 'tea', 'config.yml');
|
||||
|
||||
/**
|
||||
@@ -120,7 +134,7 @@ export async function writeTeaLogin(
|
||||
token: string,
|
||||
): Promise<{ ok: boolean; error?: string }> {
|
||||
const osUser = await osUserFor(userId);
|
||||
const command = ['tea', 'login', 'add', '--name', LOGIN_NAME, '--url', instanceUrl, '--token', token];
|
||||
const command = [teaBinary(), 'login', 'add', '--name', LOGIN_NAME, '--url', instanceUrl, '--token', token];
|
||||
|
||||
try {
|
||||
const proc = isOwnerUser(osUser)
|
||||
@@ -154,7 +168,7 @@ export async function writeTeaLogin(
|
||||
*/
|
||||
export async function testTeaLogin(userId: number): Promise<{ ok: boolean; user?: string; error?: string }> {
|
||||
const osUser = await osUserFor(userId);
|
||||
const out = await asUser(osUser, ['tea', 'whoami']);
|
||||
const out = await asUser(osUser, [teaBinary(), 'whoami']);
|
||||
if (out === null) {
|
||||
return { ok: false, error: 'tea could not authenticate — the token may be wrong, or tea is not installed' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user