@staff0rd/assist 0.287.0 → 0.288.0

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/dist/index.js +138 -41
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.287.0",
9
+ version: "0.288.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -18570,6 +18570,33 @@ function shutdownSessions(sessions) {
18570
18570
  }
18571
18571
  }
18572
18572
 
18573
+ // src/commands/sessions/daemon/autoHealWindowsDaemon.ts
18574
+ async function autoHealWindowsDaemon(conn, state, heal, version2) {
18575
+ daemonLog(`windows proxy: auto-healing windows daemon (mismatch ${version2})`);
18576
+ notifyHealing(state);
18577
+ try {
18578
+ conn.dispose();
18579
+ await heal();
18580
+ daemonLog("windows proxy: heal complete, reconnecting to windows daemon");
18581
+ await conn.ensure();
18582
+ } catch (e) {
18583
+ const message = e instanceof Error ? e.message : String(e);
18584
+ daemonLog(`windows proxy: auto-heal failed: ${message}`);
18585
+ state.broadcast({
18586
+ type: "error",
18587
+ message: `Windows host auto-update failed: ${message}`
18588
+ });
18589
+ }
18590
+ }
18591
+ function notifyHealing(state) {
18592
+ for (const client of state.pendingCreators)
18593
+ sendTo(client, {
18594
+ type: "error",
18595
+ message: "Windows host is out of date; updating it now \u2014 reselect the repo once the update finishes."
18596
+ });
18597
+ state.pendingCreators = [];
18598
+ }
18599
+
18573
18600
  // src/commands/sessions/daemon/connectToWindowsDaemon.ts
18574
18601
  import * as net2 from "net";
18575
18602
 
@@ -18600,18 +18627,6 @@ async function isWindowsDaemonRunning() {
18600
18627
  }
18601
18628
  }
18602
18629
 
18603
- // src/commands/sessions/daemon/discoverWindowsSessions.ts
18604
- async function discoverWindowsSessions(conn) {
18605
- if (conn.connected || !await isWindowsDaemonRunning()) return;
18606
- daemonLog("windows proxy: discovering sessions on running windows daemon");
18607
- try {
18608
- await conn.ensure();
18609
- } catch (e) {
18610
- const message = e instanceof Error ? e.message : String(e);
18611
- daemonLog(`windows proxy: discovery failed: ${message}`);
18612
- }
18613
- }
18614
-
18615
18630
  // src/commands/sessions/daemon/ensureWindowsDaemonRunning.ts
18616
18631
  import { spawn as spawn10 } from "child_process";
18617
18632
  var SPAWN_TIMEOUT_MS2 = 3e4;
@@ -18661,6 +18676,24 @@ function delay2(ms) {
18661
18676
  return new Promise((resolve16) => setTimeout(resolve16, ms));
18662
18677
  }
18663
18678
 
18679
+ // src/commands/sessions/daemon/defaultConnect.ts
18680
+ async function defaultConnect() {
18681
+ await ensureWindowsDaemonRunning();
18682
+ return connectToWindowsDaemon();
18683
+ }
18684
+
18685
+ // src/commands/sessions/daemon/discoverWindowsSessions.ts
18686
+ async function discoverWindowsSessions(conn) {
18687
+ if (conn.connected || !await isWindowsDaemonRunning()) return;
18688
+ daemonLog("windows proxy: discovering sessions on running windows daemon");
18689
+ try {
18690
+ await conn.ensure();
18691
+ } catch (e) {
18692
+ const message = e instanceof Error ? e.message : String(e);
18693
+ daemonLog(`windows proxy: discovery failed: ${message}`);
18694
+ }
18695
+ }
18696
+
18664
18697
  // src/commands/sessions/daemon/forwardWindowsCreate.ts
