@edgehero/pi-dispatch 2.1.0 → 3.0.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 (71) hide show
  1. package/.env.example +41 -5
  2. package/README.md +11 -5
  3. package/deploy/docker-compose.yml +12 -0
  4. package/deploy/egress-proxy.conf +28 -3
  5. package/deploy/pi-dispatch-egress-proxy.container +8 -2
  6. package/package.json +8 -1
  7. package/src/allocation.mjs +731 -0
  8. package/src/backends.mjs +243 -0
  9. package/src/budget.mjs +40 -4
  10. package/src/cli.mjs +222 -11
  11. package/src/config.mjs +126 -5
  12. package/src/daemon-facts.mjs +3 -0
  13. package/src/deployment-venue.mjs +1 -0
  14. package/src/doctor.mjs +2261 -203
  15. package/src/dollar-budget.mjs +373 -0
  16. package/src/dollar-fingerprint.mjs +83 -0
  17. package/src/egress-cli.mjs +316 -0
  18. package/src/egress-proxy-state.mjs +35 -5
  19. package/src/egress.mjs +12 -0
  20. package/src/env-allowlist.mjs +107 -6
  21. package/src/env-file.mjs +194 -25
  22. package/src/envelope.mjs +413 -0
  23. package/src/exit-code.mjs +22 -0
  24. package/src/fleet-lease.mjs +85 -25
  25. package/src/get-token.mjs +16 -5
  26. package/src/git-dirty.mjs +67 -0
  27. package/src/github-app-setup.mjs +6 -3
  28. package/src/github-host.mjs +5 -3
  29. package/src/identity.mjs +2 -1
  30. package/src/image-preflight.mjs +98 -24
  31. package/src/image-ref.mjs +37 -0
  32. package/src/import-pi.mjs +4 -2
  33. package/src/index.mjs +407 -62
  34. package/src/init.mjs +18 -0
  35. package/src/job-id.mjs +26 -3
  36. package/src/live-probes.mjs +24 -9
  37. package/src/model-catalog.mjs +297 -0
  38. package/src/model-endpoints.mjs +649 -0
  39. package/src/model-ref.mjs +151 -0
  40. package/src/models-json.mjs +262 -0
  41. package/src/money.mjs +144 -0
  42. package/src/octokit-log.mjs +65 -0
  43. package/src/outbox-plan.mjs +218 -0
  44. package/src/outbox.mjs +29 -9
  45. package/src/output-cap.mjs +157 -0
  46. package/src/pause-windows.mjs +81 -2
  47. package/src/pi-model-loader.mjs +77 -0
  48. package/src/podman-stack.mjs +16 -3
  49. package/src/portfolio-snapshot.mjs +304 -0
  50. package/src/prepare-local.mjs +247 -12
  51. package/src/prepare.mjs +35 -3
  52. package/src/priorities.mjs +569 -0
  53. package/src/processor.mjs +599 -170
  54. package/src/project-id.mjs +17 -0
  55. package/src/projects.mjs +238 -0
  56. package/src/provider-steering.mjs +179 -65
  57. package/src/queue.mjs +111 -6
  58. package/src/reserved-env.mjs +30 -0
  59. package/src/run-container.mjs +59 -5
  60. package/src/run-history.mjs +379 -24
  61. package/src/run-mirror.mjs +30 -0
  62. package/src/runtime-settings.mjs +104 -9
  63. package/src/schedules.mjs +33 -1
  64. package/src/scoped-limits.mjs +447 -27
  65. package/src/service.mjs +15 -4
  66. package/src/session-store.mjs +131 -6
  67. package/src/start.mjs +528 -40
  68. package/src/triggers-file.mjs +65 -4
  69. package/src/triggers.mjs +135 -7
  70. package/src/up.mjs +308 -34
  71. package/src/valkey-endpoint.mjs +3 -2
package/.env.example CHANGED
@@ -35,6 +35,13 @@ ANTHROPIC_API_KEY=
35
35
  PI_PROVIDER=anthropic
