@evident-ai/runner-cdk 3.5.2-dev.bcf1140 → 3.5.2-dev.da3340e
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 +4 -3
- package/dist/controller-lambda/handler.js +58 -63
- package/dist/microvm/controller/{doorbell.d.ts → doorbell-request.d.ts} +6 -6
- package/dist/microvm/controller/{doorbell.js → doorbell-request.js} +2 -2
- package/dist/microvm/controller/handle-doorbell.js +60 -60
- package/dist/microvm/image/stage-context.d.ts +1 -1
- package/dist/microvm/image/stage-context.js +1 -1
- package/dist/microvm-image-context/hooks/common.sh +0 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,8 +76,8 @@ agent.selfStopRoleArn;
|
|
|
76
76
|
agent.replicaPrefix;
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
For
|
|
80
|
-
|
|
79
|
+
For the full end-to-end walkthrough for this construct, see
|
|
80
|
+
[GETTING-STARTED.md](https://github.com/evident-run/evident-run/blob/main/aws/runner-cdk/GETTING-STARTED.md).
|
|
81
81
|
|
|
82
82
|
## Props
|
|
83
83
|
|
|
@@ -162,7 +162,8 @@ new EvidentMicrovmConstruct(this, 'Runner', {
|
|
|
162
162
|
Pass `overlayDir` to add deployment-specific build steps to the staged image. The
|
|
163
163
|
directory provides `setup-root` for root-owned installs and `setup-workspace` for
|
|
164
164
|
dependency installation and workspace build warm-up. See the [MicroVM image
|
|
165
|
-
README](
|
|
165
|
+
README](https://github.com/evident-run/evident-run/blob/main/runner/docker-images/microvm/README.md)
|
|
166
|
+
for the full overlay contract.
|
|
166
167
|
|
|
167
168
|
The image bakes your repository checked out but not installed and makes no assumptions about
|
|
168
169
|
its package manager. To speed up first boot, install dependencies from your overlay's
|
|
@@ -49818,7 +49818,7 @@ function verifyWakeSignature(rawBody, signatureHeader, secret) {
|
|
|
49818
49818
|
return (0, import_node_crypto11.timingSafeEqual)(providedBuffer, expectedBuffer);
|
|
49819
49819
|
}
|
|
49820
49820
|
|
|
49821
|
-
// src/microvm/controller/doorbell.ts
|
|
49821
|
+
// src/microvm/controller/doorbell-request.ts
|
|
49822
49822
|
var WAKE_EVENT_TYPES = ["agent.message_queued", "agent.wake_requested"];
|
|
49823
49823
|
var SUSPEND_EVENT_TYPE = "runner.suspend_requested";
|
|
49824
49824
|
var SHAPES_EVENT_TYPE = "runner.shapes_requested";
|
|
@@ -49838,7 +49838,7 @@ function nonBlankString(value) {
|
|
|
49838
49838
|
function isWakeType(value) {
|
|
49839
49839
|
return WAKE_EVENT_TYPES.includes(value);
|
|
49840
49840
|
}
|
|
49841
|
-
function
|
|
49841
|
+
function parseDoorbellRequest(rawBody) {
|
|
49842
49842
|
let body;
|
|
49843
49843
|
try {
|
|
49844
49844
|
body = JSON.parse(rawBody);
|
|
@@ -49979,9 +49979,9 @@ function decide({
|
|
|
49979
49979
|
action: { kind: action }
|
|
49980
49980
|
};
|
|
49981
49981
|
}
|
|
49982
|
-
function clientTokenFor(
|
|
49983
|
-
const generation =
|
|
49984
|
-
return (0, import_node_crypto12.createHash)("sha256").update(`${
|
|
49982
|
+
function clientTokenFor(doorbellRequest) {
|
|
49983
|
+
const generation = doorbellRequest.microvmId ?? doorbellRequest.occurredAt;
|
|
49984
|
+
return (0, import_node_crypto12.createHash)("sha256").update(`${doorbellRequest.runnerId}:${generation}`).digest("hex");
|
|
49985
49985
|
}
|
|
49986
49986
|
function parseImageVersion(value) {
|
|
49987
49987
|
const parts = value.trim().split(".");
|
|
@@ -50001,17 +50001,17 @@ function compareImageVersions(a7, b7) {
|
|
|
50001
50001
|
}
|
|
50002
50002
|
return 0;
|
|
50003
50003
|
}
|
|
50004
|
-
async function shouldRecreateForNewerImage(
|
|
50004
|
+
async function shouldRecreateForNewerImage(doorbellRequest, shape, runningVersion, microvm) {
|
|
50005
50005
|
if (runningVersion === void 0) {
|
|
50006
50006
|
console.warn(
|
|
50007
|
-
`[doorbell] GetMicrovm reported no imageVersion for runner ${
|
|
50007
|
+
`[doorbell] GetMicrovm reported no imageVersion for runner ${doorbellRequest.runnerId}; resuming`
|
|
50008
50008
|
);
|
|
50009
50009
|
return false;
|
|
50010
50010
|
}
|
|
50011
50011
|
const running = parseImageVersion(runningVersion);
|
|
50012
50012
|
if (running === void 0) {
|
|
50013
50013
|
console.warn(
|
|
50014
|
-
`[doorbell] unparseable running imageVersion "${runningVersion}" for runner ${
|
|
50014
|
+
`[doorbell] unparseable running imageVersion "${runningVersion}" for runner ${doorbellRequest.runnerId}; resuming`
|
|
50015
50015
|
);
|
|
50016
50016
|
return false;
|
|
50017
50017
|
}
|
|
@@ -50020,14 +50020,14 @@ async function shouldRecreateForNewerImage(doorbell, shape, runningVersion, micr
|
|
|
50020
50020
|
latestVersion = await microvm.latestImageVersion(shape.imageArn);
|
|
50021
50021
|
} catch (error3) {
|
|
50022
50022
|
console.warn(
|
|
50023
|
-
`[doorbell] GetMicrovmImage failed for runner ${
|
|
50023
|
+
`[doorbell] GetMicrovmImage failed for runner ${doorbellRequest.runnerId}; resuming on the running image ${runningVersion}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
50024
50024
|
);
|
|
50025
50025
|
return false;
|
|
50026
50026
|
}
|
|
50027
50027
|
const latest = latestVersion === void 0 ? void 0 : parseImageVersion(latestVersion);
|
|
50028
50028
|
if (latest === void 0) {
|
|
50029
50029
|
console.warn(
|
|
50030
|
-
`[doorbell] no comparable latest image version (${latestVersion ?? "none"}) for runner ${
|
|
50030
|
+
`[doorbell] no comparable latest image version (${latestVersion ?? "none"}) for runner ${doorbellRequest.runnerId}; resuming on the running image ${runningVersion}`
|
|
50031
50031
|
);
|
|
50032
50032
|
return false;
|
|
50033
50033
|
}
|
|
@@ -50049,7 +50049,7 @@ async function handleDoorbell({
|
|
|
50049
50049
|
reason: REASON.invalidSignature
|
|
50050
50050
|
});
|
|
50051
50051
|
}
|
|
50052
|
-
const parsed =
|
|
50052
|
+
const parsed = parseDoorbellRequest(rawBody);
|
|
50053
50053
|
if (!parsed.ok) {
|
|
50054
50054
|
return decide({
|
|
50055
50055
|
statusCode: 400,
|
|
@@ -50057,9 +50057,9 @@ async function handleDoorbell({
|
|
|
50057
50057
|
reason: parsed.reason
|
|
50058
50058
|
});
|
|
50059
50059
|
}
|
|
50060
|
-
const
|
|
50060
|
+
const doorbellRequest = parsed.doorbell;
|
|
50061
50061
|
const timing = { sleep: sleep4, random };
|
|
50062
|
-
if (
|
|
50062
|
+
if (doorbellRequest.type === SHAPES_EVENT_TYPE) {
|
|
50063
50063
|
return decide({
|
|
50064
50064
|
statusCode: 200,
|
|
50065
50065
|
action: "describe",
|
|
@@ -50067,43 +50067,43 @@ async function handleDoorbell({
|
|
|
50067
50067
|
shapes: shapes.advertise()
|
|
50068
50068
|
});
|
|
50069
50069
|
}
|
|
50070
|
-
if (
|
|
50071
|
-
const resolvedShape = shapes.resolve(
|
|
50070
|
+
if (doorbellRequest.type !== "runner.suspend_requested") {
|
|
50071
|
+
const resolvedShape = shapes.resolve(doorbellRequest.shape);
|
|
50072
50072
|
if (resolvedShape === void 0) {
|
|
50073
50073
|
return decide({
|
|
50074
50074
|
statusCode: 400,
|
|
50075
50075
|
action: "rejected",
|
|
50076
50076
|
reason: REASON.unknownShape,
|
|
50077
|
-
runnerId:
|
|
50078
|
-
microvmId:
|
|
50077
|
+
runnerId: doorbellRequest.runnerId,
|
|
50078
|
+
microvmId: doorbellRequest.microvmId
|
|
50079
50079
|
});
|
|
50080
50080
|
}
|
|
50081
|
-
return handleWakeDoorbell(
|
|
50081
|
+
return handleWakeDoorbell(doorbellRequest, resolvedShape, microvm, timing);
|
|
50082
50082
|
}
|
|
50083
|
-
return suspendMicrovm(
|
|
50083
|
+
return suspendMicrovm(doorbellRequest, microvm, timing);
|
|
50084
50084
|
}
|
|
50085
|
-
async function handleWakeDoorbell(
|
|
50086
|
-
if (
|
|
50087
|
-
return runMicrovm(
|
|
50085
|
+
async function handleWakeDoorbell(doorbellRequest, shape, microvm, timing) {
|
|
50086
|
+
if (doorbellRequest.microvmId === void 0) {
|
|
50087
|
+
return runMicrovm(doorbellRequest, shape, microvm, timing, false, REASON.noMicrovmId);
|
|
50088
50088
|
}
|
|
50089
50089
|
let described;
|
|
50090
50090
|
let polled = false;
|
|
50091
50091
|
try {
|
|
50092
|
-
described = await microvm.describe(
|
|
50092
|
+
described = await microvm.describe(doorbellRequest.microvmId);
|
|
50093
50093
|
if (described.state === "SUSPENDING") {
|
|
50094
|
-
described = await pollWhileSuspending(
|
|
50094
|
+
described = await pollWhileSuspending(doorbellRequest.microvmId, microvm, timing.sleep);
|
|
50095
50095
|
polled = true;
|
|
50096
50096
|
}
|
|
50097
50097
|
} catch (error3) {
|
|
50098
50098
|
console.error(
|
|
50099
|
-
`[doorbell] GetMicrovm failed for runner ${
|
|
50099
|
+
`[doorbell] GetMicrovm failed for runner ${doorbellRequest.runnerId}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
50100
50100
|
);
|
|
50101
50101
|
return decide({
|
|
50102
50102
|
statusCode: 500,
|
|
50103
50103
|
action: "none",
|
|
50104
50104
|
reason: REASON.getStateFailed,
|
|
50105
|
-
runnerId:
|
|
50106
|
-
microvmId:
|
|
50105
|
+
runnerId: doorbellRequest.runnerId,
|
|
50106
|
+
microvmId: doorbellRequest.microvmId
|
|
50107
50107
|
});
|
|
50108
50108
|
}
|
|
50109
50109
|
const state3 = described.state;
|
|
@@ -50113,8 +50113,8 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
50113
50113
|
action: "none",
|
|
50114
50114
|
reason: REASON.suspendingPollExhausted,
|
|
50115
50115
|
state: state3,
|
|
50116
|
-
runnerId:
|
|
50117
|
-
microvmId:
|
|
50116
|
+
runnerId: doorbellRequest.runnerId,
|
|
50117
|
+
microvmId: doorbellRequest.microvmId,
|
|
50118
50118
|
polled
|
|
50119
50119
|
});
|
|
50120
50120
|
}
|
|
@@ -50126,16 +50126,16 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
50126
50126
|
action: "none",
|
|
50127
50127
|
reason: state3 === "RUNNING" ? REASON.alreadyRunning : REASON.alreadyStarting,
|
|
50128
50128
|
state: state3,
|
|
50129
|
-
runnerId:
|
|
50130
|
-
microvmId:
|
|
50129
|
+
runnerId: doorbellRequest.runnerId,
|
|
50130
|
+
microvmId: doorbellRequest.microvmId,
|
|
50131
50131
|
polled,
|
|
50132
50132
|
startedAt: described.startedAt,
|
|
50133
50133
|
imageVersion: described.imageVersion
|
|
50134
50134
|
});
|
|
50135
50135
|
case "SUSPENDED":
|
|
50136
|
-
if (
|
|
50136
|
+
if (doorbellRequest.recreateOnOutdatedImage === true && await shouldRecreateForNewerImage(doorbellRequest, shape, described.imageVersion, microvm)) {
|
|
50137
50137
|
return runMicrovm(
|
|
50138
|
-
|
|
50138
|
+
doorbellRequest,
|
|
50139
50139
|
shape,
|
|
50140
50140
|
microvm,
|
|
50141
50141
|
timing,
|
|
@@ -50145,8 +50145,8 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
50145
50145
|
);
|
|
50146
50146
|
}
|
|
50147
50147
|
return resumeMicrovm(
|
|
50148
|
-
|
|
50149
|
-
|
|
50148
|
+
doorbellRequest,
|
|
50149
|
+
doorbellRequest.microvmId,
|
|
50150
50150
|
shape,
|
|
50151
50151
|
microvm,
|
|
50152
50152
|
timing,
|
|
@@ -50156,7 +50156,7 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
50156
50156
|
);
|
|
50157
50157
|
default:
|
|
50158
50158
|
return runMicrovm(
|
|
50159
|
-
|
|
50159
|
+
doorbellRequest,
|
|
50160
50160
|
shape,
|
|
50161
50161
|
microvm,
|
|
50162
50162
|
timing,
|
|
@@ -50182,14 +50182,14 @@ function isClientTokenParameterMismatch(error3) {
|
|
|
50182
50182
|
const message = typeof e7.message === "string" ? e7.message.toLowerCase() : "";
|
|
50183
50183
|
return message.includes("clienttoken") && message.includes("different request parameters");
|
|
50184
50184
|
}
|
|
50185
|
-
async function runMicrovm(
|
|
50185
|
+
async function runMicrovm(doorbellRequest, shape, microvm, timing, polled, reason, state3) {
|
|
50186
50186
|
let started;
|
|
50187
50187
|
try {
|
|
50188
50188
|
started = await withThrottleRetry(
|
|
50189
50189
|
() => microvm.run({
|
|
50190
50190
|
imageIdentifier: shape.imageArn,
|
|
50191
|
-
runHookPayload:
|
|
50192
|
-
clientToken: clientTokenFor(
|
|
50191
|
+
runHookPayload: doorbellRequest.runHookPayload,
|
|
50192
|
+
clientToken: clientTokenFor(doorbellRequest)
|
|
50193
50193
|
}),
|
|
50194
50194
|
timing
|
|
50195
50195
|
);
|
|
@@ -50198,26 +50198,26 @@ async function runMicrovm(doorbell, shape, microvm, timing, polled, reason, stat
|
|
|
50198
50198
|
const name = error3 instanceof Error ? error3.name : "unknown";
|
|
50199
50199
|
const message = error3 instanceof Error ? error3.message : String(error3);
|
|
50200
50200
|
console.warn(
|
|
50201
|
-
`[doorbell] RunMicrovm clientToken already used for runner ${
|
|
50201
|
+
`[doorbell] RunMicrovm clientToken already used for runner ${doorbellRequest.runnerId}: ${name}: ${message}`
|
|
50202
50202
|
);
|
|
50203
50203
|
return decide({
|
|
50204
50204
|
statusCode: 200,
|
|
50205
50205
|
action: "run",
|
|
50206
50206
|
reason: REASON.runAlreadyStartedForToken,
|
|
50207
50207
|
state: state3,
|
|
50208
|
-
runnerId:
|
|
50208
|
+
runnerId: doorbellRequest.runnerId,
|
|
50209
50209
|
polled
|
|
50210
50210
|
});
|
|
50211
50211
|
}
|
|
50212
50212
|
console.error(
|
|
50213
|
-
`[doorbell] RunMicrovm failed for runner ${
|
|
50213
|
+
`[doorbell] RunMicrovm failed for runner ${doorbellRequest.runnerId}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
50214
50214
|
);
|
|
50215
50215
|
return decide({
|
|
50216
50216
|
statusCode: 500,
|
|
50217
50217
|
action: "run",
|
|
50218
50218
|
reason: REASON.runFailed,
|
|
50219
50219
|
state: state3,
|
|
50220
|
-
runnerId:
|
|
50220
|
+
runnerId: doorbellRequest.runnerId,
|
|
50221
50221
|
polled
|
|
50222
50222
|
});
|
|
50223
50223
|
}
|
|
@@ -50226,22 +50226,22 @@ async function runMicrovm(doorbell, shape, microvm, timing, polled, reason, stat
|
|
|
50226
50226
|
action: "run",
|
|
50227
50227
|
reason,
|
|
50228
50228
|
state: state3,
|
|
50229
|
-
runnerId:
|
|
50229
|
+
runnerId: doorbellRequest.runnerId,
|
|
50230
50230
|
microvmId: started.microvmId,
|
|
50231
50231
|
polled,
|
|
50232
50232
|
startedAt: started.startedAt,
|
|
50233
50233
|
imageVersion: started.imageVersion
|
|
50234
50234
|
});
|
|
50235
50235
|
}
|
|
50236
|
-
async function resumeMicrovm(
|
|
50236
|
+
async function resumeMicrovm(doorbellRequest, microvmId, shape, microvm, timing, polled, startedAt, imageVersion) {
|
|
50237
50237
|
try {
|
|
50238
50238
|
await withThrottleRetry(() => microvm.resume(microvmId), timing);
|
|
50239
50239
|
} catch (error3) {
|
|
50240
50240
|
console.error(
|
|
50241
|
-
`[doorbell] ResumeMicrovm failed for runner ${
|
|
50241
|
+
`[doorbell] ResumeMicrovm failed for runner ${doorbellRequest.runnerId}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
50242
50242
|
);
|
|
50243
50243
|
return runMicrovm(
|
|
50244
|
-
|
|
50244
|
+
doorbellRequest,
|
|
50245
50245
|
shape,
|
|
50246
50246
|
microvm,
|
|
50247
50247
|
timing,
|
|
@@ -50255,21 +50255,21 @@ async function resumeMicrovm(doorbell, microvmId, shape, microvm, timing, polled
|
|
|
50255
50255
|
action: "resume",
|
|
50256
50256
|
reason: REASON.resumed,
|
|
50257
50257
|
state: "SUSPENDED",
|
|
50258
|
-
runnerId:
|
|
50258
|
+
runnerId: doorbellRequest.runnerId,
|
|
50259
50259
|
microvmId,
|
|
50260
50260
|
polled,
|
|
50261
50261
|
startedAt,
|
|
50262
50262
|
imageVersion
|
|
50263
50263
|
});
|
|
50264
50264
|
}
|
|
50265
|
-
async function suspendMicrovm(
|
|
50266
|
-
const microvmId =
|
|
50265
|
+
async function suspendMicrovm(doorbellRequest, microvm, timing) {
|
|
50266
|
+
const microvmId = doorbellRequest.microvmId;
|
|
50267
50267
|
if (microvmId === void 0) {
|
|
50268
50268
|
return decide({
|
|
50269
50269
|
statusCode: 200,
|
|
50270
50270
|
action: "none",
|
|
50271
50271
|
reason: REASON.suspendWithoutMicrovmId,
|
|
50272
|
-
runnerId:
|
|
50272
|
+
runnerId: doorbellRequest.runnerId
|
|
50273
50273
|
});
|
|
50274
50274
|
}
|
|
50275
50275
|
let state3;
|
|
@@ -50277,13 +50277,13 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
50277
50277
|
({ state: state3 } = await microvm.describe(microvmId));
|
|
50278
50278
|
} catch (error3) {
|
|
50279
50279
|
console.error(
|
|
50280
|
-
`[doorbell] GetMicrovm failed for runner ${
|
|
50280
|
+
`[doorbell] GetMicrovm failed for runner ${doorbellRequest.runnerId}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
50281
50281
|
);
|
|
50282
50282
|
return decide({
|
|
50283
50283
|
statusCode: 500,
|
|
50284
50284
|
action: "none",
|
|
50285
50285
|
reason: REASON.getStateFailed,
|
|
50286
|
-
runnerId:
|
|
50286
|
+
runnerId: doorbellRequest.runnerId,
|
|
50287
50287
|
microvmId
|
|
50288
50288
|
});
|
|
50289
50289
|
}
|
|
@@ -50293,7 +50293,7 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
50293
50293
|
action: "none",
|
|
50294
50294
|
reason: REASON.suspendAlreadyGone,
|
|
50295
50295
|
state: state3,
|
|
50296
|
-
runnerId:
|
|
50296
|
+
runnerId: doorbellRequest.runnerId,
|
|
50297
50297
|
microvmId
|
|
50298
50298
|
});
|
|
50299
50299
|
}
|
|
@@ -50301,14 +50301,14 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
50301
50301
|
await withThrottleRetry(() => microvm.suspend(microvmId), timing);
|
|
50302
50302
|
} catch (error3) {
|
|
50303
50303
|
console.error(
|
|
50304
|
-
`[doorbell] SuspendMicrovm failed for runner ${
|
|
50304
|
+
`[doorbell] SuspendMicrovm failed for runner ${doorbellRequest.runnerId}: ${error3 instanceof Error ? error3.message : String(error3)}`
|
|
50305
50305
|
);
|
|
50306
50306
|
return decide({
|
|
50307
50307
|
statusCode: 500,
|
|
50308
50308
|
action: "suspend",
|
|
50309
50309
|
reason: REASON.suspendFailed,
|
|
50310
50310
|
state: state3,
|
|
50311
|
-
runnerId:
|
|
50311
|
+
runnerId: doorbellRequest.runnerId,
|
|
50312
50312
|
microvmId
|
|
50313
50313
|
});
|
|
50314
50314
|
}
|
|
@@ -50317,7 +50317,7 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
50317
50317
|
action: "suspend",
|
|
50318
50318
|
reason: REASON.suspended,
|
|
50319
50319
|
state: state3,
|
|
50320
|
-
runnerId:
|
|
50320
|
+
runnerId: doorbellRequest.runnerId,
|
|
50321
50321
|
microvmId
|
|
50322
50322
|
});
|
|
50323
50323
|
}
|
|
@@ -50468,12 +50468,7 @@ function createRuntimeMicrovm(executionRoleArn) {
|
|
|
50468
50468
|
// No `idlePolicy` (AC5, #732) — deliberately, not an oversight.
|
|
50469
50469
|
// `maxIdleDurationSeconds` measures INBOUND endpoint traffic only; our
|
|
50470
50470
|
// runner is outbound-only (it dials the Cloudflare relay), so AWS sees
|
|
50471
|
-
// an idle endpoint no matter how busy the agent is.
|
|
50472
|
-
// this directly: a VM with `maxIdleDurationSeconds=60` and zero inbound
|
|
50473
|
-
// traffic was suspended after 71 s while its guest process was alive
|
|
50474
|
-
// and working (docs/spikes/lambda-microvms-phase0/README.md). AWS's own
|
|
50475
|
-
// Claude-agents guidance recommending `maxIdleDurationSeconds: 120`
|
|
50476
|
-
// targets VMs reached inbound and does not apply here. Idle is instead
|
|
50471
|
+
// an idle endpoint no matter how busy the agent is. Idle is instead
|
|
50477
50472
|
// decided IN the VM by `evident run --idle-timeout`
|
|
50478
50473
|
// (packages/runner-cdk/microvm-image/hooks/common.sh's `IDLE_TIMEOUT_SECONDS`), whose
|
|
50479
50474
|
// clean exit is what drives the controller's own `suspend()` below via
|
|
@@ -27,12 +27,12 @@ declare const WAKE_EVENT_TYPES: readonly ["agent.message_queued", "agent.wake_re
|
|
|
27
27
|
declare const SUSPEND_EVENT_TYPE = "runner.suspend_requested";
|
|
28
28
|
export declare const SHAPES_EVENT_TYPE = "runner.shapes_requested";
|
|
29
29
|
type WakeEventType = (typeof WAKE_EVENT_TYPES)[number];
|
|
30
|
-
type
|
|
30
|
+
type DoorbellRequestFields = {
|
|
31
31
|
runnerId: string;
|
|
32
32
|
occurredAt: string;
|
|
33
33
|
microvmId?: string;
|
|
34
34
|
};
|
|
35
|
-
export type
|
|
35
|
+
export type WakeDoorbellRequest = DoorbellRequestFields & {
|
|
36
36
|
type: WakeEventType;
|
|
37
37
|
runHookPayload: string;
|
|
38
38
|
/**
|
|
@@ -46,7 +46,7 @@ export type WakeDoorbell = DoorbellFields & {
|
|
|
46
46
|
*/
|
|
47
47
|
recreateOnOutdatedImage?: boolean;
|
|
48
48
|
};
|
|
49
|
-
export type
|
|
49
|
+
export type DoorbellRequest = WakeDoorbellRequest | (DoorbellRequestFields & {
|
|
50
50
|
type: typeof SUSPEND_EVENT_TYPE;
|
|
51
51
|
}) | {
|
|
52
52
|
type: typeof SHAPES_EVENT_TYPE;
|
|
@@ -67,12 +67,12 @@ export declare const DOORBELL_REJECTION: {
|
|
|
67
67
|
readonly runPayloadTooLarge: "run_payload_too_large";
|
|
68
68
|
};
|
|
69
69
|
export type DoorbellRejection = (typeof DOORBELL_REJECTION)[keyof typeof DOORBELL_REJECTION];
|
|
70
|
-
export type
|
|
70
|
+
export type ParsedDoorbellRequest = {
|
|
71
71
|
ok: true;
|
|
72
|
-
doorbell:
|
|
72
|
+
doorbell: DoorbellRequest;
|
|
73
73
|
} | {
|
|
74
74
|
ok: false;
|
|
75
75
|
reason: DoorbellRejection;
|
|
76
76
|
};
|
|
77
|
-
export declare function
|
|
77
|
+
export declare function parseDoorbellRequest(rawBody: string): ParsedDoorbellRequest;
|
|
78
78
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DOORBELL_REJECTION = exports.SHAPES_EVENT_TYPE = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.parseDoorbellRequest = parseDoorbellRequest;
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
/**
|
|
7
7
|
* The doorbell Evident POSTs to the controller's Function URL. `run_payload` is
|
|
@@ -52,7 +52,7 @@ function nonBlankString(value) {
|
|
|
52
52
|
function isWakeType(value) {
|
|
53
53
|
return WAKE_EVENT_TYPES.includes(value);
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function parseDoorbellRequest(rawBody) {
|
|
56
56
|
let body;
|
|
57
57
|
try {
|
|
58
58
|
body = JSON.parse(rawBody);
|
|
@@ -4,7 +4,7 @@ exports.handleDoorbell = handleDoorbell;
|
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const sdk_1 = require("@evident/sdk");
|
|
6
6
|
const constants_1 = require("../constants");
|
|
7
|
-
const
|
|
7
|
+
const doorbell_request_1 = require("./doorbell-request");
|
|
8
8
|
const throttle_retry_1 = require("./throttle-retry");
|
|
9
9
|
const REASON = {
|
|
10
10
|
invalidSignature: 'invalid_signature',
|
|
@@ -65,7 +65,7 @@ function decide({ statusCode, action, reason, state, runnerId, microvmId, starte
|
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* `RunMicrovm`'s idempotency key. Keyed on `runner_id` plus a "generation"
|
|
68
|
-
* marker: the dead VM's `microvm_id` on the recovery arms (`
|
|
68
|
+
* marker: the dead VM's `microvm_id` on the recovery arms (`doorbellRequest.microvmId`
|
|
69
69
|
* is defined — TERMINATED/TERMINATING/not_found and the resume-failed
|
|
70
70
|
* fallback), else `occurred_at` on the cold arm (no `microvm_id` yet). The id
|
|
71
71
|
* advances every VM generation — `mvm-A` dies, the token keyed on it starts
|
|
@@ -92,9 +92,9 @@ function decide({ statusCode, action, reason, state, runnerId, microvmId, starte
|
|
|
92
92
|
* that retried the same event with a different shape starts TWO concurrent
|
|
93
93
|
* 8-hour VMs instead of deduping to one.
|
|
94
94
|
*/
|
|
95
|
-
function clientTokenFor(
|
|
96
|
-
const generation =
|
|
97
|
-
return (0, node_crypto_1.createHash)('sha256').update(`${
|
|
95
|
+
function clientTokenFor(doorbellRequest) {
|
|
96
|
+
const generation = doorbellRequest.microvmId ?? doorbellRequest.occurredAt;
|
|
97
|
+
return (0, node_crypto_1.createHash)('sha256').update(`${doorbellRequest.runnerId}:${generation}`).digest('hex');
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* MicroVM image versions are dotted decimals (`48.0`, `50.0`), so they must NOT
|
|
@@ -140,15 +140,15 @@ function compareImageVersions(a, b) {
|
|
|
140
140
|
* unparseable all warn and return false, i.e. resume exactly as before
|
|
141
141
|
* (`development-workflow.mdc`: never fail a gate on absent evidence).
|
|
142
142
|
*/
|
|
143
|
-
async function shouldRecreateForNewerImage(
|
|
143
|
+
async function shouldRecreateForNewerImage(doorbellRequest, shape, runningVersion, microvm) {
|
|
144
144
|
if (runningVersion === undefined) {
|
|
145
|
-
console.warn(`[doorbell] GetMicrovm reported no imageVersion for runner ${
|
|
145
|
+
console.warn(`[doorbell] GetMicrovm reported no imageVersion for runner ${doorbellRequest.runnerId}; resuming`);
|
|
146
146
|
return false;
|
|
147
147
|
}
|
|
148
148
|
const running = parseImageVersion(runningVersion);
|
|
149
149
|
if (running === undefined) {
|
|
150
150
|
console.warn(`[doorbell] unparseable running imageVersion "${runningVersion}" for runner ` +
|
|
151
|
-
`${
|
|
151
|
+
`${doorbellRequest.runnerId}; resuming`);
|
|
152
152
|
return false;
|
|
153
153
|
}
|
|
154
154
|
let latestVersion;
|
|
@@ -159,14 +159,14 @@ async function shouldRecreateForNewerImage(doorbell, shape, runningVersion, micr
|
|
|
159
159
|
latestVersion = await microvm.latestImageVersion(shape.imageArn);
|
|
160
160
|
}
|
|
161
161
|
catch (error) {
|
|
162
|
-
console.warn(`[doorbell] GetMicrovmImage failed for runner ${
|
|
162
|
+
console.warn(`[doorbell] GetMicrovmImage failed for runner ${doorbellRequest.runnerId}; resuming on the ` +
|
|
163
163
|
`running image ${runningVersion}: ${error instanceof Error ? error.message : String(error)}`);
|
|
164
164
|
return false;
|
|
165
165
|
}
|
|
166
166
|
const latest = latestVersion === undefined ? undefined : parseImageVersion(latestVersion);
|
|
167
167
|
if (latest === undefined) {
|
|
168
168
|
console.warn(`[doorbell] no comparable latest image version (${latestVersion ?? 'none'}) for runner ` +
|
|
169
|
-
`${
|
|
169
|
+
`${doorbellRequest.runnerId}; resuming on the running image ${runningVersion}`);
|
|
170
170
|
return false;
|
|
171
171
|
}
|
|
172
172
|
// Strictly below, not merely different: a running version ABOVE the latest
|
|
@@ -182,7 +182,7 @@ async function handleDoorbell({ rawBody, signatureHeader, doorbellSecret, shapes
|
|
|
182
182
|
reason: REASON.invalidSignature,
|
|
183
183
|
});
|
|
184
184
|
}
|
|
185
|
-
const parsed = (0,
|
|
185
|
+
const parsed = (0, doorbell_request_1.parseDoorbellRequest)(rawBody);
|
|
186
186
|
if (!parsed.ok) {
|
|
187
187
|
return decide({
|
|
188
188
|
statusCode: 400,
|
|
@@ -190,10 +190,10 @@ async function handleDoorbell({ rawBody, signatureHeader, doorbellSecret, shapes
|
|
|
190
190
|
reason: parsed.reason,
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
|
-
const
|
|
193
|
+
const doorbellRequest = parsed.doorbell;
|
|
194
194
|
const timing = { sleep, random };
|
|
195
|
-
if (
|
|
196
|
-
// A pure read of deploy-time metadata — see doorbell.ts's header comment
|
|
195
|
+
if (doorbellRequest.type === doorbell_request_1.SHAPES_EVENT_TYPE) {
|
|
196
|
+
// A pure read of deploy-time metadata — see doorbell-request.ts's header comment
|
|
197
197
|
// (D2) for why replay is deliberately undefended here.
|
|
198
198
|
return decide({
|
|
199
199
|
statusCode: 200,
|
|
@@ -202,46 +202,46 @@ async function handleDoorbell({ rawBody, signatureHeader, doorbellSecret, shapes
|
|
|
202
202
|
shapes: shapes.advertise(),
|
|
203
203
|
});
|
|
204
204
|
}
|
|
205
|
-
// Resolve the requested shape immediately after
|
|
205
|
+
// Resolve the requested shape immediately after parseDoorbellRequest/the describe
|
|
206
206
|
// arm — BEFORE the suspend_requested branch and the whole state machine
|
|
207
207
|
// (D5). Resolving lazily inside runMicrovm would return 200 and silently
|
|
208
208
|
// ignore a removed shape on the RUNNING/PENDING no-op path and the
|
|
209
209
|
// successful-resume path, since runMicrovm is never reached on either. A
|
|
210
210
|
// suspend doorbell has no `shape` field, so nothing here runs for it.
|
|
211
|
-
if (
|
|
212
|
-
const resolvedShape = shapes.resolve(
|
|
211
|
+
if (doorbellRequest.type !== 'runner.suspend_requested') {
|
|
212
|
+
const resolvedShape = shapes.resolve(doorbellRequest.shape);
|
|
213
213
|
if (resolvedShape === undefined) {
|
|
214
214
|
// 400, not 404/409: a redelivery of the identical body cannot fix this.
|
|
215
215
|
// Never echo the requested shape name — the closed-constant rule
|
|
216
|
-
// (doorbell.ts's DOORBELL_REJECTION) applies to every reason here too,
|
|
216
|
+
// (doorbell-request.ts's DOORBELL_REJECTION) applies to every reason here too,
|
|
217
217
|
// and Evident persists response_body.
|
|
218
218
|
return decide({
|
|
219
219
|
statusCode: 400,
|
|
220
220
|
action: 'rejected',
|
|
221
221
|
reason: REASON.unknownShape,
|
|
222
|
-
runnerId:
|
|
223
|
-
microvmId:
|
|
222
|
+
runnerId: doorbellRequest.runnerId,
|
|
223
|
+
microvmId: doorbellRequest.microvmId,
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
|
-
return handleWakeDoorbell(
|
|
226
|
+
return handleWakeDoorbell(doorbellRequest, resolvedShape, microvm, timing);
|
|
227
227
|
}
|
|
228
|
-
return suspendMicrovm(
|
|
228
|
+
return suspendMicrovm(doorbellRequest, microvm, timing);
|
|
229
229
|
}
|
|
230
|
-
async function handleWakeDoorbell(
|
|
231
|
-
if (
|
|
232
|
-
return runMicrovm(
|
|
230
|
+
async function handleWakeDoorbell(doorbellRequest, shape, microvm, timing) {
|
|
231
|
+
if (doorbellRequest.microvmId === undefined) {
|
|
232
|
+
return runMicrovm(doorbellRequest, shape, microvm, timing, false, REASON.noMicrovmId);
|
|
233
233
|
}
|
|
234
234
|
let described;
|
|
235
235
|
let polled = false;
|
|
236
236
|
try {
|
|
237
|
-
described = await microvm.describe(
|
|
237
|
+
described = await microvm.describe(doorbellRequest.microvmId);
|
|
238
238
|
if (described.state === 'SUSPENDING') {
|
|
239
|
-
described = await pollWhileSuspending(
|
|
239
|
+
described = await pollWhileSuspending(doorbellRequest.microvmId, microvm, timing.sleep);
|
|
240
240
|
polled = true;
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
catch (error) {
|
|
244
|
-
console.error(`[doorbell] GetMicrovm failed for runner ${
|
|
244
|
+
console.error(`[doorbell] GetMicrovm failed for runner ${doorbellRequest.runnerId}: ` +
|
|
245
245
|
`${error instanceof Error ? error.message : String(error)}`);
|
|
246
246
|
// `polled` is omitted here, not passed as `false`: it is only assigned
|
|
247
247
|
// `true` after `pollWhileSuspending` returns, so a throw mid-poll would
|
|
@@ -250,8 +250,8 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
250
250
|
statusCode: 500,
|
|
251
251
|
action: 'none',
|
|
252
252
|
reason: REASON.getStateFailed,
|
|
253
|
-
runnerId:
|
|
254
|
-
microvmId:
|
|
253
|
+
runnerId: doorbellRequest.runnerId,
|
|
254
|
+
microvmId: doorbellRequest.microvmId,
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
257
|
const state = described.state;
|
|
@@ -264,8 +264,8 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
264
264
|
action: 'none',
|
|
265
265
|
reason: REASON.suspendingPollExhausted,
|
|
266
266
|
state,
|
|
267
|
-
runnerId:
|
|
268
|
-
microvmId:
|
|
267
|
+
runnerId: doorbellRequest.runnerId,
|
|
268
|
+
microvmId: doorbellRequest.microvmId,
|
|
269
269
|
polled,
|
|
270
270
|
});
|
|
271
271
|
}
|
|
@@ -279,8 +279,8 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
279
279
|
action: 'none',
|
|
280
280
|
reason: state === 'RUNNING' ? REASON.alreadyRunning : REASON.alreadyStarting,
|
|
281
281
|
state,
|
|
282
|
-
runnerId:
|
|
283
|
-
microvmId:
|
|
282
|
+
runnerId: doorbellRequest.runnerId,
|
|
283
|
+
microvmId: doorbellRequest.microvmId,
|
|
284
284
|
polled,
|
|
285
285
|
startedAt: described.startedAt,
|
|
286
286
|
imageVersion: described.imageVersion,
|
|
@@ -290,15 +290,15 @@ async function handleWakeDoorbell(doorbell, shape, microvm, timing) {
|
|
|
290
290
|
// request (#1906) can still set the same wire flag. Recreate loses VM-local
|
|
291
291
|
// filesystem state; durable provider credentials survive in object storage,
|
|
292
292
|
// but a manual paste/upload not yet synced there may be lost (#2071).
|
|
293
|
-
if (
|
|
294
|
-
(await shouldRecreateForNewerImage(
|
|
295
|
-
return runMicrovm(
|
|
293
|
+
if (doorbellRequest.recreateOnOutdatedImage === true &&
|
|
294
|
+
(await shouldRecreateForNewerImage(doorbellRequest, shape, described.imageVersion, microvm))) {
|
|
295
|
+
return runMicrovm(doorbellRequest, shape, microvm, timing, polled, REASON.imageVersionOutdated, state);
|
|
296
296
|
}
|
|
297
|
-
return resumeMicrovm(
|
|
297
|
+
return resumeMicrovm(doorbellRequest, doorbellRequest.microvmId, shape, microvm, timing, polled, described.startedAt, described.imageVersion);
|
|
298
298
|
default:
|
|
299
299
|
// TERMINATED, TERMINATING, not_found — gone or going, so a fresh VM either
|
|
300
300
|
// way. This is what self-heals a stale `microvm_id`.
|
|
301
|
-
return runMicrovm(
|
|
301
|
+
return runMicrovm(doorbellRequest, shape, microvm, timing, polled, state === 'not_found' ? REASON.microvmNotFound : REASON.terminated, state);
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
/**
|
|
@@ -333,13 +333,13 @@ function isClientTokenParameterMismatch(error) {
|
|
|
333
333
|
const message = typeof e.message === 'string' ? e.message.toLowerCase() : '';
|
|
334
334
|
return message.includes('clienttoken') && message.includes('different request parameters');
|
|
335
335
|
}
|
|
336
|
-
async function runMicrovm(
|
|
336
|
+
async function runMicrovm(doorbellRequest, shape, microvm, timing, polled, reason, state) {
|
|
337
337
|
let started;
|
|
338
338
|
try {
|
|
339
339
|
started = await (0, throttle_retry_1.withThrottleRetry)(() => microvm.run({
|
|
340
340
|
imageIdentifier: shape.imageArn,
|
|
341
|
-
runHookPayload:
|
|
342
|
-
clientToken: clientTokenFor(
|
|
341
|
+
runHookPayload: doorbellRequest.runHookPayload,
|
|
342
|
+
clientToken: clientTokenFor(doorbellRequest),
|
|
343
343
|
}), timing);
|
|
344
344
|
}
|
|
345
345
|
catch (error) {
|
|
@@ -353,7 +353,7 @@ async function runMicrovm(doorbell, shape, microvm, timing, polled, reason, stat
|
|
|
353
353
|
// wakes.
|
|
354
354
|
const name = error instanceof Error ? error.name : 'unknown';
|
|
355
355
|
const message = error instanceof Error ? error.message : String(error);
|
|
356
|
-
console.warn(`[doorbell] RunMicrovm clientToken already used for runner ${
|
|
356
|
+
console.warn(`[doorbell] RunMicrovm clientToken already used for runner ${doorbellRequest.runnerId}: ` +
|
|
357
357
|
`${name}: ${message}`);
|
|
358
358
|
// No `microvmId` (see the comment above), and therefore deliberately no
|
|
359
359
|
// `startedAt` either: `decide`'s invariant is that `microvm_started_at`
|
|
@@ -365,18 +365,18 @@ async function runMicrovm(doorbell, shape, microvm, timing, polled, reason, stat
|
|
|
365
365
|
action: 'run',
|
|
366
366
|
reason: REASON.runAlreadyStartedForToken,
|
|
367
367
|
state,
|
|
368
|
-
runnerId:
|
|
368
|
+
runnerId: doorbellRequest.runnerId,
|
|
369
369
|
polled,
|
|
370
370
|
});
|
|
371
371
|
}
|
|
372
|
-
console.error(`[doorbell] RunMicrovm failed for runner ${
|
|
372
|
+
console.error(`[doorbell] RunMicrovm failed for runner ${doorbellRequest.runnerId}: ` +
|
|
373
373
|
`${error instanceof Error ? error.message : String(error)}`);
|
|
374
374
|
return decide({
|
|
375
375
|
statusCode: 500,
|
|
376
376
|
action: 'run',
|
|
377
377
|
reason: REASON.runFailed,
|
|
378
378
|
state,
|
|
379
|
-
runnerId:
|
|
379
|
+
runnerId: doorbellRequest.runnerId,
|
|
380
380
|
polled,
|
|
381
381
|
});
|
|
382
382
|
}
|
|
@@ -385,14 +385,14 @@ async function runMicrovm(doorbell, shape, microvm, timing, polled, reason, stat
|
|
|
385
385
|
action: 'run',
|
|
386
386
|
reason,
|
|
387
387
|
state,
|
|
388
|
-
runnerId:
|
|
388
|
+
runnerId: doorbellRequest.runnerId,
|
|
389
389
|
microvmId: started.microvmId,
|
|
390
390
|
polled,
|
|
391
391
|
startedAt: started.startedAt,
|
|
392
392
|
imageVersion: started.imageVersion,
|
|
393
393
|
});
|
|
394
394
|
}
|
|
395
|
-
async function resumeMicrovm(
|
|
395
|
+
async function resumeMicrovm(doorbellRequest, microvmId, shape, microvm, timing, polled,
|
|
396
396
|
// Both values come from the `describe()` result that led here because
|
|
397
397
|
// `ResumeMicrovm` returns no lifecycle metadata of its own.
|
|
398
398
|
startedAt, imageVersion) {
|
|
@@ -400,24 +400,24 @@ startedAt, imageVersion) {
|
|
|
400
400
|
await (0, throttle_retry_1.withThrottleRetry)(() => microvm.resume(microvmId), timing);
|
|
401
401
|
}
|
|
402
402
|
catch (error) {
|
|
403
|
-
console.error(`[doorbell] ResumeMicrovm failed for runner ${
|
|
403
|
+
console.error(`[doorbell] ResumeMicrovm failed for runner ${doorbellRequest.runnerId}: ` +
|
|
404
404
|
`${error instanceof Error ? error.message : String(error)}`);
|
|
405
|
-
return runMicrovm(
|
|
405
|
+
return runMicrovm(doorbellRequest, shape, microvm, timing, polled, REASON.resumeFailedFallingBackToRun, 'SUSPENDED');
|
|
406
406
|
}
|
|
407
407
|
return decide({
|
|
408
408
|
statusCode: 200,
|
|
409
409
|
action: 'resume',
|
|
410
410
|
reason: REASON.resumed,
|
|
411
411
|
state: 'SUSPENDED',
|
|
412
|
-
runnerId:
|
|
412
|
+
runnerId: doorbellRequest.runnerId,
|
|
413
413
|
microvmId,
|
|
414
414
|
polled,
|
|
415
415
|
startedAt,
|
|
416
416
|
imageVersion,
|
|
417
417
|
});
|
|
418
418
|
}
|
|
419
|
-
async function suspendMicrovm(
|
|
420
|
-
const microvmId =
|
|
419
|
+
async function suspendMicrovm(doorbellRequest, microvm, timing) {
|
|
420
|
+
const microvmId = doorbellRequest.microvmId;
|
|
421
421
|
if (microvmId === undefined) {
|
|
422
422
|
// Nothing to suspend, and a redelivery would not change that — 200 so the
|
|
423
423
|
// doorbell is not retried, but loudly enough to notice a mis-wired sender.
|
|
@@ -425,7 +425,7 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
425
425
|
statusCode: 200,
|
|
426
426
|
action: 'none',
|
|
427
427
|
reason: REASON.suspendWithoutMicrovmId,
|
|
428
|
-
runnerId:
|
|
428
|
+
runnerId: doorbellRequest.runnerId,
|
|
429
429
|
});
|
|
430
430
|
}
|
|
431
431
|
let state;
|
|
@@ -434,13 +434,13 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
434
434
|
({ state } = await microvm.describe(microvmId));
|
|
435
435
|
}
|
|
436
436
|
catch (error) {
|
|
437
|
-
console.error(`[doorbell] GetMicrovm failed for runner ${
|
|
437
|
+
console.error(`[doorbell] GetMicrovm failed for runner ${doorbellRequest.runnerId}: ` +
|
|
438
438
|
`${error instanceof Error ? error.message : String(error)}`);
|
|
439
439
|
return decide({
|
|
440
440
|
statusCode: 500,
|
|
441
441
|
action: 'none',
|
|
442
442
|
reason: REASON.getStateFailed,
|
|
443
|
-
runnerId:
|
|
443
|
+
runnerId: doorbellRequest.runnerId,
|
|
444
444
|
microvmId,
|
|
445
445
|
});
|
|
446
446
|
}
|
|
@@ -453,7 +453,7 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
453
453
|
action: 'none',
|
|
454
454
|
reason: REASON.suspendAlreadyGone,
|
|
455
455
|
state,
|
|
456
|
-
runnerId:
|
|
456
|
+
runnerId: doorbellRequest.runnerId,
|
|
457
457
|
microvmId,
|
|
458
458
|
});
|
|
459
459
|
}
|
|
@@ -461,14 +461,14 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
461
461
|
await (0, throttle_retry_1.withThrottleRetry)(() => microvm.suspend(microvmId), timing);
|
|
462
462
|
}
|
|
463
463
|
catch (error) {
|
|
464
|
-
console.error(`[doorbell] SuspendMicrovm failed for runner ${
|
|
464
|
+
console.error(`[doorbell] SuspendMicrovm failed for runner ${doorbellRequest.runnerId}: ` +
|
|
465
465
|
`${error instanceof Error ? error.message : String(error)}`);
|
|
466
466
|
return decide({
|
|
467
467
|
statusCode: 500,
|
|
468
468
|
action: 'suspend',
|
|
469
469
|
reason: REASON.suspendFailed,
|
|
470
470
|
state,
|
|
471
|
-
runnerId:
|
|
471
|
+
runnerId: doorbellRequest.runnerId,
|
|
472
472
|
microvmId,
|
|
473
473
|
});
|
|
474
474
|
}
|
|
@@ -477,7 +477,7 @@ async function suspendMicrovm(doorbell, microvm, timing) {
|
|
|
477
477
|
action: 'suspend',
|
|
478
478
|
reason: REASON.suspended,
|
|
479
479
|
state,
|
|
480
|
-
runnerId:
|
|
480
|
+
runnerId: doorbellRequest.runnerId,
|
|
481
481
|
microvmId,
|
|
482
482
|
});
|
|
483
483
|
}
|
|
@@ -35,7 +35,7 @@ export interface StageMicrovmImageContextOptions {
|
|
|
35
35
|
*
|
|
36
36
|
* The Dockerfile, hook server and phase hooks come from this package's
|
|
37
37
|
* published `dist/`, so a consumer needs no checkout of the source repository and no
|
|
38
|
-
* copy of those files
|
|
38
|
+
* copy of those files. A caller may supply an overlay; the default is
|
|
39
39
|
* deterministic no-op scripts. `repositoryPath` is the only required caller
|
|
40
40
|
* input.
|
|
41
41
|
*/
|
|
@@ -127,7 +127,7 @@ function stageScriptDirectory(source, destination) {
|
|
|
127
127
|
*
|
|
128
128
|
* The Dockerfile, hook server and phase hooks come from this package's
|
|
129
129
|
* published `dist/`, so a consumer needs no checkout of the source repository and no
|
|
130
|
-
* copy of those files
|
|
130
|
+
* copy of those files. A caller may supply an overlay; the default is
|
|
131
131
|
* deterministic no-op scripts. `repositoryPath` is the only required caller
|
|
132
132
|
* input.
|
|
133
133
|
*/
|
|
@@ -468,7 +468,6 @@ kill_litestream() {
|
|
|
468
468
|
rm -f "${LITESTREAM_PID_FILE}"
|
|
469
469
|
log "litestream stop signalled (no wait)"
|
|
470
470
|
}
|
|
471
|
-
# --- litestream replicate (end) ----------------------------------------------
|
|
472
471
|
|
|
473
472
|
# --- flush_session_db (#812 WI-4) -------------------------------------------
|
|
474
473
|
#
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evident-ai/runner-cdk",
|
|
3
|
-
"version": "3.5.2-dev.
|
|
3
|
+
"version": "3.5.2-dev.da3340e",
|
|
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",
|