@evident-ai/runner-cdk 0.1.1-dev.04ef8b1 → 0.1.1-dev.04f2155

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 CHANGED
@@ -9,9 +9,9 @@ Reusable AWS CDK constructs for running an [Evident](https://evident.run) agent
9
9
  `desiredCount 0`, and an HMAC-authenticated waker Lambda scales it back up on the next
10
10
  message. You pay for the time the agent is actually working.
11
11
  - **`EvidentMicrovmConstruct`** — a per-session AWS Lambda MicroVM that boots on demand
12
- and suspends between messages. Installing this package is all you need: the image build
13
- context ships inside it (see [The MicroVM image](#the-microvm-image) below), so there is
14
- no checkout of `sroze/evident` involved. See
12
+ and suspends between messages. Installing the `@dev` tag of this package is all you
13
+ need: the image build context ships inside it (see [The MicroVM image](#the-microvm-image)
14
+ below), so there is no checkout of `sroze/evident` involved. See
15
15
  [the AWS runner doc](https://evident.run/docs/aws-runner) for the full picture of both
16
16
  strategies.
17
17
 
@@ -34,8 +34,9 @@ cluster can host several agents.
34
34
  ## Getting started
35
35
 
36
36
  For a full end-to-end walkthrough — shared infra, secrets without SOPS/KMS, the image,
37
- the two grants, deploy, and wiring the wake webhook — see
38
- [`GETTING-STARTED.md`](./GETTING-STARTED.md). The snippet below is the short version.
37
+ the two grants, deploy, and wiring the wake webhook — see `GETTING-STARTED.md` in this
38
+ package's directory (not published to npm; read it from a checkout of the repo). The
39
+ snippet below is the short version.
39
40
 
40
41
  ```ts
41
42
  import { EvidentScaleToZeroConstruct } from "@evident-ai/runner-cdk";
@@ -126,6 +127,7 @@ the per-phase hook scripts and the bundled hook server, published inside the tar
126
127
  the agent's workspace. `stageMicrovmImageContext()` puts the two together:
127
128
 
128
129
  ```ts
130
+ import path from 'node:path';
129
131
  import {
130
132
  EvidentMicrovmConstruct,
131
133
  HOOKS_PORT,
@@ -149,6 +151,8 @@ new EvidentMicrovmConstruct(this, 'Runner', {
149
151
  baseImageArn,
150
152
  baseImageVersion,
151
153
  doorbellSecret,
154
+ runnerSecret,
155
+ runnerOpencodeConfigPath: 'opencode.runner.jsonc',
152
156
  });
153
157
  ```
154
158
 
@@ -156,14 +160,19 @@ Your repo does not have to be a pnpm workspace. If it commits a `pnpm-lock.yaml`
156
160
  pre-installs dependencies at build time (a faster first boot); if not, that step is skipped
157
161
  and the agent installs on first use.
158
162
 
163
+ | MicroVM prop | Omitted behavior |
164
+ | --- | --- |
165
+ | `runnerSecret` | No GitHub or MCP credentials are exported at `/run`. |
166
+ | `runnerOpencodeConfigPath` | OpenCode uses the baked project configuration. Relative paths resolve from the workspace; absolute paths resolve in the image. |
167
+ | `gitUserName` / `gitUserEmail` | The hook uses the Evident bot defaults for git identity. |
168
+
159
169
  ## Status / limitations
160
170
 
161
- - **Published to npm** as `@evident-ai/runner-cdk` (MIT) — see
162
- [#957](https://github.com/sroze/evident/issues/957). Install it with `npm install
163
- @evident-ai/runner-cdk` (or `pnpm add`/`yarn add`); no checkout of this repo is
164
- required. It builds to a self-contained `dist/` (`pnpm --filter @evident-ai/runner-cdk
165
- build`) with no `workspace:`/`@evident/*` runtime dependency. This repo's own
166
- consumer (`infrastructure/evident-runner`) still consumes it as a `workspace:*`
167
- dependency for dogfooding.
171
+ - **Published to npm** as `@evident-ai/runner-cdk` (MIT). Install the `@dev` tag
172
+ `npm install @evident-ai/runner-cdk@dev` (or `pnpm add`/`yarn add`); no checkout of
173
+ this repo is required. It builds to a self-contained `dist/` (`pnpm --filter
174
+ @evident-ai/runner-cdk build`) with no `workspace:`/`@evident/*` runtime dependency.
175
+ This repo's own consumer (`infrastructure/evident-runner`) still consumes it as a
176
+ `workspace:*` dependency for dogfooding.
168
177
  - **AWS/ECS-specific.** Non-AWS clouds are an open question on the epic, not a supported
169
178
  path.
@@ -115,10 +115,9 @@ class EvidentScaleToZeroConstruct extends constructs_1.Construct {
115
115
  // window (protected/active sessions are never touched). Tightened from 7d
116
116
  // to 24h per #537: at 7d, opencode.db was observed growing ~11x (77 MB ->
117
117
  // 849 MB) in ~24h, so 7d was far too permissive at the observed growth
118
- // rate. 24h is a tighter mitigation while the growth's root cause is
119
- // investigated (see also the OPENCODE_VERSION pin above and the
120
- // [memwatch] telemetry in entrypoint.sh). Sweep runs on the default 1h
121
- // interval and never touches a protected/active session.
118
+ // rate. 24h is the tighter mitigation that came out of that (see also the
119
+ // OPENCODE_VERSION pin above). Sweep runs on the default 1h interval and
120
+ // never touches a protected/active session.
122
121
  EVIDENT_SESSION_CLEANUP_MAX_AGE: '24h',
123
122
  CLUSTER: cluster.clusterName,
124
123
  SERVICE: serviceName,
@@ -12,7 +12,7 @@ export interface EvidentMicrovmConstructProps {
12
12
  readonly imageSource: string;
13
13
  /**
14
14
  * ARN of the Lambda-managed base MicroVM image, from
15
- * `aws lambda list-managed-microvm-images`. A string, not a `CfnParameter`
15
+ * `aws lambda-microvms list-managed-microvm-images`. A string, not a `CfnParameter`
16
16
  * built here: the caller creates the parameter at ITS OWN stack scope
17
17
  * (preserving its logical id) and passes its resolved value down — a
18
18
  * construct must not mint parameters into a consumer's stack.
@@ -20,7 +20,7 @@ export interface EvidentMicrovmConstructProps {
20
20
  readonly baseImageArn: string;
21
21
  /**
22
22
  * Version of the Lambda-managed base MicroVM image, from
23
- * `aws lambda list-managed-microvm-images`. See `baseImageArn` for why this
23
+ * `aws lambda-microvms list-managed-microvm-images`. See `baseImageArn` for why this
24
24
  * is a plain string.
25
25
  */
26
26
  readonly baseImageVersion: string;
@@ -41,6 +41,20 @@ export interface EvidentMicrovmConstructProps {
41
41
  * in its own scope, and passes the result in either case.
42
42
  */
43
43
  readonly doorbellSecret: secretsmanager.ISecret;
44
+ /**
45
+ * Optional runner secret whose JSON values are exported by `/run`. Omitting it
46
+ * leaves GitHub and MCP credentials unavailable to a generic consumer.
47
+ */
48
+ readonly runnerSecret?: secretsmanager.ISecret;
49
+ /**
50
+ * Optional runner OpenCode overlay. Absolute paths are image paths; relative
51
+ * paths are resolved from the baked workspace by the `/run` hook.
52
+ */
53
+ readonly runnerOpencodeConfigPath?: string;
54
+ /** Optional git identity the `/run` hook configures for agent commits. */
55
+ readonly gitUserName?: string;
56
+ /** Optional git email the `/run` hook configures for agent commits. */
57
+ readonly gitUserEmail?: string;
44
58
  /**
45
59
  * TCP port the image's hook server listens on, baked into both the image
46
60
  * (`HOOKS_PORT` env var) and the `MicrovmImage`'s `hooks.port` — a mismatch
@@ -81,7 +81,9 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
81
81
  // ANDed with the age condition and counts newer noncurrent versions of
82
82
  // the SAME key, but litestream's LTX keys are txid-unique and churn by
83
83
  // DELETE, so a churned key has exactly one noncurrent version and zero
84
- // newer ones.
84
+ // newer ones. `expiredObjectDeleteMarker` reaps the delete marker itself once
85
+ // its noncurrent version is gone — without it, the version index grows
86
+ // unboundedly, one marker per churned LTX key.
85
87
  const durableState = new s3.Bucket(this, 'DurableState', {
86
88
  encryption: s3.BucketEncryption.S3_MANAGED,
87
89
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
@@ -98,6 +100,7 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
98
100
  {
99
101
  id: 'ExpireNoncurrentVersions',
100
102
  noncurrentVersionExpiration: cdk.Duration.days(7),
103
+ expiredObjectDeleteMarker: true,
101
104
  },
102
105
  ],
103
106
  });
@@ -118,6 +121,12 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
118
121
  // already bakes in for its own runners (evident-scale-to-zero-construct.ts:191).
119
122
  // Safe to bake: identical for every VM from this image version.
120
123
  EVIDENT_SESSION_CLEANUP_MAX_AGE: '24h',
124
+ ...(props.runnerOpencodeConfigPath
125
+ ? { RUNNER_OPENCODE_CONFIG: props.runnerOpencodeConfigPath }
126
+ : {}),
127
+ ...(props.runnerSecret ? { RUNNER_SECRET_ARN: props.runnerSecret.secretArn } : {}),
128
+ ...(props.gitUserName ? { GIT_USER_NAME: props.gitUserName } : {}),
129
+ ...(props.gitUserEmail ? { GIT_USER_EMAIL: props.gitUserEmail } : {}),
121
130
  };
122
131
  // ...and the hooks configuration every shape's image is built with.
123
132
  const imageHooks = {
@@ -176,6 +185,8 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
176
185
  // customer account, and what Phase 4 (#263) has to tighten when this goes
177
186
  // multi-tenant.
178
187
  durableState.grantReadWrite(defaultImage.executionRole);
188
+ // The image receives only an ARN; `/run` reads the secret value at runtime.
189
+ props.runnerSecret?.grantRead(defaultImage.executionRole);
179
190
  // The shape catalogue the controller resolves a doorbell's `shape` field
180
191
  // against, and advertises over the describe channel (#723). One JSON
181
192
  // array, built with `Stack.toJsonString` — not `JSON.stringify` — because
@@ -37,7 +37,7 @@ RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
37
37
  # binary assertion further down fails the build if any required binary is gone.
38
38
  RUN apt-get update \
39
39
  && apt-get install -y --no-install-recommends \
40
- ca-certificates curl git jq less procps ripgrep \
40
+ ca-certificates curl git jq less procps psmisc ripgrep \
41
41
  postgresql postgresql-contrib direnv \
42
42
  gnupg unzip \
43
43
  # --- GitHub CLI (`gh`) from the official apt repo ---
@@ -211,13 +211,13 @@ load_state_config() {
211
211
  # `model-auth-ready` therefore stays a boot-time diagnostic: its only output is
212
212
  # a log line, since the runtime forwards no hook stderr to the doorbell caller.
213
213
 
214
- # How long restore_credentials' shared step budget is, covering all THREE of
215
- # its synchroniser calls (restore claude, restore opencode, model-auth-ready)
216
- # as one window rather than a timeout apiece — a fixed per-call cap at the same
214
+ # How long restore_credentials' shared step budget is, covering the runner-secret
215
+ # fetch plus its three synchroniser calls (restore claude, restore opencode,
216
+ # model-auth-ready) as one window rather than a timeout apiece — a fixed per-call cap at the same
217
217
  # total would false-fire on the ordinary case of one slow call (a slow S3 GET),
218
218
  # which is precisely the boot this exists to keep healthy. From #930's 3-boot
219
- # sample: the step measured ~3-5s total (~1.2s per call, three node cold
220
- # starts of a 1.8 MB bundle) — a WEAK estimate this file's own
219
+ # sample: the step measured ~3-5s total (~1.2s per synchroniser call, three
220
+ # node cold starts of a 1.8 MB bundle) — a WEAK estimate this file's own
221
221
  # SYNCHRONISER-TIMING lines are what will sharpen for real. Too tight and a
222
222
  # routine slow call boots this VM with no model credentials until an operator
223
223
  # reconnects it; too loose and a hung call burns more of the hook's own
@@ -229,6 +229,7 @@ CREDENTIAL_RESTORE_DEADLINE_SECONDS="${EVIDENT_CREDENTIAL_RESTORE_DEADLINE_SECON
229
229
  # and a call that ignored it would be unbounded again). Hard ceiling on the
230
230
  # step: DEADLINE + this = 10s, once — not per call.
231
231
  CREDENTIAL_RESTORE_KILL_GRACE_SECONDS=2
232
+ GITHUB_PROBE_DEADLINE_SECONDS="${EVIDENT_GITHUB_PROBE_DEADLINE_SECONDS:-10}"
232
233
 
233
234
  # What is left of the shared step budget, in whole seconds, `step_started_s`
234
235
  # seconds after it began. `SECONDS` (a bash builtin with no failure mode,
@@ -268,12 +269,66 @@ bounded_restore_call() {
268
269
  return 0
269
270
  }
270
271
 
272
+ fetch_runner_secret() {
273
+ local step_started_s="$1" remaining rc=0 payload value stderr_file started_ms populated=0 missing=0
274
+ if [ -z "${RUNNER_SECRET_ARN:-}" ]; then
275
+ log "runner secret is not configured; continuing without GitHub and MCP credentials"
276
+ return 0
277
+ fi
278
+ remaining="$(remaining_credential_budget "${step_started_s}")"
279
+ if [ "${remaining}" -lt 1 ]; then
280
+ warn "CREDENTIAL-RESTORE-SKIPPED: runner-secret skipped; the ${CREDENTIAL_RESTORE_DEADLINE_SECONDS}s credential-restore budget is exhausted"
281
+ return 0
282
+ fi
283
+ if ! stderr_file="$(mktemp /dev/shm/runner-secret-stderr.XXXXXX)"; then
284
+ warn "RUNNER-SECRET-STDERR-UNAVAILABLE: could not allocate diagnostic storage; continuing without runner credentials"
285
+ return 0
286
+ fi
287
+ started_ms="$(now_ms)"
288
+ payload="$(timeout -k "${CREDENTIAL_RESTORE_KILL_GRACE_SECONDS}" "${remaining}" aws secretsmanager get-secret-value --secret-id "${RUNNER_SECRET_ARN}" --query SecretString --output text 2>"${stderr_file}")" || rc=$?
289
+ log_elapsed_since runner-secret-fetch "${started_ms}" "${rc}"
290
+ if [ "${rc}" -eq 124 ] || [ "${rc}" -eq 137 ]; then
291
+ warn "CREDENTIAL-RESTORE-TIMEOUT: runner-secret did not finish within its ${remaining}s share of the ${CREDENTIAL_RESTORE_DEADLINE_SECONDS}s credential-restore deadline; continuing without it"
292
+ rm -f "${stderr_file}"
293
+ return 0
294
+ fi
295
+ if [ "${rc}" -ne 0 ]; then
296
+ warn "RUNNER-SECRET-UNREADABLE: $(<"${stderr_file}")"
297
+ rm -f "${stderr_file}"
298
+ return 0
299
+ fi
300
+ rm -f "${stderr_file}"
301
+ if ! jq -e 'type == "object"' >/dev/null 2>&1 <<<"${payload}"; then
302
+ warn "RUNNER-SECRET-UNPARSEABLE: secret value is not a JSON object"
303
+ return 0
304
+ fi
305
+ local -a populated_keys=()
306
+ for key in GH_TOKEN BRAVE_API_KEY CLOUDFLARE_API_TOKEN NEON_API_KEY; do
307
+ value="$(jq -r --arg k "${key}" '.[$k] // empty' <<<"${payload}")"
308
+ if [ -n "${value}" ]; then
309
+ export "${key}=${value}"
310
+ populated=$((populated + 1))
311
+ populated_keys+=("${key}")
312
+ else
313
+ missing=$((missing + 1))
314
+ fi
315
+ done
316
+ if [ "${populated}" -eq 0 ]; then
317
+ warn "RUNNER-SECRET-UNPOPULATED: populate the runner secret as documented in infrastructure/evident-microvm/README.md"
318
+ else
319
+ log "RUNNER-SECRET-OK: populated ${populated_keys[*]}; ${missing} allow-listed keys empty or absent"
320
+ fi
321
+ return 0
322
+ }
323
+
271
324
  restore_credentials() {
272
325
  local step_started_ms step_started_s
273
326
  step_started_ms="$(now_ms)"
274
327
  step_started_s="${SECONDS}"
275
328
 
276
329
  load_state_config || return 1
330
+ # This shares the existing bounded window so /run's worst-case duration does not grow.
331
+ fetch_runner_secret "${step_started_s}"
277
332
  bounded_restore_call restore-claude "${step_started_s}" restore claude || return 1
278
333
  bounded_restore_call restore-opencode "${step_started_s}" restore opencode || return 1
279
334
 
@@ -313,7 +368,7 @@ infrastructure/evident-microvm/README.md."
313
368
  fi
314
369
  # The step total, in the SAME greppable shape as the per-call lines above, so
315
370
  # one query answers both "what does credential restore cost?" and "which of
316
- # its three calls cost it". Bounded now by CREDENTIAL_RESTORE_DEADLINE_SECONDS
371
+ # its four operations cost it". Bounded now by CREDENTIAL_RESTORE_DEADLINE_SECONDS
317
372
  # + CREDENTIAL_RESTORE_KILL_GRACE_SECONDS as one shared window (see the
318
373
  # comment above that constant) rather than per call. `rc=0` is deliberate
319
374
  # here, not an absence of failure modes: a timed-out call already warned by
@@ -323,6 +378,55 @@ infrastructure/evident-microvm/README.md."
323
378
  return 0
324
379
  }
325
380
 
381
+ apply_runner_opencode_config() {
382
+ if [ -z "${RUNNER_OPENCODE_CONFIG:-}" ]; then
383
+ log "runner OpenCode config is not configured; using the baked project config"
384
+ return 0
385
+ fi
386
+ local source="${RUNNER_OPENCODE_CONFIG}" target="opencode.json"
387
+ [[ "${source}" = /* ]] || source="${WORKSPACE}/${source}"
388
+ [ -f "${WORKSPACE}/opencode.jsonc" ] && target="opencode.jsonc"
389
+ if [ ! -f "${source}" ]; then
390
+ error "RUNNER-OPENCODE-CONFIG-MISSING: ${source} is not a file; headless turns will wedge on the first external-directory permission prompt (#563)"
391
+ return 0
392
+ fi
393
+ cp "${source}" "${WORKSPACE}/${target}"
394
+ git -C "${WORKSPACE}" update-index --skip-worktree "${target}" 2>/dev/null \
395
+ || warn "could not mark ${target} skip-worktree; it may show as a local change"
396
+ log "Applied runner OpenCode config ${source} to ${WORKSPACE}/${target}"
397
+ }
398
+
399
+ configure_github_access() {
400
+ if [ -z "${GH_TOKEN:-}" ]; then
401
+ warn "GITHUB-CREDENTIALS-MISSING: GH_TOKEN is unavailable; see RUNNER-SECRET-* above"
402
+ return 0
403
+ fi
404
+ export GIT_CONFIG_GLOBAL=/tmp/gitconfig
405
+ if ! : >"${GIT_CONFIG_GLOBAL}" ||
406
+ ! git config --global user.name "${GIT_USER_NAME:-evident-bot}" ||
407
+ ! git config --global user.email "${GIT_USER_EMAIL:-evident-bot@users.noreply.github.com}" ||
408
+ ! git config --global init.defaultBranch main ||
409
+ ! printf '%s\n' '#!/usr/bin/env bash' '[ "$1" = get ] || exit 0' 'echo username=x-access-token' 'echo "password=${GH_TOKEN}"' >/tmp/git-credential-helper.sh ||
410
+ ! chmod 0700 /tmp/git-credential-helper.sh ||
411
+ ! git config --global credential."https://github.com".helper /tmp/git-credential-helper.sh; then
412
+ warn "GITHUB-SETUP-FAILED: could not configure local git credentials; continuing without GitHub access"
413
+ return 0
414
+ fi
415
+ (
416
+ local output rc=0 login repo_url repo
417
+ output="$(timeout -k 1 "${GITHUB_PROBE_DEADLINE_SECONDS}" gh api user --jq .login 2>&1)" || rc=$?
418
+ if [ "${rc}" -eq 124 ] || [ "${rc}" -eq 137 ]; then warn "GITHUB-PROBE-TIMEOUT: auth probe exceeded ${GITHUB_PROBE_DEADLINE_SECONDS}s"; return; fi
419
+ if [ "${rc}" -ne 0 ]; then warn "GITHUB-AUTH-REJECTED: ${output}"; return; fi
420
+ log "GITHUB-AUTH-OK: ${output}"
421
+ repo_url="$(git -C "${WORKSPACE}" remote get-url origin 2>/dev/null || true)"
422
+ repo="$(printf '%s' "${repo_url}" | sed -E 's#(https://github.com/|git@github.com:)##; s#\.git$##')"
423
+ [ -n "${repo}" ] || return
424
+ output="$(timeout -k 1 "${GITHUB_PROBE_DEADLINE_SECONDS}" gh api "repos/${repo}" --jq .full_name 2>&1)" || rc=$?
425
+ if [ "${rc}" -eq 124 ] || [ "${rc}" -eq 137 ]; then warn "GITHUB-PROBE-TIMEOUT: repository probe for ${repo} exceeded ${GITHUB_PROBE_DEADLINE_SECONDS}s"; return; fi
426
+ [ "${rc}" -eq 0 ] || warn "GITHUB-REPO-INACCESSIBLE: ${repo}: ${output}"
427
+ ) &
428
+ }
429
+
326
430
  # Best-effort by design: /suspend must still drop the tunnel and /terminate must
327
431
  # still clean up, so a flush that cannot happen is loud but never fatal.
328
432
  sync_credentials() {
@@ -79,7 +79,13 @@ check_runner_key "${runner_key}" "${api_url}" || exit 1
79
79
  restore_session_db
80
80
  log "session DB restore done ${SECONDS}s into the hook"
81
81
 
82
- # 7 — opencode. Started here, not at build time: a warm process in the shared
82
+ # 7 — apply the overlay before OpenCode resolves its project configuration.
83
+ apply_runner_opencode_config
84
+
85
+ # 8 — configure git after credentials are restored and before agent shells start.
86
+ configure_github_access
87
+
88
+ # 9 — opencode. Started here, not at build time: a warm process in the shared
83
89
  # snapshot would carry its installation id and database into every VM. Not
84
90
  # waited on: a slow opencode boot is not a reason to fail /run (the tunnel CLI
85
91
  # auto-starts opencode when it finds none healthy,
@@ -87,14 +93,14 @@ log "session DB restore done ${SECONDS}s into the hook"
87
93
  # reclaims a runner that never comes online).
88
94
  start_opencode
89
95
 
90
- # 8 — begin replicating the session DB (#812 WI-3), now that opencode has
96
+ # 10 — begin replicating the session DB (#812 WI-3), now that opencode has
91
97
  # opened it and before any work can arrive over the tunnel. Bare, like
92
98
  # restore_session_db above: start_litestream never returns non-zero (every
93
99
  # guard inside it is its own `return 0`), so there is nothing here for
94
100
  # `set -e` to abort on.
95
101
  start_litestream
96
102
 
97
- # 9 — the first per-VM identity on the wire. The subshell's umask makes the file
103
+ # 11 — the first per-VM identity on the wire. The subshell's umask makes the file
98
104
  # unreadable to anyone else from the moment it exists, before the key is in it.
99
105
  (
100
106
  umask 077
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evident-ai/runner-cdk",
3
- "version": "0.1.1-dev.04ef8b1",
3
+ "version": "0.1.1-dev.04f2155",
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,13 +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 src/microvm/image/hook-server.ts, which imports @evident-ai/lambda-microvm-runtime by its built `main`. Chained into `build` rather than left to turbo's `^build` because publish-runner-cdk.yaml invokes `pnpm --filter @evident-ai/runner-cdk build` directly, which bypasses turbo entirely.",
19
- "build-bundled-deps": "pnpm --filter @evident-ai/lambda-microvm-runtime build",
18
+ "//build-bundled-deps": "scripts/build.ts esbuild-bundles src/microvm/image/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
+ "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 infrastructure/evident-runner/src/runner-cdk-published-surface.test.ts. The matching devDependency is workspace:* so this monorepo's own build compiles against packages/lambda-microvm-cdk's source.",
25
26
  "peerDependencies": {
26
27
  "@evident-ai/lambda-microvm-cdk": "^0.1.1",
27
28
  "aws-cdk-lib": "^2.240.0",
@@ -31,7 +32,7 @@
31
32
  "@aws-sdk/client-ecs": "^3.682.0",
32
33
  "@aws-sdk/client-lambda-microvms": "^3.1095.0",
33
34
  "@aws-sdk/client-secrets-manager": "^3.682.0",
34
- "@evident-ai/lambda-microvm-cdk": "^0.1.1",
35
+ "@evident-ai/lambda-microvm-cdk": "workspace:*",
35
36
  "@evident-ai/lambda-microvm-runtime": "workspace:*",
36
37
  "@evident/webhook-signature": "workspace:*",
37
38
  "@types/node": "^22",
@@ -51,6 +52,6 @@
51
52
  "directory": "packages/runner-cdk"
52
53
  },
53
54
  "homepage": "https://evident.run",
54
- "author": "Evident <contact@evident.run>",
55
+ "author": "Evident <hello@evident.run>",
55
56
  "license": "MIT"
56
57
  }