Skip to content
Merged
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
5 changes: 4 additions & 1 deletion src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ export interface DaemonStartResult {
pid?: number;
message: string;
logPath: string;
/** True when the daemon was already running — caller should suppress output */
alreadyRunning?: boolean;
}

export function isPortInUse(port: number): Promise<boolean> {
Expand Down Expand Up @@ -305,6 +307,7 @@ export async function startDaemon(
pid: currentStatus.pid,
message: `ModelWeaver is already running (PID ${currentStatus.pid})`,
logPath: getLogPath(),
alreadyRunning: true,
};
}

Expand Down Expand Up @@ -391,7 +394,7 @@ async function reloadLaunchdService(): Promise<void> {
if (existsSync(plistPath)) {
const { execFileSync } = await import("node:child_process");
execFileSync("launchctl", ["load", plistPath], { stdio: "pipe" });
console.warn("[daemon] Reloaded launchd service — KeepAlive re-enabled");
console.warn("[daemon] Reloaded launchd service — KeepAlive uses PathState (won't restart if entry script is missing)");
}
} catch {
// Not macOS or plist missing — skip
Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,10 @@ async function main() {
if (process.argv[2] === 'start') {
const { startDaemon } = await import('./daemon.js');
const result = await startDaemon(args.config, args.port, args.verbose);
console.log(` ${result.message}`);
console.log(` Log file: ${result.logPath}`);
if (!result.alreadyRunning) {
console.log(` ${result.message}`);
console.log(` Log file: ${result.logPath}`);
}
process.exit(result.success ? 0 : 1);
}

Expand Down
7 changes: 6 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,9 +748,13 @@ export function createApp(initConfig: AppConfig, logLevel: LogLevel, metricsStor
broadcastProviderHealth(buildProviderHealth(config, metricsStore));
}

// Add request ID to response (responses from fetch have immutable headers, so create new)
// Add request ID and provider to response (responses from fetch have immutable headers, so create new)
const newHeaders = new Headers(response.headers);
newHeaders.set("x-request-id", requestId);
const resolvedProvider = result.actualProvider || successfulProvider;
if (resolvedProvider) {
newHeaders.set("x-modelweaver-provider", resolvedProvider);
}
// Force Transfer-Encoding: chunked to bypass @hono/node-server's buffering logic.
// The buffering phase (which reads up to 2 chunks before deciding to stream) causes
// "socket closed unexpectedly" when the upstream body is a slow/long SSE stream — if the
Expand Down Expand Up @@ -779,6 +783,7 @@ export function createApp(initConfig: AppConfig, logLevel: LogLevel, metricsStor
tier: ctx.tier,
status: finalResponse.status,
latencyMs: latency,
...(resolvedProvider ? { provider: resolvedProvider } : {}),
});

return finalResponse;
Expand Down
10 changes: 8 additions & 2 deletions src/service-darwin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ function getPlistContent(): string {
<string>start</string>
</array>
<key>KeepAlive</key>
<true/>
<dict>
<key>PathState</key>
<dict>
<key>${nodePath}</key>
<true/>
</dict>
</dict>
<key>RunAtLoad</key>
<true/>
<key>WorkingDirectory</key>
Expand Down Expand Up @@ -78,7 +84,7 @@ export function install(): void {
console.log(` Try manually: launchctl load ${PLIST_PATH}`);
}

console.log(` Auto-starts on login and auto-restarts if stopped (KeepAlive + RunAtLoad enabled)`);
console.log(` Auto-starts on login and auto-restarts if stopped (KeepAlive gated by entry script existence)`);
console.log(` Logs: ${LOG_DIR}/stdout.log and ${LOG_DIR}/stderr.log`);
}

Expand Down
26 changes: 23 additions & 3 deletions src/session-pool.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// src/session-pool.ts
import { Agent, type Dispatcher } from "undici";
import { readdir, readFile } from "fs/promises";
import { join } from "path";
import { statSync } from "fs";
import { join, sep } from "path";
import { homedir } from "os";

export interface SessionStats {
Expand Down Expand Up @@ -157,8 +158,27 @@ export class SessionAgentPool {
this.sessionNames.set(sessionId, name);
return name;
}
// 2. Use project directory name (e.g. "-Users-foo-bar" → "bar")
const projectName = dir.split("-").pop() || dir;
// 2. Extract project name from encoded directory
// e.g. "-Users-kianwoonwong-Downloads-Mousecape-swiftUI" → "Mousecape-swiftUI"
// Can't naively replace all '-' with '/' since '-' can appear in real names.
// Strategy: try all possible split points from right to left,
// checking which candidate path actually exists on the filesystem.
// The part after the last valid path separator is the project name.
let projectName = dir; // fallback
const parts = dir.split("-");
for (let splitAt = parts.length - 1; splitAt >= 1; splitAt--) {
const parentPath = sep + parts.slice(0, splitAt).join(sep);
try {
const parentStat = statSync(parentPath);
if (parentStat.isDirectory()) {
// parent exists — remaining segments form the project name
projectName = parts.slice(splitAt).join("-");
break;
}
} catch {
// parent doesn't exist, try earlier split
}
}
this.sessionNames.set(sessionId, projectName);
return projectName;
} catch {
Expand Down
Loading