From 292e6ad7a20d4c7becdfde4173ac426cc430a61e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 23 May 2026 16:06:15 +0000 Subject: [PATCH 1/3] Use more idiomatic Effect helpers Co-authored-by: Julius Marminge --- .../backend/tailscaleEndpointProvider.test.ts | 10 +- .../src/backend/tailscaleEndpointProvider.ts | 12 +-- .../diagnostics/ProcessDiagnostics.test.ts | 102 +++++++++++++----- .../src/diagnostics/ProcessDiagnostics.ts | 42 +++++--- packages/tailscale/src/tailscale.test.ts | 18 ++-- packages/tailscale/src/tailscale.ts | 79 ++++++++------ 6 files changed, 168 insertions(+), 95 deletions(-) diff --git a/apps/desktop/src/backend/tailscaleEndpointProvider.test.ts b/apps/desktop/src/backend/tailscaleEndpointProvider.test.ts index 612ef3bd73f..a594721ea8b 100644 --- a/apps/desktop/src/backend/tailscaleEndpointProvider.test.ts +++ b/apps/desktop/src/backend/tailscaleEndpointProvider.test.ts @@ -1,6 +1,7 @@ import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; +import * as Option from "effect/Option"; import { HttpClient } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; @@ -15,10 +16,7 @@ const unusedTailscaleExternalServicesLayer = Layer.mergeAll( HttpClient.HttpClient, HttpClient.make(() => Effect.die("unexpected Tailscale HTTPS probe")), ), - Layer.succeed( - ChildProcessSpawner.ChildProcessSpawner, - ChildProcessSpawner.make(() => Effect.die("unexpected tailscale status process")), - ), + Layer.mock(ChildProcessSpawner.ChildProcessSpawner, {}), ); describe("tailscale endpoint provider", () => { @@ -34,8 +32,8 @@ describe("tailscale endpoint provider", () => { const dnsName = yield* parseTailscaleMagicDnsName( `{"Self":{"DNSName":"desktop.tail.ts.net."}}`, ); - assert.equal(dnsName, "desktop.tail.ts.net"); - assert.equal(yield* parseTailscaleMagicDnsName("{}"), null); + assert.deepEqual(dnsName, Option.some("desktop.tail.ts.net")); + assert.deepEqual(yield* parseTailscaleMagicDnsName("{}"), Option.none()); const malformed = yield* Effect.result(parseTailscaleMagicDnsName("not-json")); assert.isTrue(malformed._tag === "Failure"); }), diff --git a/apps/desktop/src/backend/tailscaleEndpointProvider.ts b/apps/desktop/src/backend/tailscaleEndpointProvider.ts index bd46e9f03f5..cc844837684 100644 --- a/apps/desktop/src/backend/tailscaleEndpointProvider.ts +++ b/apps/desktop/src/backend/tailscaleEndpointProvider.ts @@ -61,17 +61,17 @@ function resolveTailscaleIpAdvertisedEndpoints(input: { const resolveTailscaleMagicDnsAdvertisedEndpoint = Effect.fn( "resolveTailscaleMagicDnsAdvertisedEndpoint", )(function* (input: { - readonly dnsName: string | null; + readonly dnsName: Option.Option; readonly serveEnabled: boolean; readonly servePort?: number; readonly probe?: (baseUrl: string) => Effect.Effect; }): Effect.fn.Return, never, HttpClient.HttpClient> { - if (!input.dnsName) { + if (Option.isNone(input.dnsName)) { return Option.none(); } const httpBaseUrl = buildTailscaleHttpsBaseUrl({ - magicDnsName: input.dnsName, + magicDnsName: input.dnsName.value, ...(input.servePort === undefined ? {} : { servePort: input.servePort }), }); const probe = @@ -116,13 +116,13 @@ export const resolveTailscaleAdvertisedEndpoints = Effect.fn("resolveTailscaleAd input.statusJson === undefined ? yield* readTailscaleStatus.pipe( Effect.map((status) => status.magicDnsName), - Effect.catch(() => Effect.succeed(null)), + Effect.catch(() => Effect.succeed(Option.none())), ) : input.statusJson ? yield* parseTailscaleMagicDnsName(input.statusJson).pipe( - Effect.catch(() => Effect.succeed(null)), + Effect.catch(() => Effect.succeed(Option.none())), ) - : null; + : Option.none(); const magicDnsEndpoint = yield* resolveTailscaleMagicDnsAdvertisedEndpoint({ dnsName, serveEnabled: input.serveEnabled === true, diff --git a/apps/server/src/diagnostics/ProcessDiagnostics.test.ts b/apps/server/src/diagnostics/ProcessDiagnostics.test.ts index 18a54326de1..d3ca4b06f07 100644 --- a/apps/server/src/diagnostics/ProcessDiagnostics.test.ts +++ b/apps/server/src/diagnostics/ProcessDiagnostics.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "@effect/vitest"; +import { assert, describe, it } from "@effect/vitest"; import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; @@ -41,7 +41,7 @@ describe("ProcessDiagnostics", () => { ].join("\n"), ); - expect(rows).toEqual([ + assert.deepEqual(rows, [ { pid: 10, ppid: 1, @@ -66,6 +66,61 @@ describe("ProcessDiagnostics", () => { }), ); + it.effect("parses Windows process JSON through schema decoding", () => + Effect.sync(() => { + const rows = ProcessDiagnostics.parseWindowsProcessRows( + JSON.stringify([ + { + ProcessId: 10, + ParentProcessId: 1, + Name: "node.exe", + CommandLine: "node server.js", + Status: "Running", + WorkingSetSize: 1024, + PercentProcessorTime: 1.5, + }, + { + ProcessId: 11, + ParentProcessId: 10, + Name: "child.exe", + CommandLine: "", + WorkingSetSize: 2048.2, + }, + { + ProcessId: "bad", + ParentProcessId: 10, + Name: "ignored.exe", + }, + ]), + ); + + assert.deepEqual(rows, [ + { + pid: 10, + ppid: 1, + pgid: null, + status: "Running", + cpuPercent: 1.5, + rssBytes: 1024, + elapsed: "", + command: "node server.js", + }, + { + pid: 11, + ppid: 10, + pgid: null, + status: "Live", + cpuPercent: 0, + rssBytes: 2048, + elapsed: "", + command: "child.exe", + }, + ]); + + assert.deepEqual(ProcessDiagnostics.parseWindowsProcessRows("not-json"), []); + }), + ); + it.effect("aggregates only descendants of the server process", () => Effect.sync(() => { const diagnostics = ProcessDiagnostics.aggregateProcessDiagnostics({ @@ -125,15 +180,15 @@ describe("ProcessDiagnostics", () => { ], }); - expect(diagnostics.serverPid).toBe(100); - expect(DateTime.formatIso(diagnostics.readAt)).toBe("2026-05-05T10:00:00.000Z"); - expect(diagnostics.processCount).toBe(2); - expect(diagnostics.totalRssBytes).toBe(6_000); - expect(diagnostics.totalCpuPercent).toBe(4.75); - expect(diagnostics.processes.map((process) => process.pid)).toEqual([101, 102]); - expect(diagnostics.processes.map((process) => process.depth)).toEqual([0, 1]); - expect(Option.getOrNull(diagnostics.processes[0]!.pgid)).toBe(100); - expect(diagnostics.processes[0]?.childPids).toEqual([102]); + assert.equal(diagnostics.serverPid, 100); + assert.equal(DateTime.formatIso(diagnostics.readAt), "2026-05-05T10:00:00.000Z"); + assert.equal(diagnostics.processCount, 2); + assert.equal(diagnostics.totalRssBytes, 6_000); + assert.equal(diagnostics.totalCpuPercent, 4.75); + assert.deepEqual(diagnostics.processes.map((process) => process.pid), [101, 102]); + assert.deepEqual(diagnostics.processes.map((process) => process.depth), [0, 1]); + assert.equal(Option.getOrNull(diagnostics.processes[0]!.pgid), 100); + assert.deepEqual(diagnostics.processes[0]?.childPids, [102]); }), ); @@ -176,7 +231,7 @@ describe("ProcessDiagnostics", () => { ], }); - expect(diagnostics.processes.map((process) => process.pid)).toEqual([101, 102, 103]); + assert.deepEqual(diagnostics.processes.map((process) => process.pid), [101, 102, 103]); }), ); @@ -184,9 +239,8 @@ describe("ProcessDiagnostics", () => { Effect.gen(function* () { const commands: Array<{ readonly command: string; readonly args: ReadonlyArray }> = []; - const spawnerLayer = Layer.succeed( - ChildProcessSpawner.ChildProcessSpawner, - ChildProcessSpawner.make((command) => { + const spawnerLayer = Layer.mock(ChildProcessSpawner.ChildProcessSpawner, { + spawn: (command) => { const childProcess = command as unknown as { readonly command: string; readonly args: ReadonlyArray; @@ -200,8 +254,8 @@ describe("ProcessDiagnostics", () => { ].join("\n"), }), ); - }), - ); + }, + }); const layer = ProcessDiagnostics.layer.pipe(Layer.provide(spawnerLayer)); const diagnostics = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( @@ -209,8 +263,8 @@ describe("ProcessDiagnostics", () => { Effect.provide(layer), ); - expect(diagnostics.processes.map((process) => process.pid)).toEqual([4242]); - expect(commands).toEqual([ + assert.deepEqual(diagnostics.processes.map((process) => process.pid), [4242]); + assert.deepEqual(commands, [ { command: "ps", args: ["-axo", "pid=,ppid=,pgid=,stat=,pcpu=,rss=,etime=,command="], @@ -221,9 +275,8 @@ describe("ProcessDiagnostics", () => { it.effect("does not allow signaling the diagnostics query process", () => Effect.gen(function* () { - const spawnerLayer = Layer.succeed( - ChildProcessSpawner.ChildProcessSpawner, - ChildProcessSpawner.make(() => + const spawnerLayer = Layer.mock(ChildProcessSpawner.ChildProcessSpawner, { + spawn: () => Effect.succeed( mockHandle({ stdout: [ @@ -232,8 +285,7 @@ describe("ProcessDiagnostics", () => { ].join("\n"), }), ), - ), - ); + }); const layer = ProcessDiagnostics.layer.pipe(Layer.provide(spawnerLayer)); const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( @@ -241,7 +293,7 @@ describe("ProcessDiagnostics", () => { Effect.provide(layer), ); - expect(result).toEqual({ + assert.deepEqual(result, { pid: 4242, signal: "SIGINT", signaled: false, diff --git a/apps/server/src/diagnostics/ProcessDiagnostics.ts b/apps/server/src/diagnostics/ProcessDiagnostics.ts index f56bf216513..e0858fbdf11 100644 --- a/apps/server/src/diagnostics/ProcessDiagnostics.ts +++ b/apps/server/src/diagnostics/ProcessDiagnostics.ts @@ -26,7 +26,7 @@ export interface ProcessRow { readonly command: string; } -const PROCESS_QUERY_TIMEOUT_MS = 1_000; +const PROCESS_QUERY_TIMEOUT = Duration.seconds(1); const POSIX_PROCESS_QUERY_COMMAND = "pid=,ppid=,pgid=,stat=,pcpu=,rss=,etime=,command="; const PROCESS_QUERY_MAX_OUTPUT_BYTES = 2 * 1024 * 1024; @@ -74,6 +74,22 @@ function parseNumber(value: string): number | null { return Number.isFinite(parsed) ? parsed : null; } +const WindowsProcessRecord = Schema.Struct({ + CommandLine: Schema.optional(Schema.Unknown), + Name: Schema.optional(Schema.Unknown), + ParentProcessId: Schema.optional(Schema.Unknown), + PercentProcessorTime: Schema.optional(Schema.Unknown), + ProcessId: Schema.optional(Schema.Unknown), + Status: Schema.optional(Schema.Unknown), + WorkingSetSize: Schema.optional(Schema.Unknown), +}); +type WindowsProcessRecord = typeof WindowsProcessRecord.Type; + +const WindowsProcessOutput = Schema.fromJsonString( + Schema.Union([WindowsProcessRecord, Schema.Array(WindowsProcessRecord)]), +); +const decodeWindowsProcessOutput = Schema.decodeUnknownOption(WindowsProcessOutput); + export function parsePosixProcessRows(output: string): ReadonlyArray { const rows: ProcessRow[] = []; const rowPattern = @@ -139,9 +155,7 @@ export function parsePosixProcessRows(output: string): ReadonlyArray return rows; } -function normalizeWindowsProcessRow(value: unknown): ProcessRow | null { - if (typeof value !== "object" || value === null) return null; - const record = value as Record; +function normalizeWindowsProcessRow(record: WindowsProcessRecord): ProcessRow | null { const pid = typeof record.ProcessId === "number" ? record.ProcessId : null; const ppid = typeof record.ParentProcessId === "number" ? record.ParentProcessId : null; const commandLine = @@ -172,18 +186,18 @@ function normalizeWindowsProcessRow(value: unknown): ProcessRow | null { }; } -function parseWindowsProcessRows(output: string): ReadonlyArray { +export function parseWindowsProcessRows(output: string): ReadonlyArray { if (output.trim().length === 0) return []; - try { - const parsed = JSON.parse(output) as unknown; - const records = Array.isArray(parsed) ? parsed : [parsed]; - return records.flatMap((record) => { - const row = normalizeWindowsProcessRow(record); - return row ? [row] : []; - }); - } catch { + const parsed = decodeWindowsProcessOutput(output); + if (Option.isNone(parsed)) { return []; } + + const records = Array.isArray(parsed.value) ? parsed.value : [parsed.value]; + return records.flatMap((record) => { + const row = normalizeWindowsProcessRow(record); + return row ? [row] : []; + }); } export function buildDescendantEntries( @@ -309,7 +323,7 @@ const runProcess = Effect.fn("runProcess")( (effect, input) => effect.pipe( Effect.scoped, - Effect.timeoutOption(Duration.millis(PROCESS_QUERY_TIMEOUT_MS)), + Effect.timeoutOption(PROCESS_QUERY_TIMEOUT), Effect.flatMap((result) => Option.match(result, { onNone: () => Effect.fail(toProcessDiagnosticsError(`${input.errorMessage} timed out.`)), diff --git a/packages/tailscale/src/tailscale.test.ts b/packages/tailscale/src/tailscale.test.ts index dd2b1772fd6..02f4a057522 100644 --- a/packages/tailscale/src/tailscale.test.ts +++ b/packages/tailscale/src/tailscale.test.ts @@ -1,6 +1,7 @@ import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; +import * as Option from "effect/Option"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { ChildProcessSpawner } from "effect/unstable/process"; @@ -41,16 +42,15 @@ function mockSpawnerLayer( args: ReadonlyArray, ) => { stdout?: string; stderr?: string; code?: number }, ) { - return Layer.succeed( - ChildProcessSpawner.ChildProcessSpawner, - ChildProcessSpawner.make((command) => { + return Layer.mock(ChildProcessSpawner.ChildProcessSpawner, { + spawn: (command) => { const childProcess = command as unknown as { readonly command: string; readonly args: ReadonlyArray; }; return Effect.succeed(mockHandle(handler(childProcess.command, childProcess.args))); - }), - ); + }, + }); } describe("tailscale", () => { @@ -66,8 +66,8 @@ describe("tailscale", () => { it.effect("parses MagicDNS names from tailscale status", () => Effect.gen(function* () { const dnsName = yield* parseTailscaleMagicDnsName(tailscaleStatusJson); - assert.equal(dnsName, "desktop.tail.ts.net"); - assert.equal(yield* parseTailscaleMagicDnsName("{}"), null); + assert.deepEqual(dnsName, Option.some("desktop.tail.ts.net")); + assert.deepEqual(yield* parseTailscaleMagicDnsName("{}"), Option.none()); }), ); @@ -75,7 +75,7 @@ describe("tailscale", () => { Effect.gen(function* () { const status = yield* parseTailscaleStatus(tailscaleStatusJson); assert.deepEqual(status, { - magicDnsName: "desktop.tail.ts.net", + magicDnsName: Option.some("desktop.tail.ts.net"), tailnetIpv4Addresses: ["100.100.100.100"], }); }), @@ -106,7 +106,7 @@ describe("tailscale", () => { return Effect.gen(function* () { const status = yield* readTailscaleStatus.pipe(Effect.provide(layer)); assert.deepEqual(status, { - magicDnsName: "desktop.tail.ts.net", + magicDnsName: Option.some("desktop.tail.ts.net"), tailnetIpv4Addresses: ["100.90.1.2"], }); }); diff --git a/packages/tailscale/src/tailscale.ts b/packages/tailscale/src/tailscale.ts index c40cd54fc44..fcee141ec3d 100644 --- a/packages/tailscale/src/tailscale.ts +++ b/packages/tailscale/src/tailscale.ts @@ -1,4 +1,4 @@ -import * as Data from "effect/Data"; +import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; @@ -7,24 +7,33 @@ import { HttpClient, HttpClientRequest } from "effect/unstable/http"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; export const DEFAULT_TAILSCALE_SERVE_PORT = 443; -export const TAILSCALE_STATUS_TIMEOUT_MS = 1_500; -export const TAILSCALE_SERVE_TIMEOUT_MS = 10_000; -export const TAILSCALE_PROBE_TIMEOUT_MS = 2_500; +export const TAILSCALE_STATUS_TIMEOUT = Duration.millis(1_500); +export const TAILSCALE_SERVE_TIMEOUT = Duration.seconds(10); +export const TAILSCALE_PROBE_TIMEOUT = Duration.millis(2_500); -export class TailscaleCommandError extends Data.TaggedError("TailscaleCommandError")<{ - readonly command: readonly string[]; - readonly message: string; - readonly exitCode: number | null; - readonly stderr: string; -}> {} +export class TailscaleCommandError extends Schema.TaggedErrorClass()( + "TailscaleCommandError", + { + command: Schema.Array(Schema.String), + message: Schema.String, + exitCode: Schema.NullOr(Schema.Number), + stderr: Schema.String, + }, +) {} -export class TailscaleStatusParseError extends Data.TaggedError("TailscaleStatusParseError")<{ - readonly cause: unknown; -}> {} +export class TailscaleStatusParseError extends Schema.TaggedErrorClass()( + "TailscaleStatusParseError", + { + cause: Schema.Defect, + }, +) {} -export class TailscaleUnavailableError extends Data.TaggedError("TailscaleUnavailableError")<{ - readonly reason: string; -}> {} +export class TailscaleUnavailableError extends Schema.TaggedErrorClass()( + "TailscaleUnavailableError", + { + reason: Schema.String, + }, +) {} const TailscaleStatusSelf = Schema.Struct({ DNSName: Schema.optional(Schema.Unknown), @@ -39,7 +48,7 @@ export type TailscaleStatusSelf = typeof TailscaleStatusSelf.Type; export type TailscaleStatusJson = typeof TailscaleStatusJson.Type; export interface TailscaleStatus { - readonly magicDnsName: string | null; + readonly magicDnsName: Option.Option; readonly tailnetIpv4Addresses: readonly string[]; } @@ -69,19 +78,19 @@ const tailscaleCommandError = ( const decodeTailscaleStatusJson = Schema.decodeEffect(Schema.fromJsonString(TailscaleStatusJson)); -function normalizeMagicDnsName(status: TailscaleStatusJson): string | null { +function normalizeMagicDnsName(status: TailscaleStatusJson): Option.Option { const dnsName = status.Self?.DNSName; if (typeof dnsName !== "string") { - return null; + return Option.none(); } const normalized = dnsName.trim().replace(/\.$/u, ""); - return normalized.length > 0 ? normalized : null; + return normalized.length > 0 ? Option.some(normalized) : Option.none(); } export const parseTailscaleMagicDnsName = ( rawStatusJson: string, -): Effect.Effect => +): Effect.Effect, TailscaleStatusParseError> => decodeTailscaleStatusJson(rawStatusJson).pipe( Effect.mapError((cause) => new TailscaleStatusParseError({ cause })), Effect.map(normalizeMagicDnsName), @@ -174,7 +183,7 @@ export const readTailscaleStatus: Effect.Effect< return yield* parseTailscaleStatus(stdout); }).pipe( Effect.scoped, - Effect.timeoutOption(TAILSCALE_STATUS_TIMEOUT_MS), + Effect.timeoutOption(TAILSCALE_STATUS_TIMEOUT), Effect.flatMap((result) => Option.match(result, { onNone: () => @@ -206,7 +215,7 @@ const runTailscaleCommand = ( readonly runMessage: string; readonly exitMessage: (exitCode: number) => string; readonly timeoutMessage: string; - readonly timeoutMs: number; + readonly timeout: Duration.DurationInput; }, ): Effect.Effect => Effect.gen(function* () { @@ -243,7 +252,7 @@ const runTailscaleCommand = ( } }).pipe( Effect.scoped, - Effect.timeoutOption(input.timeoutMs), + Effect.timeoutOption(input.timeout), Effect.flatMap((result) => Option.match(result, { onNone: () => Effect.fail(tailscaleCommandError(args, input.timeoutMessage, null)), @@ -265,7 +274,7 @@ export const ensureTailscaleServe = (input: { runMessage: "Failed to run tailscale serve.", exitMessage: (exitCode) => `Tailscale serve exited with code ${exitCode}.`, timeoutMessage: "Tailscale serve timed out.", - timeoutMs: TAILSCALE_SERVE_TIMEOUT_MS, + timeout: TAILSCALE_SERVE_TIMEOUT, }); }; @@ -281,13 +290,13 @@ export const disableTailscaleServe = ( runMessage: "Failed to run tailscale serve off.", exitMessage: (exitCode) => `Tailscale serve off exited with code ${exitCode}.`, timeoutMessage: "Tailscale serve off timed out.", - timeoutMs: TAILSCALE_SERVE_TIMEOUT_MS, + timeout: TAILSCALE_SERVE_TIMEOUT, }); }); export const probeTailscaleHttpsEndpoint = (input: { readonly baseUrl: string; - readonly timeoutMs?: number; + readonly timeout?: Duration.DurationInput; }): Effect.Effect => Effect.gen(function* () { const client = yield* HttpClient.HttpClient; @@ -295,7 +304,7 @@ export const probeTailscaleHttpsEndpoint = (input: { const url = new URL("/.well-known/t3/environment", input.baseUrl); const request = HttpClientRequest.get(url.toString()); return yield* client.execute(request); - }).pipe(Effect.timeoutOption(input.timeoutMs ?? TAILSCALE_PROBE_TIMEOUT_MS)); + }).pipe(Effect.timeoutOption(input.timeout ?? TAILSCALE_PROBE_TIMEOUT)); return Option.match(response, { onNone: () => false, @@ -308,17 +317,17 @@ export const resolveTailscaleHttpsBaseUrl = ( readonly servePort?: number; } = {}, ): Effect.Effect< - string | null, + Option.Option, TailscaleCommandError | TailscaleStatusParseError, ChildProcessSpawner.ChildProcessSpawner > => readTailscaleStatus.pipe( Effect.map((status) => - status.magicDnsName - ? buildTailscaleHttpsBaseUrl({ - magicDnsName: status.magicDnsName, - ...(input.servePort === undefined ? {} : { servePort: input.servePort }), - }) - : null, + Option.map(status.magicDnsName, (magicDnsName) => + buildTailscaleHttpsBaseUrl({ + magicDnsName, + ...(input.servePort === undefined ? {} : { servePort: input.servePort }), + }), + ), ), ); From 67c8cd230ef7792cea1af60723bdfdc31771852c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 23 May 2026 16:07:24 +0000 Subject: [PATCH 2/3] Use concrete Effect duration types Co-authored-by: Julius Marminge --- packages/tailscale/src/tailscale.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tailscale/src/tailscale.ts b/packages/tailscale/src/tailscale.ts index fcee141ec3d..2101abbb719 100644 --- a/packages/tailscale/src/tailscale.ts +++ b/packages/tailscale/src/tailscale.ts @@ -215,7 +215,7 @@ const runTailscaleCommand = ( readonly runMessage: string; readonly exitMessage: (exitCode: number) => string; readonly timeoutMessage: string; - readonly timeout: Duration.DurationInput; + readonly timeout: Duration.Duration; }, ): Effect.Effect => Effect.gen(function* () { @@ -296,7 +296,7 @@ export const disableTailscaleServe = ( export const probeTailscaleHttpsEndpoint = (input: { readonly baseUrl: string; - readonly timeout?: Duration.DurationInput; + readonly timeout?: Duration.Duration; }): Effect.Effect => Effect.gen(function* () { const client = yield* HttpClient.HttpClient; From bc3c829ccaea660d0f911506031774b23e95c927 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 23 May 2026 16:07:43 +0000 Subject: [PATCH 3/3] Format diagnostics assertions Co-authored-by: Julius Marminge --- .../diagnostics/ProcessDiagnostics.test.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/server/src/diagnostics/ProcessDiagnostics.test.ts b/apps/server/src/diagnostics/ProcessDiagnostics.test.ts index d3ca4b06f07..319435c7342 100644 --- a/apps/server/src/diagnostics/ProcessDiagnostics.test.ts +++ b/apps/server/src/diagnostics/ProcessDiagnostics.test.ts @@ -185,8 +185,14 @@ describe("ProcessDiagnostics", () => { assert.equal(diagnostics.processCount, 2); assert.equal(diagnostics.totalRssBytes, 6_000); assert.equal(diagnostics.totalCpuPercent, 4.75); - assert.deepEqual(diagnostics.processes.map((process) => process.pid), [101, 102]); - assert.deepEqual(diagnostics.processes.map((process) => process.depth), [0, 1]); + assert.deepEqual( + diagnostics.processes.map((process) => process.pid), + [101, 102], + ); + assert.deepEqual( + diagnostics.processes.map((process) => process.depth), + [0, 1], + ); assert.equal(Option.getOrNull(diagnostics.processes[0]!.pgid), 100); assert.deepEqual(diagnostics.processes[0]?.childPids, [102]); }), @@ -231,7 +237,10 @@ describe("ProcessDiagnostics", () => { ], }); - assert.deepEqual(diagnostics.processes.map((process) => process.pid), [101, 102, 103]); + assert.deepEqual( + diagnostics.processes.map((process) => process.pid), + [101, 102, 103], + ); }), ); @@ -263,7 +272,10 @@ describe("ProcessDiagnostics", () => { Effect.provide(layer), ); - assert.deepEqual(diagnostics.processes.map((process) => process.pid), [4242]); + assert.deepEqual( + diagnostics.processes.map((process) => process.pid), + [4242], + ); assert.deepEqual(commands, [ { command: "ps",