@cat-factory/local-server 0.15.0 → 0.17.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.
Files changed (39) hide show
  1. package/dist/LocalContainerRunnerTransport.d.ts +97 -9
  2. package/dist/LocalContainerRunnerTransport.d.ts.map +1 -1
  3. package/dist/LocalContainerRunnerTransport.js +355 -93
  4. package/dist/LocalContainerRunnerTransport.js.map +1 -1
  5. package/dist/LocalProcessRunnerTransport.d.ts +63 -0
  6. package/dist/LocalProcessRunnerTransport.d.ts.map +1 -0
  7. package/dist/LocalProcessRunnerTransport.js +188 -0
  8. package/dist/LocalProcessRunnerTransport.js.map +1 -0
  9. package/dist/NativeRoutingRunnerTransport.d.ts +20 -0
  10. package/dist/NativeRoutingRunnerTransport.d.ts.map +1 -0
  11. package/dist/NativeRoutingRunnerTransport.js +50 -0
  12. package/dist/NativeRoutingRunnerTransport.js.map +1 -0
  13. package/dist/container.d.ts +13 -0
  14. package/dist/container.d.ts.map +1 -1
  15. package/dist/container.js +165 -22
  16. package/dist/container.js.map +1 -1
  17. package/dist/harnessHttp.d.ts +58 -0
  18. package/dist/harnessHttp.d.ts.map +1 -0
  19. package/dist/harnessHttp.js +95 -0
  20. package/dist/harnessHttp.js.map +1 -0
  21. package/dist/runtimes/appleContainerRuntime.d.ts +3 -0
  22. package/dist/runtimes/appleContainerRuntime.d.ts.map +1 -1
  23. package/dist/runtimes/appleContainerRuntime.js +8 -1
  24. package/dist/runtimes/appleContainerRuntime.js.map +1 -1
  25. package/dist/runtimes/containerRuntime.d.ts +30 -1
  26. package/dist/runtimes/containerRuntime.d.ts.map +1 -1
  27. package/dist/runtimes/containerRuntime.js +5 -0
  28. package/dist/runtimes/containerRuntime.js.map +1 -1
  29. package/dist/runtimes/dockerRuntime.d.ts +4 -0
  30. package/dist/runtimes/dockerRuntime.d.ts.map +1 -1
  31. package/dist/runtimes/dockerRuntime.js +21 -2
  32. package/dist/runtimes/dockerRuntime.js.map +1 -1
  33. package/dist/runtimes/index.d.ts.map +1 -1
  34. package/dist/runtimes/index.js +1 -0
  35. package/dist/runtimes/index.js.map +1 -1
  36. package/dist/server.d.ts.map +1 -1
  37. package/dist/server.js +4 -14
  38. package/dist/server.js.map +1 -1
  39. package/package.json +9 -8
@@ -1,4 +1,5 @@
1
1
  import type { RunnerDispatchKind, RunnerDispatchOptions, RunnerJobRef, RunnerJobView, RunnerTransport } from '@cat-factory/kernel';
2
+ import type { LocalSettings } from '@cat-factory/contracts';
2
3
  import { type ContainerExec, type ContainerRuntimeAdapter } from './runtimes/index.js';
3
4
  /** Injectable CLI runner — overridable in tests. Re-exported for callers/tests. */
4
5
  export type { ContainerExec } from './runtimes/index.js';
@@ -36,48 +37,135 @@ export interface LocalContainerRunnerTransportOptions {
36
37
  * best-effort rootless daemon (e.g. under rootless Podman).
37
38
  */
38
39
  privilegedTestJobs?: boolean;
40
+ /**
41
+ * Warm-pool size: the max number of idle harness containers kept ready for re-lease.
42
+ * `0` (default) disables pooling entirely — every run cold-starts its own container and
43
+ * is torn down on release, the classic behaviour. Pooling additionally requires a
44
+ * runtime whose `capabilities.pooling` is true (the Docker family); it is ignored on
45
+ * Apple `container`.
46
+ */
47
+ poolSize?: number;
48
+ /** Members to pre-warm at boot (clamped to `poolMax`). Default 0. */
49
+ poolMinWarm?: number;
50
+ /**
51
+ * Hard cap on total members (leased + idle). A lease beyond this starts a TRANSIENT
52
+ * member that is removed on release rather than returned to the pool. Default = poolSize.
53
+ */
54
+ poolMax?: number;
55
+ /** How long an idle pooled member is kept before eviction. Default 10 min. */
56
+ poolIdleTtlMs?: number;
39
57
  }
