Repository navigation
fix(desktop): the t3 command warns instead of installing behind another t3 #17351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,6 +146,14 @@ export const make = Effect.gen(function* () { | |
| return Option.none<string>(); | ||
| }); | ||
|
|
||
| /** The `t3` a new shell runs when it is not this app's, on Unix. */ | ||
| const foreignFirstOnPath = Effect.gen(function* () { | ||
| if (windows) return Option.none<string>(); | ||
| const first = yield* firstOnPath; | ||
| if (Option.isNone(first) || (yield* isOurLink(first.value))) return Option.none<string>(); | ||
| return first; | ||
| }); | ||
|
|
||
| /** Where this app's command is installed now, if anywhere. */ | ||
| const installedAt = Effect.gen(function* () { | ||
| if (windows) { | ||
|
|
@@ -167,12 +175,16 @@ export const make = Effect.gen(function* () { | |
| return { supported: false, installedPath: null, onPath: false } as const; | ||
| } | ||
| const installed = yield* installedAt; | ||
| if (Option.isNone(installed)) return { supported: true, installedPath: null, onPath: false }; | ||
| const shadowedBy = yield* foreignFirstOnPath; | ||
| const shadow = Option.isSome(shadowedBy) ? { shadowedBy: shadowedBy.value } : {}; | ||
| if (Option.isNone(installed)) { | ||
| return { supported: true, installedPath: null, onPath: false, ...shadow }; | ||
| } | ||
| // On Windows only terminals opened after the change see it. On Unix the | ||
| // first `t3` on PATH must be ours; a `t3` earlier on PATH would shadow it. | ||
| const first = yield* firstOnPath; | ||
| const onPath = windows || (Option.isSome(first) && (yield* isOurLink(first.value))); | ||
| return { supported: true, installedPath: installed.value, onPath }; | ||
| return { supported: true, installedPath: installed.value, onPath, ...shadow }; | ||
| }).pipe(Effect.orElseSucceed(() => ({ supported: false, installedPath: null, onPath: false }))); | ||
|
|
||
| /** Writes the launcher if the app has not yet, e.g. when no local backend runs. */ | ||
|
|
@@ -209,6 +221,13 @@ export const make = Effect.gen(function* () { | |
| .remove(existing.value) | ||
| .pipe(Effect.mapError(() => fail(`Could not replace ${existing.value}.`))); | ||
| } | ||
| // A link behind another `t3` never runs, so installing one would only hide the problem. | ||
| const shadowedBy = yield* foreignFirstOnPath; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 High When a previous-home launcher is first on 🚀 Reply "fix it for me" or copy this AI Prompt for your agent: |
||
| if (Option.isSome(shadowedBy)) { | ||
|
Comment on lines
+224
to
+226
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '100,260p' apps/desktop/src/app/DesktopCliCommand.ts
sed -n '1,235p' apps/desktop/src/app/DesktopCliCommand.test.tsRepository: pingdotgg/t3code Length of output: 17737 🏁 Script executed: nl -ba apps/desktop/src/app/DesktopCliCommand.ts | sed -n '190,285p'
nl -ba apps/desktop/src/app/DesktopCliCommand.ts | sed -n '285,345p'Repository: pingdotgg/t3code Length of output: 4917 Check for a shadow before removing an existing link. When Move the shadow check before the existing-link replacement. Suggested fix+ // A link behind another `t3` never runs, so installing one would only hide the problem.
+ const shadowedBy = yield* foreignFirstOnPath;
+ if (Option.isSome(shadowedBy)) {
+ return yield* fail(
+ `Another t3 at ${shadowedBy.value} runs first in a new terminal. Remove it, or run the launcher directly at ${launcher}.`,
+ );
+ }
+
const existing = yield* installedAt;
if (Option.isSome(existing)) {
const target = yield* fs.readLink(existing.value).pipe(Effect.option);
if (Option.getOrUndefined(target) === launcher) return yield* state;
// A link to a previous T3 home's launcher: point it at this one instead.
@@
- // A link behind another `t3` never runs, so installing one would only hide the problem.
- const shadowedBy = yield* foreignFirstOnPath;
- if (Option.isSome(shadowedBy)) {
- return yield* fail(
- `Another t3 at ${shadowedBy.value} runs first in a new terminal. Remove it, or run the launcher directly at ${launcher}.`,
- );
- }🤖 Prompt for AI Agents |
||
| return yield* fail( | ||
| `Another t3 at ${shadowedBy.value} runs first in a new terminal. Remove it, or run the launcher directly at ${launcher}.`, | ||
| ); | ||
| } | ||
| const onPath = pathEntries(process.env.PATH, ":"); | ||
| const candidates = unixCandidates(environment.homeDirectory, environment.platform); | ||
| // Prefer a folder already on PATH that the user can write to without admin rights. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Check whether the PATH entry can run.
firstOnPathselects the first existingt3, even when that file is not executable or is a directory. On Unix, a shell can skip that entry and run a latert3.foreignFirstOnPaththen reports a false conflict, andinstallrefuses a usable installation. Check that a candidate is an executable file before treating it as the first command on PATH.🤖 Prompt for AI Agents