@dylanpiercey/work 0.0.145 → 0.0.146

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 (73) hide show
  1. package/dist/_CCA0c-I0.js +2 -0
  2. package/dist/{_RdvbSrnZ.js → _OZ9nLhXp.js} +126 -24
  3. package/dist/index.mjs +306 -384
  4. package/dist/public/assets/_2f6GkTCF.js +1 -0
  5. package/dist/public/assets/{_DQ3bjKhC.js → _BJq6B3f8.js} +4 -4
  6. package/dist/public/assets/_BJq6B3f8.js.br +0 -0
  7. package/dist/public/assets/_BRiyTqqx.js +1 -0
  8. package/dist/public/assets/_BXJd6h5q.js +1 -0
  9. package/dist/public/assets/_Bc7wOUYH.js +1 -0
  10. package/dist/public/assets/{_Dy1DNuXx.js → _Bkg2RxMC.js} +4 -4
  11. package/dist/public/assets/_Bkg2RxMC.js.br +0 -0
  12. package/dist/public/assets/_BnihlWIp.js +1 -0
  13. package/dist/public/assets/_C-wKjwXX.js +1 -0
  14. package/dist/public/assets/_C408Iqy7.js +1 -0
  15. package/dist/public/assets/_CVunWnOY.js +1 -0
  16. package/dist/public/assets/_CeK9LJkw.js +1 -0
  17. package/dist/public/assets/_CgfKx3kO.js +1 -0
  18. package/dist/public/assets/_DCCIL6WI.js +1 -0
  19. package/dist/public/assets/_DQDxPXHD.js +1 -0
  20. package/dist/public/assets/_DRLjyh60.js +1 -0
  21. package/dist/public/assets/_DlE-V9GA.js +1 -0
  22. package/dist/public/assets/_DzgxFeeE.js +1 -0
  23. package/dist/public/assets/_Xp-Chjfv.js +1 -0
  24. package/dist/public/assets/_b6v9fOcC.js +1 -0
  25. package/dist/public/assets/{_C7gmftFo.js → _oalV3UwK.js} +1 -1
  26. package/dist/public/assets/_oalV3UwK.js.br +0 -0
  27. package/dist/public/assets/_vQM4oRsw.js +1 -0
  28. package/dist/public/assets/_xiuhy85t.js +1 -0
  29. package/dist/public/assets/{app-page-k1J1kX4l.css → app-page-IRq6b-Ob.css} +1 -1
  30. package/dist/public/assets/app-page-IRq6b-Ob.css.br +0 -0
  31. package/dist/public/assets/{diff-view-Ch_De7qH.css → diff-view-6-VC0hOC.css} +1 -1
  32. package/dist/public/assets/diff-view-6-VC0hOC.css.br +0 -0
  33. package/dist/public/assets/diff-view-CHN5eqyz.js +2 -0
  34. package/dist/public/assets/{icon-arrow-down-CMd0RUdb.css → icon-arrow-down-Dcmmz4Cl.css} +1 -1
  35. package/dist/public/assets/icon-arrow-down-Dcmmz4Cl.css.br +0 -0
  36. package/dist/public/assets/new-BpPnfh6F.js +1 -0
  37. package/dist/public/assets/settings-BpPnfh6F.js +1 -0
  38. package/dist/public/assets/w-BpPnfh6F.js +1 -0
  39. package/dist/public/assets/w._-B5NuNac4.js +1 -0
  40. package/dist/public/assets/w._-BpPnfh6F.js +1 -0
  41. package/package.json +1 -1
  42. package/dist/_BF-bl9wz.js +0 -2
  43. package/dist/public/assets/_21mgW-1Z.js +0 -1
  44. package/dist/public/assets/_B9sRUkZv.js +0 -1
  45. package/dist/public/assets/_BQDnrQES.js +0 -1
  46. package/dist/public/assets/_BgCHTkW2.js +0 -1
  47. package/dist/public/assets/_BvOfxkzU.js +0 -1
  48. package/dist/public/assets/_C448NmFH.js +0 -1
  49. package/dist/public/assets/_C4_-B9wF.js +0 -1
  50. package/dist/public/assets/_C7gmftFo.js.br +0 -0
  51. package/dist/public/assets/_CEw_Mye5.js +0 -1
  52. package/dist/public/assets/_CFi5anE6.js +0 -1
  53. package/dist/public/assets/_CGcmWQOb.js +0 -1
  54. package/dist/public/assets/_CeGQErY4.js +0 -1
  55. package/dist/public/assets/_D2C1S8zb.js +0 -1
  56. package/dist/public/assets/_D5RLfyQD.js +0 -1
  57. package/dist/public/assets/_DBeqA_AC.js +0 -1
  58. package/dist/public/assets/_DFvgQmuO.js +0 -1
  59. package/dist/public/assets/_DQ3bjKhC.js.br +0 -0
  60. package/dist/public/assets/_DQO13XHH.js +0 -1
  61. package/dist/public/assets/_DsCpEqH5.js +0 -1
  62. package/dist/public/assets/_Dy1DNuXx.js.br +0 -0
  63. package/dist/public/assets/_VnRbDdk0.js +0 -1
  64. package/dist/public/assets/_bVO2SjRK.js +0 -1
  65. package/dist/public/assets/app-page-k1J1kX4l.css.br +0 -0
  66. package/dist/public/assets/diff-view-Ch_De7qH.css.br +0 -0
  67. package/dist/public/assets/diff-view-D7PqjZCP.js +0 -2
  68. package/dist/public/assets/icon-arrow-down-CMd0RUdb.css.br +0 -0
  69. package/dist/public/assets/new-ieyLBknO.js +0 -1
  70. package/dist/public/assets/settings-ieyLBknO.js +0 -1
  71. package/dist/public/assets/w-ieyLBknO.js +0 -1
  72. package/dist/public/assets/w._-DRZmWqrE.js +0 -1
  73. package/dist/public/assets/w._-ieyLBknO.js +0 -1
