@vellumai/cli 0.8.10 → 0.8.11-staging.1
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/AGENTS.md +2 -0
- package/node_modules/@vellumai/local-mode/src/config.ts +13 -0
- package/node_modules/@vellumai/local-mode/src/guardian-token.ts +2 -2
- package/node_modules/@vellumai/local-mode/src/index.ts +1 -1
- package/node_modules/@vellumai/local-mode/src/lockfile-contract.test.ts +20 -1
- package/node_modules/@vellumai/local-mode/src/lockfile-contract.ts +3 -0
- package/node_modules/@vellumai/local-mode/src/lockfile.test.ts +169 -0
- package/node_modules/@vellumai/local-mode/src/lockfile.ts +9 -4
- package/package.json +1 -1
- package/src/__tests__/confirm.test.ts +85 -0
- package/src/__tests__/device-id.test.ts +167 -0
- package/src/__tests__/guardian-token.test.ts +79 -0
- package/src/__tests__/helpers/env.ts +19 -0
- package/src/__tests__/statefulset.test.ts +149 -0
- package/src/__tests__/upgrade-replay-env.test.ts +165 -0
- package/src/__tests__/wake.test.ts +68 -0
- package/src/commands/backup.ts +3 -2
- package/src/commands/client.ts +22 -5
- package/src/commands/confirm.ts +144 -0
- package/src/commands/connect.ts +1 -1
- package/src/commands/devices.ts +4 -3
- package/src/commands/hatch.ts +16 -1
- package/src/commands/pair.ts +3 -2
- package/src/commands/restore.ts +3 -2
- package/src/commands/retire.ts +2 -1
- package/src/commands/roadmap.ts +2 -1
- package/src/commands/rollback.ts +9 -37
- package/src/commands/unpair.ts +1 -1
- package/src/commands/upgrade.ts +13 -44
- package/src/commands/wake.ts +49 -1
- package/src/index.ts +11 -4
- package/src/lib/assistant-client.ts +3 -2
- package/src/lib/backup-ops.ts +5 -4
- package/src/lib/device-id.ts +85 -0
- package/src/lib/docker.ts +19 -3
- package/src/lib/guardian-token.ts +44 -8
- package/src/lib/hatch-local.ts +2 -1
- package/src/lib/health-check.ts +6 -4
- package/src/lib/http-client.ts +3 -1
- package/src/lib/local-runtime-client.ts +5 -4
- package/src/lib/local.ts +1 -0
- package/src/lib/loopback-fetch.ts +28 -0
- package/src/lib/ngrok.ts +2 -1
- package/src/lib/platform-client.ts +28 -21
- package/src/lib/platform-releases.ts +3 -2
- package/src/lib/statefulset.ts +43 -0
- package/src/lib/terminal-client.ts +6 -5
- package/src/lib/upgrade-lifecycle.ts +114 -53
package/src/lib/statefulset.ts
CHANGED
|
@@ -262,6 +262,49 @@ export interface BuildServiceRunArgsOpts extends DockerRunSecrets {
|
|
|
262
262
|
avatarDevicePath?: string;
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
interface BuilderManagedEnvKeys {
|
|
266
|
+
/** Always set by buildServiceRunArgs (spec static/secret entries, builder-computed extras, image-baked PATH). Never replay. */
|
|
267
|
+
always: ReadonlySet<string>;
|
|
268
|
+
/**
|
|
269
|
+
* Spec host-forwarded entries. `name` is the container-side env key (what
|
|
270
|
+
* docker inspect captures); `hostVar` is the host process.env variable
|
|
271
|
+
* buildServiceRunArgs reads. Exclude captured `name` from replay only when
|
|
272
|
+
* process.env[hostVar] is set.
|
|
273
|
+
*/
|
|
274
|
+
hostForwarded: ReadonlyArray<{ name: string; hostVar: string }>;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Env var names that `buildServiceRunArgs` manages for a service, derived
|
|
279
|
+
* from the spec so future entries are picked up automatically.
|
|
280
|
+
*/
|
|
281
|
+
export function getBuilderManagedEnvKeys(
|
|
282
|
+
service: ServiceName,
|
|
283
|
+
spec = DOCKER_STATEFUL_SET_SPEC,
|
|
284
|
+
): BuilderManagedEnvKeys {
|
|
285
|
+
const container = spec.containers.find((c) => c.internalName === service);
|
|
286
|
+
if (!container) throw new Error(`docker-statefulset: unknown service "${service}"`);
|
|
287
|
+
|
|
288
|
+
const always = new Set<string>(["PATH"]);
|
|
289
|
+
const hostForwarded: Array<{ name: string; hostVar: string }> = [];
|
|
290
|
+
for (const entry of container.env) {
|
|
291
|
+
if (entry.kind === "host") {
|
|
292
|
+
hostForwarded.push({ name: entry.name, hostVar: entry.hostVar ?? entry.name });
|
|
293
|
+
} else {
|
|
294
|
+
always.add(entry.name);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Builder-computed extras added outside the spec env arrays
|
|
299
|
+
if (service === "assistant") {
|
|
300
|
+
always.add("VELLUM_ASSISTANT_NAME");
|
|
301
|
+
always.add("GATEWAY_INTERNAL_URL");
|
|
302
|
+
always.add(AVATAR_DEVICE_ENV_VAR);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return { always, hostForwarded };
|
|
306
|
+
}
|
|
307
|
+
|
|
265
308
|
function resolveVolume(
|
|
266
309
|
spec: DockerStatefulSetSpec,
|
|
267
310
|
instanceName: string,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { authHeaders, getPlatformUrl } from "./platform-client.js";
|
|
10
|
+
import { loopbackSafeFetch } from "./loopback-fetch.js";
|
|
10
11
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
// Create / Close
|
|
@@ -25,7 +26,7 @@ export async function createTerminalSession(
|
|
|
25
26
|
if (service) {
|
|
26
27
|
body.service = service;
|
|
27
28
|
}
|
|
28
|
-
const response = await
|
|
29
|
+
const response = await loopbackSafeFetch(
|
|
29
30
|
`${baseUrl}/v1/assistants/${assistantId}/terminal/sessions/`,
|
|
30
31
|
{
|
|
31
32
|
method: "POST",
|
|
@@ -49,7 +50,7 @@ export async function closeTerminalSession(
|
|
|
49
50
|
platformUrl?: string,
|
|
50
51
|
): Promise<void> {
|
|
51
52
|
const baseUrl = platformUrl || getPlatformUrl();
|
|
52
|
-
const response = await
|
|
53
|
+
const response = await loopbackSafeFetch(
|
|
53
54
|
`${baseUrl}/v1/assistants/${assistantId}/terminal/sessions/${sessionId}/`,
|
|
54
55
|
{
|
|
55
56
|
method: "DELETE",
|
|
@@ -76,7 +77,7 @@ export async function sendTerminalInput(
|
|
|
76
77
|
platformUrl?: string,
|
|
77
78
|
): Promise<void> {
|
|
78
79
|
const baseUrl = platformUrl || getPlatformUrl();
|
|
79
|
-
const response = await
|
|
80
|
+
const response = await loopbackSafeFetch(
|
|
80
81
|
`${baseUrl}/v1/assistants/${assistantId}/terminal/sessions/${sessionId}/input/`,
|
|
81
82
|
{
|
|
82
83
|
method: "POST",
|
|
@@ -100,7 +101,7 @@ export async function resizeTerminalSession(
|
|
|
100
101
|
platformUrl?: string,
|
|
101
102
|
): Promise<void> {
|
|
102
103
|
const baseUrl = platformUrl || getPlatformUrl();
|
|
103
|
-
const response = await
|
|
104
|
+
const response = await loopbackSafeFetch(
|
|
104
105
|
`${baseUrl}/v1/assistants/${assistantId}/terminal/sessions/${sessionId}/resize/`,
|
|
105
106
|
{
|
|
106
107
|
method: "POST",
|
|
@@ -137,7 +138,7 @@ export async function* subscribeTerminalEvents(
|
|
|
137
138
|
signal?: AbortSignal,
|
|
138
139
|
): AsyncGenerator<TerminalOutputEvent> {
|
|
139
140
|
const baseUrl = platformUrl || getPlatformUrl();
|
|
140
|
-
const response = await
|
|
141
|
+
const response = await loopbackSafeFetch(
|
|
141
142
|
`${baseUrl}/v1/assistants/${assistantId}/terminal/sessions/${sessionId}/events/`,
|
|
142
143
|
{
|
|
143
144
|
headers: await authHeaders(token, platformUrl),
|
|
@@ -7,6 +7,7 @@ import type { AssistantEntry } from "./assistant-config.js";
|
|
|
7
7
|
import { saveAssistantEntry } from "./assistant-config.js";
|
|
8
8
|
import { createBackup, pruneOldBackups, restoreBackup } from "./backup-ops.js";
|
|
9
9
|
import { emitCliError } from "./cli-error.js";
|
|
10
|
+
import { getOrCreateHostDeviceId } from "./device-id.js";
|
|
10
11
|
import {
|
|
11
12
|
captureImageRefs,
|
|
12
13
|
DOCKER_READY_TIMEOUT_MS,
|
|
@@ -19,8 +20,14 @@ import { getStateDir } from "./environments/paths.js";
|
|
|
19
20
|
import { getCurrentEnvironment } from "./environments/resolve.js";
|
|
20
21
|
import { loadGuardianToken } from "./guardian-token.js";
|
|
21
22
|
import { resolveImageRefs } from "./platform-releases.js";
|
|
23
|
+
import {
|
|
24
|
+
getBuilderManagedEnvKeys,
|
|
25
|
+
type DockerStatefulSetSpec,
|
|
26
|
+
type ServiceName,
|
|
27
|
+
} from "./statefulset.js";
|
|
22
28
|
import { exec, execOutput } from "./step-runner.js";
|
|
23
29
|
import { compareVersions } from "./version-compat.js";
|
|
30
|
+
import { loopbackSafeFetch } from "./loopback-fetch.js";
|
|
24
31
|
|
|
25
32
|
// ---------------------------------------------------------------------------
|
|
26
33
|
// Failure log capture
|
|
@@ -141,20 +148,6 @@ export function buildUpgradeCommitMessage(options: {
|
|
|
141
148
|
return lines.join("\n");
|
|
142
149
|
}
|
|
143
150
|
|
|
144
|
-
/**
|
|
145
|
-
* Environment variable keys that are set by CLI run arguments and should
|
|
146
|
-
* not be replayed from a captured container environment during upgrades
|
|
147
|
-
* or rollbacks. Shared between upgrade.ts and rollback.ts.
|
|
148
|
-
*/
|
|
149
|
-
export const CONTAINER_ENV_EXCLUDE_KEYS: ReadonlySet<string> = new Set([
|
|
150
|
-
"CES_SERVICE_TOKEN",
|
|
151
|
-
"GUARDIAN_BOOTSTRAP_SECRET",
|
|
152
|
-
"VELLUM_ASSISTANT_NAME",
|
|
153
|
-
"RUNTIME_HTTP_HOST",
|
|
154
|
-
"PATH",
|
|
155
|
-
"ACTOR_TOKEN_SIGNING_KEY",
|
|
156
|
-
]);
|
|
157
|
-
|
|
158
151
|
/**
|
|
159
152
|
* Capture environment variables from a running Docker container so they
|
|
160
153
|
* can be replayed onto the replacement container after upgrade.
|
|
@@ -183,6 +176,96 @@ export async function captureContainerEnv(
|
|
|
183
176
|
return captured;
|
|
184
177
|
}
|
|
185
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Filter a captured container env down to the entries safe to replay onto a
|
|
181
|
+
* replacement container.
|
|
182
|
+
*
|
|
183
|
+
* Drops every key `buildServiceRunArgs` sets itself (spec static/secret
|
|
184
|
+
* entries, builder-computed extras, PATH). Spec-managed secrets re-enter via
|
|
185
|
+
* the dedicated `DockerRunSecrets` path, so adding a secret to the spec
|
|
186
|
+
* automatically excludes it here. Spec host-forwarded keys are dropped only
|
|
187
|
+
* when the host variable is currently set, so fresh host values win over
|
|
188
|
+
* stale captured ones.
|
|
189
|
+
*
|
|
190
|
+
* Security contract: the returned env is memory-only — never persist it to
|
|
191
|
+
* disk, and log counts only, never values.
|
|
192
|
+
*/
|
|
193
|
+
export function buildReplayEnv(
|
|
194
|
+
capturedEnv: Record<string, string>,
|
|
195
|
+
service: ServiceName,
|
|
196
|
+
spec?: DockerStatefulSetSpec,
|
|
197
|
+
): Record<string, string> {
|
|
198
|
+
const { always, hostForwarded } = getBuilderManagedEnvKeys(service, spec);
|
|
199
|
+
const hostManaged = new Set(
|
|
200
|
+
hostForwarded.filter((h) => process.env[h.hostVar]).map((h) => h.name),
|
|
201
|
+
);
|
|
202
|
+
return Object.fromEntries(
|
|
203
|
+
Object.entries(capturedEnv).filter(
|
|
204
|
+
([key]) => !always.has(key) && !hostManaged.has(key),
|
|
205
|
+
),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Secrets and replay env derived from the outgoing containers. */
|
|
210
|
+
interface ReplayState {
|
|
211
|
+
bootstrapSecret: string | undefined;
|
|
212
|
+
cesServiceToken: string;
|
|
213
|
+
signingKey: string;
|
|
214
|
+
extraAssistantEnv: Record<string, string>;
|
|
215
|
+
extraGatewayEnv: Record<string, string>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Derive the secrets and replay env for replacement containers from
|
|
220
|
+
* already-captured assistant/gateway envs. GUARDIAN_BOOTSTRAP_SECRET is only
|
|
221
|
+
* set on the gateway; CES_SERVICE_TOKEN and ACTOR_TOKEN_SIGNING_KEY fall back
|
|
222
|
+
* to fresh values for instances that predate them. VELLUM_DEVICE_ID is
|
|
223
|
+
* backfilled on the gateway from the host, and the assistant inherits the
|
|
224
|
+
* gateway's value (captured values win) so the pair always matches.
|
|
225
|
+
*/
|
|
226
|
+
export function buildReplayState(
|
|
227
|
+
capturedEnv: Record<string, string>,
|
|
228
|
+
gatewayEnv: Record<string, string>,
|
|
229
|
+
): ReplayState {
|
|
230
|
+
const extraGatewayEnv = buildReplayEnv(gatewayEnv, "gateway");
|
|
231
|
+
extraGatewayEnv.VELLUM_DEVICE_ID ??= getOrCreateHostDeviceId();
|
|
232
|
+
|
|
233
|
+
const extraAssistantEnv = buildReplayEnv(capturedEnv, "assistant");
|
|
234
|
+
extraAssistantEnv.VELLUM_DEVICE_ID ??= extraGatewayEnv.VELLUM_DEVICE_ID;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
bootstrapSecret: gatewayEnv["GUARDIAN_BOOTSTRAP_SECRET"],
|
|
238
|
+
cesServiceToken:
|
|
239
|
+
capturedEnv["CES_SERVICE_TOKEN"] || randomBytes(32).toString("hex"),
|
|
240
|
+
signingKey:
|
|
241
|
+
capturedEnv["ACTOR_TOKEN_SIGNING_KEY"] || randomBytes(32).toString("hex"),
|
|
242
|
+
extraAssistantEnv,
|
|
243
|
+
extraGatewayEnv,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Capture the assistant and gateway container envs and derive the replay
|
|
249
|
+
* state for the replacement containers. Logs only the assistant env-var
|
|
250
|
+
* count (security contract on `buildReplayEnv`).
|
|
251
|
+
*/
|
|
252
|
+
export async function captureReplayState(
|
|
253
|
+
res: Pick<
|
|
254
|
+
ReturnType<typeof dockerResourceNames>,
|
|
255
|
+
"assistantContainer" | "gatewayContainer"
|
|
256
|
+
>,
|
|
257
|
+
): Promise<ReplayState> {
|
|
258
|
+
console.log("💾 Capturing existing container environment...");
|
|
259
|
+
const [capturedEnv, gatewayEnv] = await Promise.all([
|
|
260
|
+
captureContainerEnv(res.assistantContainer),
|
|
261
|
+
captureContainerEnv(res.gatewayContainer),
|
|
262
|
+
]);
|
|
263
|
+
console.log(
|
|
264
|
+
` Captured ${Object.keys(capturedEnv).length} env var(s) from ${res.assistantContainer}\n`,
|
|
265
|
+
);
|
|
266
|
+
return buildReplayState(capturedEnv, gatewayEnv);
|
|
267
|
+
}
|
|
268
|
+
|
|
186
269
|
/**
|
|
187
270
|
* Best-effort fetch of the running service group version from the gateway
|
|
188
271
|
* `/healthz` endpoint. Returns `undefined` when the endpoint is
|
|
@@ -192,7 +275,7 @@ export async function fetchCurrentVersion(
|
|
|
192
275
|
runtimeUrl: string,
|
|
193
276
|
): Promise<string | undefined> {
|
|
194
277
|
try {
|
|
195
|
-
const resp = await
|
|
278
|
+
const resp = await loopbackSafeFetch(`${runtimeUrl}/healthz`, {
|
|
196
279
|
signal: AbortSignal.timeout(5000),
|
|
197
280
|
});
|
|
198
281
|
if (resp.ok) {
|
|
@@ -217,7 +300,7 @@ export async function fetchAssistantIngressUrl(
|
|
|
217
300
|
): Promise<string | undefined> {
|
|
218
301
|
if (!bearerToken) return undefined;
|
|
219
302
|
try {
|
|
220
|
-
const resp = await
|
|
303
|
+
const resp = await loopbackSafeFetch(`${runtimeUrl}/integrations/ingress/config`, {
|
|
221
304
|
headers: { Authorization: `Bearer ${bearerToken}` },
|
|
222
305
|
signal: AbortSignal.timeout(5000),
|
|
223
306
|
});
|
|
@@ -259,7 +342,7 @@ export async function fetchPreviousVersion(
|
|
|
259
342
|
try {
|
|
260
343
|
const { getPlatformUrl } = await import("./platform-client.js");
|
|
261
344
|
const platformUrl = getPlatformUrl();
|
|
262
|
-
const resp = await
|
|
345
|
+
const resp = await loopbackSafeFetch(`${platformUrl}/v1/releases/?stable=true`, {
|
|
263
346
|
signal: AbortSignal.timeout(10_000),
|
|
264
347
|
});
|
|
265
348
|
if (!resp.ok) return undefined;
|
|
@@ -291,7 +374,7 @@ export async function waitForReady(runtimeUrl: string): Promise<boolean> {
|
|
|
291
374
|
|
|
292
375
|
while (Date.now() - start < DOCKER_READY_TIMEOUT_MS) {
|
|
293
376
|
try {
|
|
294
|
-
const resp = await
|
|
377
|
+
const resp = await loopbackSafeFetch(readyUrl, {
|
|
295
378
|
signal: AbortSignal.timeout(5000),
|
|
296
379
|
});
|
|
297
380
|
if (resp.ok) {
|
|
@@ -337,7 +420,7 @@ export async function broadcastUpgradeEvent(
|
|
|
337
420
|
if (token?.accessToken) {
|
|
338
421
|
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
|
339
422
|
}
|
|
340
|
-
await
|
|
423
|
+
await loopbackSafeFetch(`${gatewayUrl}/v1/admin/upgrade-broadcast`, {
|
|
341
424
|
method: "POST",
|
|
342
425
|
headers,
|
|
343
426
|
body: JSON.stringify(event),
|
|
@@ -366,7 +449,7 @@ export async function commitWorkspaceViaGateway(
|
|
|
366
449
|
if (token?.accessToken) {
|
|
367
450
|
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
|
368
451
|
}
|
|
369
|
-
await
|
|
452
|
+
await loopbackSafeFetch(`${gatewayUrl}/v1/admin/workspace-commit`, {
|
|
370
453
|
method: "POST",
|
|
371
454
|
headers,
|
|
372
455
|
body: JSON.stringify({ message }),
|
|
@@ -409,7 +492,7 @@ export async function rollbackMigrations(
|
|
|
409
492
|
body.targetWorkspaceMigrationId = targetWorkspaceMigrationId;
|
|
410
493
|
if (rollbackToRegistryCeiling) body.rollbackToRegistryCeiling = true;
|
|
411
494
|
|
|
412
|
-
const resp = await
|
|
495
|
+
const resp = await loopbackSafeFetch(`${gatewayUrl}/v1/admin/rollback-migrations`, {
|
|
413
496
|
method: "POST",
|
|
414
497
|
headers,
|
|
415
498
|
body: JSON.stringify(body),
|
|
@@ -490,7 +573,7 @@ export async function performDockerRollback(
|
|
|
490
573
|
lastWorkspaceMigrationId?: string;
|
|
491
574
|
} = {};
|
|
492
575
|
try {
|
|
493
|
-
const healthResp = await
|
|
576
|
+
const healthResp = await loopbackSafeFetch(
|
|
494
577
|
`${entry.runtimeUrl}/healthz?include=migrations`,
|
|
495
578
|
{ signal: AbortSignal.timeout(5000) },
|
|
496
579
|
);
|
|
@@ -581,37 +664,13 @@ export async function performDockerRollback(
|
|
|
581
664
|
console.warn("⚠️ Pre-rollback backup failed (continuing with rollback)\n");
|
|
582
665
|
}
|
|
583
666
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
// Capture GUARDIAN_BOOTSTRAP_SECRET from the gateway container (it is only
|
|
592
|
-
// set on gateway, not assistant) so it persists across container restarts.
|
|
593
|
-
const gatewayEnv = await captureContainerEnv(res.gatewayContainer);
|
|
594
|
-
const bootstrapSecret = gatewayEnv["GUARDIAN_BOOTSTRAP_SECRET"];
|
|
595
|
-
|
|
596
|
-
const cesServiceToken =
|
|
597
|
-
capturedEnv["CES_SERVICE_TOKEN"] || randomBytes(32).toString("hex");
|
|
598
|
-
|
|
599
|
-
const signingKey =
|
|
600
|
-
capturedEnv["ACTOR_TOKEN_SIGNING_KEY"] || randomBytes(32).toString("hex");
|
|
601
|
-
|
|
602
|
-
// Build extra env vars, excluding keys managed by buildServiceRunArgs
|
|
603
|
-
const envKeysSetByRunArgs = new Set(CONTAINER_ENV_EXCLUDE_KEYS);
|
|
604
|
-
for (const envVar of ["ANTHROPIC_API_KEY", "VELLUM_PLATFORM_URL"]) {
|
|
605
|
-
if (process.env[envVar]) {
|
|
606
|
-
envKeysSetByRunArgs.add(envVar);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
const extraAssistantEnv: Record<string, string> = {};
|
|
610
|
-
for (const [key, value] of Object.entries(capturedEnv)) {
|
|
611
|
-
if (!envKeysSetByRunArgs.has(key)) {
|
|
612
|
-
extraAssistantEnv[key] = value;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
667
|
+
const {
|
|
668
|
+
bootstrapSecret,
|
|
669
|
+
cesServiceToken,
|
|
670
|
+
signingKey,
|
|
671
|
+
extraAssistantEnv,
|
|
672
|
+
extraGatewayEnv,
|
|
673
|
+
} = await captureReplayState(res);
|
|
615
674
|
|
|
616
675
|
// Parse gateway port from entry's runtimeUrl
|
|
617
676
|
let gatewayPort = GATEWAY_INTERNAL_PORT;
|
|
@@ -684,6 +743,7 @@ export async function performDockerRollback(
|
|
|
684
743
|
bootstrapSecret,
|
|
685
744
|
cesServiceToken,
|
|
686
745
|
extraAssistantEnv,
|
|
746
|
+
extraGatewayEnv,
|
|
687
747
|
gatewayPort,
|
|
688
748
|
imageTags: targetImageTags,
|
|
689
749
|
instanceName,
|
|
@@ -801,6 +861,7 @@ export async function performDockerRollback(
|
|
|
801
861
|
bootstrapSecret,
|
|
802
862
|
cesServiceToken,
|
|
803
863
|
extraAssistantEnv,
|
|
864
|
+
extraGatewayEnv,
|
|
804
865
|
gatewayPort,
|
|
805
866
|
imageTags: currentImageRefs,
|
|
806
867
|
instanceName,
|