@sideboard-ai/core 0.1.225 → 0.1.226

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.
@@ -14429,6 +14429,34 @@ function pipeLines(stream, onLine) {
14429
14429
  const rl = (0, import_node_readline3.createInterface)({ input: stream });
14430
14430
  rl.on("line", onLine);
14431
14431
  }
14432
+ function killListenersOnPorts(ports) {
14433
+ for (const port of ports) {
14434
+ if (!Number.isFinite(port) || port <= 0) continue;
14435
+ try {
14436
+ if (process.platform === "win32") {
14437
+ (0, import_node_child_process4.execFileSync)(
14438
+ "powershell",
14439
+ [
14440
+ "-NoProfile",
14441
+ "-Command",
14442
+ `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
14443
+ ],
14444
+ { stdio: "ignore" }
14445
+ );
14446
+ } else {
14447
+ (0, import_node_child_process4.execFileSync)(
14448
+ "zsh",
14449
+ [
14450
+ "-lc",
14451
+ `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`
14452
+ ],
14453
+ { stdio: "ignore" }
14454
+ );
14455
+ }
14456
+ } catch {
14457
+ }
14458
+ }
14459
+ }
14432
14460
  function killScriptTree(child, ports = []) {
14433
14461
  const pid = child.pid;
14434
14462
  if (pid) {
@@ -14451,26 +14479,7 @@ function killScriptTree(child, ports = []) {
14451
14479
  }
14452
14480
  }
14453
14481
  }
14454
- for (const port of ports) {
14455
- if (!Number.isFinite(port) || port <= 0) continue;
14456
- if (process.platform === "win32") {
14457
- void (0, import_execa4.execa)(
14458
- "powershell",
14459
- [
14460
- "-NoProfile",
14461
- "-Command",
14462
- `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
14463
- ],
14464
- { reject: false }
14465
- );
14466
- } else {
14467
- void (0, import_execa4.execa)(
14468
- "zsh",
14469
- ["-lc", `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`],
14470
- { reject: false }
14471
- );
14472
- }
14473
- }
14482
+ killListenersOnPorts(ports);
14474
14483
  }
14475
14484
  async function spawnWorkspaceScript(command, opts) {
14476
14485
  const loginEnv = await captureLoginEnv();
@@ -14661,11 +14670,12 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
14661
14670
  done: handle.done
14662
14671
  };
14663
14672
  }
