gpui-bridge 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ import type { HostEvents, HostTransport } from "./app.js";
2
+ import type { ClientMessage } from "./protocol.js";
3
+ export interface HostProcessOptions extends HostEvents {
4
+ path: string;
5
+ args: string[];
6
+ debug: boolean;
7
+ }
8
+ export declare class HostProcess implements HostTransport {
9
+ private readonly child;
10
+ private readonly stderrTail;
11
+ private exited;
12
+ constructor(options: HostProcessOptions);
13
+ get running(): boolean;
14
+ send(message: ClientMessage): void;
15
+ /** Closes stdin; the host exits on EOF. */
16
+ close(): void;
17
+ kill(): void;
18
+ }
@@ -0,0 +1,56 @@
1
+ // Spawns the host binary and speaks JSON Lines over its stdin/stdout.
2
+ import { spawn } from "node:child_process";
3
+ import { createInterface } from "node:readline";
4
+ const STDERR_TAIL_LINES = 20;
5
+ export class HostProcess {
6
+ child;
7
+ stderrTail = [];
8
+ exited = false;
9
+ constructor(options) {
10
+ this.child = spawn(options.path, options.args, { stdio: ["pipe", "pipe", "pipe"] });
11
+ createInterface({ input: this.child.stdout }).on("line", (line) => {
12
+ let message;
13
+ try {
14
+ message = JSON.parse(line);
15
+ }
16
+ catch {
17
+ options.onCorruptLine(line);
18
+ return;
19
+ }
20
+ options.onMessage(message);
21
+ });
22
+ createInterface({ input: this.child.stderr }).on("line", (line) => {
23
+ this.stderrTail.push(line);
24
+ if (this.stderrTail.length > STDERR_TAIL_LINES)
25
+ this.stderrTail.shift();
26
+ if (options.debug)
27
+ console.error(`[host] ${line}`);
28
+ });
29
+ // Writes racing a dying host fail with EPIPE; the exit handler reports the real cause.
30
+ this.child.stdin.on("error", () => { });
31
+ const finish = (exit) => {
32
+ if (this.exited)
33
+ return;
34
+ this.exited = true;
35
+ options.onExit({ ...exit, stderrTail: [...this.stderrTail] });
36
+ };
37
+ this.child.on("error", (spawnError) => finish({ code: null, signal: null, spawnError }));
38
+ // "close" (not "exit") so all stdout/stderr lines are delivered first.
39
+ this.child.on("close", (code, signal) => finish({ code, signal }));
40
+ }
41
+ get running() {
42
+ return !this.exited;
43
+ }
44
+ send(message) {
45
+ if (this.exited)
46
+ return;
47
+ this.child.stdin.write(JSON.stringify(message) + "\n");
48
+ }
49
+ /** Closes stdin; the host exits on EOF. */
50
+ close() {
51
+ this.child.stdin.end();
52
+ }
53
+ kill() {
54
+ this.child.kill();
55
+ }
56
+ }
@@ -0,0 +1,15 @@
1
+ import { type App } from "./app.js";
2
+ export * from "./app.js";
3
+ export * from "./protocol.js";
4
+ export interface AppOptions {
5
+ /** Host binary. Defaults to `$GPUI_BRIDGE_HOST`, then the platform package installed with this one, then the debug build in this repository. */
6
+ hostPath?: string;
7
+ /** Forward host logs (stderr) to the console. */
8
+ debug?: boolean;
9
+ /** Run the host without a window (`--no-window`), e.g. for tests. */
10
+ headless?: boolean;
11
+ }
12
+ /** The host to run: `$GPUI_BRIDGE_HOST`, then the packaged one, then a build in this repository. */
13
+ export declare function defaultHostPath(): string;
14
+ /** Starts the host and completes the handshake. Rejects with a clear message if the host cannot start. */
15
+ export declare function createApp(options?: AppOptions): Promise<App>;
package/dist/index.js ADDED
@@ -0,0 +1,58 @@
1
+ // Public API of the gpui-bridge Node client.
2
+ import { existsSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { connect } from "./app.js";
7
+ import { HostProcess } from "./host-process.js";
8
+ export * from "./app.js";
9
+ export * from "./protocol.js";
10
+ const exe = process.platform === "win32" ? "gpui-bridge-host.exe" : "gpui-bridge-host";
11
+ // dist/index.js -> the package root
12
+ const pkg = join(dirname(fileURLToPath(import.meta.url)), "..");
13
+ const nodeRequire = createRequire(import.meta.url);
14
+ // The platform the host must run on, and the suffix of the package that ships it.
15
+ const platform = `${process.platform}-${process.arch}`;
16
+ /**
17
+ * Walks up from this file looking for a Cargo build of the host. The number of folders
18
+ * between a client and the workspace root is not part of the contract, so this survives
19
+ * a client moving inside `packages/` (or anywhere else).
20
+ */
21
+ function repoTarget() {
22
+ let dir = dirname(fileURLToPath(import.meta.url));
23
+ for (let depth = 0; depth < 6; depth++) {
24
+ for (const profile of ["debug", "release"]) {
25
+ const candidate = join(dir, "target", profile, exe);
26
+ if (existsSync(candidate))
27
+ return candidate;
28
+ }
29
+ dir = dirname(dir);
30
+ }
31
+ return undefined;
32
+ }
33
+ /**
34
+ * The binary of `@indios/gpui-bridge-<platform>-<arch>`, an optional dependency of this package:
35
+ * npm and pnpm install only the one whose `os`/`cpu` match the machine, so whatever is
36
+ * installed here is a host this machine can run. That package carries no code, only the
37
+ * asset, so it is found through its manifest (it declares no `exports` to walk).
38
+ */
39
+ function packagedHost() {
40
+ try {
41
+ const manifest = nodeRequire.resolve(`@indios/gpui-bridge-${platform}/package.json`);
42
+ const candidate = join(dirname(manifest), "bin", exe);
43
+ return existsSync(candidate) ? candidate : undefined;
44
+ }
45
+ catch {
46
+ return undefined; // another platform, or a checkout where the asset was never staged
47
+ }
48
+ }
49
+ /** The host to run: `$GPUI_BRIDGE_HOST`, then the packaged one, then a build in this repository. */
50
+ export function defaultHostPath() {
51
+ return process.env.GPUI_BRIDGE_HOST ?? packagedHost() ?? repoTarget() ?? join(pkg, "..", "..", "..", "target", "debug", exe);
52
+ }
53
+ /** Starts the host and completes the handshake. Rejects with a clear message if the host cannot start. */
54
+ export function createApp(options = {}) {
55
+ const path = options.hostPath ?? defaultHostPath();
56
+ const args = options.headless ? ["--no-window"] : [];
57
+ return connect(path, (events) => new HostProcess({ path, args, debug: options.debug ?? false, ...events }));
58
+ }