@rubytech/create-maxy-code 0.1.92 → 0.1.94
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/__tests__/claude-ptys-slice.test.js +25 -0
- package/dist/index.js +10 -1
- package/dist/port-resolution.js +29 -0
- package/package.json +1 -1
- package/payload/platform/plugins/docs/references/platform.md +4 -2
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +61 -2
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +337 -32
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +2 -3
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +316 -211
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +62 -13
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +14 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +165 -33
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +283 -92
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +4 -2
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.js +23 -22
- package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +55 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +170 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +114 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +209 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -0
- package/payload/server/public/assets/{ChatInput-dLWJ0Q8I.css → ChatInput-CzQhPt90.css} +1 -1
- package/payload/server/public/assets/{Checkbox-Bx8mRHQ3.js → Checkbox-ynZtDWi3.js} +1 -1
- package/payload/server/public/assets/{admin-BhEvxMrC.js → admin-ux6TKHS1.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BEW_bD50.js → architectureDiagram-Q4EWVU46-Ko_izKmY.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DFLT98C3.js → blockDiagram-DXYQGD6D-D58VDC0w.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BemKtROA.js → c4Diagram-AHTNJAMY-C-eRXyKi.js} +1 -1
- package/payload/server/public/assets/channel-CejaITMF.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-BL8fGJ7_.js → chunk-336JU56O-DZLEfLAb.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-BD6by0MP.js → chunk-426QAEUC-BriTU3L8.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-DX-cb6Nd.js → chunk-4TB4RGXK-BdxRL-AY.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-B2QqtIZS.js → chunk-5FUZZQ4R-BgEmy7Y3.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-CzJWmsmd.js → chunk-5PVQY5BW-emnbDRLA.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DP9vcvEU.js → chunk-EDXVE4YY-BvCf2Xf2.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-D5xWEjF0.js → chunk-ENJZ2VHE-DTpEYJRb.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-AKduf9o4.js → chunk-ICPOFSXX-Ced2j48Y.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-D6jsVNyQ.js → chunk-OYMX7WX6-B21_a3Dv.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-BNAnTYh8.js → chunk-U2HBQHQK-CPS3yyXK.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-UdQhYDnh.js → chunk-X2U36JSP-BJ_B5NW7.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-DSSmSGmS.js → chunk-YZCP3GAM-CMLy-WXm.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-Czm7IDMC.js → chunk-ZZ45TVLE-BkLnzCHe.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BmoqALf4.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-vMajwCNi.js +1 -0
- package/payload/server/public/assets/clone-mvVfHkyg.js +1 -0
- package/payload/server/public/assets/{dagre-hSIMHJcO.js → dagre-Dd_gq4yj.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-CBuRCJE7.js → dagre-KV5264BT-RY0E5UAn.js} +1 -1
- package/payload/server/public/assets/data-BK4r8Dqy.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-dbMrH0le.js → diagram-5BDNPKRD-DR7XiPLh.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DBc6hs9a.js → diagram-G4DWMVQ6-2EbOfKFs.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-ubSGybUk.js → diagram-MMDJMWI5-CGcY0Ze7.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-WxL3cv-9.js → diagram-TYMM5635-Dk6MmAso.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-CrBBOAN8.js → erDiagram-SMLLAGMA-CKvdlUqk.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-CXUq5Hzt.js → flowDiagram-DWJPFMVM-BqKhvCDz.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-vTCBnYVS.js → ganttDiagram-T4ZO3ILL-CeiE-IRb.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BHrj_bRa.js → gitGraphDiagram-UUTBAWPF-BbPKn7v2.js} +1 -1
- package/payload/server/public/assets/graph-Cb6A8Hhs.js +1 -0
- package/payload/server/public/assets/graph-labels-DCsUNtnE.js +1 -0
- package/payload/server/public/assets/{graphlib-CDO75l2X.js → graphlib-tG6kR59B.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-CcQdaOhi.js → infoDiagram-42DDH7IO-6MiRGCI1.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-3KvltFgV.js → ishikawaDiagram-UXIWVN3A-BwY4p5mg.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DZCXaGFE.js → journeyDiagram-VCZTEJTY-BXAQ_fUi.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DAIAKy1-.js → kanban-definition-6JOO6SKY-ChiiO7Yt.js} +1 -1
- package/payload/server/public/assets/{lib-CCLURO3E.js → lib-DbPz9cfu.js} +1 -1
- package/payload/server/public/assets/{line-C8iCE2Wx.js → line-B5g5OrOD.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-ElvUhnV9.js → mermaid-parser.core-BMGzMKJx.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-7dSH31Ao.js → mermaid.core-DLMgkjXn.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-FZ0rcfac.js → mindmap-definition-QFDTVHPH-BvyzRLAi.js} +1 -1
- package/payload/server/public/assets/{page-C1hj89Gf.js → page-BeYR_GI2.js} +1 -1
- package/payload/server/public/assets/{page-Bu8kO4jO.js → page-DVNp-Dfy.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CDuvMZbO.js → pieDiagram-DEJITSTG--EV9pVfs.js} +1 -1
- package/payload/server/public/assets/{public-g_2skbnY.js → public-sYW0Dwku.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-xDjhkqNc.js → quadrantDiagram-34T5L4WZ-DJcL08ft.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-BPKpyT0n.js → requirementDiagram-MS252O5E-D4VdmmSu.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-BvFIrihC.js → sankeyDiagram-XADWPNL6-Dsw3nHRS.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-C6G1oAE0.js → sequenceDiagram-FGHM5R23-CAgemUx5.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DbOJT_Xh.js → stateDiagram-FHFEXIEX-B7dyL9_X.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CImN6Wk6.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D1DSxHh6.js → timeline-definition-GMOUNBTQ-B7iKswhp.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BYoSeMdo.js → vennDiagram-DHZGUBPP-uFS0i1qX.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-DH3kZdea.js → wardleyDiagram-NUSXRM2D-lDUh4LAD.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-C6MROMLG.js → xychartDiagram-5P7HB3ND-0v7MEVwW.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/public/assets/channel-CfpByn-m.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DNc9R1IX.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DM1DQh8N.js +0 -1
- package/payload/server/public/assets/clone-BSrNdvLW.js +0 -1
- package/payload/server/public/assets/data-Cbo0tTS3.js +0 -1
- package/payload/server/public/assets/graph-Beucu3_J.js +0 -1
- package/payload/server/public/assets/graph-labels-D15-eFQK.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Ce4GFk-H.js +0 -1
- /package/payload/server/public/assets/{ChatInput-DoS80kaC.js → ChatInput-Cj9JQtYt.js} +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/** Single source of truth for the slice unit name. Used by the spawn argv,
|
|
2
|
+
* the boot-seed list-units filter, and the installer's slice unit
|
|
3
|
+
* emission. */
|
|
4
|
+
export declare const CLAUDE_PTYS_SLICE = "claude-ptys.slice";
|
|
5
|
+
/** Hard cap on SIGTERM→SIGKILL escalation inside the scope. systemd
|
|
6
|
+
* delivers SIGTERM on stop, waits TimeoutStopSec, then SIGKILL. 5 s
|
|
7
|
+
* matches the manager's prior killGraceMs (`config.killGraceMs`); the
|
|
8
|
+
* manager no longer manages the escalation itself. */
|
|
9
|
+
export declare const SCOPE_TIMEOUT_STOP_SEC = 5;
|
|
10
|
+
/** Result of one systemctl invocation. Mirrors the shape of
|
|
11
|
+
* spawnSync('utf8') return — exit code plus captured streams. The
|
|
12
|
+
* scope-stop and list-units paths both go through this shape so tests
|
|
13
|
+
* can inject a single stub. */
|
|
14
|
+
export interface SystemctlResult {
|
|
15
|
+
exitCode: number;
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
}
|
|
19
|
+
/** Synchronous runner for `systemctl <args...>`. Default impl uses
|
|
20
|
+
* spawnSync (matches uninstall.ts's existing idiom and keeps the call
|
|
21
|
+
* site free of async glue for what is in practice a sub-50ms call).
|
|
22
|
+
* Tests inject a recording stub. */
|
|
23
|
+
export type SystemctlRunner = (args: readonly string[]) => SystemctlResult;
|
|
24
|
+
export declare function defaultSystemctlRunner(args: readonly string[]): SystemctlResult;
|
|
25
|
+
export declare function scopeUnitName(unitToken: string): string;
|
|
26
|
+
/** Inverse of `scopeUnitName` — extracts the unitToken from a unit name
|
|
27
|
+
* emitted by `systemctl --user list-units --type=scope 'claude-session-*.scope'`.
|
|
28
|
+
* Returns null when the name doesn't match the prefix/suffix shape. */
|
|
29
|
+
export declare function unitTokenFromScopeUnit(unit: string): string | null;
|
|
30
|
+
/** One row from `systemctl --user list-units --type=scope --no-legend
|
|
31
|
+
* 'claude-session-*.scope'`. The `loaded`/`active`/`sub` columns let the
|
|
32
|
+
* boot-seed reconciler ignore units that are draining (`sub=stop-sigterm`)
|
|
33
|
+
* vs running (`sub=running`). */
|
|
34
|
+
export interface SurvivingScope {
|
|
35
|
+
unitName: string;
|
|
36
|
+
unitToken: string;
|
|
37
|
+
loaded: string;
|
|
38
|
+
active: string;
|
|
39
|
+
sub: string;
|
|
40
|
+
}
|
|
41
|
+
/** Parse the `--no-legend` output of `systemctl --user list-units`.
|
|
42
|
+
* Tolerant of leading column whitespace and trailing description column.
|
|
43
|
+
* Rows whose unit name doesn't extract a unitToken are skipped (defensive
|
|
44
|
+
* against future systemctl-output drift). */
|
|
45
|
+
export declare function parseSurvivingScopes(stdout: string): SurvivingScope[];
|
|
46
|
+
/** Issue `systemctl --user list-units --type=scope --no-legend
|
|
47
|
+
* 'claude-session-*.scope'` and return the parsed rows. The boot-seed
|
|
48
|
+
* uses this to discover surviving PTYs across a manager restart. */
|
|
49
|
+
export declare function listSurvivingScopes(run: SystemctlRunner): {
|
|
50
|
+
scopes: SurvivingScope[];
|
|
51
|
+
result: SystemctlResult;
|
|
52
|
+
};
|
|
53
|
+
/** Outcome surface for a scope-stop attempt. `already-gone` is the
|
|
54
|
+
* idempotent success case: systemd reports the unit isn't loaded
|
|
55
|
+
* (exit code 5) because claude exited naturally and systemd retired the
|
|
56
|
+
* scope. */
|
|
57
|
+
export type StopScopeOutcome = 'ok' | 'already-gone' | 'error';
|
|
58
|
+
export interface StopScopeResult {
|
|
59
|
+
outcome: StopScopeOutcome;
|
|
60
|
+
exitCode: number;
|
|
61
|
+
stderr: string;
|
|
62
|
+
}
|
|
63
|
+
/** Issue `systemctl --user stop --no-block <unit>`. The --no-block flag
|
|
64
|
+
* returns once the stop request is queued; the scope's TimeoutStopSec=5
|
|
65
|
+
* handles SIGTERM→SIGKILL escalation. Exit code 5 ("not loaded") means
|
|
66
|
+
* the scope retired naturally — treated as success. */
|
|
67
|
+
export declare function stopScopeUnit(run: SystemctlRunner, unitToken: string): StopScopeResult;
|
|
68
|
+
/** Argv shape produced by `buildSystemdRunArgv`. The cmd is always
|
|
69
|
+
* `systemd-run`; the manager passes this to node-pty. */
|
|
70
|
+
export interface SystemdRunArgv {
|
|
71
|
+
cmd: 'systemd-run';
|
|
72
|
+
args: string[];
|
|
73
|
+
}
|
|
74
|
+
export interface BuildSystemdRunArgvParams {
|
|
75
|
+
unitToken: string;
|
|
76
|
+
claudeBin: string;
|
|
77
|
+
claudeArgs: readonly string[];
|
|
78
|
+
/** Full env map to thread into the scope via `--setenv=KEY=VAL`. Every
|
|
79
|
+
* pair lands as one flag. The default systemd-run inherit-from-caller
|
|
80
|
+
* is NOT relied on; spawnPtyAdapter composes the explicit env block
|
|
81
|
+
* (per-spawn overrides + a copy of process.env it wants in the scope)
|
|
82
|
+
* and the helper threads exactly that shape. */
|
|
83
|
+
env: Readonly<Record<string, string>>;
|
|
84
|
+
/** Working directory passed via `--working-directory=`. The manager's
|
|
85
|
+
* prior node-pty spawn passed `cwd`; the scope receives it the same way. */
|
|
86
|
+
cwd: string;
|
|
87
|
+
}
|
|
88
|
+
/** Build the systemd-run argv that node-pty wraps for one claude spawn.
|
|
89
|
+
* Inside the scope, `/bin/sh -c 'trap "" HUP; exec "$@"'` wraps the
|
|
90
|
+
* launch so claude ignores SIGHUP when the manager closes the PTY master
|
|
91
|
+
* fd on restart. Without the wrap, master-close → kernel SIGHUP → claude
|
|
92
|
+
* exits, and the brief's "PTY pid survives manager restart" verify-gate
|
|
93
|
+
* fails.
|
|
94
|
+
*
|
|
95
|
+
* The shell `exec` replaces the sh process image with claude in-place
|
|
96
|
+
* rather than forking, so pty.pid == claude.pid (the pid is preserved
|
|
97
|
+
* across execve). This matters because the manager's
|
|
98
|
+
* `watcher.waitForPid(pty.pid, …)` resolves when Claude Code writes its
|
|
99
|
+
* own PID file at `$CLAUDE_CONFIG_DIR/sessions/<claude-pid>.json`; with
|
|
100
|
+
* a non-exec sh wrap, pty.pid would be sh's pid (≠ claude's pid) and
|
|
101
|
+
* waitForPid would never resolve. */
|
|
102
|
+
export declare function buildSystemdRunArgv(p: BuildSystemdRunArgvParams): SystemdRunArgv;
|
|
103
|
+
/** Query `systemctl --user show -p MainPID claude-session-<token>.scope`
|
|
104
|
+
* and return the main pid (the claude process). Boot-seed uses this to
|
|
105
|
+
* bridge from the surviving scope unit to the PID file claude wrote at
|
|
106
|
+
* `$CLAUDE_CONFIG_DIR/sessions/<pid>.json`, which in turn carries claude's
|
|
107
|
+
* intrinsic sessionId. Returns null when the unit has no main pid (the
|
|
108
|
+
* scope is draining) or when the systemctl call fails. */
|
|
109
|
+
export declare function getScopeMainPid(run: SystemctlRunner, unitToken: string): number | null;
|
|
110
|
+
/** Slice unit declared at install time so every claude scope shares one
|
|
111
|
+
* cohort cgroup. `systemctl --user status claude-ptys.slice` gives the
|
|
112
|
+
* operator a one-glance view of total live-session memory + CPU. */
|
|
113
|
+
export declare function buildClaudePtysSliceUnitFile(): string;
|
|
114
|
+
//# sourceMappingURL=systemd-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemd-scope.d.ts","sourceRoot":"","sources":["../src/systemd-scope.ts"],"names":[],"mappings":"AAuBA;;gBAEgB;AAChB,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AAEpD;;;uDAGuD;AACvD,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;gCAGgC;AAChC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;qCAGqC;AACrC,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,eAAe,CAAA;AAE1E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CAU/E;AAiBD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;wEAEwE;AACxE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGlE;AAED;;;kCAGkC;AAClC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;8CAG8C;AAC9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAiBrE;AAED;;qEAEqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG;IACzD,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,MAAM,EAAE,eAAe,CAAA;CACxB,CAaA;AAED;;;aAGa;AACb,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,cAAc,GAAG,OAAO,CAAA;AAE9D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;wDAGwD;AACxD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,GAChB,eAAe,CAcjB;AAED;0DAC0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,aAAa,CAAA;IAClB,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B;;;;qDAIiD;IACjD,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACrC;iFAC6E;IAC7E,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;;;;;;;;;sCAasC;AACtC,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,yBAAyB,GAAG,cAAc,CA8BhF;AAED;;;;;2DAK2D;AAC3D,wBAAgB,eAAe,CAC7B,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAQf;AAED;;qEAEqE;AACrE,wBAAgB,4BAA4B,IAAI,MAAM,CAWrD"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Task 250 — every `claude --verbose` PTY spawn lives in its own systemd
|
|
2
|
+
// transient scope unit under a shared `claude-ptys.slice`, instead of inside
|
|
3
|
+
// the session-manager unit's cgroup. The scope cgroup is owned by user
|
|
4
|
+
// systemd, not by the manager; stopping the manager unit (installer rerun,
|
|
5
|
+
// crash, watchdog, restart) no longer cascades a control-group kill to
|
|
6
|
+
// active operator PTYs.
|
|
7
|
+
//
|
|
8
|
+
// This module holds the pure argv/parser helpers that the spawn adapter,
|
|
9
|
+
// stop path, reaper, boot-seed, and the installer share. I/O happens via an
|
|
10
|
+
// injected systemctl callable so tests can stub it without invoking real
|
|
11
|
+
// systemd. node-pty owns the spawn of `systemd-run` itself — this module
|
|
12
|
+
// never touches node-pty.
|
|
13
|
+
//
|
|
14
|
+
// SIGHUP wrap. Inside the scope, the claude invocation is wrapped with
|
|
15
|
+
// `/bin/sh -c 'trap "" HUP; <invoke> "$@"' --`. The wrap exists because
|
|
16
|
+
// closing the PTY master fd (which the manager owns) makes the kernel
|
|
17
|
+
// deliver SIGHUP to the foreground process group of the slave session —
|
|
18
|
+
// and claude's default disposition is exit. With SIGHUP set to ignore,
|
|
19
|
+
// claude survives the master closing on manager restart, which is what
|
|
20
|
+
// the cgroup separation is there to enable.
|
|
21
|
+
import { spawnSync } from 'node:child_process';
|
|
22
|
+
/** Single source of truth for the slice unit name. Used by the spawn argv,
|
|
23
|
+
* the boot-seed list-units filter, and the installer's slice unit
|
|
24
|
+
* emission. */
|
|
25
|
+
export const CLAUDE_PTYS_SLICE = 'claude-ptys.slice';
|
|
26
|
+
/** Hard cap on SIGTERM→SIGKILL escalation inside the scope. systemd
|
|
27
|
+
* delivers SIGTERM on stop, waits TimeoutStopSec, then SIGKILL. 5 s
|
|
28
|
+
* matches the manager's prior killGraceMs (`config.killGraceMs`); the
|
|
29
|
+
* manager no longer manages the escalation itself. */
|
|
30
|
+
export const SCOPE_TIMEOUT_STOP_SEC = 5;
|
|
31
|
+
export function defaultSystemctlRunner(args) {
|
|
32
|
+
const r = spawnSync('systemctl', [...args], {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
timeout: 10_000,
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
exitCode: typeof r.status === 'number' ? r.status : 1,
|
|
38
|
+
stdout: typeof r.stdout === 'string' ? r.stdout : '',
|
|
39
|
+
stderr: typeof r.stderr === 'string' ? r.stderr : '',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// systemd unit names accept `[a-zA-Z0-9:_.\-]`. The unit-token is a
|
|
43
|
+
// per-spawn stable id the manager passes in: for resume spawns it equals
|
|
44
|
+
// Claude Code's intrinsic sessionId (a UUID, which satisfies the char
|
|
45
|
+
// set); for fresh spawns it's a manager-minted UUID. Either way the
|
|
46
|
+
// helper refuses any token that doesn't match the systemd char set
|
|
47
|
+
// rather than silently rewriting — preserving the lookup-by-name contract
|
|
48
|
+
// the brief depends on ("no clever uniqueness suffixes").
|
|
49
|
+
//
|
|
50
|
+
// Why the token isn't always Claude Code's sessionId: at fresh-spawn
|
|
51
|
+
// time the manager has no sessionId yet (Claude Code mints it during
|
|
52
|
+
// boot and writes it to its PID file). The manager mints a separate
|
|
53
|
+
// token for the unit name; boot-seed reconciles unit → claude pid →
|
|
54
|
+
// PID-file → claude sessionId after a restart.
|
|
55
|
+
const SCOPE_NAME_VALID = /^[a-zA-Z0-9:_.\-]+$/;
|
|
56
|
+
export function scopeUnitName(unitToken) {
|
|
57
|
+
if (!unitToken || !SCOPE_NAME_VALID.test(unitToken)) {
|
|
58
|
+
throw new Error(`[systemd-scope] invalid unitToken for unit name: ${JSON.stringify(unitToken)}`);
|
|
59
|
+
}
|
|
60
|
+
return `claude-session-${unitToken}.scope`;
|
|
61
|
+
}
|
|
62
|
+
/** Inverse of `scopeUnitName` — extracts the unitToken from a unit name
|
|
63
|
+
* emitted by `systemctl --user list-units --type=scope 'claude-session-*.scope'`.
|
|
64
|
+
* Returns null when the name doesn't match the prefix/suffix shape. */
|
|
65
|
+
export function unitTokenFromScopeUnit(unit) {
|
|
66
|
+
const m = unit.match(/^claude-session-(.+)\.scope$/);
|
|
67
|
+
return m ? m[1] : null;
|
|
68
|
+
}
|
|
69
|
+
/** Parse the `--no-legend` output of `systemctl --user list-units`.
|
|
70
|
+
* Tolerant of leading column whitespace and trailing description column.
|
|
71
|
+
* Rows whose unit name doesn't extract a unitToken are skipped (defensive
|
|
72
|
+
* against future systemctl-output drift). */
|
|
73
|
+
export function parseSurvivingScopes(stdout) {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const rawLine of stdout.split('\n')) {
|
|
76
|
+
const line = rawLine.trim();
|
|
77
|
+
if (!line)
|
|
78
|
+
continue;
|
|
79
|
+
const cols = line.split(/\s+/);
|
|
80
|
+
if (cols.length < 4)
|
|
81
|
+
continue;
|
|
82
|
+
const unitName = cols[0];
|
|
83
|
+
const loaded = cols[1];
|
|
84
|
+
const active = cols[2];
|
|
85
|
+
const sub = cols[3];
|
|
86
|
+
if (!unitName.startsWith('claude-session-') || !unitName.endsWith('.scope'))
|
|
87
|
+
continue;
|
|
88
|
+
const unitToken = unitTokenFromScopeUnit(unitName);
|
|
89
|
+
if (!unitToken)
|
|
90
|
+
continue;
|
|
91
|
+
out.push({ unitName, unitToken, loaded, active, sub });
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/** Issue `systemctl --user list-units --type=scope --no-legend
|
|
96
|
+
* 'claude-session-*.scope'` and return the parsed rows. The boot-seed
|
|
97
|
+
* uses this to discover surviving PTYs across a manager restart. */
|
|
98
|
+
export function listSurvivingScopes(run) {
|
|
99
|
+
const result = run([
|
|
100
|
+
'--user',
|
|
101
|
+
'list-units',
|
|
102
|
+
'--type=scope',
|
|
103
|
+
'--no-legend',
|
|
104
|
+
'--all',
|
|
105
|
+
'claude-session-*.scope',
|
|
106
|
+
]);
|
|
107
|
+
if (result.exitCode !== 0) {
|
|
108
|
+
return { scopes: [], result };
|
|
109
|
+
}
|
|
110
|
+
return { scopes: parseSurvivingScopes(result.stdout), result };
|
|
111
|
+
}
|
|
112
|
+
/** Issue `systemctl --user stop --no-block <unit>`. The --no-block flag
|
|
113
|
+
* returns once the stop request is queued; the scope's TimeoutStopSec=5
|
|
114
|
+
* handles SIGTERM→SIGKILL escalation. Exit code 5 ("not loaded") means
|
|
115
|
+
* the scope retired naturally — treated as success. */
|
|
116
|
+
export function stopScopeUnit(run, unitToken) {
|
|
117
|
+
const unit = scopeUnitName(unitToken);
|
|
118
|
+
const result = run(['--user', 'stop', '--no-block', unit]);
|
|
119
|
+
if (result.exitCode === 0) {
|
|
120
|
+
return { outcome: 'ok', exitCode: 0, stderr: result.stderr };
|
|
121
|
+
}
|
|
122
|
+
// systemctl exits 5 when the unit isn't loaded — i.e. systemd already
|
|
123
|
+
// retired the scope because claude exited on its own. Operator pressed
|
|
124
|
+
// End on a row whose claude had already quit; we treat the request as
|
|
125
|
+
// satisfied.
|
|
126
|
+
if (result.exitCode === 5) {
|
|
127
|
+
return { outcome: 'already-gone', exitCode: 5, stderr: result.stderr };
|
|
128
|
+
}
|
|
129
|
+
return { outcome: 'error', exitCode: result.exitCode, stderr: result.stderr };
|
|
130
|
+
}
|
|
131
|
+
/** Build the systemd-run argv that node-pty wraps for one claude spawn.
|
|
132
|
+
* Inside the scope, `/bin/sh -c 'trap "" HUP; exec "$@"'` wraps the
|
|
133
|
+
* launch so claude ignores SIGHUP when the manager closes the PTY master
|
|
134
|
+
* fd on restart. Without the wrap, master-close → kernel SIGHUP → claude
|
|
135
|
+
* exits, and the brief's "PTY pid survives manager restart" verify-gate
|
|
136
|
+
* fails.
|
|
137
|
+
*
|
|
138
|
+
* The shell `exec` replaces the sh process image with claude in-place
|
|
139
|
+
* rather than forking, so pty.pid == claude.pid (the pid is preserved
|
|
140
|
+
* across execve). This matters because the manager's
|
|
141
|
+
* `watcher.waitForPid(pty.pid, …)` resolves when Claude Code writes its
|
|
142
|
+
* own PID file at `$CLAUDE_CONFIG_DIR/sessions/<claude-pid>.json`; with
|
|
143
|
+
* a non-exec sh wrap, pty.pid would be sh's pid (≠ claude's pid) and
|
|
144
|
+
* waitForPid would never resolve. */
|
|
145
|
+
export function buildSystemdRunArgv(p) {
|
|
146
|
+
const unit = scopeUnitName(p.unitToken);
|
|
147
|
+
const args = [
|
|
148
|
+
'--user',
|
|
149
|
+
'--scope',
|
|
150
|
+
'--quiet',
|
|
151
|
+
`--unit=${unit}`,
|
|
152
|
+
`--slice=${CLAUDE_PTYS_SLICE}`,
|
|
153
|
+
'-p',
|
|
154
|
+
`TimeoutStopSec=${SCOPE_TIMEOUT_STOP_SEC}`,
|
|
155
|
+
`--working-directory=${p.cwd}`,
|
|
156
|
+
];
|
|
157
|
+
// One --setenv per env key. Order is sorted-by-key for deterministic
|
|
158
|
+
// log lines and test fixtures (and to make argv-diff review tractable
|
|
159
|
+
// across spawns).
|
|
160
|
+
const keys = Object.keys(p.env).sort();
|
|
161
|
+
for (const k of keys) {
|
|
162
|
+
args.push(`--setenv=${k}=${p.env[k]}`);
|
|
163
|
+
}
|
|
164
|
+
// POSIX `--` sentinel — ends systemd-run's own flag parsing so the
|
|
165
|
+
// following positional argv (the sh wrapper + claude) doesn't get
|
|
166
|
+
// swallowed by `--setenv=` etc.
|
|
167
|
+
args.push('--');
|
|
168
|
+
// SIGHUP-trap wrap. `$@` expands every argv from $0 onwards INSIDE the
|
|
169
|
+
// shell, so passing 'sh' as $0 lets `"$@"` resolve to
|
|
170
|
+
// `<claudeBin> <claudeArgs...>` after the trap. Without the explicit
|
|
171
|
+
// $0 ('sh') the first claudeArg would be consumed as $0 and lost.
|
|
172
|
+
args.push('/bin/sh', '-c', 'trap "" HUP; exec "$@"', 'sh', p.claudeBin);
|
|
173
|
+
for (const a of p.claudeArgs)
|
|
174
|
+
args.push(a);
|
|
175
|
+
return { cmd: 'systemd-run', args };
|
|
176
|
+
}
|
|
177
|
+
/** Query `systemctl --user show -p MainPID claude-session-<token>.scope`
|
|
178
|
+
* and return the main pid (the claude process). Boot-seed uses this to
|
|
179
|
+
* bridge from the surviving scope unit to the PID file claude wrote at
|
|
180
|
+
* `$CLAUDE_CONFIG_DIR/sessions/<pid>.json`, which in turn carries claude's
|
|
181
|
+
* intrinsic sessionId. Returns null when the unit has no main pid (the
|
|
182
|
+
* scope is draining) or when the systemctl call fails. */
|
|
183
|
+
export function getScopeMainPid(run, unitToken) {
|
|
184
|
+
const unit = scopeUnitName(unitToken);
|
|
185
|
+
const result = run(['--user', 'show', '-p', 'MainPID', '--value', unit]);
|
|
186
|
+
if (result.exitCode !== 0)
|
|
187
|
+
return null;
|
|
188
|
+
const raw = result.stdout.trim();
|
|
189
|
+
if (!raw || raw === '0')
|
|
190
|
+
return null;
|
|
191
|
+
const n = Number.parseInt(raw, 10);
|
|
192
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
193
|
+
}
|
|
194
|
+
/** Slice unit declared at install time so every claude scope shares one
|
|
195
|
+
* cohort cgroup. `systemctl --user status claude-ptys.slice` gives the
|
|
196
|
+
* operator a one-glance view of total live-session memory + CPU. */
|
|
197
|
+
export function buildClaudePtysSliceUnitFile() {
|
|
198
|
+
return `[Unit]
|
|
199
|
+
Description=Claude Code PTY sessions (Task 250)
|
|
200
|
+
Documentation=https://github.com/rubytech/maxy-code/blob/main/.tasks/archive/250-claude-ptys-survive-manager-restart-via-systemd-scope-per-pty.md
|
|
201
|
+
Before=shutdown.target
|
|
202
|
+
|
|
203
|
+
[Slice]
|
|
204
|
+
|
|
205
|
+
[Install]
|
|
206
|
+
WantedBy=default.target
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=systemd-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemd-scope.js","sourceRoot":"","sources":["../src/systemd-scope.ts"],"names":[],"mappings":"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"}
|