14664
- var import_node_fs37, import_node_net, import_node_path39, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
14673
+ var import_node_fs37, import_node_child_process4, import_node_net, import_node_path39, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
14665
14674
  var init_conductor = __esm({
14666
14675
  "src/hook/conductor.ts"() {
14667
14676
  "use strict";
14668
14677
  import_node_fs37 = require("fs");
14678
+ import_node_child_process4 = require("child_process");
14669
14679
  import_node_net = require("net");
14670
14680
  import_node_path39 = require("path");
14671
14681
  import_execa4 = require("execa");
@@ -18497,7 +18507,7 @@ function importConductorWorkspace(workspaceId) {
18497
18507
  }
18498
18508
  let repoPath = worktreePath;
18499
18509
  try {
18500
- repoPath = (0, import_node_child_process4.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
18510
+ repoPath = (0, import_node_child_process5.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
18501
18511
  cwd: worktreePath,
18502
18512
  encoding: "utf8"
18503
18513
  }).trim();
@@ -18526,11 +18536,11 @@ function importConductorWorkspace(workspaceId) {
18526
18536
  async function importConductorWorkspaceAsync(workspaceId) {
18527
18537
  return importConductorWorkspace(workspaceId);
18528
18538
  }
18529
- var import_node_child_process4, import_node_fs45, import_node_os14, import_node_path45, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
18539
+ var import_node_child_process5, import_node_fs45, import_node_os14, import_node_path45, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
18530
18540
  var init_adopt = __esm({
18531
18541
  "src/threads/adopt.ts"() {
18532
18542
  "use strict";
18533
- import_node_child_process4 = require("child_process");
18543
+ import_node_child_process5 = require("child_process");
18534
18544
  import_node_fs45 = require("fs");
18535
18545
  import_node_os14 = require("os");
18536
18546
  import_node_path45 = require("path");
@@ -20929,7 +20939,7 @@ function setCaffeinateHold(enabled, opts) {
20929
20939
  if (platform !== "darwin") {
20930
20940
  return idleState(platform);
20931
20941
  }
20932
- const spawnImpl = hooks.spawn ?? import_node_child_process5.spawn;
20942
+ const spawnImpl = hooks.spawn ?? import_node_child_process6.spawn;
20933
20943
  const child = spawnImpl("caffeinate", ["-dimsu"], {
20934
20944
  detached: true,
20935
20945
  stdio: "ignore"
@@ -20956,11 +20966,11 @@ function releaseCaffeinateHoldForThread(threadId) {
20956
20966
  }
20957
20967
  return setCaffeinateHold(false, { threadId: id });
20958
20968
  }
20959
- var import_node_child_process5, import_node_fs52, import_node_path51, hooks;
20969
+ var import_node_child_process6, import_node_fs52, import_node_path51, hooks;
20960
20970
  var init_caffeinate_hold = __esm({
20961
20971
  "src/store/caffeinate-hold.ts"() {
20962
20972
  "use strict";
20963
- import_node_child_process5 = require("child_process");
20973
+ import_node_child_process6 = require("child_process");
20964
20974
  import_node_fs52 = require("fs");
20965
20975
  import_node_path51 = require("path");
20966
20976
  init_paths();
@@ -21458,6 +21468,16 @@ function writeLiveStatus(threadId, status, lastError) {
21458
21468
  if (!latest || latest.status === "archived") return latest;
21459
21469
  return setStatus(threadId, status, lastError);
21460
21470
  }
21471
+ function collectActiveRunPorts(runs) {
21472
+ const ports = /* @__PURE__ */ new Set();
21473
+ for (const run2 of runs) {
21474
+ const list = run2.ports?.length ? run2.ports : run2.port != null ? [run2.port] : [];
21475
+ for (const p of list) {
21476
+ if (Number.isFinite(p) && p > 0) ports.add(p);
21477
+ }
21478
+ }
21479
+ return [...ports];
21480
+ }
21461
21481
  function getOrchestrator() {
21462
21482
  if (!singleton) {
21463
21483
  singleton = new Orchestrator();
@@ -21733,6 +21753,9 @@ var init_orchestrator = __esm({
21733
21753
  this.emit({ type: "status_changed", threadId: thread.id, status: "stopped" });
21734
21754
  }
21735
21755
  }
21756
+ if (reclaimStaleTurns) {
21757
+ this.reapOrphanedRunScripts();
21758
+ }
21736
21759
  const repoPaths = (repoPath ? [repoPath] : [...new Set(listThreads({ includeArchived: true }).map((t) => t.repoPath))]).filter((p) => !isGlobalRepoPath(p));
21737
21760
  await this.reconcileOrphans(repoPaths);
21738
21761
  try {
@@ -22951,13 +22974,30 @@ var init_orchestrator = __esm({
22951
22974
  worktreeRunProcessKey(thread.worktreePath, scriptName),
22952
22975
  ...siblings.map((t) => `${t.id}:run:${scriptName}`)
22953
22976
  ];
22977
+ let hadHandle2 = false;
22954
22978
  for (const key of keys) {
22955
22979
  const proc = this.processes.get(key);
22956
- if (proc) proc.kill();
22980
+ if (proc) {
22981
+ proc.kill();
22982
+ hadHandle2 = true;
22983
+ }
22957
22984
  this.processes.delete(key);
22958
22985
  }
22986
+ const legacyKey = worktreeDevProcessKey(thread.worktreePath);
22987
+ const legacy = this.processes.get(legacyKey);
22988
+ if (legacy?.scriptName === scriptName || !legacy?.scriptName && scriptName === "dev") {
22989
+ if (legacy && !hadHandle2) {
22990
+ legacy.kill();
22991
+ hadHandle2 = true;
22992
+ }
22993
+ this.processes.delete(legacyKey);
22994
+ }
22995
+ const run2 = shared.activeRuns.find((r) => r.scriptName === scriptName);
22996
+ if (!hadHandle2 && run2) {
22997
+ killListenersOnPorts(collectActiveRunPorts([run2]));
22998
+ }
22959
22999
  const remaining = shared.activeRuns.filter((r) => r.scriptName !== scriptName);
22960
- const isPrimary = shared.devPort != null && shared.activeRuns.find((r) => r.scriptName === scriptName)?.port === shared.devPort;
23000
+ const isPrimary = shared.devPort != null && run2?.port === shared.devPort;
22961
23001
  this.syncWorktreeRuns(
22962
23002
  thread.worktreePath,
22963
23003
  remaining,
@@ -22966,15 +23006,66 @@ var init_orchestrator = __esm({
22966
23006
  this.emit({ type: "dev_server_stopped", threadId: thread.id, scriptName });
22967
23007
  return;
22968
23008
  }
23009
+ let hadHandle = false;
22969
23010
  for (const [key, proc] of [...this.processes.entries()]) {
22970
23011
  if (isWorktreeRunProcessKey(key, thread.worktreePath) || siblings.some((t) => key.startsWith(`${t.id}:run:`) || key === `${t.id}:dev`)) {
22971
23012
  proc.kill();
23013
+ hadHandle = true;
22972
23014
  this.processes.delete(key);
22973
23015
  }
22974
23016
  }
23017
+ if (!hadHandle && shared.activeRuns.length > 0) {
23018
+ killListenersOnPorts(collectActiveRunPorts(shared.activeRuns));
23019
+ }
22975
23020
  this.syncWorktreeRuns(thread.worktreePath, [], null);
22976
23021
  this.emit({ type: "dev_server_stopped", threadId: thread.id });
22977
23022
  }
23023
+ /**
23024
+ * App quit: tear down every registered run/setup child and free persisted
23025
+ * ports so the next launch is not blocked by orphans.
23026
+ */
23027
+ stopAllRunScripts() {
23028
+ for (const [key, proc] of [...this.processes.entries()]) {
23029
+ if (proc.kind !== "dev" && proc.kind !== "setup") continue;
23030
+ try {
23031
+ proc.kill();
23032
+ } catch {
23033
+ }
23034
+ this.processes.delete(key);
23035
+ }
23036
+ this.reapOrphanedRunScripts({ force: true });
23037
+ }
23038
+ /**
23039
+ * After restart (or force quit), in-memory handles are gone but `activeRuns`
23040
+ * and OS listeners may remain. Kill by port and clear disk metadata.
23041
+ * Skips worktrees that still have a live handle unless `force` (quit path).
23042
+ */
23043
+ reapOrphanedRunScripts(opts) {
23044
+ const force = opts?.force === true;
23045
+ const seen = /* @__PURE__ */ new Set();
23046
+ for (const thread of listThreads({ includeArchived: true })) {
23047
+ if (thread.status === "archived") continue;
23048
+ if (isGlobalThread(thread)) continue;
23049
+ const wt = normalizeWorktreePath(thread.worktreePath);
23050
+ if (seen.has(wt)) continue;
23051
+ seen.add(wt);
23052
+ const siblings = threadsSharingWorktree(thread.worktreePath);
23053
+ const shared = mergeWorktreeActiveRuns(siblings);
23054
+ if (shared.activeRuns.length === 0 && shared.devPort == null) continue;
23055
+ const hasLiveHandle = [...this.processes.keys()].some(
23056
+ (k) => isWorktreeRunProcessKey(k, thread.worktreePath) || siblings.some((t) => k.startsWith(`${t.id}:run:`) || k === `${t.id}:dev`)
23057
+ );
23058
+ if (!force && hasLiveHandle) continue;
23059
+ killListenersOnPorts(collectActiveRunPorts(shared.activeRuns));
23060
+ if (shared.devPort != null) {
23061
+ killListenersOnPorts([shared.devPort]);
23062
+ }
23063
+ this.syncWorktreeRuns(thread.worktreePath, [], null);
23064
+ for (const sibling of siblings) {
23065
+ this.emit({ type: "dev_server_stopped", threadId: sibling.id });
23066
+ }
23067
+ }
23068
+ }
22978
23069
  syncWorktreeRuns(worktreePath, activeRuns, devPort) {
22979
23070
  for (const sibling of threadsSharingWorktree(worktreePath)) {
22980
23071
  updateThread(sibling.id, { activeRuns, devPort });
@@ -50,7 +50,7 @@ import {
50
50
  syncBoardPins,
51
51
  updateSchedule,
52
52
  waitForDetachedJob
53
- } from "../chunk-5L66IONI.js";
53
+ } from "../chunk-EGKSFWVZ.js";
54
54
  import "../chunk-JFOPOMK7.js";
55
55
  import "../chunk-YWI3IBP2.js";
56
56
  import {
@@ -8,7 +8,7 @@ import {
8
8
  resolveSendFollowUp,
9
9
  startOrchestration,
10
10
  waitForPidExit
11
- } from "./chunk-MPSXJ7WU.js";
11
+ } from "./chunk-VQDYONM4.js";
12
12
  import "./chunk-DJU54RYW.js";
13
13
  import "./chunk-VUW3VKLW.js";
14
14
  import "./chunk-N2VST3NL.js";
@@ -10,7 +10,7 @@ import {
10
10
  resolveSendFollowUp,
11
11
  startOrchestration,
12
12
  waitForPidExit
13
- } from "./chunk-5L66IONI.js";
13
+ } from "./chunk-EGKSFWVZ.js";
14
14
  import "./chunk-JFOPOMK7.js";
15
15
  import "./chunk-YWI3IBP2.js";
16
16
  import "./chunk-SQHRPJ7P.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.225",
3
+ "version": "0.1.226",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",