18665
18698
  async function forwardWindowsCreate(conn, state, client, data) {
18666
18699
  daemonLog(`windows proxy: routing ${data.type} (cwd=${data.cwd})`);
@@ -18693,13 +18726,14 @@ function versionsMatch(a, b) {
18693
18726
 
18694
18727
  // src/commands/sessions/daemon/WindowsProxyState.ts
18695
18728
  var MAX_SCROLLBACK2 = 256 * 1024;
18696
- function createState(broadcast2, onSessionsChanged) {
18729
+ function createState(broadcast2, onSessionsChanged, onVersionMismatch) {
18697
18730
  return {
18698
18731
  windowsSessions: [],
18699
18732
  scrollback: /* @__PURE__ */ new Map(),
18700
18733
  pendingCreators: [],
18701
18734
  broadcast: broadcast2,
18702
- onSessionsChanged
18735
+ onSessionsChanged,
18736
+ onVersionMismatch
18703
18737
  };
18704
18738
  }
18705
18739
  function resetState(state) {
@@ -18730,7 +18764,7 @@ function handleInbound(state, line) {
18730
18764
  inbound[msg.type]?.(state, msg);
18731
18765
  }
18732
18766
  var inbound = {
18733
- hello: (_state, msg) => handleHello(msg),
18767
+ hello: handleHello,
18734
18768
  created: handleCreated,
18735
18769
  sessions: handleSessions,
18736
18770
  output: handleOutput,
@@ -18740,11 +18774,13 @@ var inbound = {
18740
18774
  state.broadcast(msg);
18741
18775
  }
18742
18776
  };
18743
- function handleHello(msg) {
18744
- if (isHello(msg) && !versionsMatch(msg.version, ASSIST_VERSION))
18777
+ function handleHello(state, msg) {
18778
+ if (isHello(msg) && !versionsMatch(msg.version, ASSIST_VERSION)) {
18745
18779
  daemonLog(
18746
18780
  `windows daemon version mismatch: ${msg.version} (wsl ${ASSIST_VERSION})`
18747
18781
  );
18782
+ state.onVersionMismatch(msg.version);
18783
+ }
18748
18784
  }
18749
18785
  function handleCreated(state, msg) {
18750
18786
  daemonLog(`windows daemon: created session ${nsId(msg)}`);
@@ -18775,6 +18811,57 @@ function nsId(msg) {
18775
18811
  return toWindowsSessionId(msg.sessionId);
18776
18812
  }
18777
18813
 
18814
+ // src/commands/sessions/daemon/healWindowsDaemon.ts
18815
+ import { spawn as spawn11 } from "child_process";
18816
+ import { createInterface as createInterface5 } from "readline";
18817
+ var UPDATE_TIMEOUT_MS = 5 * 6e4;
18818
+ var STOP_TIMEOUT_MS2 = 3e4;
18819
+ async function healWindowsDaemon() {
18820
+ daemonLog("windows daemon: auto-heal: running `assist update` on host");
18821
+ await runOnWindowsHost("assist update", UPDATE_TIMEOUT_MS);
18822
+ daemonLog("windows daemon: auto-heal: update done, stopping stale daemon");
18823
+ await runOnWindowsHost("assist daemon stop", STOP_TIMEOUT_MS2);
18824
+ daemonLog("windows daemon: auto-heal: stale daemon stopped");
18825
+ }
18826
+ function runOnWindowsHost(command, timeoutMs) {
18827
+ return new Promise((resolve16, reject) => {
18828
+ const child = spawn11("pwsh.exe", ["-Command", command], {
18829
+ stdio: ["ignore", "pipe", "pipe"]
18830
+ });
18831
+ logStream(child.stdout, command);
18832
+ logStream(child.stderr, command);
18833
+ const timer = setTimeout(() => {
18834
+ child.kill();
18835
+ reject(
18836
+ new Error(
18837
+ `'${command}' on the Windows host timed out after ${timeoutMs}ms`
18838
+ )
18839
+ );
18840
+ }, timeoutMs);
18841
+ child.on("error", (e) => {
18842
+ clearTimeout(timer);
18843
+ reject(e);
18844
+ });
18845
+ child.on("exit", (code) => {
18846
+ clearTimeout(timer);
18847
+ if (code === 0) resolve16();
18848
+ else
18849
+ reject(
18850
+ new Error(
18851
+ `'${command}' on the Windows host exited with code ${code}`
18852
+ )
18853
+ );
18854
+ });
18855
+ });
18856
+ }
18857
+ function logStream(stream, command) {
18858
+ if (!stream) return;
18859
+ const lines = createInterface5({ input: stream });
18860
+ lines.on("line", (line) => daemonLog(`[windows ${command}] ${line}`));
18861
+ lines.on("error", () => {
18862
+ });
18863
+ }
18864
+
18778
18865
  // src/commands/sessions/daemon/isWindowsCwd.ts
18779
18866
  function isWindowsCwd(cwd) {
18780
18867
  return /^[A-Za-z]:[\\/]/.test(cwd);
@@ -18783,8 +18870,16 @@ function shouldProxyToWindows(cwd) {
18783
18870
  return detectPlatform() === "wsl" && isWindowsCwd(cwd);
18784
18871
  }
18785
18872
 
18873
+ // src/commands/sessions/daemon/isWindowsCreate.ts
18874
+ function isWindowsCreate(data) {
18875
+ return typeof data.cwd === "string" && shouldProxyToWindows(data.cwd);
18876
+ }
18877
+ function isWindowsIo(data) {
18878
+ return typeof data.sessionId === "string" && isWindowsSessionId(data.sessionId);
18879
+ }
18880
+
18786
18881
  // src/commands/sessions/daemon/WindowsConnection.ts
18787
- import { createInterface as createInterface5 } from "readline";
18882
+ import { createInterface as createInterface6 } from "readline";
18788
18883
  var WindowsConnection = class {
18789
18884
  constructor(deps2) {
18790
18885
  this.deps = deps2;
@@ -18821,7 +18916,7 @@ var WindowsConnection = class {
18821
18916
  const socket = await this.deps.connect();
18822
18917
  daemonLog("windows connection: tcp connected, sending hello");
18823
18918
  this.socket = socket;
18824
- const lines = createInterface5({ input: socket });
18919
+ const lines = createInterface6({ input: socket });
18825
18920
  lines.on("error", () => {
18826
18921
  });
18827
18922
  lines.on("line", (line) => this.deps.onLine(line));
@@ -18842,12 +18937,12 @@ var WindowsConnection = class {
18842
18937
 
18843
18938
  // src/commands/sessions/daemon/WindowsProxy.ts
18844
18939
  var WindowsProxy = class {
18845
- state;
18846
- conn;
18847
- constructor(clients, onSessionsChanged, connect3 = defaultConnect) {
18940
+ constructor(clients, onSessionsChanged, connect3 = defaultConnect, heal = healWindowsDaemon) {
18941
+ this.heal = heal;
18848
18942
  this.state = createState(
18849
18943
  (msg) => broadcast(clients, msg),
18850
- onSessionsChanged
18944
+ onSessionsChanged,
18945
+ (version2) => void this.handleVersionMismatch(version2)
18851
18946
  );
18852
18947
  this.conn = new WindowsConnection({
18853
18948
  connect: connect3,
@@ -18855,6 +18950,11 @@ var WindowsProxy = class {
18855
18950
  onClose: () => this.handleClose()
18856
18951
  });
18857
18952
  }
18953
+ state;
18954
+ conn;
18955
+ // why: heal runs once per proxy lifetime; if WSL is the older side, updating Windows can't close the gap, so a repeat mismatch must not loop
18956
+ healAttempted = false;
18957
+ healing = false;
18858
18958
  sessions() {
18859
18959
  return this.state.windowsSessions;
18860
18960
  }
@@ -18868,7 +18968,7 @@ var WindowsProxy = class {
18868
18968
  // windows-origin cwd is forwarded, as is I/O for a namespaced session id.
18869
18969
  route(client, data) {
18870
18970
  if (isWindowsCreate(data)) {
18871
- void this.forwardCreate(client, data);
18971
+ void forwardWindowsCreate(this.conn, this.state, client, data);
18872
18972
  return true;
18873
18973
  }
18874
18974
  if (isWindowsIo(data)) {
@@ -18881,8 +18981,15 @@ var WindowsProxy = class {
18881
18981
  dispose() {
18882
18982
  this.conn.dispose();
18883
18983
  }
18884
- async forwardCreate(client, data) {
18885
- await forwardWindowsCreate(this.conn, this.state, client, data);
18984
+ async handleVersionMismatch(version2) {
18985
+ if (this.healing || this.healAttempted) return;
18986
+ this.healing = true;
18987
+ this.healAttempted = true;
18988
+ try {
18989
+ await autoHealWindowsDaemon(this.conn, this.state, this.heal, version2);
18990
+ } finally {
18991
+ this.healing = false;
18992
+ }
18886
18993
  }
18887
18994
  handleClose() {
18888
18995
  daemonLog("windows proxy: connection to windows daemon closed");
@@ -18895,16 +19002,6 @@ var WindowsProxy = class {
18895
19002
  this.state.onSessionsChanged();
18896
19003
  }
18897
19004
  };
18898
- function isWindowsCreate(data) {
18899
- return typeof data.cwd === "string" && shouldProxyToWindows(data.cwd);
18900
- }
18901
- function isWindowsIo(data) {
18902
- return typeof data.sessionId === "string" && isWindowsSessionId(data.sessionId);
18903
- }
18904
- async function defaultConnect() {
18905
- await ensureWindowsDaemonRunning();
18906
- return connectToWindowsDaemon();
18907
- }
18908
19005
 
18909
19006
  // src/commands/sessions/daemon/watchClaudeSessionId.ts
18910
19007
  import * as fs27 from "fs";
@@ -19300,7 +19397,7 @@ import { unlinkSync as unlinkSync19 } from "fs";
19300
19397
  import * as net3 from "net";
19301
19398
 
19302
19399
  // src/commands/sessions/daemon/handleConnection.ts
19303
- import { createInterface as createInterface6 } from "readline";
19400
+ import { createInterface as createInterface7 } from "readline";
19304
19401
 
19305
19402
  // src/commands/sessions/shared/parseTranscript.ts
19306
19403
  import * as fs28 from "fs";
@@ -19394,10 +19491,10 @@ function safeParse2(line) {
19394
19491
  }
19395
19492
 
19396
19493
  // src/commands/sessions/daemon/dispatchMessage.ts
19397
- function creator(spawn11) {
19494
+ function creator(spawn12) {
19398
19495
  return (client, m, d) => {
19399
19496
  if (m.windowsProxy.route(client, d)) return;
19400
- sendTo(client, { type: "created", sessionId: spawn11(m, d) });
19497
+ sendTo(client, { type: "created", sessionId: spawn12(m, d) });
19401
19498
  };
19402
19499
  }
19403
19500
  function handleHistory(client) {
@@ -19478,7 +19575,7 @@ function handleConnection(socket, manager) {
19478
19575
  }
19479
19576
  };
19480
19577
  manager.addClient(client);
19481
- const lines = createInterface6({ input: socket });
19578
+ const lines = createInterface7({ input: socket });
19482
19579
  lines.on("error", () => {
19483
19580
  });
19484
19581
  lines.on("line", (line) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.287.0",
3
+ "version": "0.288.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {