Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions apps/server/scripts/workspace-search-mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// @effect-diagnostics nodeBuiltinImport:off
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
// Native IPC tests use Node sockets to signal blocked and retired workers.
import * as NodeNet from "node:net";
import * as Exit from "effect/Exit";
import * as Schema from "effect/Schema";
import { SearchRequest, SearchResponse } from "../src/workspace/workspaceSearchProtocol.ts";
import {
WorkspaceSearchIndexRefreshFailed,
WorkspaceSearchIndexScanTimedOut,
WorkspaceSearchIndexSearchFailed,
} from "../src/workspace/WorkspaceSearchIndexService.ts";

const decodeSearchRequest = Schema.decodeUnknownSync(SearchRequest);
const encodeSearchResponse = Schema.encodeSync(SearchResponse);

const directories = new Map<number, string>();
const hold = (value: unknown) => {
const socket = NodeNet.connect(Number(process.env.T3_SEARCH_TEST_RECEIPT_PORT), "127.0.0.1", () =>
socket.write("blocked"),
);
socket.once("data", () => {
socket.end();
process.send?.(encodeSearchResponse(Exit.succeed(value)));
});
};
const block = () => {
const socket = NodeNet.connect(
Number(process.env.T3_SEARCH_TEST_RECEIPT_PORT),
"127.0.0.1",
() => {
socket.write("blocked");
// Model a synchronous FFI call which cannot service IPC or JS timers.
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0);
},
);
};
process.on("disconnect", () => process.exit(0));
process.on("message", (message) => {
const { id, operation: input } = decodeSearchRequest(message);
if (input.method === "initialize") directories.set(id, input.cwd);
const cwd = directories.get(id) ?? "";
if (
input.method === "initialize" &&
cwd === "hold-initialize" &&
process.env.T3_SEARCH_TEST_HOLD_INITIALIZE === "1"
) {
hold(null);
return;
}
if (input.method === "dispose") {
if (cwd === "block-dispose") {
block();
return;
}
directories.delete(id);
process.send?.(encodeSearchResponse(Exit.succeed(null)));
return;
}
if (cwd === "block-initialize" || (input.method === "search" && input.query === "block")) {
block();
return;
}
if (input.method === "search" && input.query === "crash") process.exit(1);
if (input.method === "search" && input.query === "search-error") {
process.send?.(
encodeSearchResponse(
Exit.fail(
new WorkspaceSearchIndexSearchFailed({
cwd,
queryLength: input.query.length,
pageSize: input.limit + 1,
reason: "search rejected",
}),
),
),
);
return;
}
if (input.method === "search" && input.query === "hold") {
hold({
entries: [{ path: String(process.pid), kind: "file" }],
truncated: false,
});
return;
}
if (input.method === "refresh") {
process.send?.(
encodeSearchResponse(
Exit.fail(new WorkspaceSearchIndexRefreshFailed({ cwd, reason: "scan failed" })),
),
);
return;
}
if (cwd === "scan-timeout") {
process.send?.(
encodeSearchResponse(
Exit.fail(new WorkspaceSearchIndexScanTimedOut({ cwd, timeout: "15 seconds" })),
),
);
return;
}
process.send?.(
encodeSearchResponse(
Exit.succeed(
input.method === "initialize"
? null
: {
entries: [{ path: String(process.pid), kind: "file" }],
truncated: false,
},
),
),
);
});
2 changes: 2 additions & 0 deletions apps/server/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { serviceCommand } from "./cli/service.ts";
import { uninstallCommand } from "./cli/uninstall.ts";
import { updateCommand } from "./cli/update.ts";
import { claudeHistoryCommand } from "./cli/claudeHistory.ts";
import { workspaceSearchCommand } from "./cli/workspaceSearch.ts";
import { serviceLauncherCommand } from "./cli/serviceLauncher.ts";
import { servicePreflightCommand } from "./cli/servicePreflight.ts";
import { sshHelperCommand } from "./cli/sshHelper.ts";
Expand Down Expand Up @@ -69,6 +70,7 @@ export const makeCli = ({ cloudEnabled = hasCloudPublicConfig } = {}) =>
uninstallCommand,
serviceLauncherCommand,
claudeHistoryCommand,
workspaceSearchCommand,
servicePreflightCommand,
sshHelperCommand,
themeCommand,
Expand Down
13 changes: 13 additions & 0 deletions apps/server/src/cli/workspaceSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Effect from "effect/Effect";
import { Command } from "effect/unstable/cli";

// Load the native index only in the worker process, including standalone installs.
export const workspaceSearchCommand = Command.make("__workspace-search").pipe(
Command.unlisted,
Command.withHandler(() =>
Effect.promise(async () => {
const { runWorkspaceSearchWorker } = await import("../workspaceSearchWorker.ts");
runWorkspaceSearchWorker();
}).pipe(Effect.andThen(Effect.never)),
),
);
3 changes: 3 additions & 0 deletions apps/server/src/workspace-search-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { runWorkspaceSearchWorker } from "./workspaceSearchWorker.ts";

runWorkspaceSearchWorker();
Loading
Loading