40
58
  export declare class LocalContainerRunnerTransport implements RunnerTransport {
41
59
  private readonly adapter;
42
60
  private readonly image;
43
61
  private readonly sharedSecret;
44
62
  private readonly network?;
45
- private readonly extraEnv;
63
+ private extraEnv;
46
64
  private readonly exec;
47
65
  private readonly fetchImpl;
48
66
  private readonly readyTimeoutMs;
49
67
  private readonly requestTimeoutMs;
50
68
  private readonly privilegedTestJobs;
69
+ private poolSize;
70
+ private poolMax;
71
+ private poolMinWarm;
72
+ private poolIdleTtlMs;
51
73
  /** runId → resolved container handle, to spare a CLI lookup on the hot poll path. */
52
74
  private readonly cache;
75
+ /** Warm-pool members (only used when pooling is enabled). Leased in-process by run id. */
76
+ private readonly members;
77
+ /**
78
+ * Members whose container start is in flight (started but not yet pushed to `members`).
79
+ * Counted toward the cap so concurrent cold-starts can't all read a low `members.length`
80
+ * and overshoot `poolMax`.
81
+ */
82
+ private pendingStarts;
53
83
  constructor(options: LocalContainerRunnerTransportOptions);
84
+ /**
85
+ * Set the warm-pool sizing fields from (defaulting) options. Shared by the constructor
86
+ * and {@link applySettings} so the clamps live in one place.
87
+ */
88
+ private configurePool;
89
+ /**
90
+ * Re-read the DB-backed local-mode settings into the ALREADY-BUILT transport so an edit
91
+ * in the settings panel takes effect WITHOUT a restart (the serving transport is built
92
+ * once and cached). Warm-pool sizing is applied live — `trimIdle` reaps idle members
93
+ * beyond the new `poolSize` and (when pooling is enabled) `prewarmPool` tops back up to
94
+ * `minWarm`; the checkout env applies to containers STARTED after this call. In-flight
95
+ * runs are never stranded: poll/release/dispatch route a run to the backend it ALREADY
96
+ * holds (a leased member or a per-run container), independent of the current pool mode,
97
+ * so even toggling pooling on/off mid-flight is safe.
98
+ */
99
+ applySettings(settings?: LocalSettings): void;
100
+ /** Bring the warm set in line with the current sizing: trim excess idle, then re-warm. */
101
+ private reconcilePool;
54
102
  /** The runtime's capabilities (e.g. whether local Docker-in-Docker testing is possible). */
55
103
  get capabilities(): import("./runtimes/containerRuntime.js").RuntimeCapabilities;
104
+ /** Whether the warm pool is active (a size is configured AND the runtime supports it). */
105
+ private get poolingEnabled();
106
+ /** Whether a pool member is currently leased to this run (in-process lease state). */
107
+ private hasLeasedMember;
56
108
  dispatch(ref: RunnerJobRef, spec: Record<string, unknown>, kind?: RunnerDispatchKind, options?: RunnerDispatchOptions): Promise<void>;
109
+ private dispatchPerRun;
57
110
  poll(ref: RunnerJobRef): Promise<RunnerJobView>;
58
111
  /**
59
112
  * Reclaim the per-RUN container now rather than leaving it idle — this tears down the
60
113
  * whole run's container (and with it any step still running in it). Best-effort and
61
- * idempotent: removing an already-gone container is a no-op.
114
+ * idempotent: removing an already-gone container is a no-op. A run that holds a pool
115
+ * member instead RETURNS it to the pool (or removes a transient/over-capacity one).
62
116
  */
63
117
  release(ref: RunnerJobRef): Promise<void>;
64
118
  /**
65
119
  * Reap exited per-run containers this transport manages — orphans a crash or hard
66
120
  * kill left behind (release() never ran for them). Best-effort; returns the count
67
- * removed. Call once at boot, before any job is in flight.
121
+ * removed. Call once at boot, before any job is in flight. When pooling is enabled it
122
+ * ALSO drains pool members orphaned by a previous process (their in-process lease state
123
+ * died with it, so they can't be safely re-leased) and pre-warms the configured minimum.
68
124
  */
69
125
  reapExited(): Promise<number>;
70
- private url;
126
+ private dispatchPooled;
127
+ /** POST a job body to a harness, throwing on a non-OK response. */
128
+ private postJob;
129
+ private pollPooled;
130
+ private releasePooled;
131
+ /** Lease a member for `runId`, preferring repo affinity, starting one if needed. */
132
+ private acquireMember;
133
+ /** Start a fresh pool member container and wait for it to be healthy. */
134
+ private startMember;
135
+ /**
136
+ * Pre-warm IDLE members up to `poolMinWarm`. The deficit counts only currently-idle
137
+ * members (plus in-flight starts), NOT leased ones: counting leased members would
138
+ * under-warm the idle floor whenever this runs while runs are in flight (e.g. a live
139
+ * `applySettings`/`reconcilePool` mid-run), leaving the next runs to cold-start. The
140
+ * missing members are started CONCURRENTLY — one at a time would stack each container's
141
+ * full boot+health latency, delaying pool readiness by ~deficit×. Best-effort: a member
142
+ * that fails to start is skipped (the pool then fills the rest on demand).
143
+ */
144
+ private prewarmPool;
145
+ /**
146
+ * Remove every pool member the runtime reports from a PREVIOUS process — their
147
+ * in-process lease state died with that process, so they can't be safely re-leased.
148
+ * Best-effort and idempotent.
149
+ */
150
+ private drainPoolOrphans;
151
+ /** Remove idle members beyond `poolSize` (oldest first) so the warm set stays bounded. */
152
+ private trimIdle;
153
+ private scheduleIdleEviction;
154
+ private clearIdleEviction;
155
+ /** Drop a member from the in-process pool (does NOT remove the container). */
156
+ private dropMember;
71
157
  /** The container handle for a run from the cache, else rediscovered via the runtime. */
72
158
  private resolve;
73
159
  private waitForEndpoint;
74
160
  private waitForHealth;
75
161
  }
