tycono 0.1.96-beta.50 → 0.1.96-beta.51

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.
Files changed (2) hide show
  1. package/bin/tycono.ts +48 -24
  2. package/package.json +1 -1
package/bin/tycono.ts CHANGED
@@ -219,43 +219,67 @@ async function startServerForTui(): Promise<void> {
219
219
  const logFd = fs.openSync(logFile, 'a');
220
220
  const logStream = fs.createWriteStream(logFile, { fd: logFd });
221
221
  const origStdoutWrite = process.stdout.write.bind(process.stdout);
222
- // Redirect ALL non-Ink output to log file.
223
- // Ink uses stdout.write with ANSI sequences. Server uses console.log (which calls stdout.write).
224
- // We must intercept stdout.write but ALWAYS pass through to real stdout,
225
- // just also copy non-Ink output to log file.
226
- // The key insight: DON'T BLOCK anything — just copy server output to log file.
227
- // Ink can handle interleaved output by re-rendering.
228
- console.log = (...args: unknown[]) => { logStream.write(args.join(' ') + '\n'); };
229
- console.error = (...args: unknown[]) => { logStream.write(args.join(' ') + '\n'); };
230
- console.warn = (...args: unknown[]) => { logStream.write(args.join(' ') + '\n'); };
231
- // Also intercept direct stderr.write (used by our debug logging)
232
- process.stderr.write = ((chunk: any, ...args: any[]) => {
233
- logStream.write(typeof chunk === 'string' ? chunk : chunk.toString());
234
- return true;
235
- }) as any;
236
222
  const origLog = (...args: unknown[]) => origStdoutWrite(args.join(' ') + '\n');
237
223
 
238
- const { createHttpServer } = await import('../src/api/src/create-server.js');
239
- const server = createHttpServer();
224
+ // Start API server as a CHILD PROCESS to isolate stdout completely
225
+ // This prevents server console.log from corrupting Ink's frame buffer
226
+ const { fork } = await import('node:child_process');
227
+ const serverScript = path.resolve(__dirname, '..', 'src', 'api', 'src', 'server.ts');
228
+
229
+ const child = fork(serverScript, [], {
230
+ execArgv: ['--import', 'tsx'],
231
+ env: {
232
+ ...process.env,
233
+ PORT: String(port),
234
+ COMPANY_ROOT: process.env.COMPANY_ROOT,
235
+ },
236
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
237
+ });
240
238
 
241
- const host = process.env.HOST || '0.0.0.0';
239
+ // Redirect child stdout/stderr to log file
240
+ child.stdout?.on('data', (data: Buffer) => { logStream.write(data); });
241
+ child.stderr?.on('data', (data: Buffer) => { logStream.write(data); });
242
+
243
+ // Wait for server to be ready (poll health endpoint)
244
+ const waitForServer = async () => {
245
+ const http = await import('node:http');
246
+ for (let i = 0; i < 30; i++) {
247
+ try {
248
+ await new Promise<void>((resolve, reject) => {
249
+ const req = http.get(`http://localhost:${port}/api/health`, (res) => {
250
+ res.resume();
251
+ resolve();
252
+ });
253
+ req.on('error', reject);
254
+ req.setTimeout(1000, () => { req.destroy(); reject(new Error('timeout')); });
255
+ });
256
+ return true;
257
+ } catch {
258
+ await new Promise(r => setTimeout(r, 500));
259
+ }
260
+ }
261
+ return false;
262
+ };
242
263
 
243
- await new Promise<void>((resolve) => {
244
- server.listen(port, host, () => resolve());
245
- });
264
+ const serverReady = await waitForServer();
265
+ if (!serverReady) {
266
+ origLog(' Failed to start API server. Check logs:', logFile);
267
+ process.exit(1);
268
+ }
246
269
 
247
270
  origLog(` API server started on port ${port}`);
248
271
  origLog(` Logs: ${logFile}`);
249
272
 
250
- // Graceful shutdown
273
+ // Graceful shutdown — kill child server
251
274
  const shutdown = () => {
252
- server.close(() => process.exit(0));
253
- setTimeout(() => process.exit(1), 5000);
275
+ child.kill();
276
+ process.exit(0);
254
277
  };
255
278
  process.on('SIGINT', shutdown);
256
279
  process.on('SIGTERM', shutdown);
280
+ child.on('exit', () => { process.exit(0); });
257
281
 
258
- // Start TUI
282
+ // Start TUI — clean stdout, no server interference
259
283
  const { startTui } = await import('../src/tui/index.tsx');
260
284
  await startTui({ port });
261
285
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.96-beta.50",
3
+ "version": "0.1.96-beta.51",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {