@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.
- package/dist/_CCA0c-I0.js +2 -0
- package/dist/{_RdvbSrnZ.js → _OZ9nLhXp.js} +126 -24
- package/dist/index.mjs +306 -384
- package/dist/public/assets/_2f6GkTCF.js +1 -0
- package/dist/public/assets/{_DQ3bjKhC.js → _BJq6B3f8.js} +4 -4
- package/dist/public/assets/_BJq6B3f8.js.br +0 -0
- package/dist/public/assets/_BRiyTqqx.js +1 -0
- package/dist/public/assets/_BXJd6h5q.js +1 -0
- package/dist/public/assets/_Bc7wOUYH.js +1 -0
- package/dist/public/assets/{_Dy1DNuXx.js → _Bkg2RxMC.js} +4 -4
- package/dist/public/assets/_Bkg2RxMC.js.br +0 -0
- package/dist/public/assets/_BnihlWIp.js +1 -0
- package/dist/public/assets/_C-wKjwXX.js +1 -0
- package/dist/public/assets/_C408Iqy7.js +1 -0
- package/dist/public/assets/_CVunWnOY.js +1 -0
- package/dist/public/assets/_CeK9LJkw.js +1 -0
- package/dist/public/assets/_CgfKx3kO.js +1 -0
- package/dist/public/assets/_DCCIL6WI.js +1 -0
- package/dist/public/assets/_DQDxPXHD.js +1 -0
- package/dist/public/assets/_DRLjyh60.js +1 -0
- package/dist/public/assets/_DlE-V9GA.js +1 -0
- package/dist/public/assets/_DzgxFeeE.js +1 -0
- package/dist/public/assets/_Xp-Chjfv.js +1 -0
- package/dist/public/assets/_b6v9fOcC.js +1 -0
- package/dist/public/assets/{_C7gmftFo.js → _oalV3UwK.js} +1 -1
- package/dist/public/assets/_oalV3UwK.js.br +0 -0
- package/dist/public/assets/_vQM4oRsw.js +1 -0
- package/dist/public/assets/_xiuhy85t.js +1 -0
- package/dist/public/assets/{app-page-k1J1kX4l.css → app-page-IRq6b-Ob.css} +1 -1
- package/dist/public/assets/app-page-IRq6b-Ob.css.br +0 -0
- package/dist/public/assets/{diff-view-Ch_De7qH.css → diff-view-6-VC0hOC.css} +1 -1
- package/dist/public/assets/diff-view-6-VC0hOC.css.br +0 -0
- package/dist/public/assets/diff-view-CHN5eqyz.js +2 -0
- package/dist/public/assets/{icon-arrow-down-CMd0RUdb.css → icon-arrow-down-Dcmmz4Cl.css} +1 -1
- package/dist/public/assets/icon-arrow-down-Dcmmz4Cl.css.br +0 -0
- package/dist/public/assets/new-BpPnfh6F.js +1 -0
- package/dist/public/assets/settings-BpPnfh6F.js +1 -0
- package/dist/public/assets/w-BpPnfh6F.js +1 -0
- package/dist/public/assets/w._-B5NuNac4.js +1 -0
- package/dist/public/assets/w._-BpPnfh6F.js +1 -0
- package/package.json +1 -1
- package/dist/_BF-bl9wz.js +0 -2
- package/dist/public/assets/_21mgW-1Z.js +0 -1
- package/dist/public/assets/_B9sRUkZv.js +0 -1
- package/dist/public/assets/_BQDnrQES.js +0 -1
- package/dist/public/assets/_BgCHTkW2.js +0 -1
- package/dist/public/assets/_BvOfxkzU.js +0 -1
- package/dist/public/assets/_C448NmFH.js +0 -1
- package/dist/public/assets/_C4_-B9wF.js +0 -1
- package/dist/public/assets/_C7gmftFo.js.br +0 -0
- package/dist/public/assets/_CEw_Mye5.js +0 -1
- package/dist/public/assets/_CFi5anE6.js +0 -1
- package/dist/public/assets/_CGcmWQOb.js +0 -1
- package/dist/public/assets/_CeGQErY4.js +0 -1
- package/dist/public/assets/_D2C1S8zb.js +0 -1
- package/dist/public/assets/_D5RLfyQD.js +0 -1
- package/dist/public/assets/_DBeqA_AC.js +0 -1
- package/dist/public/assets/_DFvgQmuO.js +0 -1
- package/dist/public/assets/_DQ3bjKhC.js.br +0 -0
- package/dist/public/assets/_DQO13XHH.js +0 -1
- package/dist/public/assets/_DsCpEqH5.js +0 -1
- package/dist/public/assets/_Dy1DNuXx.js.br +0 -0
- package/dist/public/assets/_VnRbDdk0.js +0 -1
- package/dist/public/assets/_bVO2SjRK.js +0 -1
- package/dist/public/assets/app-page-k1J1kX4l.css.br +0 -0
- package/dist/public/assets/diff-view-Ch_De7qH.css.br +0 -0
- package/dist/public/assets/diff-view-D7PqjZCP.js +0 -2
- package/dist/public/assets/icon-arrow-down-CMd0RUdb.css.br +0 -0
- package/dist/public/assets/new-ieyLBknO.js +0 -1
- package/dist/public/assets/settings-ieyLBknO.js +0 -1
- package/dist/public/assets/w-ieyLBknO.js +0 -1
- package/dist/public/assets/w._-DRZmWqrE.js +0 -1
- package/dist/public/assets/w._-ieyLBknO.js +0 -1
|
@@ -72,7 +72,7 @@ var tmux = (args, env) => run$1("tmux", [
|
|
|
72
72
|
"-L",
|
|
73
73
|
TMUX_SOCKET,
|
|
74
74
|
...args
|
|
75
|
-
],
|
|
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("./
|
|
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
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
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
|
-
|
|
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 {
|
|
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 };
|