36
36
  # A DATED model id is deterministic; a floating alias (e.g. claude-sonnet-4-5) can change cost.
37
37
  PI_MODEL=claude-sonnet-4-5-20250929
38
+ # optional: the only models a job may call, comma-separated provider/model (at most 16). A trigger's own
39
+ # run.models replaces this list for its jobs. The default model must be on it, or every job that uses the
40
+ # default is refused before it spends. Env only: the settings overlay cannot set or widen it.
41
+ # Unset = any model. Needs a job image that declares the modelPolicy capability.
42
+ # No spaces, not even after a comma: a shell that sources this file cuts the value at the first space
43
+ # and leaves it unset, so jobs would run with no list (the worker refuses a spaced value at boot).
44
+ # PI_ALLOWED_MODELS=anthropic/claude-sonnet-4-5-20250929,anthropic/claude-haiku-4-5-20251001
38
45
 
39
46
  # --- Spend + concurrency guards (money bounds; all have conservative defaults) ---
40
47
  # per-job turn cap -- pi has none of its own, so the harness imposes one
@@ -55,6 +62,21 @@ PI_DAILY_CAP=25
55
62
  # optional daily token cap: once a day's recorded spend reaches it, the NEXT job is refused pre-container.
56
63
  # Check-AFTER by nature (token cost is only known post-run), unlike PI_DAILY_CAP; unset = disabled
57
64
  # PI_DAILY_TOKEN_CAP=
65
+ # optional per-job DOLLAR cap (issue #501): the runner stops a job before any model call that could take its
66
+ # spend past it. A plain decimal, at most 6 decimals, above 0, at most 1000000 (e.g. 2.50). A trigger's run.maxCostUsd
67
+ # can only lower it for that trigger's jobs. Needs a job image that declares the `costCap` capability: an older image
68
+ # is refused for every capped job before anything is spent. Unset = no dollar cap (the token and job caps still apply)
69
+ # PI_MAX_COST_USD=
70
+ # optional dollar WINDOWS (issue #501): what all jobs may spend per UTC day, Monday week and month. Before a job
71
+ # starts, its per-job cap is reserved in every window set here; a job that does not fit is refused (dollar-cap) and
72
+ # spends nothing. After the run the reservation is replaced by the metered cost; when the cost is not fully known
73
+ # the window is charged at least the reservation, and the metered cost when that is higher. Each needs
74
+ # PI_MAX_COST_USD, the amount reserved per job. Same format as PI_MAX_COST_USD. Unset = off
75
+ # With an allocation envelope (PI_ENVELOPE_FILE below), its total caps its window too: the smaller number wins, and a
76
+ # job refused by the envelope's number or by its project's share is refused as allocation-cap, not dollar-cap
77
+ # PI_DAILY_COST_USD=
78
+ # PI_WEEKLY_COST_USD=
79
+ # PI_MONTHLY_COST_USD=
58
80
  # how many jobs run in parallel
59
81
  PI_CONCURRENCY=3
60
82
 
@@ -159,23 +181,33 @@ PI_JOB_IMAGE=pi-job:latest
159
181
  # Unset = cron disabled for the worker; the receiver falls back to ./triggers.json in the folder it starts from (what `pi-dispatch init` scaffolds)
160
182
  # and refuses to start when neither exists (it holds the label/comment/pull_request trigger config)
161
183
  # PI_TRIGGERS_FILE=
162
- # The two keys below are the only ones where uncommenting WITHOUT filling in a path is worse than leaving
163
- # them alone: the worker keeps an empty value and refuses to start, rather than treating it as unset.
164
- # `pi-dispatch up` fills them in for you; by hand, write the path or leave the line commented.
184
+ # The keys below are the ones where uncommenting WITHOUT filling in a path is worse than leaving them alone:
185
+ # the worker keeps an empty value and refuses to start, rather than treating it as unset.
186
+ # `pi-dispatch up` fills in the first three for you; by hand, write the path or leave the line commented.
165
187
  # ABSOLUTE path to pause-windows.json — "quiet hours" per folder/repo (pause runs between certain times/days/dates, auto-resume). Unset = feature off. See docs/pause-windows.md
