Skip to content

Commit d017edf

Browse files
Merge remote-tracking branch 'origin/main' into fix/acp-registry-refresh-models
2 parents 5e3c54d + 2dfbad7 commit d017edf

340 files changed

Lines changed: 12786 additions & 2536 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/release.yml‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1482,7 +1482,18 @@ jobs:
14821482
14831483
git add apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json pnpm-lock.yaml
14841484
git commit --no-verify -m "chore(release): prepare $RELEASE_TAG"
1485-
git push origin HEAD:main
1485+
1486+
# main moves while this job runs, so a rejected push rebases and retries.
1487+
for attempt in 1 2 3 4 5; do
1488+
if git push origin HEAD:main; then
1489+
exit 0
1490+
fi
1491+
echo "Push rejected (attempt $attempt). Rebasing onto latest main."
1492+
git pull --rebase origin main
1493+
done
1494+
1495+
echo "Could not push the version bump after 5 attempts." >&2
1496+
exit 1
14861497
14871498
announce_discord:
14881499
name: Announce release on Discord

‎apps/desktop/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@t3tools/desktop",
3-
"version": "0.1.8",
3+
"version": "0.1.9",
44
"private": true,
55
"type": "module",
66
"main": "dist-electron/boot.cjs",

‎apps/desktop/src/app/ClerkTokenStore.test.ts‎

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ import * as NodeFS from "node:fs";
33
import * as NodeOS from "node:os";
44
import * as NodePath from "node:path";
55
import { assert, describe, it } from "@effect/vitest";
6-
import { vi } from "vite-plus/test";
7-
8-
vi.mock("node:fs", { spy: true });
96

107
import { openClerkTokenStorage, type ClerkTokenStorage } from "./ClerkTokenStore.ts";
118

@@ -91,24 +88,6 @@ describePosix("ClerkTokenStore", () => {
9188
});
9289
});
9390