@@ -0,0 +1,2 @@
1
+ import { p as notifySessionsChanged } from "./_OZ9nLhXp.js";
2
+ export { notifySessionsChanged };
@@ -72,7 +72,7 @@ var tmux = (args, env) => run$1("tmux", [
72
72
  "-L",
73
73
  TMUX_SOCKET,
74
74
  ...args
75
- ], void 0, utf8Locale(env ?? process.env));
75
+ ], os.homedir(), utf8Locale(env ?? process.env));
76
76
  /** launchd starts the service with no locale at all, and under a non-UTF-8
77
77
  * locale tmux replaces characters it deems unprintable in format output —
78
78
  * including the \t separators sessionStates and the scraper split on, which
@@ -705,6 +705,96 @@ function preferredAgent(enabled = enabledAgents()) {
705
705
  return enabled[0] || "shell";
706
706
  }
707
707
  //#endregion
708
+ //#region src/server/cache.ts
709
+ /** Keyed stale-while-revalidate cache: a cached value is served immediately
710
+ * (revalidating in the background when older than `ttlMs`); loads are deduped
711
+ * per key, and `refresh` bypasses both the cache and any in-flight load. */
712
+ function swr(ttlMs, load, seed) {
713
+ const cache = /* @__PURE__ */ new Map();
714
+ const inflight = /* @__PURE__ */ new Map();
715
+ function start(key, refresh) {
716
+ const startedAt = Date.now();
717
+ const promise = load(key, refresh).then((value) => {
718
+ const existing = cache.get(key);
719
+ if (!existing || existing.at <= startedAt) cache.set(key, {
720
+ at: Date.now(),
721
+ value
722
+ });
723
+ return value;
724
+ }).finally(() => {
725
+ if (inflight.get(key)?.promise === promise) inflight.delete(key);
726
+ });
727
+ inflight.set(key, {
728
+ promise,
729
+ refresh
730
+ });
731
+ return promise;
732
+ }
733
+ const read = (key, refresh = false) => {
734
+ if (refresh) {
735
+ const current = inflight.get(key);
736
+ return current?.refresh ? current.promise : start(key, true);
737
+ }
738
+ let cached = cache.get(key);
739
+ if (!cached && seed) {
740
+ cached = seed(key);
741
+ if (cached) cache.set(key, cached);
742
+ }
743
+ if (cached) {
744
+ if (Date.now() - cached.at >= ttlMs && !inflight.has(key)) start(key, false).catch(() => {});
745
+ return Promise.resolve(cached.value);
746
+ }
747
+ return inflight.get(key)?.promise ?? start(key, false);
748
+ };
749
+ /** Inject an externally fetched value (e.g. from a batched load). */
750
+ read.set = (key, value) => {
751
+ cache.set(key, {
752
+ at: Date.now(),
753
+ value
754
+ });
755
+ };
756
+ /** Current cached value, if any — used when a batch update must merge
757
+ * with a richer per-key load rather than replace it. */
758
+ read.get = (key) => cache.get(key)?.value;
759
+ return read;
760
+ }
761
+ /** Serialize async work per key: calls for one key run in submission order,
762
+ * each starting only after the previous settled. The stored tail is the
763
+ * all-settled form, so a rejection neither blocks the next caller nor leaves
764
+ * the entry behind — the map holds only keys with work still in flight. */
765
+ var locks = /* @__PURE__ */ new Map();
766
+ function withLock(key, fn) {
767
+ const run = (locks.get(key) ?? Promise.resolve()).then(fn);
768
+ const tail = run.then(() => {}, () => {});
769
+ locks.set(key, tail);
770
+ tail.then(() => {
771
+ if (locks.get(key) === tail) locks.delete(key);
772
+ });
773
+ return run;
774
+ }
775
+ /** One load at a time, and a short reuse window: callers join the in-flight
776
+ * load, and a result younger than `ttlMs` is served without one. For an
777
+ * expensive process sweep (lsof, ps, list-panes) whose answer does not
778
+ * change within the window — `ttlMs` of 0 still collapses concurrent
779
+ * callers onto a single spawn. */
780
+ function shared(fn, ttlMs) {
781
+ let inflight = null;
782
+ let last = null;
783
+ return () => {
784
+ if (last && Date.now() - last.at < ttlMs) return Promise.resolve(last.value);
785
+ if (!inflight) inflight = fn().then((value) => {
786
+ last = {
787
+ at: Date.now(),
788
+ value
789
+ };
790
+ return value;
791
+ }).finally(() => {
792
+ inflight = null;
793
+ });
794
+ return inflight;
795
+ };
796
+ }
797
+ //#endregion
708
798
  //#region src/server/events.ts
