@jc_stack/ez-agents 0.1.0-beta.33 → 0.1.0-beta.36
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/.dockerignore +1 -0
- package/.env.example +9 -16
- package/AGENTS.md +17 -9
- package/CHANGELOG.md +92 -0
- package/Dockerfile +11 -9
- package/README.md +32 -27
- package/THIRD_PARTY_NOTICES.md +4 -4
- package/bin/ez +97 -0
- package/bin/ezenciel-agents-plugin-broker.mjs +16 -0
- package/bin/ezenciel-agents-provision-telegram.mjs +13 -0
- package/bin/ezenciel-agents.mjs +1 -1
- package/compose.yaml +42 -5
- package/docker/entrypoint.sh +1 -1
- package/docker/healthcheck.mjs +12 -4
- package/docker/run.ts +5 -6
- package/docker/smoke.mjs +32 -7
- package/docs/application-channel.md +38 -10
- package/docs/architecture/ai-selection.md +3 -1
- package/docs/architecture/authority-boundaries.md +2 -2
- package/docs/channel-backend.md +4 -3
- package/docs/docker-runtime.md +106 -21
- package/docs/host-service.md +17 -6
- package/docs/local-qa.md +2 -2
- package/docs/plugin-guide.md +202 -0
- package/docs/plugins.md +63 -2
- package/docs/releasing.md +13 -0
- package/docs/repair.md +1 -1
- package/docs/responsive-channels.md +1 -1
- package/docs/scheduling.md +4 -4
- package/docs/setup.md +96 -16
- package/docs/standalone-cli.md +6 -5
- package/docs/upgrades.md +46 -23
- package/package.json +5 -5
- package/scripts/smoke-scheduler.ts +12 -4
- package/scripts/smoke.ts +14 -11
- package/src/agent-guidance.ts +6 -9
- package/src/agent-install.ts +44 -9
- package/src/ai-cli.ts +9 -7
- package/src/ai.ts +7 -7
- package/src/application-channel.ts +88 -17
- package/src/application-cli.ts +2 -2
- package/src/approval-cli.ts +14 -7
- package/src/approval.ts +27 -37
- package/src/channel-backend.ts +2 -3
- package/src/codex-session.ts +5 -3
- package/src/config.ts +18 -28
- package/src/control-state.ts +187 -9
- package/src/conversation-menu.ts +6 -17
- package/src/delivery-socket-client.d.mts +4 -0
- package/src/delivery-socket-client.mjs +104 -0
- package/src/delivery-socket.ts +354 -0
- package/src/execution-authority.ts +21 -5
- package/src/executor.ts +110 -31
- package/src/files.ts +38 -11
- package/src/host-executor.ts +50 -17
- package/src/inbox.ts +15 -49
- package/src/index.ts +96 -52
- package/src/install-config.ts +2 -2
- package/src/install-tools.mjs +100 -23
- package/src/isolation.ts +25 -0
- package/src/ledger-endpoint.d.mts +2 -0
- package/src/ledger-endpoint.mjs +17 -0
- package/src/menu.ts +16 -8
- package/src/message-history.ts +13 -18
- package/src/message.ts +35 -18
- package/src/plugin-broker.mjs +396 -0
- package/src/plugins/connection.mjs +2 -2
- package/src/plugins/manager.mjs +178 -26
- package/src/plugins/native-tasks.mjs +2 -2
- package/src/prompt-suffix.ts +12 -0
- package/src/react.ts +6 -4
- package/src/runs.ts +156 -243
- package/src/schedule-cli.ts +29 -10
- package/src/scheduled-tasks.ts +53 -10
- package/src/scheduler.ts +20 -7
- package/src/service.ts +3 -3
- package/src/setup.ts +11 -7
- package/src/software-status.ts +36 -4
- package/src/task-executor.ts +6 -3
- package/src/task-workspace.ts +4 -3
- package/src/tasks.ts +1 -1
- package/src/telegram-provisioning-cli.ts +22 -0
- package/src/telegram-provisioning.ts +214 -0
- package/src/updates/control.mjs +77 -7
- package/src/updates/runtime.mjs +29 -9
- package/src/updates/status.mjs +25 -3
- package/src/updates/supervisor.mjs +31 -18
- package/src/version.ts +20 -0
- package/src/workspace-confine.ts +38 -0
- package/src/workspace.ts +24 -20
- package/templates/agent-purpose.md +1 -0
- package/templates/failure-review.md +1 -1
- package/templates/maintainer-purpose.md +1 -1
- package/test/agent-guidance.test.ts +18 -88
- package/test/agent-install.test.ts +48 -0
- package/test/ai-cli.test.ts +18 -0
- package/test/ai.test.ts +84 -0
- package/test/application-channel.test.ts +66 -15
- package/test/application-controls.test.ts +56 -3
- package/test/application-only.test.ts +16 -13
- package/test/channel-backend.test.ts +3 -7
- package/test/channel-delivery.test.ts +4 -1
- package/test/channel-owner.test.ts +6 -3
- package/test/codex-context.test.ts +63 -3
- package/test/codex-session.test.ts +6 -1
- package/test/config.test.ts +47 -37
- package/test/control-state.test.ts +83 -4
- package/test/conversation-menu.test.ts +2 -4
- package/test/delivery-transport.test.ts +72 -0
- package/test/docker-runtime.test.ts +27 -14
- package/test/engine-handoff.test.ts +12 -8
- package/test/event-sources.test.ts +2 -2
- package/test/execution-authority.test.ts +9 -9
- package/test/executor.test.ts +47 -11
- package/test/failure.test.ts +18 -5
- package/test/files.test.ts +2 -2
- package/test/helpers/ledger.ts +18 -0
- package/test/host-executor.test.ts +80 -0
- package/test/inbox.test.ts +8 -10
- package/test/install-config.test.ts +18 -6
- package/test/install-tools.test.mjs +80 -7
- package/test/intake-relay.test.ts +58 -11
- package/test/message-history.test.ts +39 -47
- package/test/native-tasks.test.ts +1 -1
- package/test/plugin-broker.test.mjs +172 -0
- package/test/plugin-manager.test.mjs +143 -5
- package/test/reaction.test.ts +4 -0
- package/test/relay.test.ts +5 -7
- package/test/repair-policy.test.ts +1 -4
- package/test/schedule-cli.test.ts +14 -9
- package/test/scheduled-tasks.test.ts +29 -6
- package/test/scheduler-host.test.ts +5 -5
- package/test/scheduler-relay.test.ts +15 -9
- package/test/scheduler.test.ts +2 -2
- package/test/security.test.ts +6 -3
- package/test/software-status.test.ts +21 -3
- package/test/telegram-provisioning.test.ts +160 -0
- package/test/updates.test.mjs +102 -13
- package/test/workspace-confine.test.ts +47 -0
- package/test/workspace.test.ts +27 -11
- package/bin/ezenciel-agents-watch.mjs +0 -8
- package/compose.workforce-watch.yaml +0 -33
- package/docker/recovery.ts +0 -11
- package/docs/pagerduty.md +0 -42
- package/docs/workforce-watch.md +0 -101
- package/src/pagerduty.ts +0 -109
- package/src/reply-context.ts +0 -44
- package/src/update-attention.ts +0 -18
- package/src/workforce-watch-cli.ts +0 -14
- package/src/workforce-watch.ts +0 -155
- package/templates/agent/AGENTS.md +0 -15
- package/templates/agent/SOUL.md +0 -11
- package/templates/agent/USER.md +0 -5
- package/templates/agent-guidance.md +0 -114
- package/test/pagerduty.test.ts +0 -104
- package/test/update-attention.test.ts +0 -20
- package/test/workforce-watch.test.ts +0 -180
package/.dockerignore
CHANGED
package/.env.example
CHANGED
|
@@ -32,22 +32,15 @@ EZ_EXECUTOR_CLI=agy
|
|
|
32
32
|
# Does not grant GitHub access, merge or publication permissions.
|
|
33
33
|
EZ_REPAIR_ENABLED=true
|
|
34
34
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
#
|
|
44
|
-
# monitor-only credentials; do not put them in an agent workspace.
|
|
45
|
-
# EZ_WATCH_ENROLL_TOKEN=
|
|
46
|
-
# EZ_WATCH_TELEGRAM_CHAT_ID=
|
|
47
|
-
|
|
48
|
-
# Optional native application-only runtime: no Telegram bot or token.
|
|
49
|
-
# Requires EZ_APPLICATION_PORT; administrator bootstrap uses the real operator ID.
|
|
50
|
-
# EZ_TELEGRAM_ENABLED=false
|
|
35
|
+
# An agent without TELEGRAM_BOT_TOKEN is application-only.
|
|
36
|
+
# It requires EZ_APPLICATION_PORT; administrator bootstrap uses the real operator ID.
|
|
37
|
+
# Isolation class from agent creation (default isolated).
|
|
38
|
+
# EZ_ISOLATION=isolated
|
|
39
|
+
# EZ_EXECUTOR_TRANSPORT=local
|
|
40
|
+
# Host-capable: EZ_ISOLATION=host-capable and EZ_EXECUTOR_TRANSPORT=host
|
|
41
|
+
# Host-capable agents get a generated ledger.compose.yaml that publishes this
|
|
42
|
+
# loopback port so host CLIs can reach the relay ledger on macOS Docker.
|
|
43
|
+
# EZ_DELIVERY_TCP_PORT=23456
|
|
51
44
|
# Private application-only local container isolation (optional; default workspace-write).
|
|
52
45
|
# EZ_CODEX_SANDBOX=external
|
|
53
46
|
|
package/AGENTS.md
CHANGED
|
@@ -14,13 +14,14 @@ Installed runtime operation uses Docker Compose. Read
|
|
|
14
14
|
[Docker setup, state and QA](docs/docker-runtime.md). Docker owns relay/plugin services; the existing host CLI and login are shared
|
|
15
15
|
through one generic transport, with separate agent workspaces and sessions.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Operating-agent instructions
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`
|
|
19
|
+
Do not inject a coding handbook into production agent minds. The package-owned
|
|
20
|
+
workspace locator is the managed `<!-- ez tools -->` block: the bound `ez`
|
|
21
|
+
launcher, `ez --help`, and `ez tools list --details`. Inventory is generated on
|
|
22
|
+
read; do not hardcode command recipes. Purpose lives in that agent's own
|
|
23
|
+
`AGENTS.md`. Coding defaults for this repository stay in this file and
|
|
24
|
+
CONTRIBUTING.md.
|
|
24
25
|
|
|
25
26
|
## Installing this package
|
|
26
27
|
|
|
@@ -53,6 +54,7 @@ This package will be published as an open-source, lightweight Telegram-to-CLI re
|
|
|
53
54
|
- Plugins are ordinary CLI tools with explicit inputs, outputs and errors. Packaging a tool does not justify another LLM worker or business workflow owner.
|
|
54
55
|
- **SUBTRACT is as valid as ADD.** Question the requirement; delete unnecessary behavior; simplify; shorten feedback; automate last. Name and try the subtraction option before proposing additions. Removing a wrapper or correcting existing engine instructions/tool contracts can be the complete fix.
|
|
55
56
|
- Add code only for a demonstrated missing transport or tool capability. Prompts, agents, retries and lifecycle owners also count as machinery; do not replace deleted code with a scripted prompt workflow. Prefer existing engine, CLI and Docker capabilities.
|
|
57
|
+
- The spawn core (`src/executor.ts` `startExecutorJob`) is spawn + stream only: callers own admission/authority and pass the one trigger suffix; the core never reads the run ledger, history or the workspace.
|
|
56
58
|
- `/goal do my daily routine` is literal task-prompt text for the engine. ez does not parse it, construct a native objective or own its workflow.
|
|
57
59
|
- Keep changes focused. State what was removed, why anything added is necessary, and which observed outcome proves the simpler system works. Preserve authority and uncertain-delivery safeguards. Unchanged, non-actionable maintenance stops quietly; queued updates and passing tests alone do not prove an installed fix.
|
|
58
60
|
|
|
@@ -102,8 +104,8 @@ find flaws:
|
|
|
102
104
|
If no answer identifies a material blocker, finish. Do not start another review
|
|
103
105
|
round merely to seek more criticism, tests or hypothetical edge cases.
|
|
104
106
|
|
|
105
|
-
## 3. Crash-Safe
|
|
106
|
-
-
|
|
107
|
+
## 3. Crash-Safe Disk State
|
|
108
|
+
- Persistent authority state (`ControlStore` owner/pairing/sessions/presets, application bindings, schedule definitions) stays in disk-backed JSON files. Run/outbox/inbox bookkeeping is relay memory reached through the delivery socket (`src/delivery-socket.ts`): a restart drops in-flight runs and queued deliveries by design, uncertain sends report unknown rather than success, and Telegram redelivery is the replay mechanism. The live delivery socket also replaces the relay flock as the single-relay guard.
|
|
107
109
|
- **Atomic write pattern:** Never write directly to a state file. Always write to a temporary file (`${target}.${process.pid}.tmp`) with mode `0o600`, then atomically `rename` it over the destination.
|
|
108
110
|
- **Path sanitization:** Every ID parameter (`runId`, `outboxId`, `pairingId`) must be validated against `/^[a-zA-Z0-9_-]+$/` before being joined into paths. Never allow directory traversal (`..`).
|
|
109
111
|
|
|
@@ -119,7 +121,7 @@ round merely to seek more criticism, tests or hypothetical edge cases.
|
|
|
119
121
|
|
|
120
122
|
## 6. Concurrency & Workspace Invariants
|
|
121
123
|
- **1 Writer Job per Workspace:** The agent's Markdown folder (`./agent/`) is its mind. Never run concurrent background processes writing to the same workspace simultaneously.
|
|
122
|
-
- Main-conversation jobs queue sequentially in
|
|
124
|
+
- Main-conversation jobs queue sequentially in the relay's memory ledger; the delivery socket carries every cross-process enqueue/receipt, never control/ files. Scheduled/background work uses separate task directories under the control directory and native CLI sessions (up to four alongside chat). Never share a mutable task directory. Delegation decisions and goal persistence belong to the agent/executor; there is no automatic planner or canned chat ACK. Production executor runs have no wall-clock timeout; cancellation is explicit.
|
|
123
125
|
|
|
124
126
|
## 7. Fail-Closed Authority (Channel Access ≠ Execution)
|
|
125
127
|
- Incoming messages outside the approved owner binding must **never** spawn the executor. A first DM or group message records a pending request only. Explicitly approved owner groups grant owner access to all human members in that exact chat; bots and anonymous posts are ignored.
|
|
@@ -146,3 +148,9 @@ fix and green tests do not justify a conflicting execution or context owner.
|
|
|
146
148
|
Existing legacy channel-backend code is a documented limitation, not a pattern
|
|
147
149
|
for new integrations. Keep internal plans and private evidence outside this
|
|
148
150
|
repository.
|
|
151
|
+
|
|
152
|
+
Source-checkout image builds are RCs: commit and open the PR first, then
|
|
153
|
+
`ezenciel-agents-install build --label X.Y.Z-beta.N.rc.M` (increasing `.rc.N`)
|
|
154
|
+
from the clean reviewed commit. The build refuses a dirty checkout or a package
|
|
155
|
+
tree that is not the checkout root; `/status` must show the RC tag for source
|
|
156
|
+
installs.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.36
|
|
4
|
+
|
|
5
|
+
- Move relay run/outbox/inbox bookkeeping into relay memory behind the control
|
|
6
|
+
delivery socket. A restart drops in-flight runs and queued deliveries by
|
|
7
|
+
design; Telegram redelivery is the replay mechanism. The live socket replaces
|
|
8
|
+
the relay flock as the single-relay guard.
|
|
9
|
+
- Slim the spawn core to spawn and stream only. Callers authorize admission and
|
|
10
|
+
pass the one trigger suffix; the core no longer reads the run ledger, history
|
|
11
|
+
or workspace.
|
|
12
|
+
- Add a dedicated plugin-broker service (isolated profile) that owns Docker
|
|
13
|
+
access with no network access. Isolated plugin commands run over a Unix socket
|
|
14
|
+
with single-use capabilities, pinned revisions and per-invocation receipts.
|
|
15
|
+
- Publish installed plugin versions through the broker and report the deployed
|
|
16
|
+
build identity; show the running RC tag, isolation/transport mode and host
|
|
17
|
+
platform in `/status`.
|
|
18
|
+
- Add `ezenciel-agents-provision-telegram` to attach a Telegram channel to an
|
|
19
|
+
existing botless deployment using that deployment's installed image.
|
|
20
|
+
- Add the Telegram `/tasks` list with numbered detail, attach Telegram identity
|
|
21
|
+
to an existing web owner, and infer Telegram transport from the token instead
|
|
22
|
+
of `EZ_TELEGRAM_ENABLED`.
|
|
23
|
+
- Remove PagerDuty and Workforce Watch from core. Authorization, transport,
|
|
24
|
+
scheduling, safety gates and delivery receipts remain.
|
|
25
|
+
- Stop waking the agent for software updates. The supervisor no longer writes an
|
|
26
|
+
attention notice and the relay no longer queues a wrapper-owned
|
|
27
|
+
`software_update_attention` run, so nothing outside the managed `ez tools`
|
|
28
|
+
locator is injected into an operating agent. `ez updates` stays owner-invoked.
|
|
29
|
+
- Stop injecting a package-owned coding handbook into operating-agent
|
|
30
|
+
`AGENTS.md`. Upgrades strip the old `ez shared guidance` block. Seed only the
|
|
31
|
+
installer purpose. The managed `ez tools` locator binds the agent launcher
|
|
32
|
+
(`ez --help` and `ez tools list --details`); inventory is generated on read.
|
|
33
|
+
Do not create `inbox/`, `work/`, `SOUL.md`, or `USER.md`.
|
|
34
|
+
- Generalize compatibility bridge upgrades so `.compat.N` builds hand off to
|
|
35
|
+
their canonical release, and allow reviewed legacy homes without `docker.env`.
|
|
36
|
+
- List the agent-bound Codex catalog in `/ai` for local relays, make declared
|
|
37
|
+
provider models authoritative and migrate persisted provider selections.
|
|
38
|
+
- Source-checkout RC builds require a clean reviewed commit and an increasing
|
|
39
|
+
`.rc.N` label; the label and commit SHA are baked into the image and shown
|
|
40
|
+
first in `/status`.
|
|
41
|
+
- Existing host-capable deployments need one installer-owned migration before
|
|
42
|
+
this release: `ezenciel-agents-install migrate-ledger --deployment <dir>` adds
|
|
43
|
+
the authenticated loopback ledger endpoint. Pre-beta.36 deployments also need
|
|
44
|
+
the installer-owned bootstrap because the new `plugin-broker` service and
|
|
45
|
+
environment changes do not match the previous release's deployment gate.
|
|
46
|
+
- Fix plugin application context delivery into the command container, plugin
|
|
47
|
+
status JSON parsing under Compose warnings, and EPERM lease liveness.
|
|
48
|
+
|
|
49
|
+
## 0.1.0-beta.35
|
|
50
|
+
|
|
51
|
+
- Harden the beta.34 compatibility upgrade path, including supervisor handoff
|
|
52
|
+
after a completed main upgrade and cleanup of obsolete terminal backups while
|
|
53
|
+
retaining active, queued and rollback state.
|
|
54
|
+
|
|
55
|
+
- Publish one plugin author standard for local-only installation, exact public
|
|
56
|
+
releases, catalog registration, package boundaries, and executor verification.
|
|
57
|
+
|
|
58
|
+
## 0.1.0-beta.34
|
|
59
|
+
|
|
60
|
+
- Correct setup guidance for host-capable custom Codex providers and replace
|
|
61
|
+
stale pinned-beta installation guidance with current release records.
|
|
62
|
+
|
|
63
|
+
- Retain only the newest completed updater backup per target and reconcile older
|
|
64
|
+
terminal backups after updates and supervisor restarts, without touching
|
|
65
|
+
active, queued or rollback state.
|
|
66
|
+
|
|
67
|
+
- Let existing agents self-upgrade across the beta.34 runtime compatibility
|
|
68
|
+
migration while retaining fail-closed rejection of structural deployment
|
|
69
|
+
changes.
|
|
70
|
+
|
|
71
|
+
- Record the reviewed Library beta14 command-contract migration and hand a
|
|
72
|
+
completed main upgrade back to the retained bootstrap so the new updater code
|
|
73
|
+
is loaded without a manual supervisor restart.
|
|
74
|
+
|
|
75
|
+
- Record an isolation class at agent creation. The default is `isolated`: the
|
|
76
|
+
native CLI runs in the relay (`EZ_EXECUTOR_TRANSPORT=local`). Pass
|
|
77
|
+
`--isolation host-capable` to reuse the installer UID and host CLI login.
|
|
78
|
+
Mismatched class and transport fail closed. Isolated Codex sets
|
|
79
|
+
`EZ_CODEX_SANDBOX=external`. Host transport is not started for isolated
|
|
80
|
+
agents. Unlabeled existing host transports stay host-capable.
|
|
81
|
+
- Install pinned Codex CLI `0.153.4` in the relay runtime image so isolated
|
|
82
|
+
agents resolve `codex` on PATH. Isolated jobs keep auth in
|
|
83
|
+
`control/cli/codex` and do not link the operator `~/.codex`.
|
|
84
|
+
|
|
85
|
+
- Confine macOS host jobs to their bound workspace and prevent cross-agent
|
|
86
|
+
plugin registry access.
|
|
87
|
+
- Support agent-scoped OpenRouter models through the native Codex engine and
|
|
88
|
+
start a new native session when the provider or model changes.
|
|
89
|
+
- Resolve native CLIs from the host PATH, expose captured presets in application
|
|
90
|
+
run receipts, and seed one purpose-scoped AGENTS.md for new agents.
|
|
91
|
+
|
|
3
92
|
## 0.1.0-beta.33
|
|
4
93
|
|
|
5
94
|
- Allow compatible plugin updates to add commands to existing services, registering
|
|
@@ -21,6 +110,9 @@
|
|
|
21
110
|
host CLI configuration needed for restricted read-only plugin commands.
|
|
22
111
|
- Clarify domain-agent positioning while preserving native engine ownership.
|
|
23
112
|
|
|
113
|
+
- Prevent accidental cross-agent plugin access by rejecting relay-bound registry
|
|
114
|
+
calls outside the owning workspace, and restrict its host-folder bindings to
|
|
115
|
+
workspace roots explicitly granted by the host.
|
|
24
116
|
- Answer native app-server requests with their current protocol shapes. Scheduled
|
|
25
117
|
`codex-gui` browser work now surfaces approval requests and grants only its
|
|
26
118
|
active turn session-scoped HTTPS browser-origin and Chrome app elicitations,
|
package/Dockerfile
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
FROM node:22.22.0-bookworm-slim@sha256:dd9d21971ec4395903fa6143c2b9267d048ae01ca6d3ea96f16cb30df6187d94 AS dependencies
|
|
2
2
|
WORKDIR /app
|
|
3
|
-
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates git util-linux ffmpeg && rm -rf /var/lib/apt/lists/*
|
|
3
|
+
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates git util-linux ffmpeg docker.io docker-compose && rm -rf /var/lib/apt/lists/*
|
|
4
4
|
RUN corepack enable && corepack prepare pnpm@10.30.3 --activate
|
|
5
5
|
COPY package.json ./
|
|
6
6
|
COPY docker/pnpm-lock.yaml ./pnpm-lock.yaml
|
|
@@ -9,15 +9,17 @@ COPY . .
|
|
|
9
9
|
FROM dependencies AS test
|
|
10
10
|
RUN pnpm verify
|
|
11
11
|
FROM dependencies AS runtime
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
# Keep this pin aligned with TASK_CODEX_VERSION. Isolated agents run this CLI
|
|
13
|
+
# in the relay; do not bind-mount the operator's ~/.codex.
|
|
14
|
+
ARG CODEX_CLI_VERSION=0.153.4
|
|
15
|
+
RUN npm install -g @openai/codex@${CODEX_CLI_VERSION} && command -v codex
|
|
16
|
+
RUN chmod +x docker/entrypoint.sh bin/ez bin/ezenciel-agents* && mkdir -p /state/control /state/home /workspace && chown node:node /state/control /state/home /workspace
|
|
17
|
+
RUN node -e 'for (const [name, target] of Object.entries(require("./package.json").bin)) require("node:fs").symlinkSync("/app/" + target, "/usr/local/bin/" + name); require("node:fs").symlinkSync("/app/bin/ez", "/usr/local/bin/ez")'
|
|
18
|
+
# Build identity for relay status (package version stays release-owned).
|
|
19
|
+
ARG BUILD_TAG=""
|
|
20
|
+
ARG BUILD_SHA=""
|
|
21
|
+
RUN node -e 'require("node:fs").writeFileSync("/app/build.json",JSON.stringify({tag:process.env.BUILD_TAG||"",sha:process.env.BUILD_SHA||""})+"\n")'
|
|
14
22
|
ENV HOME=/state/home EZ_AGENT_WORKSPACE=/workspace EZ_CONTROL_DIR=/state/control EZ_EXECUTOR_CLI=grok PATH=/app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
15
23
|
WORKDIR /workspace
|
|
16
24
|
ENTRYPOINT ["/app/docker/entrypoint.sh"]
|
|
17
25
|
CMD ["start"]
|
|
18
|
-
|
|
19
|
-
FROM dependencies AS workforce-watch
|
|
20
|
-
RUN chmod +x bin/ezenciel-agents-watch.mjs && mkdir /state
|
|
21
|
-
ENV EZ_WATCH_STATE_DIR=/state
|
|
22
|
-
WORKDIR /app
|
|
23
|
-
CMD ["node", "--import", "tsx", "src/workforce-watch-cli.ts"]
|
package/README.md
CHANGED
|
@@ -140,7 +140,8 @@ here:
|
|
|
140
140
|
|
|
141
141
|
- **Install for a user:** [setup and verified onboarding](docs/setup.md), then the installer instructions below.
|
|
142
142
|
- **Understand access and delegation:** [authority boundaries](docs/architecture/authority-boundaries.md) and [security scope](SECURITY.md).
|
|
143
|
-
- **
|
|
143
|
+
- **Build or share a plugin:** [plugin guide](docs/plugin-guide.md). It covers local unpublished installs, public releases, and catalog registration.
|
|
144
|
+
- **Discover or add capabilities:** [plugin manager contract](docs/plugins.md). Check the actual executor's native tools before adding an integration.
|
|
144
145
|
- **Contribute:** [AGENTS.md](AGENTS.md) and [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
145
146
|
|
|
146
147
|
Repository discovery is not an instruction to install or contact anyone. Act on
|
|
@@ -148,15 +149,19 @@ the user's request and preserve its scope.
|
|
|
148
149
|
|
|
149
150
|
## Package and runtime
|
|
150
151
|
|
|
151
|
-
**
|
|
152
|
-
fixtures are verified. Live account onboarding
|
|
153
|
-
|
|
154
|
-
may contain changes newer than the published beta
|
|
155
|
-
documentation when installing a pinned release.
|
|
156
|
-
|
|
157
|
-
The npm package is `@jc_stack/ez-agents
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
**Published beta releases are for testing on a trusted machine.** Offline and
|
|
153
|
+
Docker fixtures are verified for the selected release. Live account onboarding
|
|
154
|
+
and reboot acceptance remain beta gates; this is not a production-readiness
|
|
155
|
+
claim. The source branch may contain changes newer than the published beta, so
|
|
156
|
+
inspect the supplied artifact's documentation when installing a pinned release.
|
|
157
|
+
|
|
158
|
+
The npm package is `@jc_stack/ez-agents`. Approved beta publications use npm's
|
|
159
|
+
`latest` distribution tag while their versions and GitHub releases remain
|
|
160
|
+
prereleases. For a reproducible download, choose an exact version from
|
|
161
|
+
[GitHub prereleases](https://github.com/jdorado/ez-agents/releases) and substitute
|
|
162
|
+
it for `VERSION` in `npm pack @jc_stack/ez-agents@VERSION`.
|
|
163
|
+
You can also download the release's candidate tarball and verify its SHA-256
|
|
164
|
+
against the attached release receipt or release notes.
|
|
160
165
|
GitHub remains under `jdorado`; npm uses `jc_stack`. Do not install the unrelated
|
|
161
166
|
unscoped `ez-whatsapp` package.
|
|
162
167
|
|
|
@@ -165,13 +170,6 @@ services; the existing host CLI and login are shared through one generic
|
|
|
165
170
|
transport, with separate agent workspaces and sessions. See
|
|
166
171
|
[Docker setup, state and QA](docs/docker-runtime.md).
|
|
167
172
|
|
|
168
|
-
For optional PagerDuty paging of a critical Stocks outage, see
|
|
169
|
-
[PagerDuty critical-outage paging](docs/pagerduty.md).
|
|
170
|
-
|
|
171
|
-
For independent agent check-ins and concise incident context, see
|
|
172
|
-
[Workforce Watch](docs/workforce-watch.md). It runs separately from relays and
|
|
173
|
-
does not receive their executor or provider credentials.
|
|
174
|
-
|
|
175
173
|
Telegram `/status` shows the running relay and host versions plus installed
|
|
176
174
|
plugin versions. Its read-only **Scheduled tasks** control lists the paired
|
|
177
175
|
owner's active schedules in alphabetical order, with their engine/model/effort,
|
|
@@ -239,31 +237,37 @@ credentials, startup, and verified pairing; never present this as human homework
|
|
|
239
237
|
| Tool | Purpose |
|
|
240
238
|
|---|---|
|
|
241
239
|
| `ezenciel-agents-install preflight --executor <path-or-name>` | Check prerequisites, disk and executor identity hints before setup |
|
|
242
|
-
| `ezenciel-agents-install build` | Build once per artifact, with a private log and duplicate-build detection |
|
|
240
|
+
| `ezenciel-agents-install build [--label <X.Y.Z-beta.N.rc.M>]` | Build once per artifact, with a private log and duplicate-build detection; a source checkout requires the RC label and a clean reviewed commit |
|
|
241
|
+
| `ezenciel-agents-install migrate-ledger --deployment <path>` | Add the loopback host ledger endpoint to an existing host-capable deployment (idempotent; no restart) |
|
|
243
242
|
| `ezenciel-agents-install status --deployment <path>` | Distinguish runtime/configuration, pairing and Telegram reply evidence |
|
|
244
243
|
| `ezenciel-agents-create --register-cli <current-cli>` | Record the CLI that installs the initial package |
|
|
245
|
-
| `ezenciel-agents-create --name <name> --purpose <
|
|
244
|
+
| `ezenciel-agents-create --name <name> --purpose <agent-brief>` | Create an agent from the installing LLM's compact brief; packaged Codex defaults isolated, other CLIs stay host-capable; token via stdin |
|
|
245
|
+
| `ezenciel-agents-create --name <name> --purpose <agent-brief> --isolation host-capable` | Explicitly reuse the host CLI and installer UID |
|
|
246
246
|
| `ezenciel-agents-host` | Invoke the existing shared host CLI for this deployment |
|
|
247
|
-
| `ezenciel-agents-setup configure <executor>` | Private configuration and missing starter files; preserves personal files |
|
|
248
|
-
| `ezenciel-agents-setup configure <executor> --token-stdin` | Same, with the BotFather token supplied privately through stdin |
|
|
247
|
+
| `ezenciel-agents-setup configure <executor> --purpose-file <path>` | Private configuration and missing starter files; fresh workspaces require installer-scoped purpose; preserves personal files |
|
|
248
|
+
| `ezenciel-agents-setup configure <executor> --purpose-file <path> --token-stdin` | Same, with the BotFather token supplied privately through stdin |
|
|
249
249
|
| `ezenciel-agents-setup service` | Start only the Docker relay bound by `docker.env` in the current directory; register the [host service](docs/host-service.md) separately |
|
|
250
250
|
| `ezenciel-agents-owner status` | Inspect the owner and pending pairing requests |
|
|
251
251
|
| `ezenciel-agents-owner approve <telegram-user-id>` | Approve the verified owner; never an arbitrary first sender |
|
|
252
252
|
| `ezenciel-agents-setup status` | Inspect installed executor choices |
|
|
253
|
-
| `ezenciel-agents-setup init
|
|
253
|
+
| `ezenciel-agents-setup init --purpose-file <path>` | Seed missing workspace guidance; purpose is optional only when AGENTS.md already exists |
|
|
254
254
|
|
|
255
255
|
Run runtime tools through `ezenciel-agents-docker run --rm relay <command>`
|
|
256
256
|
as shown in the Docker guide. Keep the Docker engine available; Compose owns
|
|
257
|
-
restart and shutdown.
|
|
257
|
+
restart and shutdown. Host-capable agents register the small host CLI transport with the native service manager. Isolated agents do not.
|
|
258
258
|
|
|
259
259
|
## Runtime and development reference
|
|
260
260
|
|
|
261
261
|
The selected host CLI runs in this agent's persistent workspace and owns its
|
|
262
262
|
Markdown/work files. New conversations and AI changes preserve those files.
|
|
263
|
-
`AGENTS.md
|
|
264
|
-
optional.
|
|
265
|
-
|
|
266
|
-
|
|
263
|
+
One compact `AGENTS.md` is seeded with the installer purpose; `MEMORY.md` is
|
|
264
|
+
optional. The installing LLM authors the initial brief from the owner's request
|
|
265
|
+
and known application context; Ez stores it without trying to infer the product
|
|
266
|
+
itself. Installed plugin snippets and skill paths come from the bound
|
|
267
|
+
`ez tools list --details` locator; no tool inventory file needs maintenance.
|
|
268
|
+
Credentials and control state stay outside the mind. Isolated agents use relay
|
|
269
|
+
mounts as the filesystem boundary. Host-capable file separation is not OS
|
|
270
|
+
isolation against a process running as the same user.
|
|
267
271
|
|
|
268
272
|
In Telegram, **Conversations** (or `/chats`) lists this agent's saved Ez
|
|
269
273
|
conversations by name. Tap a name to continue its native engine session and
|
|
@@ -311,6 +315,7 @@ without authorization. Local registry rehearsal uses `pnpm publish:local`.
|
|
|
311
315
|
- [Security scope](SECURITY.md)
|
|
312
316
|
|
|
313
317
|
- [Contributing](CONTRIBUTING.md) and [releasing](docs/releasing.md)
|
|
318
|
+
- [Plugin author guide](docs/plugin-guide.md)
|
|
314
319
|
- [First plugin and extension contract](docs/plugins.md)
|
|
315
320
|
- [License and dependency notices](THIRD_PARTY_NOTICES.md)
|
|
316
321
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -8,7 +8,7 @@ The container is built locally; no bundled third-party binary image is promised.
|
|
|
8
8
|
|
|
9
9
|
Direct runtime dependencies: grammy (MIT), tsx (MIT). Development dependencies
|
|
10
10
|
include TypeScript (Apache-2.0) and Node type definitions (MIT). The runtime
|
|
11
|
-
image installs ffmpeg
|
|
12
|
-
review of its exact package build, notices and
|
|
13
|
-
Docker and
|
|
14
|
-
by this repository's MIT license.
|
|
11
|
+
image installs ffmpeg, Debian packages, and pinned `@openai/codex` (Apache-2.0):
|
|
12
|
+
redistributing that image requires review of its exact package build, notices and
|
|
13
|
+
corresponding-source obligations. Docker and a host-capable AI CLI remain
|
|
14
|
+
separately installed products, not covered by this repository's MIT license.
|
package/bin/ez
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createConnection } from 'node:net';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
const MAX_STDIN_BYTES = 1024 * 1024;
|
|
6
|
+
const RUN_ID = /^[A-Za-z0-9_-]{1,160}$/;
|
|
7
|
+
const socketPath = process.env.EZ_PLUGIN_BROKER_SOCKET?.trim();
|
|
8
|
+
const runId = process.env.EZ_RUN_ID?.trim();
|
|
9
|
+
|
|
10
|
+
const fail = message => {
|
|
11
|
+
process.stderr.write(`ez plugin broker: ${message}\n`);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const request = (payload, signal = undefined) => new Promise((resolve, reject) => {
|
|
16
|
+
if (!socketPath || !socketPath.startsWith('/') || socketPath.length > 100) return reject(Error('isolated plugin broker is not configured'));
|
|
17
|
+
const timeoutMs = Number(process.env.EZ_PLUGIN_BROKER_CLIENT_TIMEOUT_MS ?? 600000)
|
|
18
|
+
const socket = createConnection(socketPath);
|
|
19
|
+
let buffer = '';
|
|
20
|
+
let settled = false;
|
|
21
|
+
const finish = (error, value) => {
|
|
22
|
+
if (settled) return;
|
|
23
|
+
settled = true;
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
signal?.removeEventListener('abort', cancel);
|
|
26
|
+
socket.destroy();
|
|
27
|
+
if (error) reject(error); else resolve(value);
|
|
28
|
+
};
|
|
29
|
+
const cancel = () => {
|
|
30
|
+
socket.destroy();
|
|
31
|
+
finish(Error('plugin command cancelled'));
|
|
32
|
+
};
|
|
33
|
+
const timer = setTimeout(() => {
|
|
34
|
+
socket.destroy();
|
|
35
|
+
finish(Error('plugin broker request timed out'));
|
|
36
|
+
}, Number.isSafeInteger(timeoutMs) && timeoutMs > 0 ? timeoutMs : 600000);
|
|
37
|
+
signal?.addEventListener('abort', cancel, { once: true });
|
|
38
|
+
socket.on('error', error => finish(error));
|
|
39
|
+
socket.on('close', () => finish(Error('plugin broker closed the connection')));
|
|
40
|
+
socket.on('data', chunk => {
|
|
41
|
+
buffer += chunk.toString();
|
|
42
|
+
const end = buffer.indexOf('\n');
|
|
43
|
+
if (end < 0) return;
|
|
44
|
+
let value;
|
|
45
|
+
try { value = JSON.parse(buffer.slice(0, end)); } catch { return finish(Error('invalid plugin broker response')); }
|
|
46
|
+
finish(undefined, value);
|
|
47
|
+
});
|
|
48
|
+
socket.on('connect', () => socket.write(JSON.stringify(payload) + '\n'));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const readStdin = async () => {
|
|
52
|
+
if (process.stdin.isTTY) return '';
|
|
53
|
+
const chunks = [];
|
|
54
|
+
let bytes = 0;
|
|
55
|
+
for await (const chunk of process.stdin) {
|
|
56
|
+
const value = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
57
|
+
bytes += value.length;
|
|
58
|
+
if (bytes > MAX_STDIN_BYTES) throw Error('plugin stdin exceeds the broker limit');
|
|
59
|
+
chunks.push(value);
|
|
60
|
+
}
|
|
61
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const invoke = async (args, stdin) => {
|
|
65
|
+
if (!runId || !RUN_ID.test(runId)) throw Error('EZ_RUN_ID is required for an isolated plugin command');
|
|
66
|
+
const alias = args.shift();
|
|
67
|
+
if (!alias || !/^[a-z][a-z0-9-]{0,39}$/.test(alias) || ['plugins', 'tools', 'status', 'updates', 'message', 'owner', 'approval', 'react', 'setup', 'help', 'version'].includes(alias)) throw Error('Use a declared plugin alias');
|
|
68
|
+
const resolved = await request({ version: 1, id: randomUUID(), operation: 'resolve', runId, alias });
|
|
69
|
+
if (!resolved?.ok) throw Error(resolved?.error || 'Plugin alias resolution failed');
|
|
70
|
+
const result = await request({ version: 1, id: randomUUID(), operation: 'invoke', runId, alias, revision: resolved.revision, capability: resolved.capability, args, ...(stdin ? { stdin } : {}) });
|
|
71
|
+
if (!result?.ok) throw Error(result?.error || 'Plugin invocation failed');
|
|
72
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
73
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
74
|
+
process.exitCode = Number.isInteger(result.code) ? result.code : 1;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const manager = async args => {
|
|
78
|
+
if (!runId || !RUN_ID.test(runId)) throw Error('EZ_RUN_ID is required for isolated plugin management');
|
|
79
|
+
const result = await request({ version: 1, id: randomUUID(), operation: 'manager', runId, args });
|
|
80
|
+
if (!result?.ok) throw Error(result?.error || 'Plugin broker management failed');
|
|
81
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
82
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
83
|
+
process.exitCode = Number.isInteger(result.code) ? result.code : 0;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const main = async () => {
|
|
87
|
+
const args = process.argv.slice(2);
|
|
88
|
+
if (!args.length || args[0] === '--help' || args[0] === '-h') {
|
|
89
|
+
process.stdout.write('Use `ez tools list` to discover installed plugins, then `ez <alias> ...` for a declared command.\n');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const stdin = ['plugins', 'tools', 'updates', 'status'].includes(args[0]) ? '' : await readStdin();
|
|
93
|
+
if (['plugins', 'tools', 'updates', 'status'].includes(args[0])) await manager(args);
|
|
94
|
+
else await invoke(args, stdin);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
main().catch(error => fail(error instanceof Error ? error.message : 'Plugin broker client failed'));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const tsx = require.resolve('tsx');
|
|
10
|
+
const entry = join(here, '..', 'src', 'plugin-broker.mjs');
|
|
11
|
+
const child = spawn(process.execPath, ['--import', tsx, entry, ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
12
|
+
child.on('error', error => { console.error(error.message); process.exitCode = 1; });
|
|
13
|
+
child.on('exit', (code, signal) => {
|
|
14
|
+
if (signal) process.kill(process.pid, signal);
|
|
15
|
+
process.exit(code ?? 1);
|
|
16
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const require = createRequire(import.meta.url)
|
|
9
|
+
const child = spawn(process.execPath, ['--import', require.resolve('tsx'), join(here, '..', 'src', 'telegram-provisioning-cli.ts'), ...process.argv.slice(2)], { stdio: 'inherit' })
|
|
10
|
+
child.on('exit', (code, signal) => {
|
|
11
|
+
if (signal) process.kill(process.pid, signal)
|
|
12
|
+
process.exit(code ?? 1)
|
|
13
|
+
})
|
package/bin/ezenciel-agents.mjs
CHANGED
|
@@ -22,7 +22,7 @@ if (forwarded.length && (forwarded.length !== 1 || forwarded[0] !== 'start')) {
|
|
|
22
22
|
process.exit(1)
|
|
23
23
|
}
|
|
24
24
|
if (!existsSync('/.dockerenv') && process.env.EZ_DEVELOPMENT !== '1') {
|
|
25
|
-
const docker = spawn(join(here, 'ezenciel-agents-docker'), ['up', '-d', '--wait'
|
|
25
|
+
const docker = spawn(join(here, 'ezenciel-agents-docker'), ['up', '-d', '--wait'], { stdio: 'inherit' })
|
|
26
26
|
docker.on('error', () => { console.error('Docker launcher could not start'); process.exitCode = 1 })
|
|
27
27
|
docker.on('exit', code => { process.exitCode = code ?? 1 })
|
|
28
28
|
} else {
|
package/compose.yaml
CHANGED
|
@@ -17,10 +17,11 @@ services:
|
|
|
17
17
|
EZ_RELAY_UID: ${EZ_RELAY_UID:-1001}
|
|
18
18
|
EZ_REPAIR_ENABLED: ${EZ_REPAIR_ENABLED:-true}
|
|
19
19
|
EZ_AGENT_PURPOSE_FILE: /run/agent-purpose.md
|
|
20
|
+
EZ_ISOLATION: ${EZ_ISOLATION:-}
|
|
20
21
|
EZ_EXECUTOR_TRANSPORT: ${EZ_EXECUTOR_TRANSPORT:-host}
|
|
22
|
+
EZ_PLUGIN_BROKER_SOCKET: ${EZ_PLUGIN_BROKER_SOCKET:-}
|
|
21
23
|
EZ_CHANNEL_BACKEND_URL: ${EZ_CHANNEL_BACKEND_URL:-}
|
|
22
24
|
EZ_TELEGRAM_WEB_APP: ${EZ_TELEGRAM_WEB_APP:-}
|
|
23
|
-
EZ_TELEGRAM_ENABLED: ${EZ_TELEGRAM_ENABLED:-true}
|
|
24
25
|
EZ_APPLICATION_PORT: ${EZ_APPLICATION_PORT:-}
|
|
25
26
|
EZ_APPLICATION_HOST: ${EZ_APPLICATION_HOST:-127.0.0.1}
|
|
26
27
|
EZ_AGENT_WORKSPACE: ${EZ_AGENT_WORKSPACE:?Set this agent workspace}
|
|
@@ -29,9 +30,6 @@ services:
|
|
|
29
30
|
EZ_CODEX_SANDBOX: ${EZ_CODEX_SANDBOX:-}
|
|
30
31
|
EZ_CODEX_AUTO_COMPACT_TOKENS: ${EZ_CODEX_AUTO_COMPACT_TOKENS:-64000}
|
|
31
32
|
EZ_EXECUTOR_TIMEOUT_SECONDS: ${EZ_EXECUTOR_TIMEOUT_SECONDS:-300}
|
|
32
|
-
EZ_PAGERDUTY_STOCKS_HEALTH_URL: ${EZ_PAGERDUTY_STOCKS_HEALTH_URL:-}
|
|
33
|
-
EZ_PAGERDUTY_POLL_SECONDS: ${EZ_PAGERDUTY_POLL_SECONDS:-30}
|
|
34
|
-
EZ_PAGERDUTY_FAILURE_THRESHOLD: ${EZ_PAGERDUTY_FAILURE_THRESHOLD:-3}
|
|
35
33
|
secrets: [relay_env]
|
|
36
34
|
configs:
|
|
37
35
|
- source: agent_purpose
|
|
@@ -48,9 +46,48 @@ services:
|
|
|
48
46
|
timeout: 5s
|
|
49
47
|
start_period: 30s
|
|
50
48
|
retries: 3
|
|
49
|
+
plugin-broker:
|
|
50
|
+
profiles: [isolated]
|
|
51
|
+
image: ${EZ_RELAY_IMAGE:-ezenciel-agents:local}
|
|
52
|
+
build:
|
|
53
|
+
context: .
|
|
54
|
+
target: runtime
|
|
55
|
+
init: true
|
|
56
|
+
restart: unless-stopped
|
|
57
|
+
stop_grace_period: 10s
|
|
58
|
+
user: "0:0"
|
|
59
|
+
cap_drop: [ALL]
|
|
60
|
+
security_opt: [no-new-privileges:true]
|
|
61
|
+
network_mode: none
|
|
62
|
+
environment:
|
|
63
|
+
# Defaults keep Compose interpolation valid for host-capable deployments;
|
|
64
|
+
# the broker itself rejects these placeholders if its isolated profile is enabled.
|
|
65
|
+
EZ_PLUGIN_BROKER_HOME: ${EZ_TOOLS_HOME:-/nonexistent/ez-tools}
|
|
66
|
+
EZ_PLUGIN_BROKER_WORKSPACE: ${EZ_AGENT_WORKSPACE:-/nonexistent/ez-workspace}
|
|
67
|
+
EZ_PLUGIN_BROKER_CONTROL_DIR: ${EZ_CONTROL_DIR:-/nonexistent/ez-control}
|
|
68
|
+
EZ_PLUGIN_BROKER_SOCKET: ${EZ_PLUGIN_BROKER_SOCKET:-/nonexistent/ez-control/plugin-broker.sock}
|
|
69
|
+
EZ_PLUGIN_BROKER_HOST_CONFIG: ${EZ_PLUGIN_BROKER_HOST_CONFIG:-/nonexistent/host-executor.json}
|
|
70
|
+
EZ_PLUGIN_BROKER_TIMEOUT_MS: ${EZ_PLUGIN_BROKER_TIMEOUT_MS:-300000}
|
|
71
|
+
EZ_DOCKER_COMPOSE: standalone
|
|
72
|
+
HOME: /tmp
|
|
73
|
+
DOCKER_CONFIG: /tmp/docker-config
|
|
74
|
+
volumes:
|
|
75
|
+
- ${EZ_TOOLS_HOME:-/nonexistent/ez-tools}:${EZ_TOOLS_HOME:-/nonexistent/ez-tools}
|
|
76
|
+
- ${EZ_AGENT_WORKSPACE:-/nonexistent/ez-workspace}:${EZ_AGENT_WORKSPACE:-/nonexistent/ez-workspace}:ro
|
|
77
|
+
- ${EZ_CONTROL_DIR:-/nonexistent/ez-control}:${EZ_CONTROL_DIR:-/nonexistent/ez-control}
|
|
78
|
+
- ${EZ_PLUGIN_BROKER_HOST_CONFIG:-/nonexistent/host-executor.json}:${EZ_PLUGIN_BROKER_HOST_CONFIG:-/nonexistent/host-executor.json}:ro
|
|
79
|
+
- /var/run/docker.sock:/var/run/docker.sock
|
|
80
|
+
tmpfs: [/tmp]
|
|
81
|
+
entrypoint: [node, --import, /app/node_modules/tsx/dist/loader.mjs, /app/src/plugin-broker.mjs]
|
|
82
|
+
healthcheck:
|
|
83
|
+
test: [CMD, node, -e, "const fs=require('node:fs'); try { process.exit(fs.statSync(process.env.EZ_PLUGIN_BROKER_SOCKET).isSocket() ? 0 : 1) } catch { process.exit(1) }"]
|
|
84
|
+
interval: 2s
|
|
85
|
+
timeout: 5s
|
|
86
|
+
start_period: 5s
|
|
87
|
+
retries: 30
|
|
51
88
|
configs:
|
|
52
89
|
agent_purpose:
|
|
53
|
-
file: ${EZ_AGENT_PURPOSE_FILE:-./templates/agent
|
|
90
|
+
file: ${EZ_AGENT_PURPOSE_FILE:-./templates/agent-purpose.md}
|
|
54
91
|
secrets:
|
|
55
92
|
relay_env:
|
|
56
93
|
file: ${EZ_RELAY_ENV_FILE:?Set EZ_RELAY_ENV_FILE to the private relay env file}
|
package/docker/entrypoint.sh
CHANGED
|
@@ -28,6 +28,6 @@ fi
|
|
|
28
28
|
( : <&3 ) 2>/dev/null || exec 3</dev/null
|
|
29
29
|
case "${1:-start}" in
|
|
30
30
|
start|smoke|exec|setup)
|
|
31
|
-
exec
|
|
31
|
+
exec node --import /app/node_modules/tsx/dist/loader.mjs /app/docker/run.ts "$@" ;;
|
|
32
32
|
*) exec node --import /app/node_modules/tsx/dist/loader.mjs /app/docker/run.ts "$@" ;;
|
|
33
33
|
esac
|
package/docker/healthcheck.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
const heartbeatFile = process.env.EZ_HEARTBEAT_FILE || '/tmp/ez-relay-heartbeat.json';
|
|
2
3
|
const relayControl = process.env.EZ_HEALTH_RELAY_CONTROL_DIR || '/state/control';
|
|
3
4
|
const fail = code => { process.stderr.write(`EZ_HEALTH_${code}\n`); process.exit(1); };
|
|
4
5
|
let value;
|
|
5
|
-
try { value = JSON.parse(readFileSync(
|
|
6
|
-
catch {
|
|
7
|
-
|
|
6
|
+
try { value = JSON.parse(readFileSync(heartbeatFile, 'utf8')); }
|
|
7
|
+
catch {
|
|
8
|
+
try { value = JSON.parse(readFileSync(relayControl + '/heartbeat.json', 'utf8')); }
|
|
9
|
+
catch { fail('RELAY_UNREADABLE'); }
|
|
10
|
+
}
|
|
11
|
+
if (!value.polling && (value.applicationOnly !== true || value.telegramConfigured !== false)) fail('RELAY_NOT_POLLING');
|
|
8
12
|
if (!Number.isFinite(value.at) || Date.now() - value.at > 20000) fail('RELAY_STALE');
|
|
9
13
|
|
|
10
14
|
if (process.env.EZ_EXECUTOR_TRANSPORT === 'host') {
|
|
@@ -13,3 +17,7 @@ if (process.env.EZ_EXECUTOR_TRANSPORT === 'host') {
|
|
|
13
17
|
catch { fail('HOST_UNREADABLE'); }
|
|
14
18
|
if (!Number.isFinite(host.at) || Date.now() - host.at > 15000) fail('HOST_STALE');
|
|
15
19
|
}
|
|
20
|
+
if (process.env.EZ_PLUGIN_BROKER_SOCKET?.trim()) {
|
|
21
|
+
try { if (!statSync(process.env.EZ_PLUGIN_BROKER_SOCKET).isSocket()) fail('PLUGIN_BROKER_UNREADABLE'); }
|
|
22
|
+
catch { fail('PLUGIN_BROKER_UNREADABLE'); }
|
|
23
|
+
}
|