76
162
  /**
77
- * Build a {@link LocalContainerRunnerTransport} from the process environment. The image
78
- * ref (`LOCAL_HARNESS_IMAGE`) is required; the runtime adapter is selected by
79
- * `LOCAL_CONTAINER_RUNTIME` (docker | podman | orbstack | colima | apple); everything
80
- * else has sane local defaults.
163
+ * Build a {@link LocalContainerRunnerTransport} from the process environment plus the
164
+ * DB-stored local-mode {@link LocalSettings} (warm-pool sizing + per-repo checkout reuse —
165
+ * these REPLACED the old `LOCAL_POOL_*` / `HARNESS_*` env vars; edit them in the local-mode
166
+ * settings panel). The image ref (`LOCAL_HARNESS_IMAGE`) is required; the runtime adapter is
167
+ * selected by `LOCAL_CONTAINER_RUNTIME` (docker | podman | orbstack | colima | apple).
168
+ * `settings` omitted ⇒ pooling off + harness defaults (e.g. an early boot-reap call).
81
169
  */
82
- export declare function createLocalContainerTransportFromEnv(env: NodeJS.ProcessEnv): LocalContainerRunnerTransport;
170
+ export declare function createLocalContainerTransportFromEnv(env: NodeJS.ProcessEnv, settings?: LocalSettings): LocalContainerRunnerTransport;
83
171
  //# sourceMappingURL=LocalContainerRunnerTransport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocalContainerRunnerTransport.d.ts","sourceRoot":"","sources":["../src/LocalContainerRunnerTransport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,uBAAuB,EAG7B,MAAM,qBAAqB,CAAA;AA4B5B,mFAAmF;AACnF,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,WAAW,oCAAoC;IACnD,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,uBAAuB,CAAA;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,mFAAmF;IACnF,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAID,qBAAa,6BAA8B,YAAW,eAAe;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAE5C,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiE;IAEvF,YAAY,OAAO,EAAE,oCAAoC,EAqBxD;IAED,4FAA4F;IAC5F,IAAI,YAAY,iEAEf;IAEK,QAAQ,CACZ,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAA4B,EAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAgDf;IAEK,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAgCpD;IAED;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9C;IAED;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAElC;IAID,OAAO,CAAC,GAAG;IAIX,wFAAwF;YAC1E,OAAO;YAcP,eAAe;YAYf,aAAa;CAoB5B;AAUD;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAClD,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,6BAA6B,CAkB/B"}
1
+ {"version":3,"file":"LocalContainerRunnerTransport.d.ts","sourceRoot":"","sources":["../src/LocalContainerRunnerTransport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAU3D,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,uBAAuB,EAG7B,MAAM,qBAAqB,CAAA;AA8B5B,mFAAmF;AACnF,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,WAAW,oCAAoC;IACnD,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,uBAAuB,CAAA;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,mFAAmF;IACnF,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAiBD,qBAAa,6BAA8B,YAAW,eAAe;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAQ;IAGjC,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,aAAa,CAAQ;IAE7B,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiE;IAEvF,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAAI;IAEzB,YAAY,OAAO,EAAE,oCAAoC,EA2BxD;IAED;;;OAGG;IACH,OAAO,CAAC,aAAa;IAgBrB;;;;;;;;;OASG;IACH,aAAa,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,IAAI,CAS5C;IAED,0FAA0F;YAC5E,aAAa;IAK3B,4FAA4F;IAC5F,IAAI,YAAY,iEAEf;IAED,0FAA0F;IAC1F,OAAO,KAAK,cAAc,GAEzB;IAED,sFAAsF;IACtF,OAAO,CAAC,eAAe;IAIjB,QAAQ,CACZ,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAA4B,EAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CASf;YAEa,cAAc;IA6CtB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAuBpD;IAED;;;;;OAKG;IACG,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ9C;IAED;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAOlC;YAIa,cAAc;IAe5B,mEAAmE;IACnE,OAAO,CAAC,OAAO;YAWD,UAAU;YAsBV,aAAa;IAe3B,oFAAoF;YACtE,aAAa;IAiC3B,yEAAyE;YAC3D,WAAW;IAqBzB;;;;;;;;OAQG;YACW,WAAW;IAezB;;;;OAIG;YACW,gBAAgB;IAO9B,0FAA0F;YAC5E,QAAQ;IAWtB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,iBAAiB;IAOzB,8EAA8E;IAC9E,OAAO,CAAC,UAAU;IAQlB,wFAAwF;YAC1E,OAAO;YAcP,eAAe;IAY7B,OAAO,CAAC,aAAa;CAStB;AA0BD;;;;;;;GAOG;AACH,wBAAgB,oCAAoC,CAClD,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,QAAQ,CAAC,EAAE,aAAa,GACvB,6BAA6B,CA+B/B"}