stashes 0.1.47 → 0.1.48
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/dist/cli.js +55 -21
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +9 -6
- package/dist/commands/start.js.map +1 -1
- package/dist/mcp.js +46 -15
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -40,6 +40,10 @@ var DEFAULT_STASH_COUNT = 3;
|
|
|
40
40
|
var APP_PROXY_PORT = STASHES_PORT + 1;
|
|
41
41
|
var STASH_PORT_START = 4010;
|
|
42
42
|
var STASH_PORT_END = 4030;
|
|
43
|
+
var STASH_PORT_OFFSET = 10;
|
|
44
|
+
var STASH_PORT_RANGE = 20;
|
|
45
|
+
var PORT_SLOT_SIZE = 100;
|
|
46
|
+
var MAX_PORT_SLOTS = 10;
|
|
43
47
|
var MAX_PREVIEW_SERVERS = 5;
|
|
44
48
|
var PREVIEW_TTL_MS = 300000;
|
|
45
49
|
var PREVIEW_REAPER_INTERVAL = 30000;
|
|
@@ -1683,14 +1687,18 @@ class PreviewPool {
|
|
|
1683
1687
|
usedPorts = new Set;
|
|
1684
1688
|
maxSize;
|
|
1685
1689
|
ttlMs;
|
|
1690
|
+
portStart;
|
|
1691
|
+
portEnd;
|
|
1686
1692
|
worktreeManager;
|
|
1687
1693
|
broadcast;
|
|
1688
1694
|
reaperInterval;
|
|
1689
|
-
constructor(worktreeManager, broadcast, maxSize = MAX_PREVIEW_SERVERS, ttlMs = PREVIEW_TTL_MS) {
|
|
1695
|
+
constructor(worktreeManager, broadcast, maxSize = MAX_PREVIEW_SERVERS, ttlMs = PREVIEW_TTL_MS, portStart = STASH_PORT_START, portEnd = STASH_PORT_END) {
|
|
1690
1696
|
this.worktreeManager = worktreeManager;
|
|
1691
1697
|
this.broadcast = broadcast;
|
|
1692
1698
|
this.maxSize = maxSize;
|
|
1693
1699
|
this.ttlMs = ttlMs;
|
|
1700
|
+
this.portStart = portStart;
|
|
1701
|
+
this.portEnd = portEnd;
|
|
1694
1702
|
this.reaperInterval = setInterval(() => this.reap(), PREVIEW_REAPER_INTERVAL);
|
|
1695
1703
|
}
|
|
1696
1704
|
async getOrStart(stashId) {
|
|
@@ -1834,12 +1842,12 @@ class PreviewPool {
|
|
|
1834
1842
|
}
|
|
1835
1843
|
}
|
|
1836
1844
|
allocatePort() {
|
|
1837
|
-
for (let port =
|
|
1845
|
+
for (let port = this.portStart;port <= this.portEnd; port++) {
|
|
1838
1846
|
if (!this.usedPorts.has(port)) {
|
|
1839
1847
|
return port;
|
|
1840
1848
|
}
|
|
1841
1849
|
}
|
|
1842
|
-
throw new Error(`No available ports in range ${
|
|
1850
|
+
throw new Error(`No available ports in range ${this.portStart}-${this.portEnd}`);
|
|
1843
1851
|
}
|
|
1844
1852
|
killEntry(entry) {
|
|
1845
1853
|
try {
|
|
@@ -1877,12 +1885,12 @@ class StashService {
|
|
|
1877
1885
|
chatSessions = new Map;
|
|
1878
1886
|
stashPollTimer = null;
|
|
1879
1887
|
knownStashIds = new Set;
|
|
1880
|
-
constructor(projectPath, worktreeManager, persistence, broadcast) {
|
|
1888
|
+
constructor(projectPath, worktreeManager, persistence, broadcast, stashPortStart, stashPortEnd) {
|
|
1881
1889
|
this.projectPath = projectPath;
|
|
1882
1890
|
this.worktreeManager = worktreeManager;
|
|
1883
1891
|
this.persistence = persistence;
|
|
1884
1892
|
this.broadcast = broadcast;
|
|
1885
|
-
this.previewPool = new PreviewPool(worktreeManager, broadcast);
|
|
1893
|
+
this.previewPool = new PreviewPool(worktreeManager, broadcast, undefined, undefined, stashPortStart, stashPortEnd);
|
|
1886
1894
|
}
|
|
1887
1895
|
getActiveChatId() {
|
|
1888
1896
|
return this.activeChatId;
|
|
@@ -1909,7 +1917,7 @@ class StashService {
|
|
|
1909
1917
|
"Reply with ONLY the file path relative to the project root."
|
|
1910
1918
|
].join(`
|
|
1911
1919
|
`);
|
|
1912
|
-
const aiProcess = startAiProcess("resolve-component", prompt, this.projectPath);
|
|
1920
|
+
const aiProcess = startAiProcess("resolve-component", prompt, this.projectPath, undefined, "claude-haiku-4-5-20251001");
|
|
1913
1921
|
let resolvedPath = "";
|
|
1914
1922
|
try {
|
|
1915
1923
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
@@ -2295,10 +2303,10 @@ function broadcast(event) {
|
|
|
2295
2303
|
function getPersistenceFromWs() {
|
|
2296
2304
|
return persistence;
|
|
2297
2305
|
}
|
|
2298
|
-
function createWebSocketHandler(projectPath, userDevPort, appProxyPort) {
|
|
2306
|
+
function createWebSocketHandler(projectPath, userDevPort, appProxyPort, stashPortStart, stashPortEnd) {
|
|
2299
2307
|
worktreeManager = new WorktreeManager(projectPath);
|
|
2300
2308
|
persistence = new PersistenceService(projectPath);
|
|
2301
|
-
stashService = new StashService(projectPath, worktreeManager, persistence, broadcast);
|
|
2309
|
+
stashService = new StashService(projectPath, worktreeManager, persistence, broadcast, stashPortStart, stashPortEnd);
|
|
2302
2310
|
return {
|
|
2303
2311
|
open(ws) {
|
|
2304
2312
|
clients.add(ws);
|
|
@@ -2432,17 +2440,37 @@ app2.get("/*", async (c) => {
|
|
|
2432
2440
|
headers: { "content-type": "application/json" }
|
|
2433
2441
|
});
|
|
2434
2442
|
});
|
|
2435
|
-
function
|
|
2443
|
+
async function isPortAvailable(port) {
|
|
2444
|
+
try {
|
|
2445
|
+
const server = Bun.serve({ port, fetch: () => new Response("") });
|
|
2446
|
+
server.stop(true);
|
|
2447
|
+
return true;
|
|
2448
|
+
} catch {
|
|
2449
|
+
return false;
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
async function findAvailablePort(requestedPort) {
|
|
2453
|
+
for (let slot = 0;slot < MAX_PORT_SLOTS; slot++) {
|
|
2454
|
+
const port = requestedPort + slot * PORT_SLOT_SIZE;
|
|
2455
|
+
if (await isPortAvailable(port))
|
|
2456
|
+
return port;
|
|
2457
|
+
}
|
|
2458
|
+
throw new Error(`No available port found. Tried ${MAX_PORT_SLOTS} slots starting from ${requestedPort} (step ${PORT_SLOT_SIZE}).`);
|
|
2459
|
+
}
|
|
2460
|
+
async function startServer(projectPath, userDevPort, requestedPort = STASHES_PORT) {
|
|
2461
|
+
const port = await findAvailablePort(requestedPort);
|
|
2462
|
+
const appProxyPort = port + 1;
|
|
2463
|
+
const stashPortStart = port + STASH_PORT_OFFSET;
|
|
2464
|
+
const stashPortEnd = port + STASH_PORT_OFFSET + STASH_PORT_RANGE;
|
|
2436
2465
|
serverState = { projectPath, userDevPort };
|
|
2437
2466
|
initLogFile(projectPath);
|
|
2438
|
-
const appProxyPort = port + 1;
|
|
2439
2467
|
startAppProxy(userDevPort, appProxyPort, injectOverlayScript);
|
|
2440
|
-
const wsHandler = createWebSocketHandler(projectPath, userDevPort, appProxyPort);
|
|
2441
|
-
|
|
2468
|
+
const wsHandler = createWebSocketHandler(projectPath, userDevPort, appProxyPort, stashPortStart, stashPortEnd);
|
|
2469
|
+
Bun.serve({
|
|
2442
2470
|
port,
|
|
2443
|
-
fetch(req,
|
|
2471
|
+
fetch(req, server) {
|
|
2444
2472
|
if (req.headers.get("upgrade") === "websocket") {
|
|
2445
|
-
const success =
|
|
2473
|
+
const success = server.upgrade(req, {
|
|
2446
2474
|
data: { projectPath, userDevPort }
|
|
2447
2475
|
});
|
|
2448
2476
|
if (success)
|
|
@@ -2455,7 +2483,10 @@ function startServer(projectPath, userDevPort, port = STASHES_PORT) {
|
|
|
2455
2483
|
logger.info("server", `Stashes running at http://localhost:${port}`);
|
|
2456
2484
|
logger.info("server", `Proxying user app from http://localhost:${userDevPort}`);
|
|
2457
2485
|
logger.info("server", `Project: ${projectPath}`);
|
|
2458
|
-
|
|
2486
|
+
if (port !== requestedPort) {
|
|
2487
|
+
logger.info("server", `Port ${requestedPort} was in use, using ${port} instead`);
|
|
2488
|
+
}
|
|
2489
|
+
return { port, appProxyPort, stashPortStart, stashPortEnd };
|
|
2459
2490
|
}
|
|
2460
2491
|
|
|
2461
2492
|
// ../server/dist/services/detector.js
|
|
@@ -2535,7 +2566,7 @@ function findConfig(projectPath, candidates) {
|
|
|
2535
2566
|
// src/commands/start.ts
|
|
2536
2567
|
async function startCommand(path, options) {
|
|
2537
2568
|
const projectPath = resolve(path || ".");
|
|
2538
|
-
const
|
|
2569
|
+
const requestedPort = parseInt(options.port, 10);
|
|
2539
2570
|
console.log("");
|
|
2540
2571
|
console.log(" \u2554\u2550\u2557\u2554\u2566\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566 \u2566\u2554\u2550\u2557\u2554\u2550\u2557");
|
|
2541
2572
|
console.log(" \u255A\u2550\u2557 \u2551 \u2560\u2550\u2563\u255A\u2550\u2557\u2560\u2550\u2563\u2551\u2563 \u255A\u2550\u2557");
|
|
@@ -2546,17 +2577,20 @@ async function startCommand(path, options) {
|
|
|
2546
2577
|
console.log(` Project: ${projectPath}`);
|
|
2547
2578
|
console.log(` Framework: ${detected.framework}`);
|
|
2548
2579
|
console.log(` Dev server: http://localhost:${devPort}`);
|
|
2549
|
-
console.log(` Stashes: http://localhost:${port}`);
|
|
2550
|
-
console.log("");
|
|
2551
2580
|
const isDevRunning = await checkPort(devPort);
|
|
2552
2581
|
if (!isDevRunning) {
|
|
2582
|
+
console.log("");
|
|
2553
2583
|
console.log(` ! Your dev server is not running on port ${devPort}`);
|
|
2554
2584
|
console.log(` Start it with: ${detected.devCommand}`);
|
|
2555
|
-
console.log("");
|
|
2556
2585
|
}
|
|
2557
|
-
startServer(projectPath, devPort,
|
|
2586
|
+
const result = await startServer(projectPath, devPort, requestedPort);
|
|
2587
|
+
console.log(` Stashes: http://localhost:${result.port}`);
|
|
2588
|
+
if (result.port !== requestedPort) {
|
|
2589
|
+
console.log(` (port ${requestedPort} was in use, using ${result.port})`);
|
|
2590
|
+
}
|
|
2591
|
+
console.log("");
|
|
2558
2592
|
if (options.open !== false) {
|
|
2559
|
-
await open(`http://localhost:${port}`);
|
|
2593
|
+
await open(`http://localhost:${result.port}`);
|
|
2560
2594
|
}
|
|
2561
2595
|
}
|
|
2562
2596
|
async function checkPort(port) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCrF"}
|
package/dist/commands/start.js
CHANGED
|
@@ -4,7 +4,7 @@ import { startServer } from '@stashes/server';
|
|
|
4
4
|
import { detectFramework } from '@stashes/server/services/detector';
|
|
5
5
|
export async function startCommand(path, options) {
|
|
6
6
|
const projectPath = resolve(path || '.');
|
|
7
|
-
const
|
|
7
|
+
const requestedPort = parseInt(options.port, 10);
|
|
8
8
|
console.log('');
|
|
9
9
|
console.log(' ╔═╗╔╦╗╔═╗╔═╗╦ ╦╔═╗╔═╗');
|
|
10
10
|
console.log(' ╚═╗ ║ ╠═╣╚═╗╠═╣║╣ ╚═╗');
|
|
@@ -15,17 +15,20 @@ export async function startCommand(path, options) {
|
|
|
15
15
|
console.log(` Project: ${projectPath}`);
|
|
16
16
|
console.log(` Framework: ${detected.framework}`);
|
|
17
17
|
console.log(` Dev server: http://localhost:${devPort}`);
|
|
18
|
-
console.log(` Stashes: http://localhost:${port}`);
|
|
19
|
-
console.log('');
|
|
20
18
|
const isDevRunning = await checkPort(devPort);
|
|
21
19
|
if (!isDevRunning) {
|
|
20
|
+
console.log('');
|
|
22
21
|
console.log(` ! Your dev server is not running on port ${devPort}`);
|
|
23
22
|
console.log(` Start it with: ${detected.devCommand}`);
|
|
24
|
-
console.log('');
|
|
25
23
|
}
|
|
26
|
-
startServer(projectPath, devPort,
|
|
24
|
+
const result = await startServer(projectPath, devPort, requestedPort);
|
|
25
|
+
console.log(` Stashes: http://localhost:${result.port}`);
|
|
26
|
+
if (result.port !== requestedPort) {
|
|
27
|
+
console.log(` (port ${requestedPort} was in use, using ${result.port})`);
|
|
28
|
+
}
|
|
29
|
+
console.log('');
|
|
27
30
|
if (options.open !== false) {
|
|
28
|
-
await open(`http://localhost:${port}`);
|
|
31
|
+
await open(`http://localhost:${result.port}`);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
async function checkPort(port) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AASpE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,OAAqB;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;IACzC,MAAM,
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AASpE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,OAAqB;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAE1E,OAAO,CAAC,GAAG,CAAC,iBAAiB,WAAW,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAC;IAEzD,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,8CAA8C,OAAO,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,sBAAsB,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;IAEtE,OAAO,CAAC,GAAG,CAAC,kCAAkC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,WAAW,aAAa,sBAAsB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC3B,MAAM,IAAI,CAAC,oBAAoB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAY;IACnC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,EAAE,EAAE;YACvD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/mcp.js
CHANGED
|
@@ -36,6 +36,10 @@ var DEFAULT_STASH_COUNT = 3;
|
|
|
36
36
|
var APP_PROXY_PORT = STASHES_PORT + 1;
|
|
37
37
|
var STASH_PORT_START = 4010;
|
|
38
38
|
var STASH_PORT_END = 4030;
|
|
39
|
+
var STASH_PORT_OFFSET = 10;
|
|
40
|
+
var STASH_PORT_RANGE = 20;
|
|
41
|
+
var PORT_SLOT_SIZE = 100;
|
|
42
|
+
var MAX_PORT_SLOTS = 10;
|
|
39
43
|
var MAX_PREVIEW_SERVERS = 5;
|
|
40
44
|
var PREVIEW_TTL_MS = 300000;
|
|
41
45
|
var PREVIEW_REAPER_INTERVAL = 30000;
|
|
@@ -1879,14 +1883,18 @@ class PreviewPool {
|
|
|
1879
1883
|
usedPorts = new Set;
|
|
1880
1884
|
maxSize;
|
|
1881
1885
|
ttlMs;
|
|
1886
|
+
portStart;
|
|
1887
|
+
portEnd;
|
|
1882
1888
|
worktreeManager;
|
|
1883
1889
|
broadcast;
|
|
1884
1890
|
reaperInterval;
|
|
1885
|
-
constructor(worktreeManager, broadcast, maxSize = MAX_PREVIEW_SERVERS, ttlMs = PREVIEW_TTL_MS) {
|
|
1891
|
+
constructor(worktreeManager, broadcast, maxSize = MAX_PREVIEW_SERVERS, ttlMs = PREVIEW_TTL_MS, portStart = STASH_PORT_START, portEnd = STASH_PORT_END) {
|
|
1886
1892
|
this.worktreeManager = worktreeManager;
|
|
1887
1893
|
this.broadcast = broadcast;
|
|
1888
1894
|
this.maxSize = maxSize;
|
|
1889
1895
|
this.ttlMs = ttlMs;
|
|
1896
|
+
this.portStart = portStart;
|
|
1897
|
+
this.portEnd = portEnd;
|
|
1890
1898
|
this.reaperInterval = setInterval(() => this.reap(), PREVIEW_REAPER_INTERVAL);
|
|
1891
1899
|
}
|
|
1892
1900
|
async getOrStart(stashId) {
|
|
@@ -2030,12 +2038,12 @@ class PreviewPool {
|
|
|
2030
2038
|
}
|
|
2031
2039
|
}
|
|
2032
2040
|
allocatePort() {
|
|
2033
|
-
for (let port =
|
|
2041
|
+
for (let port = this.portStart;port <= this.portEnd; port++) {
|
|
2034
2042
|
if (!this.usedPorts.has(port)) {
|
|
2035
2043
|
return port;
|
|
2036
2044
|
}
|
|
2037
2045
|
}
|
|
2038
|
-
throw new Error(`No available ports in range ${
|
|
2046
|
+
throw new Error(`No available ports in range ${this.portStart}-${this.portEnd}`);
|
|
2039
2047
|
}
|
|
2040
2048
|
killEntry(entry) {
|
|
2041
2049
|
try {
|
|
@@ -2073,12 +2081,12 @@ class StashService {
|
|
|
2073
2081
|
chatSessions = new Map;
|
|
2074
2082
|
stashPollTimer = null;
|
|
2075
2083
|
knownStashIds = new Set;
|
|
2076
|
-
constructor(projectPath, worktreeManager, persistence, broadcast) {
|
|
2084
|
+
constructor(projectPath, worktreeManager, persistence, broadcast, stashPortStart, stashPortEnd) {
|
|
2077
2085
|
this.projectPath = projectPath;
|
|
2078
2086
|
this.worktreeManager = worktreeManager;
|
|
2079
2087
|
this.persistence = persistence;
|
|
2080
2088
|
this.broadcast = broadcast;
|
|
2081
|
-
this.previewPool = new PreviewPool(worktreeManager, broadcast);
|
|
2089
|
+
this.previewPool = new PreviewPool(worktreeManager, broadcast, undefined, undefined, stashPortStart, stashPortEnd);
|
|
2082
2090
|
}
|
|
2083
2091
|
getActiveChatId() {
|
|
2084
2092
|
return this.activeChatId;
|
|
@@ -2105,7 +2113,7 @@ class StashService {
|
|
|
2105
2113
|
"Reply with ONLY the file path relative to the project root."
|
|
2106
2114
|
].join(`
|
|
2107
2115
|
`);
|
|
2108
|
-
const aiProcess = startAiProcess("resolve-component", prompt, this.projectPath);
|
|
2116
|
+
const aiProcess = startAiProcess("resolve-component", prompt, this.projectPath, undefined, "claude-haiku-4-5-20251001");
|
|
2109
2117
|
let resolvedPath = "";
|
|
2110
2118
|
try {
|
|
2111
2119
|
for await (const chunk of parseClaudeStream(aiProcess.process)) {
|
|
@@ -2491,10 +2499,10 @@ function broadcast(event) {
|
|
|
2491
2499
|
function getPersistenceFromWs() {
|
|
2492
2500
|
return persistence;
|
|
2493
2501
|
}
|
|
2494
|
-
function createWebSocketHandler(projectPath, userDevPort, appProxyPort) {
|
|
2502
|
+
function createWebSocketHandler(projectPath, userDevPort, appProxyPort, stashPortStart, stashPortEnd) {
|
|
2495
2503
|
worktreeManager = new WorktreeManager(projectPath);
|
|
2496
2504
|
persistence = new PersistenceService(projectPath);
|
|
2497
|
-
stashService = new StashService(projectPath, worktreeManager, persistence, broadcast);
|
|
2505
|
+
stashService = new StashService(projectPath, worktreeManager, persistence, broadcast, stashPortStart, stashPortEnd);
|
|
2498
2506
|
return {
|
|
2499
2507
|
open(ws) {
|
|
2500
2508
|
clients.add(ws);
|
|
@@ -2628,17 +2636,37 @@ app2.get("/*", async (c) => {
|
|
|
2628
2636
|
headers: { "content-type": "application/json" }
|
|
2629
2637
|
});
|
|
2630
2638
|
});
|
|
2631
|
-
function
|
|
2639
|
+
async function isPortAvailable(port) {
|
|
2640
|
+
try {
|
|
2641
|
+
const server = Bun.serve({ port, fetch: () => new Response("") });
|
|
2642
|
+
server.stop(true);
|
|
2643
|
+
return true;
|
|
2644
|
+
} catch {
|
|
2645
|
+
return false;
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
async function findAvailablePort(requestedPort) {
|
|
2649
|
+
for (let slot = 0;slot < MAX_PORT_SLOTS; slot++) {
|
|
2650
|
+
const port = requestedPort + slot * PORT_SLOT_SIZE;
|
|
2651
|
+
if (await isPortAvailable(port))
|
|
2652
|
+
return port;
|
|
2653
|
+
}
|
|
2654
|
+
throw new Error(`No available port found. Tried ${MAX_PORT_SLOTS} slots starting from ${requestedPort} (step ${PORT_SLOT_SIZE}).`);
|
|
2655
|
+
}
|
|
2656
|
+
async function startServer(projectPath, userDevPort, requestedPort = STASHES_PORT) {
|
|
2657
|
+
const port = await findAvailablePort(requestedPort);
|
|
2658
|
+
const appProxyPort = port + 1;
|
|
2659
|
+
const stashPortStart = port + STASH_PORT_OFFSET;
|
|
2660
|
+
const stashPortEnd = port + STASH_PORT_OFFSET + STASH_PORT_RANGE;
|
|
2632
2661
|
serverState = { projectPath, userDevPort };
|
|
2633
2662
|
initLogFile(projectPath);
|
|
2634
|
-
const appProxyPort = port + 1;
|
|
2635
2663
|
startAppProxy(userDevPort, appProxyPort, injectOverlayScript);
|
|
2636
|
-
const wsHandler = createWebSocketHandler(projectPath, userDevPort, appProxyPort);
|
|
2637
|
-
|
|
2664
|
+
const wsHandler = createWebSocketHandler(projectPath, userDevPort, appProxyPort, stashPortStart, stashPortEnd);
|
|
2665
|
+
Bun.serve({
|
|
2638
2666
|
port,
|
|
2639
|
-
fetch(req,
|
|
2667
|
+
fetch(req, server) {
|
|
2640
2668
|
if (req.headers.get("upgrade") === "websocket") {
|
|
2641
|
-
const success =
|
|
2669
|
+
const success = server.upgrade(req, {
|
|
2642
2670
|
data: { projectPath, userDevPort }
|
|
2643
2671
|
});
|
|
2644
2672
|
if (success)
|
|
@@ -2651,7 +2679,10 @@ function startServer(projectPath, userDevPort, port = STASHES_PORT) {
|
|
|
2651
2679
|
logger.info("server", `Stashes running at http://localhost:${port}`);
|
|
2652
2680
|
logger.info("server", `Proxying user app from http://localhost:${userDevPort}`);
|
|
2653
2681
|
logger.info("server", `Project: ${projectPath}`);
|
|
2654
|
-
|
|
2682
|
+
if (port !== requestedPort) {
|
|
2683
|
+
logger.info("server", `Port ${requestedPort} was in use, using ${port} instead`);
|
|
2684
|
+
}
|
|
2685
|
+
return { port, appProxyPort, stashPortStart, stashPortEnd };
|
|
2655
2686
|
}
|
|
2656
2687
|
|
|
2657
2688
|
// ../mcp/src/tools/browse.ts
|