@bridge4dev/runner 0.56.0 → 0.58.1
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/adapters/claude.js +23 -4
- package/dist/adapters/codex-protocol.js +6 -1
- package/dist/adapters/codex.js +11 -2
- package/dist/adapters/questions.d.ts +15 -0
- package/dist/adapters/questions.js +32 -0
- package/dist/adapters/types.js +14 -0
- package/dist/cage-authority.d.ts +118 -0
- package/dist/cage-authority.js +241 -0
- package/dist/config.d.ts +83 -5
- package/dist/config.js +59 -1
- package/dist/daemon-lock.d.ts +43 -0
- package/dist/daemon-lock.js +107 -0
- package/dist/host-load.d.ts +9 -0
- package/dist/host-load.js +9 -0
- package/dist/index.js +222 -20
- package/dist/policy.d.ts +9 -0
- package/dist/policy.js +68 -0
- package/dist/protocol.d.ts +43 -27
- package/dist/recipe-schema.d.ts +12 -12
- package/dist/self-update.js +22 -1
- package/dist/service-unit.d.ts +35 -3
- package/dist/service-unit.js +82 -5
- package/dist/session-allocator.d.ts +259 -0
- package/dist/session-allocator.js +492 -0
- package/dist/session-cage.d.ts +229 -2
- package/dist/session-cage.js +590 -40
- package/dist/session-limits.d.ts +71 -0
- package/dist/session-limits.js +93 -0
- package/dist/session-stall.d.ts +353 -0
- package/dist/session-stall.js +760 -0
- package/dist/supervisor.d.ts +235 -33
- package/dist/supervisor.js +1178 -265
- package/dist/systemd-memory.js +2 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/protocol.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { HostLoadFrame } from './host-load.js';
|
|
3
|
+
import type { SessionLimitsFrame } from './session-limits.js';
|
|
3
4
|
export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
4
5
|
id: z.ZodString;
|
|
5
6
|
kind: z.ZodEnum<["TICKET", "CHAT"]>;
|
|
@@ -102,8 +103,8 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
102
103
|
budgetMinutes: z.ZodNullable<z.ZodNumber>;
|
|
103
104
|
}, "strip", z.ZodTypeAny, {
|
|
104
105
|
path: string;
|
|
105
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
106
106
|
id: string;
|
|
107
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
107
108
|
projectId: string;
|
|
108
109
|
budgetUsd: number | null;
|
|
109
110
|
budgetMinutes: number | null;
|
|
@@ -115,8 +116,8 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
115
116
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
116
117
|
}, {
|
|
117
118
|
path: string;
|
|
118
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
119
119
|
id: string;
|
|
120
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
120
121
|
projectId: string;
|
|
121
122
|
budgetUsd: number | null;
|
|
122
123
|
budgetMinutes: number | null;
|
|
@@ -208,8 +209,8 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
208
209
|
}, "strip", z.ZodTypeAny, {
|
|
209
210
|
mode: "ask" | "plan" | "auto" | "full";
|
|
210
211
|
agent: "CLAUDE" | "CODEX";
|
|
211
|
-
id: string;
|
|
212
212
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
213
|
+
id: string;
|
|
213
214
|
kind: "TICKET" | "CHAT";
|
|
214
215
|
model: string | null;
|
|
215
216
|
effort: string | null;
|
|
@@ -222,8 +223,8 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
222
223
|
extraBudgetMinutes: number | null;
|
|
223
224
|
workspace: {
|
|
224
225
|
path: string;
|
|
225
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
226
226
|
id: string;
|
|
227
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
227
228
|
projectId: string;
|
|
228
229
|
budgetUsd: number | null;
|
|
229
230
|
budgetMinutes: number | null;
|
|
@@ -256,15 +257,15 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
256
257
|
} | undefined;
|
|
257
258
|
}, {
|
|
258
259
|
agent: "CLAUDE" | "CODEX";
|
|
259
|
-
id: string;
|
|
260
260
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
261
|
+
id: string;
|
|
261
262
|
kind: "TICKET" | "CHAT";
|
|
262
263
|
prompt: string;
|
|
263
264
|
providerSessionId: string | null;
|
|
264
265
|
workspace: {
|
|
265
266
|
path: string;
|
|
266
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
267
267
|
id: string;
|
|
268
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
268
269
|
projectId: string;
|
|
269
270
|
budgetUsd: number | null;
|
|
270
271
|
budgetMinutes: number | null;
|
|
@@ -520,8 +521,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
520
521
|
budgetMinutes: z.ZodNullable<z.ZodNumber>;
|
|
521
522
|
}, "strip", z.ZodTypeAny, {
|
|
522
523
|
path: string;
|
|
523
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
524
524
|
id: string;
|
|
525
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
525
526
|
projectId: string;
|
|
526
527
|
budgetUsd: number | null;
|
|
527
528
|
budgetMinutes: number | null;
|
|
@@ -533,8 +534,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
533
534
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
534
535
|
}, {
|
|
535
536
|
path: string;
|
|
536
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
537
537
|
id: string;
|
|
538
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
538
539
|
projectId: string;
|
|
539
540
|
budgetUsd: number | null;
|
|
540
541
|
budgetMinutes: number | null;
|
|
@@ -626,8 +627,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
626
627
|
}, "strip", z.ZodTypeAny, {
|
|
627
628
|
mode: "ask" | "plan" | "auto" | "full";
|
|
628
629
|
agent: "CLAUDE" | "CODEX";
|
|
629
|
-
id: string;
|
|
630
630
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
631
|
+
id: string;
|
|
631
632
|
kind: "TICKET" | "CHAT";
|
|
632
633
|
model: string | null;
|
|
633
634
|
effort: string | null;
|
|
@@ -640,8 +641,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
640
641
|
extraBudgetMinutes: number | null;
|
|
641
642
|
workspace: {
|
|
642
643
|
path: string;
|
|
643
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
644
644
|
id: string;
|
|
645
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
645
646
|
projectId: string;
|
|
646
647
|
budgetUsd: number | null;
|
|
647
648
|
budgetMinutes: number | null;
|
|
@@ -674,15 +675,15 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
674
675
|
} | undefined;
|
|
675
676
|
}, {
|
|
676
677
|
agent: "CLAUDE" | "CODEX";
|
|
677
|
-
id: string;
|
|
678
678
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
679
|
+
id: string;
|
|
679
680
|
kind: "TICKET" | "CHAT";
|
|
680
681
|
prompt: string;
|
|
681
682
|
providerSessionId: string | null;
|
|
682
683
|
workspace: {
|
|
683
684
|
path: string;
|
|
684
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
685
685
|
id: string;
|
|
686
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
686
687
|
projectId: string;
|
|
687
688
|
budgetUsd: number | null;
|
|
688
689
|
budgetMinutes: number | null;
|
|
@@ -721,8 +722,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
721
722
|
sessions: {
|
|
722
723
|
mode: "ask" | "plan" | "auto" | "full";
|
|
723
724
|
agent: "CLAUDE" | "CODEX";
|
|
724
|
-
id: string;
|
|
725
725
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
726
|
+
id: string;
|
|
726
727
|
kind: "TICKET" | "CHAT";
|
|
727
728
|
model: string | null;
|
|
728
729
|
effort: string | null;
|
|
@@ -735,8 +736,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
735
736
|
extraBudgetMinutes: number | null;
|
|
736
737
|
workspace: {
|
|
737
738
|
path: string;
|
|
738
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
739
739
|
id: string;
|
|
740
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
740
741
|
projectId: string;
|
|
741
742
|
budgetUsd: number | null;
|
|
742
743
|
budgetMinutes: number | null;
|
|
@@ -775,15 +776,15 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
775
776
|
}, {
|
|
776
777
|
sessions: {
|
|
777
778
|
agent: "CLAUDE" | "CODEX";
|
|
778
|
-
id: string;
|
|
779
779
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
780
|
+
id: string;
|
|
780
781
|
kind: "TICKET" | "CHAT";
|
|
781
782
|
prompt: string;
|
|
782
783
|
providerSessionId: string | null;
|
|
783
784
|
workspace: {
|
|
784
785
|
path: string;
|
|
785
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
786
786
|
id: string;
|
|
787
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
787
788
|
projectId: string;
|
|
788
789
|
budgetUsd: number | null;
|
|
789
790
|
budgetMinutes: number | null;
|
|
@@ -953,8 +954,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
953
954
|
budgetMinutes: z.ZodNullable<z.ZodNumber>;
|
|
954
955
|
}, "strip", z.ZodTypeAny, {
|
|
955
956
|
path: string;
|
|
956
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
957
957
|
id: string;
|
|
958
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
958
959
|
projectId: string;
|
|
959
960
|
budgetUsd: number | null;
|
|
960
961
|
budgetMinutes: number | null;
|
|
@@ -966,8 +967,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
966
967
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
967
968
|
}, {
|
|
968
969
|
path: string;
|
|
969
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
970
970
|
id: string;
|
|
971
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
971
972
|
projectId: string;
|
|
972
973
|
budgetUsd: number | null;
|
|
973
974
|
budgetMinutes: number | null;
|
|
@@ -1059,8 +1060,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1059
1060
|
}, "strip", z.ZodTypeAny, {
|
|
1060
1061
|
mode: "ask" | "plan" | "auto" | "full";
|
|
1061
1062
|
agent: "CLAUDE" | "CODEX";
|
|
1062
|
-
id: string;
|
|
1063
1063
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
1064
|
+
id: string;
|
|
1064
1065
|
kind: "TICKET" | "CHAT";
|
|
1065
1066
|
model: string | null;
|
|
1066
1067
|
effort: string | null;
|
|
@@ -1073,8 +1074,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1073
1074
|
extraBudgetMinutes: number | null;
|
|
1074
1075
|
workspace: {
|
|
1075
1076
|
path: string;
|
|
1076
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1077
1077
|
id: string;
|
|
1078
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1078
1079
|
projectId: string;
|
|
1079
1080
|
budgetUsd: number | null;
|
|
1080
1081
|
budgetMinutes: number | null;
|
|
@@ -1107,15 +1108,15 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1107
1108
|
} | undefined;
|
|
1108
1109
|
}, {
|
|
1109
1110
|
agent: "CLAUDE" | "CODEX";
|
|
1110
|
-
id: string;
|
|
1111
1111
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
1112
|
+
id: string;
|
|
1112
1113
|
kind: "TICKET" | "CHAT";
|
|
1113
1114
|
prompt: string;
|
|
1114
1115
|
providerSessionId: string | null;
|
|
1115
1116
|
workspace: {
|
|
1116
1117
|
path: string;
|
|
1117
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1118
1118
|
id: string;
|
|
1119
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1119
1120
|
projectId: string;
|
|
1120
1121
|
budgetUsd: number | null;
|
|
1121
1122
|
budgetMinutes: number | null;
|
|
@@ -1154,8 +1155,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1154
1155
|
session: {
|
|
1155
1156
|
mode: "ask" | "plan" | "auto" | "full";
|
|
1156
1157
|
agent: "CLAUDE" | "CODEX";
|
|
1157
|
-
id: string;
|
|
1158
1158
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
1159
|
+
id: string;
|
|
1159
1160
|
kind: "TICKET" | "CHAT";
|
|
1160
1161
|
model: string | null;
|
|
1161
1162
|
effort: string | null;
|
|
@@ -1168,8 +1169,8 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1168
1169
|
extraBudgetMinutes: number | null;
|
|
1169
1170
|
workspace: {
|
|
1170
1171
|
path: string;
|
|
1171
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1172
1172
|
id: string;
|
|
1173
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1173
1174
|
projectId: string;
|
|
1174
1175
|
budgetUsd: number | null;
|
|
1175
1176
|
budgetMinutes: number | null;
|
|
@@ -1205,15 +1206,15 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1205
1206
|
}, {
|
|
1206
1207
|
session: {
|
|
1207
1208
|
agent: "CLAUDE" | "CODEX";
|
|
1208
|
-
id: string;
|
|
1209
1209
|
status: "RUNNING" | "FAILED" | "STARTING" | "WAITING_INPUT" | "WAITING_PERMISSION" | "REVIEW" | "DONE" | "STOPPED";
|
|
1210
|
+
id: string;
|
|
1210
1211
|
kind: "TICKET" | "CHAT";
|
|
1211
1212
|
prompt: string;
|
|
1212
1213
|
providerSessionId: string | null;
|
|
1213
1214
|
workspace: {
|
|
1214
1215
|
path: string;
|
|
1215
|
-
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1216
1216
|
id: string;
|
|
1217
|
+
trustMode: "STRICT" | "NORMAL" | "AUTO";
|
|
1217
1218
|
projectId: string;
|
|
1218
1219
|
budgetUsd: number | null;
|
|
1219
1220
|
budgetMinutes: number | null;
|
|
@@ -1673,7 +1674,22 @@ export type RunnerFrame = {
|
|
|
1673
1674
|
*/
|
|
1674
1675
|
| ({
|
|
1675
1676
|
type: 'host_load';
|
|
1676
|
-
} & HostLoadFrame)
|
|
1677
|
+
} & HostLoadFrame)
|
|
1678
|
+
/**
|
|
1679
|
+
* How this machine is dividing its memory between the sessions on it, right
|
|
1680
|
+
* now (#398 S4).
|
|
1681
|
+
*
|
|
1682
|
+
* Machine-scoped with a `sessions` array rather than one frame per session:
|
|
1683
|
+
* half of what has to be shown is about the MACHINE — what is free, what is
|
|
1684
|
+
* held by things that are not DevBridge, how many guarantees fit — and a
|
|
1685
|
+
* machine with no live sessions still has to be able to say it.
|
|
1686
|
+
*
|
|
1687
|
+
* Sent only when it moved, on the same tick that recomputes it. Fields mirror
|
|
1688
|
+
* `@devbridge/shared` — see `session-limits.ts`.
|
|
1689
|
+
*/
|
|
1690
|
+
| ({
|
|
1691
|
+
type: 'session_limits';
|
|
1692
|
+
} & SessionLimitsFrame) | {
|
|
1677
1693
|
type: 'pong';
|
|
1678
1694
|
};
|
|
1679
1695
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/recipe-schema.d.ts
CHANGED
|
@@ -134,17 +134,17 @@ export declare const ProjectRecipeSchema: z.ZodObject<{
|
|
|
134
134
|
env?: Record<string, string> | undefined;
|
|
135
135
|
timeoutSec?: number | undefined;
|
|
136
136
|
} | undefined;
|
|
137
|
-
|
|
137
|
+
test?: {
|
|
138
138
|
run: string;
|
|
139
139
|
env?: Record<string, string> | undefined;
|
|
140
140
|
timeoutSec?: number | undefined;
|
|
141
141
|
} | undefined;
|
|
142
|
-
|
|
142
|
+
verify?: {
|
|
143
143
|
run: string;
|
|
144
144
|
env?: Record<string, string> | undefined;
|
|
145
145
|
timeoutSec?: number | undefined;
|
|
146
146
|
} | undefined;
|
|
147
|
-
|
|
147
|
+
build?: {
|
|
148
148
|
run: string;
|
|
149
149
|
env?: Record<string, string> | undefined;
|
|
150
150
|
timeoutSec?: number | undefined;
|
|
@@ -160,17 +160,17 @@ export declare const ProjectRecipeSchema: z.ZodObject<{
|
|
|
160
160
|
env?: Record<string, string> | undefined;
|
|
161
161
|
timeoutSec?: number | undefined;
|
|
162
162
|
} | undefined;
|
|
163
|
-
|
|
163
|
+
test?: {
|
|
164
164
|
run: string;
|
|
165
165
|
env?: Record<string, string> | undefined;
|
|
166
166
|
timeoutSec?: number | undefined;
|
|
167
167
|
} | undefined;
|
|
168
|
-
|
|
168
|
+
verify?: {
|
|
169
169
|
run: string;
|
|
170
170
|
env?: Record<string, string> | undefined;
|
|
171
171
|
timeoutSec?: number | undefined;
|
|
172
172
|
} | undefined;
|
|
173
|
-
|
|
173
|
+
build?: {
|
|
174
174
|
run: string;
|
|
175
175
|
env?: Record<string, string> | undefined;
|
|
176
176
|
timeoutSec?: number | undefined;
|
|
@@ -219,17 +219,17 @@ export declare const ProjectRecipeSchema: z.ZodObject<{
|
|
|
219
219
|
env?: Record<string, string> | undefined;
|
|
220
220
|
timeoutSec?: number | undefined;
|
|
221
221
|
} | undefined;
|
|
222
|
-
|
|
222
|
+
test?: {
|
|
223
223
|
run: string;
|
|
224
224
|
env?: Record<string, string> | undefined;
|
|
225
225
|
timeoutSec?: number | undefined;
|
|
226
226
|
} | undefined;
|
|
227
|
-
|
|
227
|
+
verify?: {
|
|
228
228
|
run: string;
|
|
229
229
|
env?: Record<string, string> | undefined;
|
|
230
230
|
timeoutSec?: number | undefined;
|
|
231
231
|
} | undefined;
|
|
232
|
-
|
|
232
|
+
build?: {
|
|
233
233
|
run: string;
|
|
234
234
|
env?: Record<string, string> | undefined;
|
|
235
235
|
timeoutSec?: number | undefined;
|
|
@@ -268,17 +268,17 @@ export declare const ProjectRecipeSchema: z.ZodObject<{
|
|
|
268
268
|
env?: Record<string, string> | undefined;
|
|
269
269
|
timeoutSec?: number | undefined;
|
|
270
270
|
} | undefined;
|
|
271
|
-
|
|
271
|
+
test?: {
|
|
272
272
|
run: string;
|
|
273
273
|
env?: Record<string, string> | undefined;
|
|
274
274
|
timeoutSec?: number | undefined;
|
|
275
275
|
} | undefined;
|
|
276
|
-
|
|
276
|
+
verify?: {
|
|
277
277
|
run: string;
|
|
278
278
|
env?: Record<string, string> | undefined;
|
|
279
279
|
timeoutSec?: number | undefined;
|
|
280
280
|
} | undefined;
|
|
281
|
-
|
|
281
|
+
build?: {
|
|
282
282
|
run: string;
|
|
283
283
|
env?: Record<string, string> | undefined;
|
|
284
284
|
timeoutSec?: number | undefined;
|
package/dist/self-update.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
import { findClaudeCli, USE_BUNDLED_CLAUDE } from './agent-binary.js';
|
|
7
7
|
import { systemdUserEnv } from './environment.js';
|
|
8
|
+
import { cageAuthority, CageAuthorityError } from './cage-authority.js';
|
|
8
9
|
import { log } from './log.js';
|
|
9
10
|
import { stateDir } from './paths.js';
|
|
10
11
|
import { RUNNER_VERSION } from './version.js';
|
|
@@ -386,8 +387,28 @@ function systemctlEnv() {
|
|
|
386
387
|
return env;
|
|
387
388
|
}
|
|
388
389
|
export async function selfUpdate(options) {
|
|
390
|
+
/**
|
|
391
|
+
* The default executor installs software on somebody's machine and reloads
|
|
392
|
+
* their systemd, so it asks the same door the cages ask (#403).
|
|
393
|
+
*
|
|
394
|
+
* The check is on the DEFAULT and not on the injected one: a test hands its
|
|
395
|
+
* own executor and is testing the sequence of commands, which is exactly what
|
|
396
|
+
* it should be able to do. What must not happen is a call with no executor
|
|
397
|
+
* from a process that has no business updating anything — `npm install -g`
|
|
398
|
+
* from a vitest worker would be the incident of 10.09.2026 with a much longer
|
|
399
|
+
* tail.
|
|
400
|
+
*/
|
|
389
401
|
const exec = options.exec ??
|
|
390
|
-
((file, args, opts) =>
|
|
402
|
+
((file, args, opts) => {
|
|
403
|
+
if (cageAuthority() === 'none') {
|
|
404
|
+
throw new CageAuthorityError(`this process may not run «${file}»: updating this machine's runner is the daemon's job`);
|
|
405
|
+
}
|
|
406
|
+
return execFileAsync(file, args, {
|
|
407
|
+
timeout: opts.timeout,
|
|
408
|
+
env: opts.env,
|
|
409
|
+
maxBuffer: 4_000_000,
|
|
410
|
+
});
|
|
411
|
+
});
|
|
391
412
|
const fromVersion = RUNNER_VERSION;
|
|
392
413
|
const fail = (detail, extra = {}) => ({
|
|
393
414
|
ok: false,
|
package/dist/service-unit.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export declare function buildUnit(execStart?: string, nodeBinary?: string): stri
|
|
|
57
57
|
* which is the only way to fix the servers that already have the bad numbers
|
|
58
58
|
* baked in — and it never overwrites a unit the operator edited by hand.
|
|
59
59
|
*/
|
|
60
|
-
export declare const LIMITS_VERSION =
|
|
60
|
+
export declare const LIMITS_VERSION = 6;
|
|
61
61
|
/**
|
|
62
62
|
* Where the agent sessions live once they have a cage of their own.
|
|
63
63
|
*
|
|
@@ -239,6 +239,19 @@ export declare function managedUsageFloorBytes(facts: MemoryFacts): number;
|
|
|
239
239
|
* - cap 85 % of total: on an idle dedicated box `MemAvailable` is nearly the whole
|
|
240
240
|
* machine, and a ceiling of «everything» is the bug this function exists to fix.
|
|
241
241
|
*/
|
|
242
|
+
/**
|
|
243
|
+
* What the machine keeps for itself, whatever the agents are doing.
|
|
244
|
+
*
|
|
245
|
+
* Enough for sshd, journald, the kernel and some page cache to keep working
|
|
246
|
+
* while our cgroups sit at their ceiling. Proportional on a big machine,
|
|
247
|
+
* absolute on a small one, because 15 % of 4 GB is not enough to stay reachable.
|
|
248
|
+
*
|
|
249
|
+
* Hoisted out of {@link memoryPolicy} for #398 S3: the live allocator has to
|
|
250
|
+
* subtract exactly the same reserve when it decides how much of the machine is
|
|
251
|
+
* free right now, and a second copy of this number would drift from this one on
|
|
252
|
+
* the first machine where it mattered.
|
|
253
|
+
*/
|
|
254
|
+
export declare function machineReserveBytes(totalBytes: number): number;
|
|
242
255
|
export declare function memoryPolicy(facts: MemoryFacts, minCeilingBytes?: number): MemoryPolicy;
|
|
243
256
|
/** `SwapTotal` of this machine, or null where `/proc/meminfo` will not say. */
|
|
244
257
|
export declare function readSwapTotalBytes(): number | null;
|
|
@@ -418,7 +431,24 @@ export declare function buildLimitsOverride(cpuCount?: number, facts?: MemoryFac
|
|
|
418
431
|
* can measure one (the drift check treats a file with no
|
|
419
432
|
* `MemoryMax` as outdated).
|
|
420
433
|
*/
|
|
421
|
-
export declare function buildSessionsSliceOverride(facts?: MemoryFacts | null, sessionsUsageBytes?: number | null): string;
|
|
434
|
+
export declare function buildSessionsSliceOverride(facts?: MemoryFacts | null, sessionsUsageBytes?: number | null, maxSessions?: number): string;
|
|
435
|
+
/**
|
|
436
|
+
* Where the collective brake sits under the collective ceiling.
|
|
437
|
+
*
|
|
438
|
+
* Nine tenths: close enough that the sessions get almost the whole pot before
|
|
439
|
+
* anything is throttled, far enough that the kernel has a tenth of the pot in
|
|
440
|
+
* which to slow everyone down and reclaim, instead of arriving at the hard
|
|
441
|
+
* ceiling and choosing a victim.
|
|
442
|
+
*/
|
|
443
|
+
export declare const SLICE_BRAKE_OF_CEILING = 0.9;
|
|
444
|
+
/**
|
|
445
|
+
* Seats assumed when nobody has said otherwise.
|
|
446
|
+
*
|
|
447
|
+
* Three, which is what `DevServer.maxSessions` defaults to in the database. The
|
|
448
|
+
* runner's own config may say fewer; the slice's protection is sized for the
|
|
449
|
+
* seats the machine sells, not for the sessions that happen to be running.
|
|
450
|
+
*/
|
|
451
|
+
export declare const DEFAULT_MAX_SESSIONS = 3;
|
|
422
452
|
/**
|
|
423
453
|
* The CPU share of everything the agents run, against the daemon's own.
|
|
424
454
|
*
|
|
@@ -453,7 +483,9 @@ export declare function limitsOverrideIsOutdated(readFile?: (p: string) => strin
|
|
|
453
483
|
* arrive at the same number — otherwise a write whose floor was binding would be
|
|
454
484
|
* seen as drifted on the very next call and rewritten forever.
|
|
455
485
|
*/
|
|
456
|
-
export declare function writeLimitsOverride(force?: boolean, home?: string, facts?: MemoryFacts | null, sessionsUsageBytes?: number | null
|
|
486
|
+
export declare function writeLimitsOverride(force?: boolean, home?: string, facts?: MemoryFacts | null, sessionsUsageBytes?: number | null,
|
|
487
|
+
/** Seats this machine sells — sizes the slice's collective `MemoryLow` (#398 S3). */
|
|
488
|
+
maxSessions?: number): boolean;
|
|
457
489
|
/**
|
|
458
490
|
* Does the installed unit point at something that no longer exists?
|
|
459
491
|
*
|
package/dist/service-unit.js
CHANGED
|
@@ -129,7 +129,7 @@ export function buildUnit(execStart, nodeBinary = process.execPath) {
|
|
|
129
129
|
* which is the only way to fix the servers that already have the bad numbers
|
|
130
130
|
* baked in — and it never overwrites a unit the operator edited by hand.
|
|
131
131
|
*/
|
|
132
|
-
export const LIMITS_VERSION =
|
|
132
|
+
export const LIMITS_VERSION = 6;
|
|
133
133
|
const LIMITS_MARKER = '# devbridge-limits-version:';
|
|
134
134
|
/** `zz-` so it sorts last: an operator's own drop-in should still win. */
|
|
135
135
|
const LIMITS_FILE = 'zz-devbridge-limits.conf';
|
|
@@ -277,6 +277,21 @@ const CEILING_FLOOR_BYTES = 2 * GIB;
|
|
|
277
277
|
* - cap 85 % of total: on an idle dedicated box `MemAvailable` is nearly the whole
|
|
278
278
|
* machine, and a ceiling of «everything» is the bug this function exists to fix.
|
|
279
279
|
*/
|
|
280
|
+
/**
|
|
281
|
+
* What the machine keeps for itself, whatever the agents are doing.
|
|
282
|
+
*
|
|
283
|
+
* Enough for sshd, journald, the kernel and some page cache to keep working
|
|
284
|
+
* while our cgroups sit at their ceiling. Proportional on a big machine,
|
|
285
|
+
* absolute on a small one, because 15 % of 4 GB is not enough to stay reachable.
|
|
286
|
+
*
|
|
287
|
+
* Hoisted out of {@link memoryPolicy} for #398 S3: the live allocator has to
|
|
288
|
+
* subtract exactly the same reserve when it decides how much of the machine is
|
|
289
|
+
* free right now, and a second copy of this number would drift from this one on
|
|
290
|
+
* the first machine where it mattered.
|
|
291
|
+
*/
|
|
292
|
+
export function machineReserveBytes(totalBytes) {
|
|
293
|
+
return Math.max(1.5 * GIB, totalBytes * 0.15);
|
|
294
|
+
}
|
|
280
295
|
export function memoryPolicy(facts, minCeilingBytes = managedUsageFloorBytes(facts)) {
|
|
281
296
|
const { totalBytes, availableBytes, ownUsageBytes, sessionsUsageBytes, uptimeSec } = facts;
|
|
282
297
|
const swapMaxBytes = Math.floor(Math.max(0, facts.swapTotalBytes ?? 0) * SESSIONS_SWAP_SHARE);
|
|
@@ -307,7 +322,7 @@ export function memoryPolicy(facts, minCeilingBytes = managedUsageFloorBytes(fac
|
|
|
307
322
|
// Enough for sshd, journald, the kernel and some page cache to keep working
|
|
308
323
|
// while the cgroup sits at its ceiling. Proportional on a big machine, absolute
|
|
309
324
|
// on a small one, because 15 % of 4 GB is not enough to stay reachable.
|
|
310
|
-
const reserve =
|
|
325
|
+
const reserve = machineReserveBytes(totalBytes);
|
|
311
326
|
// Both cgroups are added back, and for the same reason ours always was: what
|
|
312
327
|
// they hold is already OUT of `MemAvailable`, so leaving the sessions out
|
|
313
328
|
// makes a loaded machine look starved and walks the ceiling down under the
|
|
@@ -682,7 +697,7 @@ const MANAGED_HEADER = [
|
|
|
682
697
|
* can measure one (the drift check treats a file with no
|
|
683
698
|
* `MemoryMax` as outdated).
|
|
684
699
|
*/
|
|
685
|
-
export function buildSessionsSliceOverride(facts = readMemoryFacts(), sessionsUsageBytes = readSessionsSliceUsageOrNull()) {
|
|
700
|
+
export function buildSessionsSliceOverride(facts = readMemoryFacts(), sessionsUsageBytes = readSessionsSliceUsageOrNull(), maxSessions = DEFAULT_MAX_SESSIONS) {
|
|
686
701
|
const memory = facts ? memoryPolicy(facts) : null;
|
|
687
702
|
return ([
|
|
688
703
|
...MANAGED_HEADER,
|
|
@@ -691,6 +706,41 @@ export function buildSessionsSliceOverride(facts = readMemoryFacts(), sessionsUs
|
|
|
691
706
|
? [
|
|
692
707
|
`# ${memory.measured ? 'measured headroom' : 'still booting — conservative fraction of total'}`,
|
|
693
708
|
`MemoryMax=${asMiB(memory.maxBytes)}`,
|
|
709
|
+
/**
|
|
710
|
+
* The COLLECTIVE brake, and the thing that makes #398 S3 safe.
|
|
711
|
+
*
|
|
712
|
+
* Since S3 each session's own brake is deliberately oversold — every
|
|
713
|
+
* session may grow into everything that is free — so the sum of the
|
|
714
|
+
* brakes is larger than this slice. That is only safe while THIS
|
|
715
|
+
* line exists: as the sessions together approach the ceiling the
|
|
716
|
+
* kernel slows all of them and reclaims first from whoever is above
|
|
717
|
+
* their `MemoryLow`, and the hard ceiling above, where the OOM
|
|
718
|
+
* killer picks a victim across the whole slice, is never reached.
|
|
719
|
+
*
|
|
720
|
+
* Measured on this host before it existed: the slice read
|
|
721
|
+
* `MemoryHigh=infinity`, overflowing it ended in a kill rather than
|
|
722
|
+
* throttling, and the one killed was the neighbour sitting under its
|
|
723
|
+
* own limit (`memory.events` of the victim: `high 0, max 0, oom 0,
|
|
724
|
+
* oom_kill 1`). The allocator reads this property back and falls
|
|
725
|
+
* back to a non-oversold formula when it is not in force.
|
|
726
|
+
*/
|
|
727
|
+
`MemoryHigh=${asMiB(Math.floor(memory.maxBytes * SLICE_BRAKE_OF_CEILING))}`,
|
|
728
|
+
/**
|
|
729
|
+
* …and the protection the guarantees are built on.
|
|
730
|
+
*
|
|
731
|
+
* `memory.low` of a child is bounded by its ancestors' protection:
|
|
732
|
+
* without this line a session's own `MemoryLow` protects nothing at
|
|
733
|
+
* all. Deliberately a CONSTANT (seats × one guarantee), not the sum
|
|
734
|
+
* of the guarantees of the sessions that happen to be live: the sum
|
|
735
|
+
* changes on every start and finish, the slice is never rewritten
|
|
736
|
+
* live (a blocker QA found on 07.09 — a rewritten slice kills the
|
|
737
|
+
* sessions under it), so a number that depended on the live roster
|
|
738
|
+
* would be true for the hour after this file is written and a lie
|
|
739
|
+
* for the rest of it. What separates one session from another lives
|
|
740
|
+
* on their own scopes; this line only gives them the right to be
|
|
741
|
+
* protected at all.
|
|
742
|
+
*/
|
|
743
|
+
`MemoryLow=${asMiB(Math.min(Math.max(1, maxSessions) * SESSION_GUARANTEE_BYTES, memory.maxBytes))}`,
|
|
694
744
|
]
|
|
695
745
|
: unmeasuredSliceCeiling(sessionsUsageBytes)),
|
|
696
746
|
// The line the cage is built on. See `session-cage.ts` and the note above.
|
|
@@ -701,6 +751,31 @@ export function buildSessionsSliceOverride(facts = readMemoryFacts(), sessionsUs
|
|
|
701
751
|
'CPUAccounting=yes',
|
|
702
752
|
].join('\n') + '\n');
|
|
703
753
|
}
|
|
754
|
+
/**
|
|
755
|
+
* Where the collective brake sits under the collective ceiling.
|
|
756
|
+
*
|
|
757
|
+
* Nine tenths: close enough that the sessions get almost the whole pot before
|
|
758
|
+
* anything is throttled, far enough that the kernel has a tenth of the pot in
|
|
759
|
+
* which to slow everyone down and reclaim, instead of arriving at the hard
|
|
760
|
+
* ceiling and choosing a victim.
|
|
761
|
+
*/
|
|
762
|
+
export const SLICE_BRAKE_OF_CEILING = 0.9;
|
|
763
|
+
/**
|
|
764
|
+
* The guarantee one session gets, in the one place the slice needs to know it.
|
|
765
|
+
*
|
|
766
|
+
* Copied rather than imported from `session-allocator.ts`: that module imports
|
|
767
|
+
* `session-cage.ts`, which imports this one, and the cycle would be real. The
|
|
768
|
+
* two are compared by a test (`service-unit.test.ts`) so they cannot drift.
|
|
769
|
+
*/
|
|
770
|
+
const SESSION_GUARANTEE_BYTES = 2048 * MIB;
|
|
771
|
+
/**
|
|
772
|
+
* Seats assumed when nobody has said otherwise.
|
|
773
|
+
*
|
|
774
|
+
* Three, which is what `DevServer.maxSessions` defaults to in the database. The
|
|
775
|
+
* runner's own config may say fewer; the slice's protection is sized for the
|
|
776
|
+
* seats the machine sells, not for the sessions that happen to be running.
|
|
777
|
+
*/
|
|
778
|
+
export const DEFAULT_MAX_SESSIONS = 3;
|
|
704
779
|
/**
|
|
705
780
|
* The `[Slice]` lines for a machine whose memory the policy could not measure.
|
|
706
781
|
* See {@link buildSessionsSliceOverride} for why each of the three answers is
|
|
@@ -853,7 +928,9 @@ function swapShareHasDrifted(contents, facts) {
|
|
|
853
928
|
* arrive at the same number — otherwise a write whose floor was binding would be
|
|
854
929
|
* seen as drifted on the very next call and rewritten forever.
|
|
855
930
|
*/
|
|
856
|
-
export function writeLimitsOverride(force = false, home = systemdUserHome(), facts = readMemoryFacts(), sessionsUsageBytes = readSessionsSliceUsageOrNull()
|
|
931
|
+
export function writeLimitsOverride(force = false, home = systemdUserHome(), facts = readMemoryFacts(), sessionsUsageBytes = readSessionsSliceUsageOrNull(),
|
|
932
|
+
/** Seats this machine sells — sizes the slice's collective `MemoryLow` (#398 S3). */
|
|
933
|
+
maxSessions = DEFAULT_MAX_SESSIONS) {
|
|
857
934
|
// Unknown usage can be a busy service whose systemd query timed out. Do not
|
|
858
935
|
// replace an existing drop-in with a guessed percentage, or remove a limit
|
|
859
936
|
// by rewriting the file without its MemoryMax line. Defer the whole policy.
|
|
@@ -866,7 +943,7 @@ export function writeLimitsOverride(force = false, home = systemdUserHome(), fac
|
|
|
866
943
|
// them is a machine whose sessions are capped but whose daemon is not
|
|
867
944
|
// prioritised — the regression described on `buildDevbridgeSliceOverride`.
|
|
868
945
|
write(limitsOverridePath(home), buildLimitsOverride(undefined, facts));
|
|
869
|
-
write(sessionsSliceOverridePath(home), buildSessionsSliceOverride(facts, sessionsUsageBytes));
|
|
946
|
+
write(sessionsSliceOverridePath(home), buildSessionsSliceOverride(facts, sessionsUsageBytes, maxSessions));
|
|
870
947
|
write(devbridgeSliceOverridePath(home), buildDevbridgeSliceOverride());
|
|
871
948
|
return true;
|
|
872
949
|
}
|