@evident-ai/runner-cdk 0.1.1-dev.04f2155 → 0.1.1-dev.15755a4
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/README.md +1 -1
- package/dist/evident-scale-to-zero-construct.js +1 -1
- package/dist/microvm/constants.js +2 -3
- package/dist/microvm/controller/handle-doorbell.js +2 -2
- package/dist/microvm/controller/microvm-client.d.ts +1 -1
- package/dist/microvm-image-context/Dockerfile +5 -2
- package/dist/microvm-image-context/hook-server.js +2 -6
- package/dist/microvm-image-context/hooks/common.sh +12 -12
- package/dist/microvm-image-context/hooks/resume +1 -1
- package/dist/waker/construct.js +1 -1
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ the construct's own dogfooding consumer.
|
|
|
112
112
|
|
|
113
113
|
This construct never builds an image. Pass any `ecs.ContainerImage` — from a registry, or
|
|
114
114
|
built from a `Dockerfile` you control. The generic runner image lives separately in
|
|
115
|
-
`
|
|
115
|
+
`runner/docker-images/fargate`, so you can adopt the image, the construct, or both.
|
|
116
116
|
|
|
117
117
|
## The MicroVM image
|
|
118
118
|
|
|
@@ -101,7 +101,7 @@ class EvidentScaleToZeroConstruct extends constructs_1.Construct {
|
|
|
101
101
|
// --endpoint / --tunnel). The agent is resolved from EVIDENT_AGENT_KEY.
|
|
102
102
|
EVIDENT_API_URL: evidentApiUrl,
|
|
103
103
|
EVIDENT_TUNNEL_URL: evidentTunnelUrl,
|
|
104
|
-
// Litestream replica target (read by
|
|
104
|
+
// Litestream replica target (read by runner/synchroniser, which
|
|
105
105
|
// generates /etc/evident/litestream.yml at boot).
|
|
106
106
|
LITESTREAM_BUCKET: replicaBucket.bucketName,
|
|
107
107
|
LITESTREAM_PREFIX: this.replicaPrefix,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Shared literals for the MicroVM controller and for the image it launches.
|
|
3
3
|
// Every module imports from here rather than restating a value that must match
|
|
4
|
-
// across them
|
|
5
|
-
// image
|
|
6
|
-
// `scripts/build.ts`, so these values reach the image as inlined constants.
|
|
4
|
+
// across them. The image template pins matching copies, guarded by
|
|
5
|
+
// `microvm/image/dockerfile.test.ts`.
|
|
7
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
7
|
exports.MICROVM_MAX_RUN_SECONDS = exports.SUSPENDING_POLL_INTERVAL_MS = exports.SUSPENDING_POLL_ATTEMPTS = exports.RUN_HOOK_PAYLOAD_MAX_BYTES = exports.HOOK_TIMEOUT_SECONDS = exports.HOOKS_DIR = exports.HOOKS_PORT = void 0;
|
|
9
8
|
// The port the image's hook server binds, baked into the published Dockerfile
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleDoorbell = handleDoorbell;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
const
|
|
5
|
+
const sdk_1 = require("@evident/sdk");
|
|
6
6
|
const constants_1 = require("../constants");
|
|
7
7
|
const doorbell_1 = require("./doorbell");
|
|
8
8
|
const throttle_retry_1 = require("./throttle-retry");
|
|
@@ -175,7 +175,7 @@ async function shouldRecreateForNewerImage(doorbell, shape, runningVersion, micr
|
|
|
175
175
|
return compareImageVersions(running, latest) < 0;
|
|
176
176
|
}
|
|
177
177
|
async function handleDoorbell({ rawBody, signatureHeader, doorbellSecret, shapes, microvm, sleep, random, }) {
|
|
178
|
-
if (!(0,
|
|
178
|
+
if (!(0, sdk_1.verifyEvidentSignature)(rawBody, signatureHeader, doorbellSecret)) {
|
|
179
179
|
return decide({
|
|
180
180
|
statusCode: 401,
|
|
181
181
|
action: 'rejected',
|
|
@@ -49,7 +49,7 @@ export interface MicrovmClient {
|
|
|
49
49
|
* (docs/spikes/lambda-microvms-phase0/README.md). AWS's own Claude-agents
|
|
50
50
|
* guidance recommending `maxIdleDurationSeconds: 120` targets VMs reached
|
|
51
51
|
* inbound and does not apply here. Idle is instead decided IN the VM by
|
|
52
|
-
* `evident run --idle-timeout` (
|
|
52
|
+
* `evident run --idle-timeout` (runner/docker-images/microvm/hooks/common.sh's
|
|
53
53
|
* `IDLE_TIMEOUT_SECONDS`), whose clean exit drives this
|
|
54
54
|
* client's own `suspend()` via the `runner.suspend_requested` doorbell.
|
|
55
55
|
*/
|
|
@@ -204,9 +204,12 @@ RUN git reset --quiet
|
|
|
204
204
|
# database stay per-VM (see entrypoint.sh's pre-warm in evident-runner).
|
|
205
205
|
RUN if [ -f pnpm-lock.yaml ]; then \
|
|
206
206
|
pnpm install --frozen-lockfile \
|
|
207
|
-
&& pnpm run build --filter='./packages/*'
|
|
207
|
+
&& pnpm run build --filter='./packages/*' \
|
|
208
|
+
--filter='./aws/runner-cdk' \
|
|
209
|
+
--filter='./aws/lambda-microvm-cdk' \
|
|
210
|
+
--filter='./aws/lambda-microvm-runtime'; \
|
|
208
211
|
else \
|
|
209
|
-
echo "[workspace-prep] no pnpm-lock.yaml in the baked repository — skipping the dependency install and the
|
|
212
|
+
echo "[workspace-prep] no pnpm-lock.yaml in the baked repository — skipping the dependency install and the workspace build; the agent installs on first use."; \
|
|
210
213
|
fi
|
|
211
214
|
|
|
212
215
|
# Data dir for the local dev Postgres cluster; listens on 5433 to match this
|
|
@@ -275,14 +275,10 @@ var require_dist = __commonJS({
|
|
|
275
275
|
}
|
|
276
276
|
});
|
|
277
277
|
|
|
278
|
-
//
|
|
278
|
+
// ../../runner/docker-images/microvm/hook-server.ts
|
|
279
279
|
var import_lambda_microvm_runtime = __toESM(require_dist());
|
|
280
|
-
|
|
281
|
-
// src/microvm/constants.ts
|
|
282
|
-
var HOOKS_PORT = 8080;
|
|
283
280
|
var HOOKS_DIR = "/etc/evident/hooks";
|
|
284
|
-
|
|
285
|
-
// src/microvm/image/hook-server.ts
|
|
281
|
+
var HOOKS_PORT = 8080;
|
|
286
282
|
var server = (0, import_lambda_microvm_runtime.createNodeRuntime)({ hooksDir: HOOKS_DIR });
|
|
287
283
|
var port = Number(process.env.HOOKS_PORT ?? HOOKS_PORT);
|
|
288
284
|
server.listen(port, "0.0.0.0", () => {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# Sourced by every hook script. Nothing here runs at image build time.
|
|
3
3
|
#
|
|
4
4
|
# These hooks are the SECOND shell speaking the runner-synchroniser CLI contract
|
|
5
|
-
# (
|
|
6
|
-
#
|
|
5
|
+
# (runner/docker-images/fargate/entrypoint.sh is the first), so both are held to it by
|
|
6
|
+
# runner/synchroniser/src/shell-contract.test.ts — which derives what
|
|
7
7
|
# `run_synchroniser` below must handle from the subcommands this shell calls.
|
|
8
8
|
|
|
9
9
|
# Installed from npm by the image (docker/Dockerfile's ARG
|
|
@@ -57,7 +57,7 @@ error() { echo "[hook:$(basename "$0")] ERROR: $*" >&2; }
|
|
|
57
57
|
# Returns the CLI's own exit code. Domain outcomes (nothing persisted yet, a
|
|
58
58
|
# corrupt object) are LOGGED and exit 0, the predicates answer "no" with 10, and
|
|
59
59
|
# `session-db-classify`'s three typed answers are 30 (fatal)/31 (replica
|
|
60
|
-
# unusable)/32 (retry) — see `
|
|
60
|
+
# unusable)/32 (retry) — see `runner/synchroniser/src/cli.ts`'s own
|
|
61
61
|
# comment for what each means, not restated here. Any OTHER non-zero status
|
|
62
62
|
# means the tool itself broke, which is the only case worth an ERROR here —
|
|
63
63
|
# EXCEPT 124/137 (a `timeout` deadline/SIGKILL) when the caller asked for one:
|
|
@@ -171,7 +171,7 @@ timed_synchroniser() {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
# Exports what `runner-synchroniser` resolves its object-store location from
|
|
174
|
-
# (
|
|
174
|
+
# (runner/synchroniser/src/config.ts). It treats either being empty as
|
|
175
175
|
# "persistence disabled" and then reports every restore as a WARNING it still
|
|
176
176
|
# exits 0 for — so an unset value has to be caught HERE, where it can still be
|
|
177
177
|
# told apart from "the store is simply empty".
|
|
@@ -441,7 +441,7 @@ sync_credentials() {
|
|
|
441
441
|
# Writes NOTHING to S3 itself; replication back to S3 is started separately by
|
|
442
442
|
# `start_litestream` (below) from the `run`/`resume` hooks. `ensure_litestream_config`/
|
|
443
443
|
# `restore_session_db` are the MicroVM side of the same contract
|
|
444
|
-
#
|
|
444
|
+
# runner/docker-images/fargate/entrypoint.sh's inlined restore already speaks, going
|
|
445
445
|
# through the SAME runner-synchroniser CLI.
|
|
446
446
|
|
|
447
447
|
# Reads the ~30 MB litestream binary into the page cache, in the background, so
|
|
@@ -624,7 +624,7 @@ restore_session_db() {
|
|
|
624
624
|
return 0
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
# #931's exact lesson, one shell over (
|
|
627
|
+
# #931's exact lesson, one shell over (runner/docker-images/fargate/entrypoint.sh):
|
|
628
628
|
# the `|| { ... }` above only catches a non-zero EXIT — an `env` that exits
|
|
629
629
|
# 0 with an INCOMPLETE contract (a runner-synchroniser version/build skew)
|
|
630
630
|
# would otherwise abort right here under `set -u` the moment
|
|
@@ -681,7 +681,7 @@ restore_session_db() {
|
|
|
681
681
|
esac
|
|
682
682
|
|
|
683
683
|
local classify_rc=0
|
|
684
|
-
run_synchroniser session-db-classify "${restore_rc}" 1 --on-unusable-replica=leave || classify_rc=$?
|
|
684
|
+
run_synchroniser session-db-classify "${restore_rc}" 1 --on-unusable-replica=leave --fresh-db-fallback || classify_rc=$?
|
|
685
685
|
case "${classify_rc}" in
|
|
686
686
|
0) ;; # restored, or no replica yet — the CLI already logged which
|
|
687
687
|
31)
|
|
@@ -691,8 +691,8 @@ restore_session_db() {
|
|
|
691
691
|
# half-restored DB, so the marker stays; a 31 is structurally guaranteed
|
|
692
692
|
# to be a FRESH one, and replicating it starts a new backup chain instead
|
|
693
693
|
# of leaving this boot with a zero-width backup window. Same reasoning,
|
|
694
|
-
# and same one-line change, as
|
|
695
|
-
warn "SESSION-DB-REPLICA-UNUSABLE: booting with a fresh opencode.db and
|
|
694
|
+
# and same one-line change, as runner/docker-images/fargate/entrypoint.sh's `31)`.
|
|
695
|
+
warn "SESSION-DB-REPLICA-UNUSABLE: booting with a fresh opencode.db and replicating into the existing prefix this boot (see the WARNING above)"
|
|
696
696
|
;;
|
|
697
697
|
32)
|
|
698
698
|
# The CLI's own contract for 32 is "re-run litestream restore and ask
|
|
@@ -1122,7 +1122,7 @@ flush_session_db() {
|
|
|
1122
1122
|
# returned its 200, so it is deliberately outside that arithmetic.
|
|
1123
1123
|
#
|
|
1124
1124
|
# EVIDENT_IDLE_TIMEOUT_SECONDS, deliberately the SAME env var name
|
|
1125
|
-
#
|
|
1125
|
+
# runner/docker-images/fargate/entrypoint.sh uses for the ECS runner's own
|
|
1126
1126
|
# idle-timeout flag, so an operator who knows one knows the other. A
|
|
1127
1127
|
# non-numeric override must never silently DROP the flag — that degrades to
|
|
1128
1128
|
# an always-on VM burning ~$2.40/day, exactly the bug this closes — so it
|
|
@@ -1161,7 +1161,7 @@ start_tunnel() {
|
|
|
1161
1161
|
# into a MicroVM, so it is the ONLY way to seed model credentials on a runner
|
|
1162
1162
|
# that is already up — the S3 credential store is read at /run and /resume,
|
|
1163
1163
|
# never mid-life. The allow-list stays a single directory, as on ECS
|
|
1164
|
-
# (
|
|
1164
|
+
# (runner/docker-images/fargate/entrypoint.sh): the flag is repeatable, but every
|
|
1165
1165
|
# extra entry widens what Evident can write into a VM that executes agent
|
|
1166
1166
|
# code. ${HOME} is set by the image (ENV HOME=/home/runner) and `set -u` makes
|
|
1167
1167
|
# an unset one abort rather than silently allow-list "/.claude".
|
|
@@ -1208,7 +1208,7 @@ CLI_SHUTDOWN_CEILING_SECONDS=32
|
|
|
1208
1208
|
# everything else in the hook. 10 s is ~4x the measured shutdown and gives a
|
|
1209
1209
|
# provable worst case of 10 + the 10 s `suspend` spends in sync_credentials
|
|
1210
1210
|
# before us = 20 s, inside the ~55 s the in-VM server allows this script
|
|
1211
|
-
# (DEFAULT_TIMEOUT_SECONDS,
|
|
1211
|
+
# (DEFAULT_TIMEOUT_SECONDS, aws/lambda-microvm-runtime/src/runtime.ts),
|
|
1212
1212
|
# itself inside AWS's 60 s (HOOK_TIMEOUT_SECONDS, src/constants.ts) —
|
|
1213
1213
|
# overrunning that fails the whole lifecycle transition, which is worse than the
|
|
1214
1214
|
# kill this replaces. The residual cost is a drain longer than 10 s being
|
|
@@ -61,7 +61,7 @@ start_litestream
|
|
|
61
61
|
# measures in ~2 s (see the waker note in common.sh).
|
|
62
62
|
#
|
|
63
63
|
# Safe to leave running past the hook's own exit: the runtime waits on the
|
|
64
|
-
# script's `'exit'`, not `'close'` (
|
|
64
|
+
# script's `'exit'`, not `'close'` (aws/lambda-microvm-runtime/src/
|
|
65
65
|
# runtime.ts), so a lingering child never delays the HTTP response. Its stdio
|
|
66
66
|
# is deliberately NOT redirected — that inherited fd is how the RUNNER-KEY-*
|
|
67
67
|
# line reaches CloudWatch, which is the whole point of running it at all.
|
package/dist/waker/construct.js
CHANGED
|
@@ -56,7 +56,7 @@ class EvidentWaker extends constructs_1.Construct {
|
|
|
56
56
|
runtime: lambda.Runtime.NODEJS_22_X,
|
|
57
57
|
// Pre-bundled at PACKAGE build time (`pnpm --filter @evident-ai/runner-cdk
|
|
58
58
|
// build`, see scripts/build.ts), not at synth time: esbuild inlines
|
|
59
|
-
// @evident/
|
|
59
|
+
// @evident/sdk's HMAC verifier into
|
|
60
60
|
// dist/waker-lambda/handler.js, so neither this package's published npm
|
|
61
61
|
// artifact (which ships dist/ already built) nor a consuming app needs
|
|
62
62
|
// that private workspace package, or esbuild, on synth's PATH. Requires
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evident-ai/runner-cdk",
|
|
3
|
-
"version": "0.1.1-dev.
|
|
3
|
+
"version": "0.1.1-dev.15755a4",
|
|
4
4
|
"description": "Reusable CDK constructs for an Evident agent runner: a single scale-to-zero Fargate runner (task + service + per-agent self-stop role + waker Lambda), or a per-session AWS Lambda MicroVM that boots on demand and suspends between messages. Instantiate once per agent from your own stack.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "pnpm run build-bundled-deps && ts-node scripts/build.ts",
|
|
18
|
-
"//build-bundled-deps": "scripts/build.ts esbuild-bundles
|
|
18
|
+
"//build-bundled-deps": "scripts/build.ts esbuild-bundles runner/docker-images/microvm/hook-server.ts, which imports @evident-ai/lambda-microvm-runtime by its built `main`. @evident-ai/lambda-microvm-cdk is a workspace:* devDependency whose types resolve through its own dist/, so tsc --project tsconfig.build.json cannot compile src/microvm/{shapes,construct}.ts without it. Chained into `build` rather than left to turbo's `^build` because publish-runner-cdk.yaml and infrastructure/evident-runner's build-runner-cdk-dep invoke `pnpm --filter @evident-ai/runner-cdk build` directly, which bypasses turbo entirely.",
|
|
19
19
|
"build-bundled-deps": "pnpm --filter @evident-ai/lambda-microvm-cdk build && pnpm --filter @evident-ai/lambda-microvm-runtime build",
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
21
21
|
"test": "node --test --require ts-node/register 'src/**/*.test.ts'",
|
|
22
22
|
"format": "prettier --write 'src/**/*.ts'",
|
|
23
23
|
"lint": "eslint 'src/**/*.ts' --max-warnings=0"
|
|
24
24
|
},
|
|
25
|
-
"//peerDependencies": "@evident-ai/lambda-microvm-cdk is a registry range here, never workspace:*: publish-runner-cdk.yaml ships this package with `npm publish`, not `pnpm publish`, so pnpm's workspace-protocol substitution never runs and a workspace: range would reach npm literally, breaking every external install. Enforced by
|
|
25
|
+
"//peerDependencies": "@evident-ai/lambda-microvm-cdk is a registry range here, never workspace:*: publish-runner-cdk.yaml ships this package with `npm publish`, not `pnpm publish`, so pnpm's workspace-protocol substitution never runs and a workspace: range would reach npm literally, breaking every external install. Enforced by src/published-surface.test.ts. The matching devDependency is workspace:* so this monorepo's own build compiles against aws/lambda-microvm-cdk's source.",
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@evident-ai/lambda-microvm-cdk": "^0.1.1",
|
|
28
28
|
"aws-cdk-lib": "^2.240.0",
|
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
"@aws-sdk/client-secrets-manager": "^3.682.0",
|
|
35
35
|
"@evident-ai/lambda-microvm-cdk": "workspace:*",
|
|
36
36
|
"@evident-ai/lambda-microvm-runtime": "workspace:*",
|
|
37
|
-
"@evident/
|
|
37
|
+
"@evident/sdk": "workspace:*",
|
|
38
38
|
"@types/node": "^22",
|
|
39
39
|
"aws-cdk-lib": "^2.240.0",
|
|
40
40
|
"constructs": "^10.5.0",
|
|
41
41
|
"esbuild": "^0.25.2",
|
|
42
42
|
"prettier": "^3.3.3",
|
|
43
43
|
"ts-node": "^10.9.2",
|
|
44
|
-
"typescript": "^5.6.3"
|
|
44
|
+
"typescript": "^5.6.3",
|
|
45
|
+
"runner-microvm-image": "workspace:*"
|
|
45
46
|
},
|
|
46
47
|
"publishConfig": {
|
|
47
48
|
"access": "public"
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"repository": {
|
|
50
51
|
"type": "git",
|
|
51
52
|
"url": "https://github.com/sroze/evident.git",
|
|
52
|
-
"directory": "
|
|
53
|
+
"directory": "aws/runner-cdk"
|
|
53
54
|
},
|
|
54
55
|
"homepage": "https://evident.run",
|
|
55
56
|
"author": "Evident <hello@evident.run>",
|