166
188
  # `pi-dispatch up` sets this to the pause-windows.json in the folder it runs in, which is the one `init` scaffolds and the one the panel defaults to, so all three agree
167
189
  # PI_PAUSE_WINDOWS_FILE=
168
- # ABSOLUTE path to scoped-limits.json — per repo/folder job-count caps (day/week/month, refused pre-spend as scope-cap) and max concurrent jobs per scope (excess deferred, never dropped).
190
+ # ABSOLUTE path to scoped-limits.json: per repo/folder job-count caps (day/week/month, refused pre-spend as scope-cap) and max concurrent jobs per scope (excess deferred, never dropped); project:<id> rows cap a whole project (projects.json), refused as project-cap.
169
191
  # Unset = no scoped caps or concurrency; the one-job-per-folder mutex for local jobs is always on and needs no file. See docs/scoped-limits.md
192
+ # Version 2 of the file adds dollar windows (dayUsd/weekUsd/monthUsd) per repo or folder, and model:<provider>/<model> rows; each needs a per-job cap (PI_MAX_COST_USD or run.maxCostUsd)
170
193
  # `pi-dispatch up` sets this to the scoped-limits.json in the folder it runs in, the same way and for the same reason as PI_PAUSE_WINDOWS_FILE above
171
194
  # PI_SCOPED_LIMITS_FILE=
195
+ # ABSOLUTE path to projects.json: named groups of repos and folders (members like github:owner/name or an absolute folder). Each run records its project's id.
196
+ # Unset = no projects, and every run records no project. See docs/projects.md
197
+ # `pi-dispatch up` sets this to the projects.json in the folder it runs in, the same way and for the same reason as PI_PAUSE_WINDOWS_FILE above
198
+ # PI_PROJECTS_FILE=
199
+ # ABSOLUTE path to envelope.json: the allocation envelope. A dollar total for one window (day, week or month), a floor per project of projects.json, default weights and the delegation rules.
200
+ # Inside it a priorities plan splits the dollar budget between projects with no keypress; jobs over their project's share are refused pre-spend as allocation-cap. Unset = no envelope and no delegation; once a split has been applied, also run `valkey-cli DEL alloc:plan alloc:envelope:expected`.
201
+ # Needs PI_MAX_COST_USD. Like the keys above, an EMPTY value refuses the start; unlike them, `pi-dispatch up` and `init` never set it, so delegation stays off until you turn it on.
202
+ # The path must be canonical (no symlink, "." or "..") and outside every job path (cron run.folder, PI_DISPATCH_RUN_ROOTS, run.skillsDir, PI_GLOBAL_PI_DIR). Every host needs the same file, or hosts refuse with envelope-mismatch (a host without it too, while the fleet has a split). See docs/allocation.md
203
+ # PI_ENVELOPE_FILE=
172
204
  # path to subscriptions.json — operator-declared subscription plan prices (the admin defaults to ./subscriptions.json in its working directory). Read by the ADMIN EXTENSION only, never at job time.
173
205
  # Subscription-backed providers bill 0 per run (their rate tables are all zeros), so this file is where the real price lives — cost analytics only; it changes no routing, auth, or job behavior
174
206
  # PI_SUBSCRIPTIONS_FILE=
175
207
  # ABSOLUTE path to the runtime settings overlay (default: ~/.pi-dispatch/settings.json); edited by the admin extension, read by the worker per job
176
208
  # DURABLE by default, beside the run history. Losing it does not stop jobs, it WIDENS them: a missing file reads as an empty overlay and every panel-set cap falls back to this file's values
177
209
  # Same per-user caveat as PI_LOGS_DIR, and the same fix: `pi-dispatch up` writes the resolved account default here explicitly, so the worker and the panel cannot drift apart silently
