everything-dev 0.3.1 → 0.3.3
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.
- package/package.json +1 -1
- package/src/components/dev-view.tsx +254 -243
- package/src/components/streaming-view.ts +128 -97
- package/src/lib/process.ts +1 -0
- package/src/lib/session-recorder/server.ts +225 -224
- package/src/plugin.ts +114 -107
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
|
-
import {
|
|
6
|
-
createSnapshotWithPlatform,
|
|
7
|
-
runSilent,
|
|
8
|
-
} from "../resource-monitor";
|
|
5
|
+
import { createSnapshotWithPlatform, runSilent } from "../resource-monitor";
|
|
9
6
|
import { ServerNotReady, ServerStartFailed } from "./errors";
|
|
10
7
|
import type { ServerHandle, ServerOrchestrator } from "./types";
|
|
11
8
|
|
|
@@ -13,255 +10,259 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
13
10
|
const CLI_DIR = resolve(__dirname, "../../..");
|
|
14
11
|
|
|
15
12
|
const sleep = (ms: number): Promise<void> =>
|
|
16
|
-
|
|
13
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
14
|
|
|
18
15
|
interface SpawnOptions {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
port?: number;
|
|
17
|
+
account?: string;
|
|
18
|
+
domain?: string;
|
|
19
|
+
interactive?: boolean;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
const createServerHandle = (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
proc: ReturnType<typeof spawn>,
|
|
24
|
+
name: string,
|
|
25
|
+
port: number,
|
|
29
26
|
): ServerHandle => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
27
|
+
proc.stdout?.on("data", () => {});
|
|
28
|
+
proc.stderr?.on("data", () => {});
|
|
29
|
+
|
|
30
|
+
let exitHandled = false;
|
|
31
|
+
let exitCode: number | null = null;
|
|
32
|
+
const exitPromise = new Promise<number | null>((resolve) => {
|
|
33
|
+
(proc as unknown as NodeJS.EventEmitter).on(
|
|
34
|
+
"exit",
|
|
35
|
+
(code: number | null) => {
|
|
36
|
+
exitHandled = true;
|
|
37
|
+
exitCode = code;
|
|
38
|
+
resolve(code);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
pid: proc.pid!,
|
|
45
|
+
port,
|
|
46
|
+
name,
|
|
47
|
+
kill: async () => {
|
|
48
|
+
proc.kill("SIGTERM");
|
|
49
|
+
const killPromise = new Promise<void>((res) => {
|
|
50
|
+
const timeout = setTimeout(() => {
|
|
51
|
+
proc.kill("SIGKILL");
|
|
52
|
+
res();
|
|
53
|
+
}, 5000);
|
|
54
|
+
if (exitHandled) {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
res();
|
|
57
|
+
} else {
|
|
58
|
+
exitPromise.then(() => {
|
|
59
|
+
clearTimeout(timeout);
|
|
60
|
+
res();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
await killPromise;
|
|
65
|
+
},
|
|
66
|
+
waitForExit: (timeoutMs = 10000): Promise<number | null> =>
|
|
67
|
+
new Promise((res) => {
|
|
68
|
+
const timeout = setTimeout(() => res(null), timeoutMs);
|
|
69
|
+
if (exitHandled) {
|
|
70
|
+
clearTimeout(timeout);
|
|
71
|
+
res(exitCode);
|
|
72
|
+
} else {
|
|
73
|
+
exitPromise.then((code) => {
|
|
74
|
+
clearTimeout(timeout);
|
|
75
|
+
res(code);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
const spawnBosStart = (options: SpawnOptions = {}): ServerHandle => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
83
|
+
const args = [
|
|
84
|
+
"src/cli.ts",
|
|
85
|
+
"start",
|
|
86
|
+
"--account",
|
|
87
|
+
options.account || "every.near",
|
|
88
|
+
"--domain",
|
|
89
|
+
options.domain || "everything.dev",
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
if (!options.interactive) {
|
|
93
|
+
args.push("--no-interactive");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (options.port) {
|
|
97
|
+
args.push("--port", String(options.port));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const proc = spawn("bun", args, {
|
|
101
|
+
cwd: CLI_DIR,
|
|
102
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
103
|
+
detached: false,
|
|
104
|
+
env: { ...process.env, NODE_ENV: "production" },
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return createServerHandle(proc, "bos-start", options.port || 3000);
|
|
107
108
|
};
|
|
108
109
|
|
|
109
110
|
const spawnBosDev = (options: SpawnOptions = {}): ServerHandle => {
|
|
110
|
-
|
|
111
|
+
const args = ["src/cli.ts", "dev"];
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
if (!options.interactive) {
|
|
114
|
+
args.push("--no-interactive");
|
|
115
|
+
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
if (options.port) {
|
|
118
|
+
args.push("--port", String(options.port));
|
|
119
|
+
}
|
|
119
120
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
const proc = spawn("bun", args, {
|
|
122
|
+
cwd: CLI_DIR,
|
|
123
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
124
|
+
detached: false,
|
|
125
|
+
env: { ...process.env, NODE_ENV: "development" },
|
|
126
|
+
});
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
return createServerHandle(proc, "bos-dev", options.port || 3000);
|
|
128
129
|
};
|
|
129
130
|
|
|
130
131
|
const waitForPortBound = async (
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
port: number,
|
|
133
|
+
timeoutMs = 60000,
|
|
133
134
|
): Promise<boolean> => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
135
|
+
const start = Date.now();
|
|
136
|
+
|
|
137
|
+
while (Date.now() - start < timeoutMs) {
|
|
138
|
+
try {
|
|
139
|
+
const snapshot = await runSilent(
|
|
140
|
+
createSnapshotWithPlatform({ ports: [port] }),
|
|
141
|
+
);
|
|
142
|
+
if (snapshot.ports[port]?.state === "LISTEN") {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
// ignore errors during polling
|
|
147
|
+
}
|
|
148
|
+
await sleep(500);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return false;
|
|
151
152
|
};
|
|
152
153
|
|
|
153
154
|
const waitForPortFree = async (
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
port: number,
|
|
156
|
+
timeoutMs = 15000,
|
|
156
157
|
): Promise<boolean> => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
158
|
+
const start = Date.now();
|
|
159
|
+
|
|
160
|
+
while (Date.now() - start < timeoutMs) {
|
|
161
|
+
try {
|
|
162
|
+
const snapshot = await runSilent(
|
|
163
|
+
createSnapshotWithPlatform({ ports: [port] }),
|
|
164
|
+
);
|
|
165
|
+
if (snapshot.ports[port]?.state === "FREE") {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
// ignore errors during polling
|
|
170
|
+
}
|
|
171
|
+
await sleep(200);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return false;
|
|
174
175
|
};
|
|
175
176
|
|
|
176
177
|
export const startServers = (
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
mode: "start" | "dev" = "start",
|
|
179
|
+
options: SpawnOptions = {},
|
|
179
180
|
): Effect.Effect<ServerOrchestrator, ServerStartFailed | ServerNotReady> =>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
181
|
+
Effect.gen(function* () {
|
|
182
|
+
const port = options.port || 3000;
|
|
183
|
+
|
|
184
|
+
yield* Effect.logInfo(`Starting BOS in ${mode} mode on port ${port}`);
|
|
185
|
+
|
|
186
|
+
const handle =
|
|
187
|
+
mode === "dev" ? spawnBosDev(options) : spawnBosStart(options);
|
|
188
|
+
|
|
189
|
+
const ready = yield* Effect.tryPromise({
|
|
190
|
+
try: () => waitForPortBound(port, 90000),
|
|
191
|
+
catch: (e) =>
|
|
192
|
+
new ServerStartFailed({
|
|
193
|
+
server: handle.name,
|
|
194
|
+
port,
|
|
195
|
+
reason: String(e),
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (!ready) {
|
|
200
|
+
yield* Effect.promise(() => handle.kill());
|
|
201
|
+
return yield* Effect.fail(
|
|
202
|
+
new ServerNotReady({
|
|
203
|
+
servers: [handle.name],
|
|
204
|
+
timeoutMs: 90000,
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
yield* Effect.logInfo(`Server ready on port ${port}`);
|
|
210
|
+
|
|
211
|
+
const orchestrator: ServerOrchestrator = {
|
|
212
|
+
handles: [handle],
|
|
213
|
+
ports: [port],
|
|
214
|
+
shutdown: async () => {
|
|
215
|
+
console.log("Shutting down servers");
|
|
216
|
+
await handle.kill();
|
|
217
|
+
await waitForPortFree(port, 15000);
|
|
218
|
+
console.log("Servers stopped");
|
|
219
|
+
},
|
|
220
|
+
waitForReady: async () => {
|
|
221
|
+
return waitForPortBound(port, 30000);
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return orchestrator;
|
|
226
|
+
});
|
|
226
227
|
|
|
227
228
|
export const shutdownServers = (
|
|
228
|
-
|
|
229
|
+
orchestrator: ServerOrchestrator,
|
|
229
230
|
): Effect.Effect<void> =>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
): Effect.Effect<boolean> =>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
231
|
+
Effect.gen(function* () {
|
|
232
|
+
yield* Effect.logInfo(
|
|
233
|
+
`Shutting down ${orchestrator.handles.length} server(s)`,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
for (const handle of orchestrator.handles) {
|
|
237
|
+
yield* Effect.logDebug(`Killing ${handle.name} (PID ${handle.pid})`);
|
|
238
|
+
yield* Effect.promise(() => handle.kill());
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const port of orchestrator.ports) {
|
|
242
|
+
yield* Effect.logDebug(`Waiting for port ${port} to be free`);
|
|
243
|
+
const freed = yield* Effect.promise(() => waitForPortFree(port, 15000));
|
|
244
|
+
if (!freed) {
|
|
245
|
+
yield* Effect.logWarning(`Port ${port} still bound after shutdown`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
yield* Effect.logInfo("All servers stopped");
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
export const checkPortsAvailable = (ports: number[]): Effect.Effect<boolean> =>
|
|
253
|
+
Effect.gen(function* () {
|
|
254
|
+
const snapshot = yield* Effect.promise(() =>
|
|
255
|
+
runSilent(createSnapshotWithPlatform({ ports })),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
for (const port of ports) {
|
|
259
|
+
if (snapshot.ports[port]?.state !== "FREE") {
|
|
260
|
+
yield* Effect.logWarning(`Port ${port} is already in use`);
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return true;
|
|
266
|
+
});
|
|
266
267
|
|
|
267
268
|
export { waitForPortBound, waitForPortFree };
|