94-
it("removes the legacy token file when the private copy already exists", async () => {
95-
await withStateDir((stateDir) => {
96-
const directory = NodePath.join(stateDir, "clerk");
97-
const legacyPath = NodePath.join(stateDir, "clerk-tokens.json");
98-
const tokenPath = NodePath.join(directory, "clerk-tokens.json");
99-
NodeFS.mkdirSync(directory, { mode: 0o755 });
100-
writeLoose(legacyPath, "stale-credential");
101-
writeLoose(tokenPath, "kept-credential");
102-
103-
openClerkTokenStorage(stateDir, () => idleStorage());
104-
105-
assert.equal(NodeFS.existsSync(legacyPath), false);
106-
assert.equal(modeOf(directory), 0o700);
107-
assert.equal(modeOf(tokenPath), 0o600);
108-
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "kept-credential");
109-
});
110-
});
111-
11291
it("resolves a relative state directory before opening storage", async () => {
11392
await withStateDir(async (stateDir) => {
11493
const relativeStateDir = NodePath.relative(process.cwd(), stateDir);
@@ -150,23 +129,6 @@ describePosix("ClerkTokenStore", () => {
150129
});
151130
});
152131

153-
it("migrates tokens when the legacy directory is on another filesystem", async () => {
154-
await withStateDir((stateDir) => {
155-
const legacyPath = NodePath.join(stateDir, "clerk-tokens.json");
156-
writeLoose(legacyPath, "existing-session");
157-
using _rename = vi.spyOn(NodeFS, "renameSync").mockImplementation(() => {
158-
throw Object.assign(new Error("cross-device link"), { code: "EXDEV" });
159-
});
160-
161-
openClerkTokenStorage(stateDir, idleStorage);
162-
163-
const tokenPath = NodePath.join(stateDir, "clerk", "clerk-tokens.json");
164-
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "existing-session");
165-
assert.equal(modeOf(tokenPath), 0o600);
166-
assert.equal(NodeFS.existsSync(legacyPath), false);
167-
});
168-
});
169-
170132
it("does not follow a symlinked clerk directory or token file", async () => {
171133
await withStateDir(async (stateDir) => {
172134
const outsideDirectory = NodePath.join(stateDir, "outside-dir");

‎apps/desktop/src/ipc/channels.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export const BOOTSTRAP_SSH_BEARER_SESSION_CHANNEL = "desktop:bootstrap-ssh-beare
7070
export const FETCH_SSH_SESSION_STATE_CHANNEL = "desktop:fetch-ssh-session-state";
7171
export const SSH_PASSWORD_PROMPT_CHANNEL = "desktop:ssh-password-prompt";
7272
export const RESOLVE_SSH_PASSWORD_PROMPT_CHANNEL = "desktop:resolve-ssh-password-prompt";
73+
export const SSH_PASSWORD_PROMPT_SETTLED_CHANNEL = "desktop:ssh-password-prompt-settled";
7374
export const GET_SERVER_EXPOSURE_STATE_CHANNEL = "desktop:get-server-exposure-state";
7475
export const SET_SERVER_EXPOSURE_MODE_CHANNEL = "desktop:set-server-exposure-mode";
7576
export const SET_TAILSCALE_SERVE_ENABLED_CHANNEL = "desktop:set-tailscale-serve-enabled";
@@ -94,6 +95,8 @@ export const PREVIEW_SET_ZOOM_FACTOR_CHANNEL = "desktop:preview-set-zoom-factor"
9495
export const PREVIEW_HARD_RELOAD_CHANNEL = "desktop:preview-hard-reload";
9596
export const PREVIEW_SET_COLOR_SCHEME_CHANNEL = "desktop:preview-set-color-scheme";
9697
export const PREVIEW_SET_AUDIO_MUTED_CHANNEL = "desktop:preview-set-audio-muted";
98+
export const PREVIEW_FIND_IN_PAGE_CHANNEL = "desktop:preview-find-in-page";
99+
export const PREVIEW_STOP_FIND_IN_PAGE_CHANNEL = "desktop:preview-stop-find-in-page";
97100
export const PREVIEW_SET_SHOWN_CHANNEL = "desktop:preview-set-shown";
98101
export const PREVIEW_OPEN_DEVTOOLS_CHANNEL = "desktop:preview-open-devtools";
99102
export const PREVIEW_CLEAR_COOKIES_CHANNEL = "desktop:preview-clear-cookies";
@@ -118,6 +121,7 @@ export const PREVIEW_RECORDING_SAVE_CHANNEL = "desktop:preview-recording-save";
118121
export const PREVIEW_STATE_CHANGE_CHANNEL = "desktop:preview-state-change";
119122
export const PREVIEW_POINTER_EVENT_CHANNEL = "desktop:preview-pointer-event";
120123
export const PREVIEW_OPEN_LINK_CHANNEL = "desktop:preview-open-link";
124+
export const PREVIEW_FIND_RESULT_CHANNEL = "desktop:preview-find-result";
121125

122126
export const MAC_PERMISSION_HELPER_CHANNEL = "desktop:mac-permission-helper";
123127

‎apps/desktop/src/ipc/methods/preview.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DesktopPreviewPickElementInputSchema,
66
DesktopPreviewArtifactInputSchema,
77
DesktopPreviewConfigInputSchema,
8+
DesktopPreviewFindInPageInputSchema,
89
DesktopPreviewNavigateInputSchema,
910
DesktopPreviewRecordingArtifactSchema,
1011
DesktopPreviewRecordingSaveInputSchema,
@@ -59,6 +60,9 @@ export const installPreviewEventForwarding = Effect.fn(
5960
yield* manager.subscribeOpenLinks((event) =>
6061
electronWindow.sendAll(IpcChannels.PREVIEW_OPEN_LINK_CHANNEL, event),
6162
);
63+
yield* manager.subscribeFindResults((event) =>
64+
electronWindow.sendAll(IpcChannels.PREVIEW_FIND_RESULT_CHANNEL, event),
65+
);
6266
});
6367

6468
const setForwardedShortcuts = DesktopIpc.makeIpcMethod({
@@ -116,6 +120,21 @@ const navigate = DesktopIpc.makeIpcMethod({
116120
}),
117121
});
118122

123+
const findInPage = DesktopIpc.makeIpcMethod({
124+
channel: IpcChannels.PREVIEW_FIND_IN_PAGE_CHANNEL,
125+
payload: DesktopPreviewFindInPageInputSchema,
126+
result: Schema.Void,
127+
handler: Effect.fn("desktop.ipc.preview.findInPage")(function* ({
128+
tabId,
129+
text,
130+
forward,
131+
newSession,
132+
}) {
133+
const manager = yield* PreviewManager.PreviewManager;
134+
yield* manager.findInPage(tabId, text, { forward, newSession });
135+
}),
136+
});
137+
119138
const tabMethod = (
120139
channel: string,
121140
name: string,
@@ -169,6 +188,11 @@ const hardReload = tabMethod(
169188
"desktop.ipc.preview.hardReload",
170189
(manager, tabId) => manager.hardReload(tabId),
171190
);
191+
const stopFindInPage = tabMethod(
192+
IpcChannels.PREVIEW_STOP_FIND_IN_PAGE_CHANNEL,
193+
"desktop.ipc.preview.stopFindInPage",
194+
(manager, tabId) => manager.stopFindInPage(tabId),
195+
);
172196
const setZoomFactor = DesktopIpc.makeIpcMethod({
173197
channel: IpcChannels.PREVIEW_SET_ZOOM_FACTOR_CHANNEL,
174198
payload: DesktopPreviewSetZoomFactorInputSchema,
@@ -452,6 +476,8 @@ export const methods = [
452476
zoomOut,
453477
resetZoom,
454478
hardReload,
479+
findInPage,
480+
stopFindInPage,
455481
setColorScheme,
456482
setZoomFactor,
457483
setAudioMuted,

‎apps/desktop/src/preload.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
DesktopBridge,
33
DesktopPreviewAnnotationVoiceEvent,
4+
DesktopPreviewFindResultEvent,
45
DesktopPreviewOpenLinkEvent,
56
DesktopPreviewPointerEvent,
67
DesktopPreviewRecordingInputEvent,
@@ -194,6 +195,17 @@ contextBridge.exposeInMainWorld("desktopBridge", {
194195
ipcRenderer.removeListener(IpcChannels.SSH_PASSWORD_PROMPT_CHANNEL, wrappedListener);
195196
};
196197
},
198+
onSshPasswordPromptSettled: (listener) => {
199+
const wrappedListener = (_event: Electron.IpcRendererEvent, requestId: unknown) => {
200+
if (typeof requestId !== "string") return;
201+
listener(requestId);
202+
};
203+
204+
ipcRenderer.on(IpcChannels.SSH_PASSWORD_PROMPT_SETTLED_CHANNEL, wrappedListener);
205+
return () => {
206+
ipcRenderer.removeListener(IpcChannels.SSH_PASSWORD_PROMPT_SETTLED_CHANNEL, wrappedListener);
207+
};
208+
},
197209
resolveSshPasswordPrompt: (requestId, password) =>
198210
ipcRenderer.invoke(IpcChannels.RESOLVE_SSH_PASSWORD_PROMPT_CHANNEL, { requestId, password }),
199211
getServerExposureState: () => ipcRenderer.invoke(IpcChannels.GET_SERVER_EXPOSURE_STATE_CHANNEL),
@@ -369,6 +381,15 @@ contextBridge.exposeInMainWorld("desktopBridge", {
369381
ipcRenderer.invoke(IpcChannels.PREVIEW_SET_COLOR_SCHEME_CHANNEL, { tabId, colorScheme }),
370382
setAudioMuted: (tabId, audioMuted) =>
371383
ipcRenderer.invoke(IpcChannels.PREVIEW_SET_AUDIO_MUTED_CHANNEL, { tabId, audioMuted }),
384+
findInPage: (tabId, text, { forward, newSession }) =>
385+
ipcRenderer.invoke(IpcChannels.PREVIEW_FIND_IN_PAGE_CHANNEL, {
386+
tabId,
387+
text,
388+
forward,
389+
newSession,
390+
}),
391+
stopFindInPage: (tabId) =>
392+
ipcRenderer.invoke(IpcChannels.PREVIEW_STOP_FIND_IN_PAGE_CHANNEL, { tabId }),
372393
setShown: (tabId, shown) =>
373394
ipcRenderer.invoke(IpcChannels.PREVIEW_SET_SHOWN_CHANNEL, { tabId, shown }),
374395
openDevTools: (tabId) =>
@@ -469,5 +490,14 @@ contextBridge.exposeInMainWorld("desktopBridge", {
469490
return () =>
470491
ipcRenderer.removeListener(IpcChannels.PREVIEW_OPEN_LINK_CHANNEL, wrappedListener);
471492
},
493+
onFindResult: (listener) => {
494+
const wrappedListener = (_event: Electron.IpcRendererEvent, findEvent: unknown) => {
495+
if (typeof findEvent !== "object" || findEvent === null) return;
496+
listener(findEvent as DesktopPreviewFindResultEvent);
497+
};
498+
ipcRenderer.on(IpcChannels.PREVIEW_FIND_RESULT_CHANNEL, wrappedListener);
499+
return () =>
500+
ipcRenderer.removeListener(IpcChannels.PREVIEW_FIND_RESULT_CHANNEL, wrappedListener);
501+
},
472502
},
473503
} satisfies DesktopBridge);

‎apps/desktop/src/preview/GuestProtocol.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const ANNOTATION_THEME_CHANNEL = "preview:annotation-theme";
66
export const ANNOTATION_SEND_ENABLED_CHANNEL = "preview:annotation-send-enabled";
77
export const HUMAN_INPUT_CHANNEL = "preview:human-input";
88
export const MOUSE_NAVIGATE_CHANNEL = "preview:mouse-navigate";
9+
export const PAGE_UNHANDLED_SHORTCUT_CHANNEL = "preview:page-unhandled-shortcut";
910
export const RECORDING_CURSOR_CHANNEL = "preview:recording-cursor";
1011
export const RECORDING_POINTER_CHANNEL = "preview:recording-pointer";
1112
export const RECORDING_KEY_CHANNEL = "preview:recording-key";

‎apps/desktop/src/preview/Manager.test.ts‎

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { it as effectIt } from "@effect/vitest";
22
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
33
import * as NodeServices from "@effect/platform-node/NodeServices";
44
import type {
5+
DesktopPreviewFindResultEvent,
56
DesktopPreviewRecordingInputEvent,
67
PreviewAnnotationPayload,
78
PreviewAnnotationRect,
@@ -812,6 +813,135 @@ describe("PreviewManager", () => {
812813
),
813814
);
814815

816+
effectIt.effect.each([
817+
["darwin", true],
818+
["linux", false],
819+
["win32", false],
820+
] as const)(
821+
"lets the page and its iframes claim the find shortcut before the host opens find on %s",
822+
([platform, usesMeta]) =>
823+
withManager(
824+
(manager) =>
825+
Effect.gen(function* () {
826+
const preview = makeFaviconWebContents();
827+
const send = vi.fn();
828+
const mainFrame = { processId: 1, routingId: 1 };
829+
Object.assign(preview.webContents, {
830+
hostWebContents: { isDestroyed: () => false, send },
831+
mainFrame,
832+
focusedFrame: mainFrame,
833+
});
834+
fromId.mockReturnValue(preview.webContents);
835+
yield* manager.createTab("tab_page_find");
836+
yield* manager.registerWebview("tab_page_find", 42);
837+
yield* manager.setForwardedShortcuts([
838+
{ command: "preview.find", shortcut: parseKeybindingShortcut("mod+f")! },
839+
]);
840+
const beforeInput = preview.listeners.get("before-input-event")!;
841+
const findKey = {
842+
type: "keyDown",
843+
key: "f",
844+
code: "KeyF",
845+
meta: usesMeta,
846+
control: !usesMeta,
847+
};
848+
const preventDefault = vi.fn();
849+
850+
beforeInput(
851+
{ preventDefault } as never,
852+
{ ...findKey, shift: false, alt: false } as never,
853+
);
854+
expect(preventDefault).not.toHaveBeenCalled();
855+
expect(send).not.toHaveBeenCalled();
856+
857+
const ipcOn = (preview.webContents as Electron.WebContents).ipc.on as ReturnType<
858+
typeof vi.fn
859+
>;
860+
const pageUnhandledShortcut = ipcOn.mock.calls.find(
861+
([channel]) => channel === "preview:page-unhandled-shortcut",
862+
)![1];
863+
pageUnhandledShortcut(
864+
{},
865+
{
866+
key: "f",
867+
code: "KeyF",
868+
metaKey: usesMeta,
869+
ctrlKey: !usesMeta,
870+
shiftKey: false,
871+
altKey: false,
872+
},
873+
);
874+
expect(send).toHaveBeenCalledExactlyOnceWith("desktop:menu-action", "preview.find");
875+
876+
Object.assign(preview.webContents, { focusedFrame: { processId: 1, routingId: 2 } });
877+
beforeInput(
878+
{ preventDefault } as never,
879+
{ ...findKey, shift: false, alt: false } as never,
880+
);
881+
expect(preventDefault).not.toHaveBeenCalled();
882+
expect(send).toHaveBeenCalledOnce();
883+
884+
Object.assign(preview.webContents, { focusedFrame: mainFrame });
885+
yield* manager.setForwardedShortcuts([
886+
{ command: "preview.find", shortcut: parseKeybindingShortcut("ctrl+alt+8")! },
887+
]);
888+
pageUnhandledShortcut(
889+
{},
890+
{
891+
key: "[",
892+
code: "Digit8",
893+
metaKey: false,
894+
ctrlKey: true,
895+
shiftKey: false,
896+
altKey: true,
897+
},
898+
);
899+
expect(send).toHaveBeenCalledTimes(usesMeta ? 2 : 1);
900+
}),
901+
platform,
902+
),
903+
);
904+
905+
effectIt.effect("searches the guest page and reports its match counts", () =>
906+
withManager((manager) =>
907+
Effect.gen(function* () {
908+
const preview = makeFaviconWebContents();
909+
const findInPage = vi.fn();
910+
const stopFindInPage = vi.fn();
911+
Object.assign(preview.webContents, { findInPage, stopFindInPage });
912+
fromId.mockReturnValue(preview.webContents);
913+
yield* manager.createTab("tab_find");
914+
yield* manager.registerWebview("tab_find", 42);
915+
const reported = yield* Deferred.make<DesktopPreviewFindResultEvent>();
916+
yield* manager.subscribeFindResults((event) => Deferred.succeed(reported, event));
917+
918+
yield* manager.findInPage("tab_find", "needle", { forward: true, newSession: true });
919+
expect(findInPage).toHaveBeenCalledExactlyOnceWith("needle", {
920+
forward: true,
921+
findNext: true,
922+
});
923+
924+
preview.listeners.get("found-in-page")!(
925+
{} as never,
926+
{
927+
requestId: 1,
928+
activeMatchOrdinal: 2,
929+
matches: 5,
930+
finalUpdate: true,
931+
} as never,
932+
);
933+
expect(yield* Deferred.await(reported)).toEqual({
934+
tabId: "tab_find",
935+
activeMatchOrdinal: 2,
936+
matches: 5,
937+
});
938+
939+
yield* manager.stopFindInPage("tab_find");
940+
expect(stopFindInPage).toHaveBeenCalledExactlyOnceWith("clearSelection");
941+
}),
942+
),
943+
);
944+
815945
effectIt.effect("isolates failed state listeners and continues delivery", () => {
816946
const loggedErrors: Array<unknown> = [];
817947
const logger = Logger.make(({ message }) => {

0 commit comments

Comments
 (0)