178
- # The setup wizard's deployment pointer does NOT carry this key (it carries the triggers, pause-windows, scoped-limits and subscriptions paths), so an explicit line here is what keeps the two in step
210
+ # The setup wizard's deployment pointer does NOT carry this key (it carries the triggers, pause-windows, scoped-limits, projects and subscriptions paths), so an explicit line here is what keeps the two in step
179
211
  # PI_SETTINGS_FILE=
180
212
  # ABSOLUTE path to the deployment pointer the /dispatch panel reads to find a deployment built elsewhere
181
213
  # (default: <your pi agent dir>/pi-dispatch-deployment.json). Read by the ADMIN EXTENSION only; the worker and receiver never look at it.
@@ -291,6 +323,10 @@ PI_SCHEDULER_STALL_MAX=2
291
323
  # the proxy container the per-job network is built around (default: pi-dispatch-egress-proxy)
292
324
  # While the policy is armed, PI_FORWARD_ENV must not carry HTTPS_PROXY/HTTP_PROXY/NO_PROXY/NODE_USE_ENV_PROXY: the policy sets them itself, and a forwarded value would redirect every job while looking like the control working.
293
325
  # PI_EGRESS_PROXY=
326
+ # path to model-endpoints.json: the local or LAN model servers (Ollama, vLLM, llama.cpp, LM Studio) a job may reach through the proxy,
327
+ # each by one host and one port, with its number of parallel slots. Default: model-endpoints.json in the folder the worker starts from,
328
+ # which `pi-dispatch init` writes empty. A missing default file declares no endpoints; a path set here that does not exist, or an empty value, is refused
329
+ # PI_MODEL_ENDPOINTS_FILE=
294
330
 
295
331
  # OS-path-delimited allowlist (; on Windows, : elsewhere) of folders the model-callable dispatch_run may target; default empty = fail-closed (dispatch_run refuses every folder until you opt in)
296
332
  # PI_DISPATCH_RUN_ROOTS=
