@blue-soda/dsh-remote 0.5.4 → 0.5.6

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/README.md CHANGED
@@ -23,7 +23,7 @@ Continue DeepSeek Harness sessions and experimental Codex workspaces from anothe
23
23
  Add the current package version to the `web` profile, then restart Harness:
24
24
 
25
25
  ```sh
26
- dsh plugin --profile web add -w @blue-soda/dsh-remote@0.5.4
26
+ dsh plugin --profile web add -w @blue-soda/dsh-remote@0.5.6
27
27
  ```
28
28
 
29
29
  ### dsh-TUI Host
@@ -31,7 +31,7 @@ dsh plugin --profile web add -w @blue-soda/dsh-remote@0.5.4
31
31
  Remote can also run as a Host in a terminal-only [dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI) profile:
32
32
 
33
33
  ```sh
34
- dsh plugin --profile dsh-tui add -w @blue-soda/dsh-remote@0.5.4
34
+ dsh plugin --profile dsh-tui add -w @blue-soda/dsh-remote@0.5.6
35
35
  ```
36
36
 
37
37
  After starting dsh-TUI, manage Remote with `/remote`, `/remote login`, `/remote status`, and `/remote logout`.
@@ -1410,7 +1410,12 @@
1410
1410
  );
1411
1411
  }
