dsh-cloudq 0.1.8 → 0.1.9

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/lib/client.js CHANGED
@@ -599,14 +599,17 @@ linear-gradient(-30deg, transparent 49.5%, #2c2c31 49.5%, #2c2c31 50.5%, transpa
599
599
  return !!sessionId && cloudqSessions.has(sessionId);
600
600
  }
601
601
  let cloudqHistorySync = null;
602
+ let reconcileQueued = false;
602
603
  function reconcileCloudqSessionsFromHost() {
603
- if (cloudqHistorySync) return cloudqHistorySync;
604
+ if (cloudqHistorySync) {
605
+ reconcileQueued = true;
606
+ return cloudqHistorySync;
607
+ }
604
608
  cloudqHistorySync = cloudqRequest(API_CLOUDQ_SESSIONS, void 0, 6e4).then((response) => {
605
609
  const detected = new Set(Array.isArray(response?.sessionIds) ? response.sessionIds : []);
606
- const snapshot = cloudqCtx?.sessions?.list?.getSnapshot?.();
607
- for (const sessionId of Array.from(cloudqSessions)) {
608
- const summary = snapshot?.byId?.[sessionId];
609
- if (summary && !summary.blank && !detected.has(sessionId)) cloudqSessions.delete(sessionId);
610
+ const known = new Set(Array.isArray(response?.knownIds) ? response.knownIds : []);
611
+ if (known.size > 0) {
612
+ for (const sessionId of Array.from(cloudqSessions)) if (!known.has(sessionId)) cloudqSessions.delete(sessionId);
610
613
  }
611
614
  for (const sessionId of detected) cloudqSessions.add(sessionId);
612
615
  saveCloudqSessions(cloudqSessions);
@@ -614,6 +617,10 @@ linear-gradient(-30deg, transparent 49.5%, #2c2c31 49.5%, #2c2c31 50.5%, transpa
614
617
  return detected;
615
618
  }).catch(() => /* @__PURE__ */ new Set()).finally(() => {
616
619
  cloudqHistorySync = null;
620
+ if (reconcileQueued) {
621
+ reconcileQueued = false;
622
+ reconcileCloudqSessionsFromHost();
623
+ }
617
624
  });
618
625
  return cloudqHistorySync;
619
626
  }
@@ -792,6 +799,17 @@ linear-gradient(-30deg, transparent 49.5%, #2c2c31 49.5%, #2c2c31 50.5%, transpa
792
799
  * order (manual and recent-order variants). Only if a collapsed/search
793
800
  * view prevents an exact sequence match do we fall back to title matching.
794
801
  */
802
+ function rowSessionId(row) {
803
+ const fiberKey = Object.keys(row).find((key) => key.startsWith("__reactFiber$"));
804
+ if (!fiberKey) return void 0;
805
+ let fiber = row[fiberKey];
806
+ for (let depth = 0; fiber && depth < 12; depth += 1) {
807
+ const props = fiber.memoizedProps;
808
+ const id = props?.node?.id ?? props?.sessionId;
809
+ if (typeof id === "string" && id.startsWith("session-")) return id;
810
+ fiber = fiber.return;
811
+ }
812
+ }
795
813
  function installSessionBadges() {
796
814
  let disposed = false;
797
815
  let frame = 0;
@@ -821,7 +839,8 @@ linear-gradient(-30deg, transparent 49.5%, #2c2c31 49.5%, #2c2c31 50.5%, transpa
821
839
  const exact = exactOrder?.[index] ?? (selected ? snapshot.byId?.[snapshot.current] : void 0);
822
840
  const candidates = exact ? [exact] : byTitle.get(title) ?? [];
823
841
  const states = new Set(candidates.map((summary) => isCloudqSession(summary.id)));
824
- const cloudq = candidates.length > 0 && states.size === 1 && states.has(true);
842
+ const fiberId = rowSessionId(row);
843
+ const cloudq = fiberId !== void 0 ? isCloudqSession(fiberId) : candidates.length > 0 && states.size === 1 && states.has(true);
825
844
  const existing = row.querySelector("[data-cloudq-session-badge=\"true\"]");
826
845
  if (!cloudq) {
827
846
  existing?.remove();
@@ -3615,12 +3634,38 @@ display: none;
3615
3634
  window.dispatchEvent(new CustomEvent("dsh-cloudq:sessions-changed"));
3616
3635
  };
3617
3636
  window.addEventListener("storage", onStorage);
3637
+ const backstop = window.setInterval(() => {
3638
+ if (!disposed) reconcileCloudqSessionsFromHost();
3639
+ }, 3e4);
3618
3640
  syncHistory();
3619
3641
  return () => {
3620
3642
  disposed = true;
3643
+ window.clearInterval(backstop);
3621
3644
  window.removeEventListener("storage", onStorage);
3622
3645
  };
3623
3646
  }, "dsh-cloudq: reconcile durable session identities");
3647
+ ctx.effect(() => {
3648
+ const markIfCloudqClaim = () => {
3649
+ const textarea = document.querySelector("textarea[class*=input]");
3650
+ const draft = typeof textarea?.value === "string" ? textarea.value : "";
3651
+ if (!/^\s*\/cloudq(?:\s|$)/i.test(draft)) return;
3652
+ const sessionId = cloudqCtx?.sessions?.selection?.getSnapshot?.()?.sessionId ?? cloudqCtx?.sessions?.list?.getSnapshot?.()?.current;
3653
+ if (sessionId) markCloudqSession(sessionId);
3654
+ };
3655
+ const onKeyDown = (event) => {
3656
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
3657
+ if (event.target instanceof HTMLTextAreaElement) markIfCloudqClaim();
3658
+ };
3659
+ const onClick = (event) => {
3660
+ if (event.target?.closest?.("button[class*=primary]")) markIfCloudqClaim();
3661
+ };
3662
+ document.addEventListener("keydown", onKeyDown, true);
3663
+ document.addEventListener("click", onClick, true);
3664
+ return () => {
3665
+ document.removeEventListener("keydown", onKeyDown, true);
3666
+ document.removeEventListener("click", onClick, true);
3667
+ };
3668
+ }, "dsh-cloudq: mark session on claim submit");
3624
3669
  ctx.effect(installStyles, "dsh-cloudq: styles");
3625
3670
  ctx.effect(() => {
3626
3671
  const slots = ctx.slots;
package/lib/index.js CHANGED
@@ -589,18 +589,23 @@ async function detectCloudqSessionIds(ctx) {
589
589
  if (query === void 0) throw new HttpError(503, "session-query-unavailable", "当前 DSH 未启用会话查询服务。");
590
590
  const records = await query.listSessions();
591
591
  const ids = [];
592
+ const knownIds = [];
592
593
  const queue = [...records];
593
594
  const worker = async () => {
594
595
  while (queue.length > 0) {
595
596
  const sessionId = queue.shift()?.header?.id;
596
597
  if (!sessionId) continue;
598
+ knownIds.push(sessionId);
597
599
  try {
598
600
  if (hasCloudqEvidence((await query.readSession(sessionId))?.events)) ids.push(sessionId);
599
601
  } catch {}
600
602
  }
601
603
  };
602
604
  await Promise.all(Array.from({ length: Math.min(6, queue.length) }, () => worker()));
603
- return ids;
605
+ return {
606
+ ids,
607
+ knownIds
608
+ };
604
609
  }
605
610
  /** Settings namespace for the browser card (must equal client card `key`). */
606
611
  const SETTINGS_NAMESPACE = "dsh-cloudq";
@@ -825,9 +830,11 @@ function apply(ctx) {
825
830
  handler: async (request, response) => {
826
831
  try {
827
832
  assertSafeRequest(request, "GET");
833
+ const { ids, knownIds } = await detectCloudqSessionIds(ctx);
828
834
  sendJson(response, 200, {
829
835
  ok: true,
830
- sessionIds: await detectCloudqSessionIds(ctx)
836
+ sessionIds: ids,
837
+ knownIds
831
838
  });
832
839
  } catch (error) {
833
840
  sendError(response, error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-cloudq",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "CloudQ integration for DeepSeek Harness with secure credential, workspace, and plugin-management surfaces",
5
5
  "license": "MIT",
6
6
  "type": "module",