package/README.md CHANGED
@@ -4,10 +4,13 @@ The worker and the `pi-dispatch` command line tool of
4
4
  **[pi-dispatch](https://github.com/edgehero/pi-dispatch)**, which runs the
5
5
  [pi](https://github.com/earendil-works/pi) coding agent as a self hosted background service.
6
6
 
7
- The worker takes jobs from a durable queue (Valkey with BullMQ). It checks the spend caps before
8
- anything is spent, starts one locked down container per job (Docker or Podman), gives the agent the
9
- job, records what it did and what it cost, and removes the container. Jobs come from the command line,
10
- from cron triggers, or from forge events that the
7
+ The worker takes jobs from a durable queue (Valkey with BullMQ). Before anything is spent it checks every
8
+ cap that applies: job counts and a daily token count for the whole deployment, job counts per repo, folder
9
+ or project, and dollars per day, week or month for the deployment and per repo, folder, project or model,
10
+ plus each project's share of the budget split. It then starts one locked down container per job (Docker or
11
+ Podman), holds the job to its own dollar cap when one is set, gives the agent the job, records what it did
12
+ and what it cost, and removes the container. Jobs come from the command line, from cron triggers, or from
13
+ forge events that the
11
14
  [receiver](https://www.npmjs.com/package/@edgehero/pi-dispatch-receiver) queues.
12
15
 
13
16
  ## Start
@@ -25,7 +28,8 @@ npx @edgehero/pi-dispatch run ./my-project --task "add type hints" --flow tidy
25
28
  ```
26
29
 
27
30
  A local job edits your folder **in place**, and there is no undo, so commit first. The worker refuses a
28
- folder with uncommitted changes unless you pass `--force`.
31
+ folder with uncommitted changes unless you pass `--force`. The folder must be the root of a git repository
32
+ with at least one commit, and `run` refuses any other folder before it queues anything.
29
33
 
30
34
  ## Commands
31
35
 
@@ -36,11 +40,13 @@ folder with uncommitted changes unless you pass `--force`.
36
40
  | `doctor [--fix] [--live]` | check the whole setup; `--live` reads the isolation guarantees back from real containers |
37
41
  | `worker` | run jobs from the queue |
38
42
  | `run <folder> --task "..."` | queue one job against a local folder |
43
+ | `run --trigger <id>` | fire one cron trigger from the triggers file now, once, as its schedule would |
39
44
  | `status`, `pause`, `resume`, `cancel <jobId>` | steer the running worker from any terminal |
40
45
  | `service render\|install\|status\|restart` | run the worker (or `--receiver`) as a service (user level on macOS and Linux, nssm on Windows) |
41
46
  | `setup github` | create a GitHub App in one browser click |
42
47
  | `import-pi` | stage your own pi setup (models, skills, persona) for every job, without credentials |
43
48
  | `sandbox <jobId>` | reopen a finished run's workspace in a fresh container with no credentials |
49
+ | `egress render` | write `model-endpoints.conf` from `model-endpoints.json`, then print the command that reloads the egress proxy |
44
50
 
45
51
  Before you rely on it, read [`SECURITY.md`](https://github.com/edgehero/pi-dispatch/blob/main/SECURITY.md).
46
52
  In short: the agent can read the provider key and the forge token inside its container, and the
@@ -134,6 +134,18 @@ services:
134
134
  # never clobbers an edited allowlist. Mounting a path that does not exist makes Docker create it as a
135
135
  # DIRECTORY and squid then fails confusingly, which is why init writes it first.
136
136
  - ../egress-allowlist.conf:/etc/pi-dispatch/allowlist.conf:ro,z
137
+ # The declared model endpoints' rules (issue #503), rendered from model-endpoints.json by
138
+ # `pi-dispatch egress render` and read by the rules' `include`. `init` scaffolds it empty. It must exist:
139
+ # squid refuses to start when an included file is missing. After a render, reload with
140
+ # `docker exec pi-dispatch-egress-proxy squid -k reconfigure`, not a restart.
141
+ - ../model-endpoints.conf:/etc/pi-dispatch/model-endpoints.conf:ro,z
142
+ # How the proxy reaches a model server on this host (issue #503). On Docker Engine (Linux) this name does
143
+ # not exist without the line, and host-gateway is the bridge gateway (docker0, 172.17.0.1). Docker
144
+ # Desktop already resolves the name, and host-gateway there is the same host address, so the line is kept
145
+ # for both. It is on the PROXY only: a job never gets it, and reaches the host only through a declared
146
+ # endpoint's CONNECT rule.
147
+ extra_hosts:
148
+ - "host.docker.internal:host-gateway"
137
149
  networks:
138
150
  - egress-out
139
151
  restart: unless-stopped
@@ -6,9 +6,12 @@
6
6
  # misordered one silently allows everything, so the file an operator edits contains no ordering at all.
7
7
  #
8
8
  # What this policy is, and is not:
9
- # - Hostname filtering on CONNECT, to port 443 only. The provider, the forge and the registry are
10
- # ordinary entries in the allowlist, and nothing is special. The one address-based rule is a DENY:
11
- # a listed name that resolves to a fixed loopback or link-local address of this host is refused.
9
+ # - Hostname filtering, and exactly two shapes pass (issue #508): a CONNECT tunnel to a listed host
10
+ # on port 443, or a plain (untunnelled) request to a listed host on port 80. Nothing else. The
11
+ # provider, the forge and the registry are ordinary entries in the allowlist, and nothing is
12
+ # special. The one address-based rule is a DENY: a listed name that resolves to a fixed loopback
13
+ # or link-local address of this host is refused. Beside those, a model server you declare (issue
14
+ # #503) is a CONNECT tunnel to its one host and port, from the include below, and nothing else.
12
15
  # - TLS is NEVER terminated. This proxy sees the name a client asks for and no byte inside the tunnel,
13
16
  # so it cannot read a credential and cannot account for a token. A proxy that decrypts provider
14
17
  # traffic is OQ-011's mechanism, a materially larger change, and it is not this.
@@ -29,6 +32,15 @@ http_port 3128
29
32
  acl allowed dstdomain -n "/etc/pi-dispatch/allowlist.conf"
30
33
 
31
34
  acl SSL_ports port 443
35
+ # Stock squid's name, narrowed to one port (issue #508). Without it `allow allowed` below let any
36
+ # tool that forwards plain HTTP (npm undici's EnvHttpProxyAgent from 8.7 on without proxyTunnel, a
37
+ # package manager, a curl -x a job brings) reach ANY port of a listed host, in clear text. What every
38
+ # job has never takes this path: pi and the runner tunnel every origin, Node 22's built-in fetch sends a
39
+ # CONNECT even for http://, and the job image's git ignores HTTP_PROXY for an http:// remote. A side
40
+ # effect: a plain forward `GET https://host/`, where this proxy would open the TLS itself, is refused
41
+ # too, since it names port 443 without a CONNECT. Measured on Docker and rootless Podman 4.9.3 and 5.8.1
42
+ # (docs/egress.md): port 8080 went from 200 to 403, port 80 stayed 200.
43
+ acl Safe_ports port 80
32
44
  acl CONNECT method CONNECT
33
45
 
34
46
  # The host's own services, by address (issue #428). On rootless Podman an account's containers.conf
@@ -55,9 +67,22 @@ acl to_host_local dst 127.0.0.0/8 0.0.0.0/32 169.254.0.0/16 10.0.2.2/32 ::1 ::/1
55
67
  # debug_options 78,3: a CONNECT to an unlisted name sent a DNS query, where without the rule it sent
56
68
  # none), which is a DNS channel out of an egress-armed job. With `allowed` first, only a listed name
57
69
  # is ever resolved here, and it is the only kind the allows below could let through anyway.
70
+ # The port rule for plain requests (issue #508) sits right before the allow it narrows: first match
71
+ # wins, so after it it would never apply. It names only port and method ACLs, which resolve nothing,
72
+ # so no unlisted name is resolved by it. It never matches a CONNECT, so the tunnel rules (including a
73
+ # CONNECT allow later placed between the first two rules, see #503) are untouched by it.
58
74
  http_access deny allowed to_host_local
75
+ # The declared model endpoints (issue #503): `model-endpoints.conf` in your deployment folder, rendered
76
+ # from model-endpoints.json by `pi-dispatch egress render`, empty by default. It only adds a CONNECT
77
+ # allow for each declared host and port pair, and a deny for such a host that resolves to loopback.
78
+ # It sits BEFORE the next line on purpose: pi tunnels every provider call, plain http:// included, so a
79
+ # call to a model server arrives as a CONNECT to its port, and first match wins, so the port-443 deny
80
+ # below would refuse it before any later allow was read. The file must exist (squid will not start
81
+ # without it). After a render, reload with `squid -k reconfigure` in this container, not a restart.
82
+ include /etc/pi-dispatch/model-endpoints.conf
59
83
  http_access deny CONNECT !SSL_ports
60
84
  http_access allow CONNECT allowed
85
+ http_access deny !Safe_ports !CONNECT
61
86
  http_access allow allowed
62
87
  http_access deny all
63
88
 
@@ -3,10 +3,10 @@
3
3
  # this account's own rootless Podman.
4
4
  #
5
5
  # Installed by `pi-dispatch service install` (user scope, with podman in PI_BACKENDS and PI_EGRESS armed) and by
6
- # `pi-dispatch up`. Unlike the Valkey unit this one is RENDERED: the two /opt/pi-dispatch paths below are
6
+ # `pi-dispatch up`. Unlike the Valkey unit this one is RENDERED: the three /opt/pi-dispatch paths below are
7
7
  # placeholders, replaced with an account-owned copy of the package's egress-proxy.conf (written beside this account's
8
8
  # config, because `z` cannot relabel a root-owned package file: measured, exit 126) and with the deployment folder's
9
- # egress-allowlist.conf. Never `systemctl --user enable` it (a generated unit refuses that); the [Install] section is
9
+ # egress-allowlist.conf and model-endpoints.conf. Never `systemctl --user enable` it (a generated unit refuses that); the [Install] section is
10
10
  # read by the generator itself.
11
11
  #
12
12
  # A RESTART DROPS EVERY PER-JOB NETWORK (measured on Podman 5.8.1). The generated unit runs `podman run --replace
@@ -34,6 +34,12 @@ Network=pi-dispatch-egress-out.network
34
34
  # on a host that also runs the compose proxy the same allowlist file is mounted by both.
35
35
  Volume=/opt/pi-dispatch/deploy/egress-proxy.conf:/etc/squid/squid.conf:ro,z
36
36
  Volume=/opt/pi-dispatch/egress-allowlist.conf:/etc/pi-dispatch/allowlist.conf:ro,z
37
+ # The declared model endpoints' rules (issue #503), the deployment folder's own file like the list, never a copy:
38
+ # `pi-dispatch egress render` rewrites it in place, and a copy would not be the file the render wrote. `init`
39
+ # scaffolds it empty. It must exist, since squid will not start when an included file is missing. After a render,
40
+ # `podman exec pi-dispatch-egress-proxy squid -k reconfigure` reloads it; a restart is not needed and drops every job
41
+ # network (above). No host-gateway line: rootless Podman resolves host.containers.internal with no flag.
42
+ Volume=/opt/pi-dispatch/model-endpoints.conf:/etc/pi-dispatch/model-endpoints.conf:ro,z
37
43
  # The exec form, a JSON array, for the reason the compose file gives: a plain string runs under /bin/sh, which on
38
44
  # this image is dash, where /dev/tcp does not exist and the check would fail forever while squid serves.
39
45
  HealthCmd=["bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/3128"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgehero/pi-dispatch",
3
- "version": "2.1.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "description": "The pi-dispatch worker and CLI: runs the pi coding agent as a self-hosted service, one locked down Docker or Podman container per job, with spend caps checked before anything is spent, plus init, up, doctor and service install.",
6
6
  "keywords": [
@@ -70,6 +70,10 @@
70
70
  "./packages": "./src/packages.mjs",
71
71
  "./pause-windows": "./src/pause-windows.mjs",
72
72
  "./scoped-limits": "./src/scoped-limits.mjs",
73
+ "./projects": "./src/projects.mjs",
74
+ "./priorities": "./src/priorities.mjs",
75
+ "./envelope": "./src/envelope.mjs",
76
+ "./allocation": "./src/allocation.mjs",
73
77
  "./wait-for": "./src/wait-for.mjs",
74
78
  "./wait-state": "./src/wait-state.mjs",
75
79
  "./cancel-state": "./src/cancel-state.mjs",
@@ -79,6 +83,7 @@
79
83
  "./forgejo-identity": "./src/forgejo-identity.mjs",
80
84
  "./azure-identity": "./src/azure-identity.mjs",
81
85
  "./get-token": "./src/get-token.mjs",
86
+ "./octokit-log": "./src/octokit-log.mjs",
82
87
  "./runtime-settings": "./src/runtime-settings.mjs",
83
88
  "./run-history": "./src/run-history.mjs",
84
89
  "./run-mirror": "./src/run-mirror.mjs",
@@ -87,6 +92,8 @@
87
92
  "./subscriptions": "./src/subscriptions.mjs",
88
93
  "./budget": "./src/budget.mjs",
89
94
  "./pricing": "./src/pricing.mjs",
95
+ "./money": "./src/money.mjs",
96
+ "./dollar-budget": "./src/dollar-budget.mjs",
90
97
  "./scheduler-stall-guard": "./src/scheduler-stall-guard.mjs",
91
98
  "./watch-closer": "./src/watch-closer.mjs"
92
99
  },