1412
1412
  function RemoteWorkspaceAction(props) {
1413
- let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [connectingHost, setConnectingHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [codexWorkspaces, setCodexWorkspaces] = React.useState([]), [codexNotice, setCodexNotice] = React.useState(void 0), [codexAvailable, setCodexAvailable] = React.useState(!1), codexProbeAvailable = React.useRef(!1), [codexOpen, setCodexOpen] = React.useState(!1), [workspaceBackend, setWorkspaceBackend] = React.useState("harness"), [codexWorkspaceId, setCodexWorkspaceId] = React.useState(void 0), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [showAllWorkspaces, setShowAllWorkspaces] = React.useState(!1), [showAllCodexWorkspaces, setShowAllCodexWorkspaces] = React.useState(!1), [devicesOpen, setDevicesOpen] = React.useState(!1), workspaceListId = "dsh-remote-workspace-list", codexWorkspaceHeadingId = "dsh-remote-codex-workspace-heading", codexWorkspaceListId = "dsh-remote-codex-workspace-list", [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), [authorizationResolved, setAuthorizationResolved] = React.useState(!1), [loginServerUrl, setLoginServerUrl] = React.useState(DEFAULT_REMOTE_SERVER_URL), [editingServerUrl, setEditingServerUrl] = React.useState(!1), [awaitingDeepSeek, setAwaitingDeepSeek] = React.useState(!1), [pendingAuthorizeUrl, setPendingAuthorizeUrl] = React.useState(void 0), [loginMethod] = React.useState(
1413
+ let { t } = props, [open, setOpen] = React.useState(!1);
1414
+ React.useEffect(() => {
1415
+ open && props.control("settings.get").catch(() => {
1416
+ });
1417
+ }, [open]);
1418
+ let [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [connectingHost, setConnectingHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [codexWorkspaces, setCodexWorkspaces] = React.useState([]), [codexNotice, setCodexNotice] = React.useState(void 0), [codexAvailable, setCodexAvailable] = React.useState(!1), codexProbeAvailable = React.useRef(!1), [codexOpen, setCodexOpen] = React.useState(!1), [workspaceBackend, setWorkspaceBackend] = React.useState("harness"), [codexWorkspaceId, setCodexWorkspaceId] = React.useState(void 0), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [showAllWorkspaces, setShowAllWorkspaces] = React.useState(!1), [showAllCodexWorkspaces, setShowAllCodexWorkspaces] = React.useState(!1), [devicesOpen, setDevicesOpen] = React.useState(!1), workspaceListId = "dsh-remote-workspace-list", codexWorkspaceHeadingId = "dsh-remote-codex-workspace-heading", codexWorkspaceListId = "dsh-remote-codex-workspace-list", [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), authorizedOnce = React.useRef(!1), [authorizationResolved, setAuthorizationResolved] = React.useState(!1), [loginServerUrl, setLoginServerUrl] = React.useState(DEFAULT_REMOTE_SERVER_URL), [editingServerUrl, setEditingServerUrl] = React.useState(!1), [awaitingDeepSeek, setAwaitingDeepSeek] = React.useState(!1), [pendingAuthorizeUrl, setPendingAuthorizeUrl] = React.useState(void 0), [loginMethod] = React.useState(
1414
1419
  isEnabledQrProvider(props.preferredQrProvider) ? props.preferredQrProvider : defaultQrProvider
1415
1420
  ), [qrSession, setQrSession] = React.useState(void 0), [qrExpired, setQrExpired] = React.useState(!1), [progress, setProgress] = React.useState(void 0), progressRun = React.useRef(0), qrFlowRun = React.useRef(0), [notice, setNotice] = React.useState(void 0), [confirmingSignOut, setConfirmingSignOut] = React.useState(!1), [confirmingRevoke, setConfirmingRevoke] = React.useState(!1), [error, setError] = React.useState(void 0);
1416
1421
  React.useEffect(() => {
@@ -1476,7 +1481,7 @@
1476
1481
  polling || settled || (polling = !0, props.control("client.account.qr.poll", { qrId: qrSession.qrId }).then(async (result) => {
1477
1482
  if (!(!active || settled || run !== qrFlowRun.current))
1478
1483
  if (result.status === "complete") {
1479
- settled = !0, timer !== void 0 && window.clearInterval(timer), setBusy(!0), setError(void 0), setQrExpired(!1), setNeedsAuthorization(!1);
1484
+ settled = !0, timer !== void 0 && window.clearInterval(timer), setBusy(!0), setError(void 0), setQrExpired(!1), authorizedOnce.current = !0, setNeedsAuthorization(!1);
1480
1485
  try {
1481
1486
  let [nextDevices, nextStatus] = await Promise.all([
1482
1487
  props.control("devices"),
@@ -1575,25 +1580,25 @@
1575
1580
  try {
1576
1581
  let nextStatus = await props.control("status");
1577
1582
  if (setStatus(nextStatus), !nextStatus.available) {
1578
- setDevices([]), setNeedsAuthorization(!1), setAuthorizationResolved(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1583
+ setDevices([]), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1579
1584
  return;
1580
1585
  }
1581
1586
  let storedLocally;
1582
1587
  try {
1583
1588
  storedLocally = (await props.control("authorization.local"))?.stored === !0, setNeedsAuthorization(!storedLocally);
1584
1589
  } catch {
1585
- setNeedsAuthorization(!1);
1590
+ authorizedOnce.current = !0, setNeedsAuthorization(!1);
1586
1591
  } finally {
1587
1592
  setAuthorizationResolved(!0);
1588
1593
  }
1589
1594
  try {
1590
1595
  let nextDevices = await props.control("devices");
1591
- if (setDevices(nextDevices), storedLocally !== void 0 && setNeedsAuthorization(authorizationStillNeeded(storedLocally, nextDevices.length)), selectedHost !== void 0) {
1596
+ if (setDevices(nextDevices), !authorizedOnce.current && storedLocally !== void 0 && setNeedsAuthorization(authorizationStillNeeded(storedLocally, nextDevices.length)), selectedHost !== void 0) {
1592
1597
  let nextSelectedHost = nextDevices.find((device) => device.deviceId === selectedHost.deviceId);
1593
1598
  nextSelectedHost === void 0 ? (setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0)) : setSelectedHost(nextSelectedHost);
1594
1599
  }
1595
1600
  } catch {
1596
- setDevices([]), setNeedsAuthorization(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1601
+ setDevices([]), authorizedOnce.current = !1, setNeedsAuthorization(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1597
1602
  }
1598
1603
  selectedHost !== void 0 && (await props.control("remote.session.recover").then((next) => setStatus(next)).catch(() => {
1599
1604
  }), await Promise.all([loadHarnessWorkspaces(selectedHost), loadCodexWorkspaces(selectedHost)]));
@@ -1604,6 +1609,17 @@
1604
1609
  }
1605
1610
  }, show = async () => {
1606
1611
  setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setOpen(!0), await refreshRemote();
1612
+ for (let attempt = 0; attempt < 10; attempt++) {
1613
+ let list = await props.control("devices").catch(() => {
1614
+ });
1615
+ if (list !== void 0) {
1616
+ setDevices(list), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0), await refreshRemote();
1617
+ return;
1618
+ }
1619
+ await new Promise((resolve) => {
1620
+ setTimeout(resolve, 1e3);
1621
+ });
1622
+ }
1607
1623
  };
1608
1624
  React.useEffect(() => {
1609
1625
  if (!(!open || selectedHost !== void 0))
@@ -1629,7 +1645,22 @@
1629
1645
  }, [open, selectedHost]);
1630
1646
  let chooseAnotherHost = () => {
1631
1647
  setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setCodexNotice(void 0), setCodexAvailable(!1), codexProbeAvailable.current = !1, setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setDirectory(void 0), setPath(""), setAddingWorkspace(!1), setError(void 0);
1632
- }, requestDeepSeekSignIn = async () => {
1648
+ }, signingIn = busy || status?.authorizing === !0 || needsAuthorization && status?.hasDeepSeekSession === !0;
1649
+ React.useEffect(() => {
1650
+ if (!signingIn) return;
1651
+ let tries = 0, timer = window.setInterval(() => {
1652
+ tries += 1, (async () => {
1653
+ let next = await props.control("status").catch(() => {
1654
+ });
1655
+ next !== void 0 && setStatus(next);
1656
+ let list = await props.control("devices").catch(() => {
1657
+ });
1658
+ list !== void 0 && (setDevices(list), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0));
1659
+ })(), tries >= 5 && window.clearInterval(timer);
1660
+ }, 1e3);
1661
+ return () => window.clearInterval(timer);
1662
+ }, [signingIn]);
1663
+ let requestDeepSeekSignIn = async () => {
1633
1664
  let transport = globalThis.__DSH_TRANSPORT__, callbackOrigin = transport?.streamBaseUrl !== void 0 ? new URL(transport.streamBaseUrl).origin : window.location.origin, result = await props.control("settings.configure", {
1634
1665
  // No `role`: a device has one identity, and the control writes only the Server address plus this
1635
1666
  // sign-in. Sending a role here was the last thing that made one look like a runtime switch.
@@ -1667,7 +1698,7 @@
1667
1698
  if (state.pending) throw new Error(t("deepseekSignInTimeout"));
1668
1699
  } else
1669
1700
  tab?.close();
1670
- setDevices(await props.control("devices")), setStatus(await props.control("status")), setNeedsAuthorization(!1), setAuthorizationResolved(!0), setNotice(void 0);
1701
+ setDevices(await props.control("devices")), setStatus(await props.control("status")), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0), setNotice(void 0);
1671
1702
  } catch (reason) {
1672
1703
  setError(remoteErrorMessage(reason, t));
1673
1704
  } finally {
@@ -1692,7 +1723,7 @@
1692
1723
  }, revokeRemote = async () => {
1693
1724
  setBusy(!0), setError(void 0), setNotice(void 0);
1694
1725
  try {
1695
- await props.control("client.device.revoke"), setDevices([]), setNeedsAuthorization(!0), setStatus(await props.control("status"));
1726
+ await props.control("client.device.revoke"), setDevices([]), authorizedOnce.current = !1, setNeedsAuthorization(!0), setStatus(await props.control("status"));
1696
1727
  } catch (reason) {
1697
1728
  setError(remoteErrorMessage(reason, t));
1698
1729
  } finally {
@@ -1701,7 +1732,7 @@
1701
1732
  }, logoutRemote = async () => {
1702
1733
  setBusy(!0), setError(void 0), setNotice(void 0);
1703
1734
  try {
1704
- await props.control("settings.logout"), setDevices([]), setNeedsAuthorization(!0), setQrSession(void 0), setQrExpired(!1), setStatus(await props.control("status"));
1735
+ await props.control("settings.logout"), setDevices([]), authorizedOnce.current = !1, setNeedsAuthorization(!0), setQrSession(void 0), setQrExpired(!1), setStatus(await props.control("status"));
1705
1736
  } catch (reason) {
1706
1737
  setError(remoteErrorMessage(reason, t));
1707
1738
  } finally {
@@ -1909,11 +1940,11 @@
1909
1940
  onClick: () => setEditingServerUrl(!0)
1910
1941
  }, loginServerUrl)
1911
1942
  ),
1912
- React.createElement(
1913
- "button",
1914
- { type: "button", disabled: busy || loginServerUrl.trim() === "", onClick: () => void signInClient() },
1915
- t(busy ? "signingIn" : "deepseekSignIn")
1916
- ),
1943
+ React.createElement("button", {
1944
+ type: "button",
1945
+ disabled: busy || signingIn || loginServerUrl.trim() === "",
1946
+ onClick: () => void signInClient()
1947
+ }, t(signingIn ? "signingIn" : "deepseekSignIn")),
1917
1948
  awaitingDeepSeek ? React.createElement(
1918
1949
  "p",
1919
1950
  { className: "dshRemoteServiceAddress" },
package/dist/client.js CHANGED
@@ -1410,7 +1410,12 @@
1410
1410
  );
1411
1411
  }
1412
1412
  function RemoteWorkspaceAction(props) {
1413
- let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [connectingHost, setConnectingHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [codexWorkspaces, setCodexWorkspaces] = React.useState([]), [codexNotice, setCodexNotice] = React.useState(void 0), [codexAvailable, setCodexAvailable] = React.useState(!1), codexProbeAvailable = React.useRef(!1), [codexOpen, setCodexOpen] = React.useState(!1), [workspaceBackend, setWorkspaceBackend] = React.useState("harness"), [codexWorkspaceId, setCodexWorkspaceId] = React.useState(void 0), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [showAllWorkspaces, setShowAllWorkspaces] = React.useState(!1), [showAllCodexWorkspaces, setShowAllCodexWorkspaces] = React.useState(!1), [devicesOpen, setDevicesOpen] = React.useState(!1), workspaceListId = "dsh-remote-workspace-list", codexWorkspaceHeadingId = "dsh-remote-codex-workspace-heading", codexWorkspaceListId = "dsh-remote-codex-workspace-list", [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), [authorizationResolved, setAuthorizationResolved] = React.useState(!1), [loginServerUrl, setLoginServerUrl] = React.useState(DEFAULT_REMOTE_SERVER_URL), [editingServerUrl, setEditingServerUrl] = React.useState(!1), [awaitingDeepSeek, setAwaitingDeepSeek] = React.useState(!1), [pendingAuthorizeUrl, setPendingAuthorizeUrl] = React.useState(void 0), [loginMethod] = React.useState(
1413
+ let { t } = props, [open, setOpen] = React.useState(!1);
1414
+ React.useEffect(() => {
1415
+ open && props.control("settings.get").catch(() => {
1416
+ });
1417
+ }, [open]);
1418
+ let [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [connectingHost, setConnectingHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [codexWorkspaces, setCodexWorkspaces] = React.useState([]), [codexNotice, setCodexNotice] = React.useState(void 0), [codexAvailable, setCodexAvailable] = React.useState(!1), codexProbeAvailable = React.useRef(!1), [codexOpen, setCodexOpen] = React.useState(!1), [workspaceBackend, setWorkspaceBackend] = React.useState("harness"), [codexWorkspaceId, setCodexWorkspaceId] = React.useState(void 0), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [showAllWorkspaces, setShowAllWorkspaces] = React.useState(!1), [showAllCodexWorkspaces, setShowAllCodexWorkspaces] = React.useState(!1), [devicesOpen, setDevicesOpen] = React.useState(!1), workspaceListId = "dsh-remote-workspace-list", codexWorkspaceHeadingId = "dsh-remote-codex-workspace-heading", codexWorkspaceListId = "dsh-remote-codex-workspace-list", [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), authorizedOnce = React.useRef(!1), [authorizationResolved, setAuthorizationResolved] = React.useState(!1), [loginServerUrl, setLoginServerUrl] = React.useState(DEFAULT_REMOTE_SERVER_URL), [editingServerUrl, setEditingServerUrl] = React.useState(!1), [awaitingDeepSeek, setAwaitingDeepSeek] = React.useState(!1), [pendingAuthorizeUrl, setPendingAuthorizeUrl] = React.useState(void 0), [loginMethod] = React.useState(
1414
1419
  isEnabledQrProvider(props.preferredQrProvider) ? props.preferredQrProvider : defaultQrProvider
1415
1420
  ), [qrSession, setQrSession] = React.useState(void 0), [qrExpired, setQrExpired] = React.useState(!1), [progress, setProgress] = React.useState(void 0), progressRun = React.useRef(0), qrFlowRun = React.useRef(0), [notice, setNotice] = React.useState(void 0), [confirmingSignOut, setConfirmingSignOut] = React.useState(!1), [confirmingRevoke, setConfirmingRevoke] = React.useState(!1), [error, setError] = React.useState(void 0);
1416
1421
  React.useEffect(() => {
@@ -1476,7 +1481,7 @@
1476
1481
  polling || settled || (polling = !0, props.control("client.account.qr.poll", { qrId: qrSession.qrId }).then(async (result) => {
1477
1482
  if (!(!active || settled || run !== qrFlowRun.current))
1478
1483
  if (result.status === "complete") {
1479
- settled = !0, timer !== void 0 && window.clearInterval(timer), setBusy(!0), setError(void 0), setQrExpired(!1), setNeedsAuthorization(!1);
1484
+ settled = !0, timer !== void 0 && window.clearInterval(timer), setBusy(!0), setError(void 0), setQrExpired(!1), authorizedOnce.current = !0, setNeedsAuthorization(!1);
1480
1485
  try {
1481
1486
  let [nextDevices, nextStatus] = await Promise.all([
1482
1487
  props.control("devices"),
@@ -1575,25 +1580,25 @@
1575
1580
  try {
1576
1581
  let nextStatus = await props.control("status");
1577
1582
  if (setStatus(nextStatus), !nextStatus.available) {
1578
- setDevices([]), setNeedsAuthorization(!1), setAuthorizationResolved(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1583
+ setDevices([]), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1579
1584
  return;
1580
1585
  }
1581
1586
  let storedLocally;
1582
1587
  try {
1583
1588
  storedLocally = (await props.control("authorization.local"))?.stored === !0, setNeedsAuthorization(!storedLocally);
1584
1589
  } catch {
1585
- setNeedsAuthorization(!1);
1590
+ authorizedOnce.current = !0, setNeedsAuthorization(!1);
1586
1591
  } finally {
1587
1592
  setAuthorizationResolved(!0);
1588
1593
  }
1589
1594
  try {
1590
1595
  let nextDevices = await props.control("devices");
1591
- if (setDevices(nextDevices), storedLocally !== void 0 && setNeedsAuthorization(authorizationStillNeeded(storedLocally, nextDevices.length)), selectedHost !== void 0) {
1596
+ if (setDevices(nextDevices), !authorizedOnce.current && storedLocally !== void 0 && setNeedsAuthorization(authorizationStillNeeded(storedLocally, nextDevices.length)), selectedHost !== void 0) {
1592
1597
  let nextSelectedHost = nextDevices.find((device) => device.deviceId === selectedHost.deviceId);
1593
1598
  nextSelectedHost === void 0 ? (setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0)) : setSelectedHost(nextSelectedHost);
1594
1599
  }
1595
1600
  } catch {
1596
- setDevices([]), setNeedsAuthorization(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1601
+ setDevices([]), authorizedOnce.current = !1, setNeedsAuthorization(!0), setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
1597
1602
  }
1598
1603
  selectedHost !== void 0 && (await props.control("remote.session.recover").then((next) => setStatus(next)).catch(() => {
1599
1604
  }), await Promise.all([loadHarnessWorkspaces(selectedHost), loadCodexWorkspaces(selectedHost)]));
@@ -1604,6 +1609,17 @@
1604
1609
  }
1605
1610
  }, show = async () => {
1606
1611
  setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setOpen(!0), await refreshRemote();
1612
+ for (let attempt = 0; attempt < 10; attempt++) {
1613
+ let list = await props.control("devices").catch(() => {
1614
+ });
1615
+ if (list !== void 0) {
1616
+ setDevices(list), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0), await refreshRemote();
1617
+ return;
1618
+ }
1619
+ await new Promise((resolve) => {
1620
+ setTimeout(resolve, 1e3);
1621
+ });
1622
+ }
1607
1623
  };
1608
1624
  React.useEffect(() => {
1609
1625
  if (!(!open || selectedHost !== void 0))
@@ -1629,7 +1645,22 @@
1629
1645
  }, [open, selectedHost]);
1630
1646
  let chooseAnotherHost = () => {
1631
1647
  setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setCodexNotice(void 0), setCodexAvailable(!1), codexProbeAvailable.current = !1, setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setDirectory(void 0), setPath(""), setAddingWorkspace(!1), setError(void 0);
1632
- }, requestDeepSeekSignIn = async () => {
1648
+ }, signingIn = busy || status?.authorizing === !0 || needsAuthorization && status?.hasDeepSeekSession === !0;
1649
+ React.useEffect(() => {
1650
+ if (!signingIn) return;
1651
+ let tries = 0, timer = window.setInterval(() => {
1652
+ tries += 1, (async () => {
1653
+ let next = await props.control("status").catch(() => {
1654
+ });
1655
+ next !== void 0 && setStatus(next);
1656
+ let list = await props.control("devices").catch(() => {
1657
+ });
1658
+ list !== void 0 && (setDevices(list), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0));
1659
+ })(), tries >= 5 && window.clearInterval(timer);
1660
+ }, 1e3);
1661
+ return () => window.clearInterval(timer);
1662
+ }, [signingIn]);
1663
+ let requestDeepSeekSignIn = async () => {
1633
1664
  let transport = globalThis.__DSH_TRANSPORT__, callbackOrigin = transport?.streamBaseUrl !== void 0 ? new URL(transport.streamBaseUrl).origin : window.location.origin, result = await props.control("settings.configure", {
1634
1665
  // No `role`: a device has one identity, and the control writes only the Server address plus this
1635
1666
  // sign-in. Sending a role here was the last thing that made one look like a runtime switch.
@@ -1667,7 +1698,7 @@
1667
1698
  if (state.pending) throw new Error(t("deepseekSignInTimeout"));
1668
1699
  } else
1669
1700
  tab?.close();
1670
- setDevices(await props.control("devices")), setStatus(await props.control("status")), setNeedsAuthorization(!1), setAuthorizationResolved(!0), setNotice(void 0);
1701
+ setDevices(await props.control("devices")), setStatus(await props.control("status")), authorizedOnce.current = !0, setNeedsAuthorization(!1), setAuthorizationResolved(!0), setNotice(void 0);
1671
1702
  } catch (reason) {
1672
1703
  setError(remoteErrorMessage(reason, t));
1673
1704
  } finally {
@@ -1692,7 +1723,7 @@
1692
1723
  }, revokeRemote = async () => {
1693
1724
  setBusy(!0), setError(void 0), setNotice(void 0);
1694
1725
  try {
1695
- await props.control("client.device.revoke"), setDevices([]), setNeedsAuthorization(!0), setStatus(await props.control("status"));
1726
+ await props.control("client.device.revoke"), setDevices([]), authorizedOnce.current = !1, setNeedsAuthorization(!0), setStatus(await props.control("status"));
1696
1727
  } catch (reason) {
1697
1728
  setError(remoteErrorMessage(reason, t));
1698
1729
  } finally {
@@ -1701,7 +1732,7 @@
1701
1732
  }, logoutRemote = async () => {
1702
1733
  setBusy(!0), setError(void 0), setNotice(void 0);
1703
1734
  try {
1704
- await props.control("settings.logout"), setDevices([]), setNeedsAuthorization(!0), setQrSession(void 0), setQrExpired(!1), setStatus(await props.control("status"));
1735
+ await props.control("settings.logout"), setDevices([]), authorizedOnce.current = !1, setNeedsAuthorization(!0), setQrSession(void 0), setQrExpired(!1), setStatus(await props.control("status"));
1705
1736
  } catch (reason) {
1706
1737
  setError(remoteErrorMessage(reason, t));
1707
1738
  } finally {
@@ -1909,11 +1940,11 @@
1909
1940
  onClick: () => setEditingServerUrl(!0)
1910
1941
  }, loginServerUrl)
1911
1942
  ),
1912
- React.createElement(
1913
- "button",
1914
- { type: "button", disabled: busy || loginServerUrl.trim() === "", onClick: () => void signInClient() },
1915
- t(busy ? "signingIn" : "deepseekSignIn")
1916
- ),
1943
+ React.createElement("button", {
1944
+ type: "button",
1945
+ disabled: busy || signingIn || loginServerUrl.trim() === "",
1946
+ onClick: () => void signInClient()
1947
+ }, t(signingIn ? "signingIn" : "deepseekSignIn")),
1917
1948
  awaitingDeepSeek ? React.createElement(
1918
1949
  "p",
1919
1950
  { className: "dshRemoteServiceAddress" },
package/dist/index.js CHANGED
@@ -13521,7 +13521,7 @@ function normalizeServerUrl(value) {
13521
13521
  }
13522
13522
 
13523
13523
  // src/version.ts
13524
- var PLUGIN_VERSION = "0.5.4";
13524
+ var PLUGIN_VERSION = "0.5.6";
13525
13525
 
13526
13526
  // src/server-api.ts
13527
13527
  var ENABLED_QR_PROVIDERS = ["github"];
@@ -14109,7 +14109,7 @@ var HostServerConnection = class {
14109
14109
  if (TERMINAL_AUTH_ERRORS.has(code)) {
14110
14110
  this.logger.warn(code === "CONNECTION_REPLACED" ? "Another instance is using this Host identity. Standing by: retrying in five minutes, and taking over as soon as it leaves." : code === "SERVER_CREDENTIALS_BUSY" ? "Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again." : "Host authorization failed. Run /remote login or authorize this Host again in Remote settings.");
14111
14111
  }
14112
- if (code === "DEVICE_REVOKED") {
14112
+ if (code === "DEVICE_REVOKED" || code === "AUTH_INVALID") {
14113
14113
  try {
14114
14114
  await this.api.clearAuthorization();
14115
14115
  } catch (clearError) {
@@ -15512,7 +15512,7 @@ var CodexRemoteClient = class {
15512
15512
  if (!isRecord(result) || !Array.isArray(result.data))
15513
15513
  throw invalidResponse("thread list");
15514
15514
  return {
15515
- sessions: result.data.map(projectCodexThread).filter((value) => value !== void 0),
15515
+ sessions: uniqueCodexThreadRows(result.data).map(projectCodexThread).filter((value) => value !== void 0),
15516
15516
  ...typeof result.nextCursor === "string" ? { nextCursor: result.nextCursor } : {}
15517
15517
  };
15518
15518
  }
@@ -15666,6 +15666,33 @@ function projectCodexThread(value) {
15666
15666
  ...typeof value.isPinned === "boolean" ? { pinned: value.isPinned } : {}
15667
15667
  };
15668
15668
  }
15669
+ var THREAD_ID_KEY = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/u;
15670
+ var LEGACY_ROW_KEY = /^([A-Za-z0-9][A-Za-z0-9._-]{0,255})~[^~]{0,64}~[0-9]{1,9}$/u;
15671
+ function codexThreadIdFromKey(key) {
15672
+ if (THREAD_ID_KEY.test(key))
15673
+ return key;
15674
+ return LEGACY_ROW_KEY.exec(key)?.[1];
15675
+ }
15676
+ function rowFreshness(row) {
15677
+ for (const field of ["updatedAt", "createdAt"]) {
15678
+ const value = row[field];
15679
+ if (typeof value === "number" && Number.isFinite(value))
15680
+ return value;
15681
+ }
15682
+ return 0;
15683
+ }
15684
+ function uniqueCodexThreadRows(values) {
15685
+ const byThread = /* @__PURE__ */ new Map();
15686
+ for (const value of values) {
15687
+ if (!isRecord(value) || typeof value.id !== "string" || value.id.length === 0)
15688
+ continue;
15689
+ const threadId = codexThreadIdFromKey(value.id) ?? value.id;
15690
+ const current = byThread.get(threadId);
15691
+ if (current === void 0 || rowFreshness(value) > rowFreshness(current))
15692
+ byThread.set(threadId, value);
15693
+ }
15694
+ return [...byThread.values()];
15695
+ }
15669
15696
  function deriveCodexCwdWorkspaces(values) {
15670
15697
  const byPath = /* @__PURE__ */ new Map();
15671
15698
  const usedIds = /* @__PURE__ */ new Set();
@@ -17540,6 +17567,17 @@ function record(value) {
17540
17567
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
17541
17568
  }
17542
17569
 
17570
+ // src/codex/session-id.ts
17571
+ var SESSION_PREFIX = "codex:";
17572
+ function parseCodexSessionId(value) {
17573
+ if (typeof value !== "string" || !value.startsWith(SESSION_PREFIX)) return void 0;
17574
+ const threadId = codexThreadIdFromKey(value.slice(SESSION_PREFIX.length));
17575
+ return threadId === void 0 ? void 0 : { sessionId: value, threadId };
17576
+ }
17577
+ function resolveCodexThreadKey(key) {
17578
+ return codexThreadIdFromKey(key) ?? key;
17579
+ }
17580
+
17543
17581
  // src/codex/method-policy.ts
17544
17582
  var id2 = external_exports.string().min(1).max(256);
17545
17583
  var cursor = external_exports.string().min(1).max(4096).nullable().optional();
@@ -19411,7 +19449,7 @@ async function loadCatalog(client, signal, pendingThreads) {
19411
19449
  updatedAt: workspace.updatedAt
19412
19450
  }));
19413
19451
  }
19414
- const projected = threads.map((thread) => {
19452
+ const projected = uniqueCodexThreadRows(threads).map((thread) => {
19415
19453
  const session = projectCodexThread(thread);
19416
19454
  return session === void 0 ? void 0 : { thread, session };
19417
19455
  }).filter((value) => value !== void 0);
@@ -20121,10 +20159,9 @@ function sessionIdFromAddress(address) {
20121
20159
  return requiredString(address.childSessionId, "childSessionId");
20122
20160
  }
20123
20161
  function nativeThreadId(sessionId) {
20124
- if (!sessionId.startsWith(CODEX_SESSION_PREFIX) || sessionId.length === CODEX_SESSION_PREFIX.length) {
20125
- throw new Error("The selected Session does not belong to CodeX.");
20126
- }
20127
- return sessionId.slice(CODEX_SESSION_PREFIX.length);
20162
+ const parsed = parseCodexSessionId(sessionId);
20163
+ if (parsed === void 0) throw new Error("The selected Session does not belong to CodeX.");
20164
+ return parsed.threadId;
20128
20165
  }
20129
20166
  function activeTurnId(value) {
20130
20167
  if (!Array.isArray(value)) return void 0;
@@ -21920,10 +21957,16 @@ var ClientModeRuntime = class {
21920
21957
  registerControl(connection, webServer) {
21921
21958
  return registerControlRoute(connection, (endpoint, payload, signal) => this.handleControl(endpoint, payload, signal), webServer);
21922
21959
  }
21960
+ /** True while the plugin is exchanging DSH's DeepSeek credential for this device's Server credentials. */
21961
+ authorizingWithDeepSeek = false;
21962
+ /** Whether DSH held a DeepSeek session at the last reconciliation. */
21963
+ hasDeepSeekSession = false;
21923
21964
  status() {
21924
21965
  const targetStatus = this.gatewaySwitch.supportsCarrier() ? this.gatewaySwitch.status() : this.proxySwitch?.status() ?? this.gatewaySwitch.status();
21925
21966
  return {
21926
21967
  available: this.config.serverUrl !== void 0,
21968
+ authorizing: this.authorizingWithDeepSeek,
21969
+ hasDeepSeekSession: this.hasDeepSeekSession,
21927
21970
  identityReady: this.identity !== void 0,
21928
21971
  deviceId: this.identity?.deviceId,
21929
21972
  deviceName: this.identity?.name,
@@ -22086,6 +22129,11 @@ var ClientModeRuntime = class {
22086
22129
  reloadCredentials() {
22087
22130
  this.server.reloadCredentials();
22088
22131
  }
22132
+ /** Whether this device holds Server credentials, i.e. the panel shows it as signed in. */
22133
+ async hasServerAuthorization() {
22134
+ if (this.server.hasStoredAuthorization === void 0) return false;
22135
+ return await this.server.hasStoredAuthorization().catch(() => false);
22136
+ }
22089
22137
  async setHostAuthorization(enabled) {
22090
22138
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
22091
22139
  if (!enabled) {
@@ -24668,8 +24716,9 @@ var CodexPeerBridge = class {
24668
24716
  method: "thread/read",
24669
24717
  params: { threadId: params.threadId, includeTurns: false }
24670
24718
  });
24671
- this.streams.set(params.streamId, params.threadId);
24672
- return { opened: true, streamId: params.streamId, threadId: params.threadId };
24719
+ const threadId = resolveCodexThreadKey(params.threadId);
24720
+ this.streams.set(params.streamId, threadId);
24721
+ return { opened: true, streamId: params.streamId, threadId };
24673
24722
  }
24674
24723
  closeStream(input2) {
24675
24724
  const params = streamCloseSchema.parse(input2);
@@ -25014,12 +25063,19 @@ var CodexRemoteDomain = class {
25014
25063
  idempotencyKey: call.params.idempotencyKey
25015
25064
  }));
25016
25065
  }
25066
+ if (isRecord12(call.params) && typeof call.params.threadId === "string") {
25067
+ call.params.threadId = resolveCodexThreadKey(call.params.threadId);
25068
+ }
25017
25069
  if (call.method === "thread/list") {
25018
25070
  const result = sanitizeThreadList(await this.callUpstream(call.method, call.params));
25019
- return filterThreadListByWorkspaceAuthority(
25071
+ const authorized = filterThreadListByWorkspaceAuthority(
25020
25072
  result,
25021
25073
  await this.readWorkspaceAuthority(result.data)
25022
25074
  );
25075
+ return {
25076
+ ...authorized,
25077
+ data: uniqueCodexThreadRows(authorized.data)
25078
+ };
25023
25079
  }
25024
25080
  if (call.method === "thread/start") {
25025
25081
  const cwd = await this.requireCodexWorkspacePath(call.params.cwd);
@@ -26228,7 +26284,6 @@ var PluginControlRuntime = class {
26228
26284
  *
26229
26285
  * `undefined` means no sign-in is in flight, which is also the state after a completed authorization.
26230
26286
  */
26231
- deepSeekSignIn;
26232
26287
  register(connection, webServer) {
26233
26288
  const statusStream = new ControlStatusStream(() => this.streamStatus());
26234
26289
  return registerControlRoute(
@@ -26337,15 +26392,13 @@ var PluginControlRuntime = class {
26337
26392
  throw new ClientModeError("METHOD_NOT_ALLOWED", "DeepSeek account sign-in is unavailable in this profile.");
26338
26393
  }
26339
26394
  const session = await this.deepseekSession.read();
26340
- const counted = session !== void 0 && this.deepSeekSignIn !== void 0;
26341
- if (!counted) {
26395
+ if (session === void 0) {
26342
26396
  const started = await this.deepseekSession.startSignIn({
26343
26397
  callbackOrigin: typeof value.callbackOrigin === "string" ? value.callbackOrigin : "",
26344
26398
  loginSource: value.loginSource === "desktop" ? "desktop" : "web",
26345
26399
  locale: typeof value.locale === "string" ? value.locale : "en",
26346
26400
  timezoneOffsetSeconds: typeof value.timezoneOffsetSeconds === "number" ? value.timezoneOffsetSeconds : 0
26347
26401
  });
26348
- this.deepSeekSignIn = true;
26349
26402
  return {
26350
26403
  status: "deepseek-sign-in-required",
26351
26404
  ...started.authorizeUrl === void 0 ? {} : { authorizeUrl: started.authorizeUrl }
@@ -26358,7 +26411,6 @@ var PluginControlRuntime = class {
26358
26411
  this.verifiedDeepSeekToken = session.token;
26359
26412
  this.verifiedDeepSeekAuthorization = authorization;
26360
26413
  }
26361
- this.deepSeekSignIn = void 0;
26362
26414
  } else {
26363
26415
  if (typeof value.email !== "string" || typeof value.password !== "string") {
26364
26416
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required for account authorization.");
@@ -26371,7 +26423,8 @@ var PluginControlRuntime = class {
26371
26423
  if (this.host !== void 0 && !hostPaused) {
26372
26424
  try {
26373
26425
  this.host.reconnectHost?.();
26374
- } catch {
26426
+ } catch (error) {
26427
+ console.error(`[dsh-remote] reconcile failed ${JSON.stringify({ code: String(error?.code ?? "") })}`);
26375
26428
  }
26376
26429
  }
26377
26430
  await this.settings.replace(editableConfig(next));
@@ -26471,14 +26524,13 @@ var PluginControlRuntime = class {
26471
26524
  await this.settings.replace(editableConfig({ ...current, acp: { enabled: current.acp?.enabled ?? true, backends } }));
26472
26525
  return this.settingsView();
26473
26526
  }
26474
- async logout() {
26527
+ async logout(options = {}) {
26475
26528
  if (this.settings === void 0) {
26476
26529
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
26477
26530
  }
26478
26531
  const config = resolveConfig(this.settings.get());
26479
26532
  this.verifiedDeepSeekToken = void 0;
26480
26533
  this.verifiedDeepSeekAuthorization = void 0;
26481
- this.deepSeekSignIn = void 0;
26482
26534
  if (config.serverUrl !== void 0) {
26483
26535
  await Promise.all([
26484
26536
  this.client?.clearClientAuthorization(),
@@ -26487,11 +26539,13 @@ var PluginControlRuntime = class {
26487
26539
  const directory = await ensureDeviceDirectory(this.identityDirectory, config.serverUrl);
26488
26540
  await new ServerCredentialStore(directory).clear();
26489
26541
  }
26542
+ if (this.client !== void 0) this.client.hasDeepSeekSession = false;
26490
26543
  let deepseekSignedOut = false;
26491
26544
  if (this.deepseekSession !== void 0) {
26492
26545
  try {
26493
- deepseekSignedOut = await this.deepseekSession.signOut();
26494
- } catch {
26546
+ deepseekSignedOut = options.releaseDeepSeek === false ? false : await this.deepseekSession.signOut();
26547
+ } catch (error) {
26548
+ console.error(`[dsh-remote] reconcile failed ${JSON.stringify({ code: String(error?.code ?? "") })}`);
26495
26549
  deepseekSignedOut = false;
26496
26550
  }
26497
26551
  }
@@ -26522,7 +26576,49 @@ var PluginControlRuntime = class {
26522
26576
  }
26523
26577
  return this.client === void 0 ? this.hostOnlyStatus() : await this.client.status();
26524
26578
  }
26579
+ /** Consecutive reconciliations without a DSH session: DSH refreshes its grant, so one empty read is not a sign-out. */
26580
+ /** Guards against re-entry through configure(), which reads the settings view back. */
26581
+ reconcilingWithDeepSeek = false;
26582
+ /**
26583
+ * Keep this device's Server credentials in step with DSH's DeepSeek session - that session is the credential, and
26584
+ * the Server verifies it before issuing this device its own tokens.
26585
+ *
26586
+ * Three cases: DSH signed in and this device not authorized -> authorize; DSH signed out and this device authorized
26587
+ * -> sign out; otherwise nothing. At most once per ten seconds, from the settings poll the panel already makes.
26588
+ */
26589
+ async reconcileWithDeepSeek() {
26590
+ if (this.deepseekSession === void 0 || this.reconcilingWithDeepSeek) return;
26591
+ this.reconcilingWithDeepSeek = true;
26592
+ try {
26593
+ const session = await this.deepseekSession.read();
26594
+ if (this.client !== void 0) this.client.hasDeepSeekSession = session !== void 0;
26595
+ const authorized = this.client === void 0 ? false : await this.client.hasServerAuthorization();
26596
+ console.error(`[dsh-remote] reconcile ${JSON.stringify({ hasSession: session !== void 0, authorized })}`);
26597
+ if (session === void 0 && authorized) {
26598
+ await this.logout({ releaseDeepSeek: false });
26599
+ return;
26600
+ }
26601
+ if (session === void 0 || authorized) return;
26602
+ if (this.client !== void 0) this.client.authorizingWithDeepSeek = true;
26603
+ void (async () => {
26604
+ try {
26605
+ await this.configure({ provider: "deepseek", serverUrl: this.config.serverUrl });
26606
+ } catch (error) {
26607
+ console.error(`[dsh-remote] reconcile failed ${JSON.stringify({ code: String(error?.code ?? "") })}`);
26608
+ } finally {
26609
+ if (this.client !== void 0) this.client.authorizingWithDeepSeek = false;
26610
+ }
26611
+ })();
26612
+ } finally {
26613
+ this.reconcilingWithDeepSeek = false;
26614
+ }
26615
+ }
26616
+ /**
26617
+ * Keeps the reconciliation alive without the panel: DSH can be signed in or out while the card is closed, and the
26618
+ * plugin has to follow. Only local reads - the exchange itself is throttled to one per ten seconds.
26619
+ */
26525
26620
  async settingsView() {
26621
+ await this.reconcileWithDeepSeek();
26526
26622
  const config = this.settings === void 0 ? editableConfig(this.config) : editableConfig(resolveConfig(this.settings.get()));
26527
26623
  const associations = await this.associations(config);
26528
26624
  const association = associations.host;
@@ -26661,13 +26757,6 @@ function redact(value, key = "") {
26661
26757
  return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
26662
26758
  }
26663
26759
 
26664
- // src/codex/session-id.ts
26665
- function parseCodexSessionId(value) {
26666
- if (typeof value !== "string") return void 0;
26667
- const match = /^codex:([A-Za-z0-9][A-Za-z0-9._-]{0,255})$/.exec(value);
26668
- return match === null ? void 0 : { sessionId: value, threadId: match[1] };
26669
- }
26670
-
26671
26760
  // src/terminal-policy.ts
26672
26761
  var TERMINAL_CALLS = /* @__PURE__ */ new Set([
26673
26762
  "terminal/environment",
@@ -26681,6 +26770,7 @@ var TERMINAL_CALLS = /* @__PURE__ */ new Set([
26681
26770
  ]);
26682
26771
  var TERMINAL_STREAMS = /* @__PURE__ */ new Set(["terminal/follow", "terminal/retain"]);
26683
26772
  var id3 = external_exports.string().regex(/^[A-Za-z0-9_:-]{1,256}$/);
26773
+ var sessionKey = external_exports.string().regex(/^[A-Za-z0-9_:~-]{1,512}$/);
26684
26774
  var TerminalPolicy = class {
26685
26775
  constructor(enabled, deviceId, owners) {
26686
26776
  this.enabled = enabled;
@@ -26695,7 +26785,7 @@ var TerminalPolicy = class {
26695
26785
  );
26696
26786
  const args = external_exports.object({ args: external_exports.record(external_exports.unknown()) }).strict().parse(payload).args;
26697
26787
  const rawSessionId = args.agentId ?? args.sessionId;
26698
- const sessionId = id3.parse(rawSessionId);
26788
+ const sessionId = sessionKey.parse(rawSessionId);
26699
26789
  const codexSession = parseCodexSessionId(sessionId);
26700
26790
  void codexSession;
26701
26791
  if (endpoint === "terminal/environment" || endpoint === "terminal/shells" || endpoint === "terminal/list") return {};