709
799
  var listeners = /* @__PURE__ */ new Set();
710
800
  function publish(type, data) {
@@ -6599,7 +6689,7 @@ function publishChange(sessionId) {
6599
6689
  const row = get("SELECT workspace_id FROM sessions WHERE id = ?", sessionId);
6600
6690
  if (row?.workspace_id) {
6601
6691
  const workspaceId = row.workspace_id;
6602
- import("./_BF-bl9wz.js").then((m) => m.notifySessionsChanged(workspaceId));
6692
+ import("./_CCA0c-I0.js").then((m) => m.notifySessionsChanged(workspaceId));
6603
6693
  return;
6604
6694
  }
6605
6695
  publish("sessions.changed");
@@ -8172,13 +8262,13 @@ async function sendToSession(id, text, size) {
8172
8262
  const wsPath = workspacePath(session.workspace_id);
8173
8263
  if (!wsPath) throw new Error("unknown workspace");
8174
8264
  const name = tmuxName(id);
8175
- await ensureTmuxServer();
8176
8265
  const env = cleanEnv();
8177
8266
  if (!await tmux([
8178
8267
  "has-session",
8179
8268
  "-t",
8180
8269
  name
8181
8270
  ], env).then(() => true, () => false)) {
8271
+ await ensureTmuxServer();
8182
8272
  if (promptAsArgv(session.agent)) {
8183
8273
  await tmux(newSessionArgs(session, wsPath, "-d", text, size), env);
8184
8274
  markTmuxStarted(id);
@@ -8219,33 +8309,45 @@ async function sendToSession(id, text, size) {
8219
8309
  * started, so a shell someone ran `claude` inside still reads as "shell". */
8220
8310
  async function sessionStates() {
8221
8311
  const result = {};
8222
- const out = await tmux([
8223
- "list-panes",
8224
- "-a",
8225
- "-F",
8226
- "#{session_name} #{pane_current_command}"
8227
- ]).catch(() => "");
8228
- for (const line of out.split("\n")) {
8229
- const [name, command] = line.split(" ");
8230
- if (!name?.startsWith("wk-")) continue;
8231
- const id = name.slice(3);
8232
- result[id] = {
8233
- alive: true,
8234
- phase: agentPhase(id),
8235
- command: command || ""
8236
- };
8237
- }
8312
+ for (const { id, command } of await listPanes() || []) if (id) result[id] = {
8313
+ alive: true,
8314
+ phase: agentPhase(id),
8315
+ command
8316
+ };
8238
8317
  return result;
8239
8318
  }
8240
8319
  /** Root pids of every tmux pane (the agent/shell each session runs). */
8241
8320
  async function panePids() {
8321
+ return new Set((await listPanes() || []).map((p) => p.pid));
8322
+ }
8323
+ /** Every tmux pane, one `list-panes` for everyone who wants one: session
8324
+ * state, pane pids and the activity scraper all ask within the same tick,
8325
+ * and a field costs tmux nothing while a spawn costs ~10 ms on macOS. No
8326
+ * reuse window — a kill-session followed by a state read must see the
8327
+ * pane gone — only concurrent callers share the spawn. Null when tmux did
8328
+ * not answer (server down, transient failure): callers that clear state
8329
+ * on "no panes" must not clear it on "no answer". */
8330
+ var listPanes = shared(listPanesNow, 0);
8331
+ async function listPanesNow() {
8242
8332
  const out = await tmux([
8243
8333
  "list-panes",
8244
8334
  "-a",
8245
8335
  "-F",
8246
- "#{pane_pid}"
8247
- ]).catch(() => "");
8248
- return new Set(out.split("\n").filter(Boolean).map(Number));
8336
+ "#{session_name} #{pane_current_command} #{pane_pid} #{window_activity}"
8337
+ ]).catch(() => null);
8338
+ if (out === null) return null;
8339
+ const panes = [];
8340
+ for (const line of out.split("\n")) {
8341
+ if (!line) continue;
8342
+ const [name, command, pid, stamp] = line.split(" ");
8343
+ panes.push({
8344
+ id: name?.startsWith("wk-") ? name.slice(3) : void 0,
8345
+ command: command || "",
8346
+ pid: Number(pid),
8347
+ activityAt: Number(stamp) * 1e3
8348
+ });
8349
+ }
8350
+ return panes;
8249
8351
  }
8250
8352
  /** Cleared once per process — boot already runs this, and a server we
8251
8353
  * create below never has PORT set, so re-running on every attach only
@@ -8283,7 +8385,7 @@ async function ensureTmuxServer() {
8283
8385
  "work-keeper",
8284
8386
  "sleep",
8285
8387
  "infinity"
8286
- ]).catch(() => {});
8388
+ ], os.homedir()).catch(() => {});
8287
8389
  }
8288
8390
  /** Full tmux command (options + new-session) for creating/attaching a
8289
8391
  * session's tmux; `mode` is "-d" (create detached) or "-A" (attach-or-create). */
@@ -8537,4 +8639,4 @@ function sleep(ms) {
8537
8639
  return new Promise((resolve) => setTimeout(resolve, ms));
8538
8640
  }
8539
8641
  //#endregion
8540
- export { setSetting as $, setScrapedPhase as A, saveSubscription as B, agentBadge as C, agentMeta as D, agentLabel as E, manifest as F, agentChoices as G, subscribe as H, normalizeAccent as I, enabledAgents as J, allPrefs as K, setInstance as L, ACCENT_RE as M, getInstance as N, clearAgentPhase as O, iconSvg as P, setPref as Q, deleteSubscription as R, blankRow as S, agentFromCommand as T, subscriberCount as U, publish as V, subscriberJoins as W, preferredAgent as X, getSetting as Y, setDisabledAgents as Z, tmuxName as _, gh as _t, ensureTmuxServer as a, setupPath as at, readHookPayload as b, tmux as bt, killWorkspaceSessions as c, TOOLS as ct, newSessionArgs as d, toolById as dt, discoverAgents as et, notifySessionsChanged as f, all as ft, sessionStates as g, TMUX_SOCKET as gt, sendToSession as h, workspacePath as ht, disposeSessions as i, miseVersions as it, setSessionViewers as j, setAgentPhase as k, listSessions as l, agentById as lt, renameSession as m, get as mt, clearTmuxStarted as n, ghKnownHosts as nt, getSession as o, which as ot, panePids as p, db as pt, disabledAgents as q, createSession as r, miseInstalls as rt, killSession as s, DEFAULT_GITHUB_HOST as st, cleanEnv as t, doctor as tt, markTmuxStarted as u, agentLabel$1 as ut, tmuxWasStarted as v, git as vt, agentFamily as w, reportsWorking as x, config as xt, AGENTS as y, run$1 as yt, pushPublicKey as z };
8642
+ export { getSetting as $, setAgentPhase as A, pushPublicKey as B, blankRow as C, run$1 as Ct, agentLabel as D, agentFromCommand as E, iconSvg as F, subscriberJoins as G, publish as H, manifest as I, withLock as J, shared as K, normalizeAccent as L, setSessionViewers as M, ACCENT_RE as N, agentMeta as O, getInstance as P, enabledAgents as Q, setInstance as R, reportsWorking as S, git as St, agentFamily as T, config as Tt, subscribe as U, saveSubscription as V, subscriberCount as W, allPrefs as X, agentChoices as Y, disabledAgents as Z, sessionStates as _, db as _t, ensureTmuxServer as a, doctor as at, AGENTS as b, TMUX_SOCKET as bt, killWorkspaceSessions as c, miseVersions as ct, markTmuxStarted as d, DEFAULT_GITHUB_HOST as dt, preferredAgent as et, newSessionArgs as f, TOOLS as ft, sendToSession as g, all as gt, renameSession as h, toolById as ht, disposeSessions as i, discoverAgents as it, setScrapedPhase as j, clearAgentPhase as k, listPanes as l, setupPath as lt, panePids as m, agentLabel$1 as mt, clearTmuxStarted as n, setPref as nt, getSession as o, ghKnownHosts as ot, notifySessionsChanged as p, agentById as pt, swr as q, createSession as r, setSetting as rt, killSession as s, miseInstalls as st, cleanEnv as t, setDisabledAgents as tt, listSessions as u, which as ut, tmuxName as v, get as vt, agentBadge as w, tmux as wt, readHookPayload as x, gh as xt, tmuxWasStarted as y, workspacePath as yt, deleteSubscription as z };