@gigaflow/gmux 0.15.0 → 0.16.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.
- package/CHANGELOG.md +110 -80
- package/README.md +81 -57
- package/dist/adapters/claude-code.js +5 -2
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/codex.js +5 -2
- package/dist/adapters/codex.js.map +1 -1
- package/dist/cli/border-client.d.ts +1 -1
- package/dist/cli/border-client.js +1 -1
- package/dist/cli/commands/cockpit.d.ts +16 -6
- package/dist/cli/commands/cockpit.js +288 -20
- package/dist/cli/commands/cockpit.js.map +1 -1
- package/dist/cli/commands/daemon.d.ts +2 -19
- package/dist/cli/commands/daemon.js +39 -88
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/cli/commands/doctor.js +24 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/organize.d.ts +18 -0
- package/dist/cli/commands/organize.js +94 -0
- package/dist/cli/commands/organize.js.map +1 -0
- package/dist/cli/commands/setup.js +13 -1
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/tmux.d.ts +56 -0
- package/dist/cli/commands/tmux.js +158 -28
- package/dist/cli/commands/tmux.js.map +1 -1
- package/dist/cli/gmux-render.d.ts +12 -1
- package/dist/cli/gmux-render.js +18 -11
- package/dist/cli/gmux-render.js.map +1 -1
- package/dist/cli/main.js +2 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/organize-confirm.d.ts +45 -0
- package/dist/cli/organize-confirm.js +57 -0
- package/dist/cli/organize-confirm.js.map +1 -0
- package/dist/cli/overlay-ask.d.ts +3 -1
- package/dist/cli/overlay-ask.js +5 -3
- package/dist/cli/overlay-ask.js.map +1 -1
- package/dist/cli/tmux-label.d.ts +11 -4
- package/dist/cli/tmux-label.js +17 -6
- package/dist/cli/tmux-label.js.map +1 -1
- package/dist/cli/work-report.d.ts +18 -0
- package/dist/cli/work-report.js +50 -0
- package/dist/cli/work-report.js.map +1 -0
- package/dist/core/bytes.d.ts +6 -0
- package/dist/core/bytes.js +13 -0
- package/dist/core/bytes.js.map +1 -0
- package/dist/core/organize-types.d.ts +95 -0
- package/dist/core/organize-types.js +33 -0
- package/dist/core/organize-types.js.map +1 -0
- package/dist/core/paths.d.ts +13 -0
- package/dist/core/paths.js +20 -1
- package/dist/core/paths.js.map +1 -1
- package/dist/services/ask-router.d.ts +44 -0
- package/dist/services/ask-router.js +63 -0
- package/dist/services/ask-router.js.map +1 -0
- package/dist/services/daemon-lock.d.ts +31 -0
- package/dist/services/daemon-lock.js +77 -0
- package/dist/services/daemon-lock.js.map +1 -0
- package/dist/services/daemon.js +35 -6
- package/dist/services/daemon.js.map +1 -1
- package/dist/services/guardian.js +4 -4
- package/dist/services/guardian.js.map +1 -1
- package/dist/services/organize.d.ts +91 -0
- package/dist/services/organize.js +462 -0
- package/dist/services/organize.js.map +1 -0
- package/dist/services/tmux-gateway.d.ts +12 -0
- package/dist/services/tmux-gateway.js +7 -1
- package/dist/services/tmux-gateway.js.map +1 -1
- package/dist/services/tmux.d.ts +17 -1
- package/dist/services/tmux.js +34 -1
- package/dist/services/tmux.js.map +1 -1
- package/dist/services/work-view.d.ts +67 -0
- package/dist/services/work-view.js +155 -0
- package/dist/services/work-view.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure plan representation for `gmux organize`.
|
|
3
|
+
*
|
|
4
|
+
* A plan never carries a destructive op: the tagged union below is 1:1 with
|
|
5
|
+
* the tmux gateway's relocation verbs (`newWindow`, `renameWindow`,
|
|
6
|
+
* `joinPane`, `breakPane`, `swapPane`, `selectLayout`) and nothing else. There
|
|
7
|
+
* is deliberately no `kill-pane`/`kill-window` step — see the design
|
|
8
|
+
* invariant in the organize spec: reorganization relocates panes, it never
|
|
9
|
+
* destroys their processes.
|
|
10
|
+
*/
|
|
11
|
+
/** tmux preset layouts we allow the planner to choose. */
|
|
12
|
+
export const LAYOUTS = [
|
|
13
|
+
"tiled",
|
|
14
|
+
"even-horizontal",
|
|
15
|
+
"even-vertical",
|
|
16
|
+
"main-horizontal",
|
|
17
|
+
"main-vertical",
|
|
18
|
+
];
|
|
19
|
+
export function isLayoutName(v) {
|
|
20
|
+
return LAYOUTS.includes(v);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Pure. The plan as numbered preview lines: the summary, then one line per step
|
|
24
|
+
* (`1. <description>`). No footer and no styling — callers append their own
|
|
25
|
+
* ("run with --apply" for the command, an apply/cancel hint for the overlay).
|
|
26
|
+
* Shared so the CLI command and the ctrl-g overlay render an identical plan.
|
|
27
|
+
*/
|
|
28
|
+
export function organizePreviewLines(plan) {
|
|
29
|
+
const lines = [plan.summary];
|
|
30
|
+
plan.steps.forEach((step, i) => lines.push(`${i + 1}. ${step.description}`));
|
|
31
|
+
return lines;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=organize-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organize-types.js","sourceRoot":"","sources":["../../src/core/organize-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,OAAO;IACP,iBAAiB;IACjB,eAAe;IACf,iBAAiB;IACjB,eAAe;CACP,CAAC;AAGX,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAQ,OAA6B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAiDD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAkB;IACrD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/core/paths.d.ts
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* category is named here and in AGENTS.md #1 rather than left as a habit,
|
|
15
15
|
* because a rule with an unwritten exception is a rule the next person
|
|
16
16
|
* "fixes".
|
|
17
|
+
*
|
|
18
|
+
* One deliberate exception: `workReportPath()` lives under `tmpdir()`, not
|
|
19
|
+
* either root, because it's a disposable render target overwritten on every
|
|
20
|
+
* report, not gmux state.
|
|
17
21
|
*/
|
|
18
22
|
/** Root of gmux's config. Honors XDG_CONFIG_HOME. */
|
|
19
23
|
export declare function configDir(): string;
|
|
@@ -26,6 +30,15 @@ export declare function indexPath(): string;
|
|
|
26
30
|
/** Directory of generated summaries, one JSON file per session. */
|
|
27
31
|
export declare function summaryDir(): string;
|
|
28
32
|
export declare function summaryPath(harness: string, sessionId: string): string;
|
|
33
|
+
/** Directory of generated work-view fragments, one JSON file per session. */
|
|
34
|
+
export declare function workViewDir(): string;
|
|
35
|
+
/** Cached work-view (metadata + HTML fragment) for one session. */
|
|
36
|
+
export declare function workViewPath(harness: string, sessionId: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* The assembled work report. One stable file in the temp dir so re-pressing `v`
|
|
39
|
+
* overwrites it and the `file://` link the cockpit shows stays the same.
|
|
40
|
+
*/
|
|
41
|
+
export declare function workReportPath(): string;
|
|
29
42
|
/**
|
|
30
43
|
* The pane→session links written by `gmux run`. Cache, not config: keyed by tmux
|
|
31
44
|
* pane ids that die with the server, disposable, regenerated by living in the
|
package/dist/core/paths.js
CHANGED
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
* category is named here and in AGENTS.md #1 rather than left as a habit,
|
|
15
15
|
* because a rule with an unwritten exception is a rule the next person
|
|
16
16
|
* "fixes".
|
|
17
|
+
*
|
|
18
|
+
* One deliberate exception: `workReportPath()` lives under `tmpdir()`, not
|
|
19
|
+
* either root, because it's a disposable render target overwritten on every
|
|
20
|
+
* report, not gmux state.
|
|
17
21
|
*/
|
|
18
|
-
import { homedir } from "node:os";
|
|
22
|
+
import { homedir, tmpdir } from "node:os";
|
|
19
23
|
import { join } from "node:path";
|
|
20
24
|
/** Root of gmux's config. Honors XDG_CONFIG_HOME. */
|
|
21
25
|
export function configDir() {
|
|
@@ -44,6 +48,21 @@ export function summaryDir() {
|
|
|
44
48
|
export function summaryPath(harness, sessionId) {
|
|
45
49
|
return join(summaryDir(), `${harness}-${sessionId}.json`);
|
|
46
50
|
}
|
|
51
|
+
/** Directory of generated work-view fragments, one JSON file per session. */
|
|
52
|
+
export function workViewDir() {
|
|
53
|
+
return join(cacheDir(), "work-views");
|
|
54
|
+
}
|
|
55
|
+
/** Cached work-view (metadata + HTML fragment) for one session. */
|
|
56
|
+
export function workViewPath(harness, sessionId) {
|
|
57
|
+
return join(workViewDir(), `${harness}-${sessionId}.json`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The assembled work report. One stable file in the temp dir so re-pressing `v`
|
|
61
|
+
* overwrites it and the `file://` link the cockpit shows stays the same.
|
|
62
|
+
*/
|
|
63
|
+
export function workReportPath() {
|
|
64
|
+
return join(tmpdir(), "gmux-work-report.html");
|
|
65
|
+
}
|
|
47
66
|
/**
|
|
48
67
|
* The pane→session links written by `gmux run`. Cache, not config: keyed by tmux
|
|
49
68
|
* pane ids that die with the server, disposable, regenerated by living in the
|
package/dist/core/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask-box intent router.
|
|
3
|
+
*
|
|
4
|
+
* The ctrl-g overlay's ask box takes free text. Most of it is a *question* for
|
|
5
|
+
* the running agents (the existing broadcast). Some of it is a request to
|
|
6
|
+
* *rearrange the tmux workspace* ("group these by project", "put the shells in
|
|
7
|
+
* their own window"). `classifyIntent` decides which, using the same provider
|
|
8
|
+
* plumbing as summaries — a one-shot CLI that reads a prompt and writes text.
|
|
9
|
+
*
|
|
10
|
+
* SAFETY: this defaults to "ask" on every uncertainty. Empty prompt, no
|
|
11
|
+
* provider configured, a spawn failure, a timeout, or any reply that is not an
|
|
12
|
+
* unambiguous "ORGANIZE" all resolve to "ask", so a normal question is never
|
|
13
|
+
* misread as a reorganization. The caller then previews-and-confirms before
|
|
14
|
+
* anything touches tmux; misclassifying the other direction (a genuine reorg
|
|
15
|
+
* read as a question) merely broadcasts it, which is harmless.
|
|
16
|
+
*/
|
|
17
|
+
import { runProviderCommand } from "./provider-process.js";
|
|
18
|
+
export type AskIntent = "organize" | "ask";
|
|
19
|
+
export interface ClassifyDeps {
|
|
20
|
+
/**
|
|
21
|
+
* The provider argv. Left `undefined` it is resolved from config (same as the
|
|
22
|
+
* summarizer). Pass `null` for "no provider" and a concrete argv (e.g. a
|
|
23
|
+
* `node -e` fake) in tests. When passed, it is used as-is — no config read.
|
|
24
|
+
*/
|
|
25
|
+
command?: string[] | null;
|
|
26
|
+
/** The spawn seam, injectable for tests. Defaults to the real provider run. */
|
|
27
|
+
run?: typeof runProviderCommand;
|
|
28
|
+
}
|
|
29
|
+
/** Pure. The one-shot classification prompt. */
|
|
30
|
+
export declare function buildClassifyPrompt(prompt: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Pure. Reads a raw provider reply as an intent, conservatively: only the
|
|
33
|
+
* unambiguous first word "organize" (case-insensitive, punctuation ignored)
|
|
34
|
+
* counts as ORGANIZE. Anything else — including empty, prose, or "ask" — is
|
|
35
|
+
* "ask". Mirrors the classifier's "reply with exactly one word" instruction
|
|
36
|
+
* while tolerating a trailing period or newline.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseIntent(raw: string): AskIntent;
|
|
39
|
+
/**
|
|
40
|
+
* Decide whether an ask-box prompt is a workspace-reorganization request
|
|
41
|
+
* ("organize") or a question for the agents ("ask"). Never throws; every
|
|
42
|
+
* failure path returns "ask".
|
|
43
|
+
*/
|
|
44
|
+
export declare function classifyIntent(prompt: string, deps?: ClassifyDeps): Promise<AskIntent>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask-box intent router.
|
|
3
|
+
*
|
|
4
|
+
* The ctrl-g overlay's ask box takes free text. Most of it is a *question* for
|
|
5
|
+
* the running agents (the existing broadcast). Some of it is a request to
|
|
6
|
+
* *rearrange the tmux workspace* ("group these by project", "put the shells in
|
|
7
|
+
* their own window"). `classifyIntent` decides which, using the same provider
|
|
8
|
+
* plumbing as summaries — a one-shot CLI that reads a prompt and writes text.
|
|
9
|
+
*
|
|
10
|
+
* SAFETY: this defaults to "ask" on every uncertainty. Empty prompt, no
|
|
11
|
+
* provider configured, a spawn failure, a timeout, or any reply that is not an
|
|
12
|
+
* unambiguous "ORGANIZE" all resolve to "ask", so a normal question is never
|
|
13
|
+
* misread as a reorganization. The caller then previews-and-confirms before
|
|
14
|
+
* anything touches tmux; misclassifying the other direction (a genuine reorg
|
|
15
|
+
* read as a question) merely broadcasts it, which is harmless.
|
|
16
|
+
*/
|
|
17
|
+
import { readConfig, resolveSummaryCommand } from "./config.js";
|
|
18
|
+
import { runProviderCommand } from "./provider-process.js";
|
|
19
|
+
/** Kept tight: this is a single-word classification, not a summary or an answer. */
|
|
20
|
+
const CLASSIFY_TIMEOUT_MS = 30_000;
|
|
21
|
+
/** Pure. The one-shot classification prompt. */
|
|
22
|
+
export function buildClassifyPrompt(prompt) {
|
|
23
|
+
return [
|
|
24
|
+
"Classify the user's message. Reply with exactly one word and nothing else:",
|
|
25
|
+
"ORGANIZE if it asks to rearrange/reorganize the tmux workspace layout (windows, panes, tiling, grouping, moving panes),",
|
|
26
|
+
"or ASK if it is a question or instruction for the running agents.",
|
|
27
|
+
"",
|
|
28
|
+
`Message: ${prompt}`,
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pure. Reads a raw provider reply as an intent, conservatively: only the
|
|
33
|
+
* unambiguous first word "organize" (case-insensitive, punctuation ignored)
|
|
34
|
+
* counts as ORGANIZE. Anything else — including empty, prose, or "ask" — is
|
|
35
|
+
* "ask". Mirrors the classifier's "reply with exactly one word" instruction
|
|
36
|
+
* while tolerating a trailing period or newline.
|
|
37
|
+
*/
|
|
38
|
+
export function parseIntent(raw) {
|
|
39
|
+
const first = raw.toLowerCase().match(/[a-z]+/);
|
|
40
|
+
return first && first[0] === "organize" ? "organize" : "ask";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Decide whether an ask-box prompt is a workspace-reorganization request
|
|
44
|
+
* ("organize") or a question for the agents ("ask"). Never throws; every
|
|
45
|
+
* failure path returns "ask".
|
|
46
|
+
*/
|
|
47
|
+
export async function classifyIntent(prompt, deps = {}) {
|
|
48
|
+
const trimmed = prompt.trim();
|
|
49
|
+
if (trimmed === "")
|
|
50
|
+
return "ask"; // Nothing to classify — and never spawn for it.
|
|
51
|
+
const argv = deps.command !== undefined ? deps.command : resolveSummaryCommand(await readConfig());
|
|
52
|
+
if (!argv || argv.length === 0)
|
|
53
|
+
return "ask"; // No provider is a "no", not an error.
|
|
54
|
+
try {
|
|
55
|
+
const run = deps.run ?? runProviderCommand;
|
|
56
|
+
const raw = await run(argv, buildClassifyPrompt(trimmed), { timeoutMs: CLASSIFY_TIMEOUT_MS });
|
|
57
|
+
return parseIntent(raw);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return "ask"; // Timeout, nonzero exit, spawn failure — default to the safe path.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=ask-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-router.js","sourceRoot":"","sources":["../../src/services/ask-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAI3D,oFAAoF;AACpF,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAanC,gDAAgD;AAChD,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO;QACL,4EAA4E;QAC5E,yHAAyH;QACzH,mEAAmE;QACnE,EAAE;QACF,YAAY,MAAM,EAAE;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,OAAqB,EAAE;IAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC,CAAC,gDAAgD;IAElF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC;IACnG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,uCAAuC;IAErF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,kBAAkB,CAAC;QAC3C,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC9F,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,mEAAmE;IACnF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `gmux daemon run` PID lockfile — extracted to its own leaf module (no
|
|
3
|
+
* dependents within `cli/`) so `cli/tmux-label.ts` can detect a live daemon
|
|
4
|
+
* without importing `cli/commands/daemon.ts` (which itself imports
|
|
5
|
+
* `cli/tmux-label.ts` for `enableBorder`, and a cycle between the two is
|
|
6
|
+
* fragile to depend on even where it happens to work).
|
|
7
|
+
*
|
|
8
|
+
* Same shape (`{ pid, startedAt }`) and staleness rule as
|
|
9
|
+
* `services/auto-summarize.ts`'s lock: owner dead, or too old. It lives under
|
|
10
|
+
* `gmuxDir()` rather than `cacheDir()` directly because it is gmux-specific
|
|
11
|
+
* ephemeral state, not shared with the rest of gmux.
|
|
12
|
+
*/
|
|
13
|
+
/** A lock older than this belongs to a process that died without cleaning up. */
|
|
14
|
+
export declare const DAEMON_LOCK_STALE_MS: number;
|
|
15
|
+
export interface DaemonLock {
|
|
16
|
+
pid: number;
|
|
17
|
+
startedAt: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function daemonLockPath(): string;
|
|
20
|
+
/** A lock is stale when its owner is gone, or when it is simply too old. */
|
|
21
|
+
export declare function isDaemonLockStale(lock: DaemonLock, now?: Date, staleMs?: number): boolean;
|
|
22
|
+
/** Corrupt or missing lock reads as "not running" — never throws. */
|
|
23
|
+
export declare function readDaemonLock(): Promise<DaemonLock | null>;
|
|
24
|
+
export declare function releaseDaemonLock(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Take the lock for this process, refusing if a live one already exists.
|
|
27
|
+
*
|
|
28
|
+
* Returns false — never throws — when another daemon is already running, so
|
|
29
|
+
* callers can print a clear message and exit rather than crash.
|
|
30
|
+
*/
|
|
31
|
+
export declare function acquireDaemonLock(now?: Date): Promise<boolean>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `gmux daemon run` PID lockfile — extracted to its own leaf module (no
|
|
3
|
+
* dependents within `cli/`) so `cli/tmux-label.ts` can detect a live daemon
|
|
4
|
+
* without importing `cli/commands/daemon.ts` (which itself imports
|
|
5
|
+
* `cli/tmux-label.ts` for `enableBorder`, and a cycle between the two is
|
|
6
|
+
* fragile to depend on even where it happens to work).
|
|
7
|
+
*
|
|
8
|
+
* Same shape (`{ pid, startedAt }`) and staleness rule as
|
|
9
|
+
* `services/auto-summarize.ts`'s lock: owner dead, or too old. It lives under
|
|
10
|
+
* `gmuxDir()` rather than `cacheDir()` directly because it is gmux-specific
|
|
11
|
+
* ephemeral state, not shared with the rest of gmux.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { gmuxDir } from "../core/paths.js";
|
|
16
|
+
/** A lock older than this belongs to a process that died without cleaning up. */
|
|
17
|
+
export const DAEMON_LOCK_STALE_MS = 10 * 60_000;
|
|
18
|
+
export function daemonLockPath() {
|
|
19
|
+
return join(gmuxDir(), "daemon.lock");
|
|
20
|
+
}
|
|
21
|
+
function processAlive(pid) {
|
|
22
|
+
if (pid <= 0)
|
|
23
|
+
return false;
|
|
24
|
+
try {
|
|
25
|
+
process.kill(pid, 0);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
// EPERM means the process exists but belongs to someone else — still alive.
|
|
30
|
+
return error.code === "EPERM";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** A lock is stale when its owner is gone, or when it is simply too old. */
|
|
34
|
+
export function isDaemonLockStale(lock, now = new Date(), staleMs = DAEMON_LOCK_STALE_MS) {
|
|
35
|
+
const started = Date.parse(lock.startedAt);
|
|
36
|
+
if (Number.isNaN(started))
|
|
37
|
+
return true;
|
|
38
|
+
if (now.getTime() - started > staleMs)
|
|
39
|
+
return true;
|
|
40
|
+
return !processAlive(lock.pid);
|
|
41
|
+
}
|
|
42
|
+
/** Corrupt or missing lock reads as "not running" — never throws. */
|
|
43
|
+
export async function readDaemonLock() {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(await readFile(daemonLockPath(), "utf8"));
|
|
46
|
+
if (typeof parsed?.startedAt !== "string")
|
|
47
|
+
return null;
|
|
48
|
+
const pid = Number(parsed.pid);
|
|
49
|
+
if (!Number.isFinite(pid))
|
|
50
|
+
return null;
|
|
51
|
+
return { pid, startedAt: parsed.startedAt };
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function writeDaemonLock(lock) {
|
|
58
|
+
await mkdir(gmuxDir(), { recursive: true });
|
|
59
|
+
await writeFile(daemonLockPath(), JSON.stringify(lock), "utf8");
|
|
60
|
+
}
|
|
61
|
+
export async function releaseDaemonLock() {
|
|
62
|
+
await rm(daemonLockPath(), { force: true });
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Take the lock for this process, refusing if a live one already exists.
|
|
66
|
+
*
|
|
67
|
+
* Returns false — never throws — when another daemon is already running, so
|
|
68
|
+
* callers can print a clear message and exit rather than crash.
|
|
69
|
+
*/
|
|
70
|
+
export async function acquireDaemonLock(now = new Date()) {
|
|
71
|
+
const existing = await readDaemonLock();
|
|
72
|
+
if (existing && !isDaemonLockStale(existing, now))
|
|
73
|
+
return false;
|
|
74
|
+
await writeDaemonLock({ pid: process.pid, startedAt: now.toISOString() });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=daemon-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-lock.js","sourceRoot":"","sources":["../../src/services/daemon-lock.ts"],"names":[],"mappings":"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"}
|
package/dist/services/daemon.js
CHANGED
|
@@ -38,6 +38,11 @@ export class Daemon {
|
|
|
38
38
|
this.sensors.delete(id);
|
|
39
39
|
this.deps.model.markGone(id);
|
|
40
40
|
}
|
|
41
|
+
// Collected here, enqueued after the loop against a single snapshot below
|
|
42
|
+
// — looking each pane's just-applied entry up via `model.snapshot()`
|
|
43
|
+
// *inside* this loop would take an O(n)-sized snapshot on every one of
|
|
44
|
+
// its n iterations.
|
|
45
|
+
const toEnqueue = [];
|
|
41
46
|
for (const id of present) {
|
|
42
47
|
this.deps.model.upsertIdentity(id);
|
|
43
48
|
let sensor = this.sensors.get(id.paneId);
|
|
@@ -48,16 +53,26 @@ export class Daemon {
|
|
|
48
53
|
try {
|
|
49
54
|
const obs = await sensor.observe(now);
|
|
50
55
|
this.deps.model.applyState(id.paneId, classifyState(obs, now), obs.lastActivityTs, now);
|
|
51
|
-
if (this.deps.semantic)
|
|
52
|
-
|
|
53
|
-
if (entry)
|
|
54
|
-
this.deps.semantic.enqueue(entry, obs, now); // fire-and-forget, never awaited
|
|
55
|
-
}
|
|
56
|
+
if (this.deps.semantic)
|
|
57
|
+
toEnqueue.push({ paneId: id.paneId, obs });
|
|
56
58
|
}
|
|
57
59
|
catch {
|
|
58
60
|
// A single sensor failure must never abort the whole tick.
|
|
59
61
|
}
|
|
60
62
|
}
|
|
63
|
+
// Enqueue any collected observations against a snapshot taken exactly
|
|
64
|
+
// once per tick, shared with the guardian's pane list below (when
|
|
65
|
+
// resources are sampled this tick) instead of each taking its own O(n)
|
|
66
|
+
// snapshot.
|
|
67
|
+
const enqueueSemantic = (byId) => {
|
|
68
|
+
if (!this.deps.semantic)
|
|
69
|
+
return;
|
|
70
|
+
for (const { paneId, obs } of toEnqueue) {
|
|
71
|
+
const entry = byId.get(paneId);
|
|
72
|
+
if (entry)
|
|
73
|
+
this.deps.semantic.enqueue(entry, obs, now); // fire-and-forget, never awaited
|
|
74
|
+
}
|
|
75
|
+
};
|
|
61
76
|
if (this.deps.resources) {
|
|
62
77
|
try {
|
|
63
78
|
const panePids = new Map(present.map((p) => [p.paneId, p.pid]));
|
|
@@ -65,6 +80,13 @@ export class Daemon {
|
|
|
65
80
|
for (const [paneId, res] of perPane)
|
|
66
81
|
this.deps.model.applyResources(paneId, res);
|
|
67
82
|
this.deps.model.setHostPressure(host);
|
|
83
|
+
// The one `model.snapshot()` call for this tick — taken after
|
|
84
|
+
// resources are applied so it (and the guardian below) sees this
|
|
85
|
+
// tick's resource data, and reused via `byId` for the semantic
|
|
86
|
+
// enqueue above instead of a second full pass.
|
|
87
|
+
const snap = this.deps.model.snapshot();
|
|
88
|
+
const byId = new Map(snap.panes.map((p) => [p.identity.paneId, p]));
|
|
89
|
+
enqueueSemantic(byId);
|
|
68
90
|
if (this.deps.guardian) {
|
|
69
91
|
const agentIds = this.deps.resolveAgents?.(present)
|
|
70
92
|
?? present.filter((p) => p.harness && p.sessionId).map((p) => p.paneId);
|
|
@@ -73,7 +95,7 @@ export class Daemon {
|
|
|
73
95
|
// identity.sessionId. `resolveAgents` is a test-only override, so
|
|
74
96
|
// when it's set, patch those fields (decision purposes only, never
|
|
75
97
|
// persisted to the model) so the guardian agrees with the override.
|
|
76
|
-
const panes =
|
|
98
|
+
const panes = snap.panes.map((p) => {
|
|
77
99
|
if (!this.deps.resolveAgents || !agentIdSet.has(p.identity.paneId) || p.identity.harness)
|
|
78
100
|
return p;
|
|
79
101
|
return { ...p, identity: { ...p.identity, harness: "test-agent", sessionId: p.identity.paneId } };
|
|
@@ -100,6 +122,13 @@ export class Daemon {
|
|
|
100
122
|
// Resource sampling / guardian failures must never abort the tick.
|
|
101
123
|
}
|
|
102
124
|
}
|
|
125
|
+
else if (this.deps.semantic) {
|
|
126
|
+
// No resource sampling this tick, so take the single snapshot here
|
|
127
|
+
// instead — same timing as before this change, minus the redundant
|
|
128
|
+
// per-pane snapshots.
|
|
129
|
+
const snap = this.deps.model.snapshot();
|
|
130
|
+
enqueueSemantic(new Map(snap.panes.map((p) => [p.identity.paneId, p])));
|
|
131
|
+
}
|
|
103
132
|
this.deps.model.evictGone();
|
|
104
133
|
}
|
|
105
134
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/services/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAe,MAAM,cAAc,CAAC;AAsB5E,MAAM,OAAO,MAAM;IAQY;IAPrB,QAAQ,CAAe;IAC/B;;+EAE2E;IACnE,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,IAAI,CAAgD;IAE5D,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;QAC3C,IAAI,CAAC,QAAQ,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC;QACT,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;YACtE,OAAO;QACT,CAAC;QACD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QAEnC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACvD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACtC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;gBACxF,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/services/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
|
+
import { formatBytes } from "../core/bytes.js";
|
|
2
3
|
/**
|
|
3
4
|
* Pure policy state machine: pressure + policy + clock in, a decision out.
|
|
4
5
|
* The ONE component that acts (types into agents via the caller), so it
|
|
@@ -63,9 +64,8 @@ function topConsumer(panes, host) {
|
|
|
63
64
|
return { culprit: null, label: "a source outside tracked panes" };
|
|
64
65
|
}
|
|
65
66
|
const name = best.identity.cwd ? basename(best.identity.cwd) : best.identity.command;
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
return { culprit: best.identity.paneId, label: `window \`${name}\` (${gb} GB)` };
|
|
67
|
+
// Shared with the cockpit's memory column so the same pane shows the same
|
|
68
|
+
// figure in both places.
|
|
69
|
+
return { culprit: best.identity.paneId, label: `window \`${name}\` (${formatBytes(bestRss)})` };
|
|
70
70
|
}
|
|
71
71
|
//# sourceMappingURL=guardian.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/services/guardian.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/services/guardian.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gmux organize`: turns the live pane set into an `OrganizePlan` (grouping
|
|
3
|
+
* work by project into windows) and, on request, executes that plan against
|
|
4
|
+
* the tmux gateway.
|
|
5
|
+
*
|
|
6
|
+
* Two planners share the `OrganizePlanner` interface:
|
|
7
|
+
* - `HeuristicOrganizePlanner` — deterministic, no model call, no I/O. The
|
|
8
|
+
* graceful floor: `gmux organize` with no intent and no provider always
|
|
9
|
+
* has something sensible to do.
|
|
10
|
+
* - `LlmOrganizePlanner` — reuses the same provider-CLI plumbing as session
|
|
11
|
+
* summaries (`resolveSummaryCommand` + `runProviderCommand`) to turn a
|
|
12
|
+
* natural-language intent into a plan, and NEVER throws: any missing
|
|
13
|
+
* provider, timeout, or malformed reply falls back to the heuristic.
|
|
14
|
+
*
|
|
15
|
+
* `applyPlan` is the only place in this file that touches the gateway. It
|
|
16
|
+
* validates every step against the live pane/window set (plus handles bound
|
|
17
|
+
* by earlier steps in the same run) and skips — never throws for — a step
|
|
18
|
+
* that no longer makes sense, so one stale reference can't abort the rest of
|
|
19
|
+
* the plan.
|
|
20
|
+
*
|
|
21
|
+
* DESTRUCTION GUARANTEE: `applyPlan` only ever calls
|
|
22
|
+
* `newWindow`/`renameWindow`/`joinPane`/`breakPane`/`swapPane`/`selectLayout`
|
|
23
|
+
* on the gateway. None of those kills a process. A pane moved out of a window
|
|
24
|
+
* that tmux then closes (now empty) has already left *with* its process.
|
|
25
|
+
* There is no code path from a plan to `kill-pane`/`kill-window` — those
|
|
26
|
+
* verbs do not appear anywhere below, and `OrganizeStep` has no op that maps
|
|
27
|
+
* to one.
|
|
28
|
+
*/
|
|
29
|
+
import { type OrganizePane, type OrganizePlan, type OrganizePlanner, type OrganizeStep } from "../core/organize-types.js";
|
|
30
|
+
import type { TmuxGateway } from "./tmux-gateway.js";
|
|
31
|
+
export type { OrganizePlanner } from "../core/organize-types.js";
|
|
32
|
+
/**
|
|
33
|
+
* Deterministic, pure, no-I/O planner. Groups agent panes by project
|
|
34
|
+
* (`basename(cwd)`) into one window each, and idle shells into a shared
|
|
35
|
+
* "shells" window. Ignores `intent` — it has no natural-language input to
|
|
36
|
+
* interpret, which is exactly why it is the safe default and the fallback
|
|
37
|
+
* for `LlmOrganizePlanner`.
|
|
38
|
+
*/
|
|
39
|
+
export declare class HeuristicOrganizePlanner implements OrganizePlanner {
|
|
40
|
+
plan(panes: OrganizePane[], _intent?: string): Promise<OrganizePlan>;
|
|
41
|
+
}
|
|
42
|
+
/** Pure. The prompt an LLM planner is asked to reply to. */
|
|
43
|
+
export declare function buildOrganizePrompt(panes: OrganizePane[], intent: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Pure. Turns a raw model reply into a validated `OrganizePlan`, dropping —
|
|
46
|
+
* never throwing on — any step that is malformed, references an unknown
|
|
47
|
+
* pane, or references a window handle not bound by an earlier step in the
|
|
48
|
+
* same reply. Mirrors `parseSummaryFields`/`parseLabelFields`: take the
|
|
49
|
+
* outermost `{`…`}` rather than trusting the whole reply to parse, because
|
|
50
|
+
* models fence or preface their JSON more often than they should.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseOrganizePlan(raw: string, panes: OrganizePane[]): OrganizePlan;
|
|
53
|
+
/**
|
|
54
|
+
* LLM-backed planner. Reuses the exact provider infra the semantic labeler
|
|
55
|
+
* uses (`resolveSummaryCommand` + `runProviderCommand`). Never throws to the
|
|
56
|
+
* caller: no provider configured, no intent given, a timeout, a malformed
|
|
57
|
+
* reply, or a reply that parses to zero usable steps all fall back to
|
|
58
|
+
* `fallback` (a fresh `HeuristicOrganizePlanner` by default).
|
|
59
|
+
*
|
|
60
|
+
* `argv`, when passed to the constructor, is used as-is (mainly for tests, to
|
|
61
|
+
* avoid touching real config/PATH); when omitted, it is resolved fresh from
|
|
62
|
+
* config on every `plan()` call, same as the labeler does.
|
|
63
|
+
*/
|
|
64
|
+
export declare class LlmOrganizePlanner implements OrganizePlanner {
|
|
65
|
+
private readonly fallback;
|
|
66
|
+
private readonly argv?;
|
|
67
|
+
constructor(fallback?: OrganizePlanner, argv?: string[] | null | undefined);
|
|
68
|
+
plan(panes: OrganizePane[], intent?: string): Promise<OrganizePlan>;
|
|
69
|
+
}
|
|
70
|
+
export interface ApplyResult {
|
|
71
|
+
applied: OrganizeStep[];
|
|
72
|
+
skipped: Array<{
|
|
73
|
+
step: OrganizeStep;
|
|
74
|
+
reason: string;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Execute a plan against `gateway`, one step at a time, in order.
|
|
79
|
+
*
|
|
80
|
+
* Every step is validated against the live pane/window set (snapshotted once,
|
|
81
|
+
* up front) plus any window handle a prior step in THIS run has bound —
|
|
82
|
+
* never against a fresh `listPanes()` call, so the validation set is stable
|
|
83
|
+
* for the whole run. A step that fails validation, or whose gateway call
|
|
84
|
+
* throws (a tmux race, "can't break the only pane in a window", …), is
|
|
85
|
+
* pushed to `skipped` with a reason and execution continues — one bad step
|
|
86
|
+
* never aborts the rest of the plan.
|
|
87
|
+
*
|
|
88
|
+
* Only `newWindow`/`renameWindow`/`joinPane`/`breakPane`/`swapPane`/
|
|
89
|
+
* `selectLayout` are ever called here. None of those kills a process.
|
|
90
|
+
*/
|
|
91
|
+
export declare function applyPlan(plan: OrganizePlan, gateway: TmuxGateway): Promise<ApplyResult>;
|