@mycadet/cli 0.12.11 → 0.13.0-dev.202610171617.9ea363e
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 +13 -0
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/channels.ts +0 -1
- package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/channels.ts +0 -1
- package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
- package/node_modules/@mycadet/service-contracts/src/channels.ts +0 -1
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
- package/package.json +8 -3
- package/src/__tests__/assistant-client-cloud-auth.test.ts +2 -2
- package/src/__tests__/backup-ops-matching.test.ts +15 -13
- package/src/__tests__/backup-ops.test.ts +105 -0
- package/src/__tests__/backup.test.ts +114 -673
- package/src/__tests__/cloud-releases.test.ts +82 -0
- package/src/__tests__/commands-mock.ts +101 -0
- package/src/__tests__/debug.test.ts +17 -91
- package/src/__tests__/devices.test.ts +2 -2
- package/src/__tests__/gateway-token.test.ts +267 -0
- package/src/__tests__/host-teleport-docker.test.ts +371 -0
- package/src/__tests__/host-teleport.test.ts +405 -0
- package/src/__tests__/nginx-ingress.test.ts +536 -23
- package/src/__tests__/orphan-detection.test.ts +1 -1
- package/src/__tests__/paired-lifecycle.test.ts +2 -2
- package/src/__tests__/ps-cloud-status.test.ts +6 -6
- package/src/__tests__/root-backup-docker.test.ts +294 -0
- package/src/__tests__/root-backup.test.ts +527 -0
- package/src/__tests__/root-restore-command.test.ts +147 -0
- package/src/__tests__/root-signing-key.test.ts +50 -0
- package/src/__tests__/setup.test.ts +3 -3
- package/src/__tests__/shell-mode.test.ts +83 -0
- package/src/__tests__/sleep.test.ts +11 -0
- package/src/__tests__/teleport.test.ts +68 -2996
- package/src/__tests__/tunnel.test.ts +10 -2
- package/src/__tests__/upgrade-local.test.ts +21 -11
- package/src/__tests__/upgrade-replay-env.test.ts +2 -2
- package/src/__tests__/upgrade-root-recovery.test.ts +185 -0
- package/src/__tests__/wake.test.ts +4 -1
- package/src/__tests__/windows-lifecycle.test.ts +25 -0
- package/src/commands/backup.ts +44 -399
- package/src/commands/client.ts +3 -3
- package/src/commands/completions.ts +0 -2
- package/src/commands/debug.ts +75 -131
- package/src/commands/devices.ts +51 -17
- package/src/commands/gateway/token.ts +77 -23
- package/src/commands/pair.ts +2 -2
- package/src/commands/restore.ts +77 -444
- package/src/commands/retire.ts +2 -3
- package/src/commands/sleep.ts +15 -2
- package/src/commands/teleport.ts +47 -1559
- package/src/commands/tunnel.ts +48 -7
- package/src/commands/upgrade.ts +78 -156
- package/src/commands/wake.ts +1 -1
- package/src/index.ts +0 -4
- package/src/lib/__tests__/http-client-version.test.ts +29 -0
- package/src/lib/__tests__/runtime-url.test.ts +3 -75
- package/src/lib/__tests__/web-dist.test.ts +27 -1
- package/src/lib/assistant-client.ts +1 -1
- package/src/lib/assistant-config.ts +23 -18
- package/src/lib/backup-ops.ts +37 -251
- package/src/lib/cloud-client.ts +10 -13
- package/src/lib/cloud-login.ts +2 -1
- package/src/lib/cloud-releases.ts +54 -10
- package/src/lib/cloud-values.ts +1 -1
- package/src/lib/confirm-action.ts +2 -2
- package/src/lib/docker.ts +59 -22
- package/src/lib/github-repository.ts +2 -0
- package/src/lib/guardian-token.ts +1 -23
- package/src/lib/hatch-local.ts +17 -1
- package/src/lib/host-teleport.ts +506 -0
- package/src/lib/http-client.ts +25 -0
- package/src/lib/ingress-config.ts +31 -0
- package/src/lib/live-voice/tunnel.test.ts +1 -1
- package/src/lib/live-voice/tunnel.ts +1 -1
- package/src/lib/local.ts +4 -2
- package/src/lib/nginx-ingress.ts +454 -79
- package/src/lib/process.ts +13 -0
- package/src/lib/root-backup.ts +917 -0
- package/src/lib/root-signing-key.ts +31 -0
- package/src/lib/runtime-url.ts +0 -29
- package/src/lib/teleport-backup.ts +3 -16
- package/src/lib/tunnel.ts +1 -0
- package/src/lib/upgrade-lifecycle.ts +63 -141
- package/src/lib/web-dist.ts +79 -1
- package/src/__tests__/bundle-staging.test.ts +0 -268
- package/src/__tests__/unpair.test.ts +0 -160
- package/src/commands/rollback.ts +0 -479
- package/src/commands/unpair.ts +0 -122
- package/src/lib/__tests__/job-polling.test.ts +0 -278
- package/src/lib/__tests__/local-runtime-client.test.ts +0 -666
- package/src/lib/bundle-staging.ts +0 -220
- package/src/lib/job-polling.ts +0 -195
- package/src/lib/local-runtime-client.ts +0 -374
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { CADET_GITHUB_REPOSITORY } from "./github-repository.js";
|
|
1
4
|
import { getCloudUrl } from "./cloud-client.js";
|
|
2
5
|
import { dockerhubImages } from "./docker.js";
|
|
3
6
|
import type { ServiceName } from "./docker.js";
|
|
@@ -22,12 +25,40 @@ function releasesOptsFor(channel: ReleaseChannel): { channel?: "preview" } {
|
|
|
22
25
|
return channel === "preview" ? { channel: "preview" } : {};
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
version: string
|
|
27
|
-
is_stable
|
|
28
|
-
assistant_image_ref
|
|
29
|
-
gateway_image_ref
|
|
30
|
-
credential_executor_image_ref
|
|
28
|
+
const ReleaseListItemSchema = z.object({
|
|
29
|
+
version: z.string().min(1),
|
|
30
|
+
is_stable: z.boolean().optional(),
|
|
31
|
+
assistant_image_ref: z.string().nullable().optional(),
|
|
32
|
+
gateway_image_ref: z.string().nullable().optional(),
|
|
33
|
+
credential_executor_image_ref: z.string().nullable().optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type ReleaseListItem = z.infer<typeof ReleaseListItemSchema>;
|
|
37
|
+
|
|
38
|
+
const STABLE_VERSION = /^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
39
|
+
const GitHubReleaseSchema = z.object({
|
|
40
|
+
tag_name: z.string().regex(STABLE_VERSION),
|
|
41
|
+
draft: z.literal(false),
|
|
42
|
+
prerelease: z.literal(false),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
async function fetchLatestGitHubStableVersion(): Promise<string | null> {
|
|
46
|
+
try {
|
|
47
|
+
const response = await loopbackSafeFetch(
|
|
48
|
+
`https://api.github.com/repos/${CADET_GITHUB_REPOSITORY}/releases/latest`,
|
|
49
|
+
{
|
|
50
|
+
headers: { Accept: "application/vnd.github+json" },
|
|
51
|
+
signal: AbortSignal.timeout(10_000),
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const parsed = GitHubReleaseSchema.safeParse(await response.json());
|
|
58
|
+
return parsed.success ? parsed.data.tag_name.replace(/^v/, "") : null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
31
62
|
}
|
|
32
63
|
|
|
33
64
|
/**
|
|
@@ -56,15 +87,21 @@ export async function fetchReleases(opts?: {
|
|
|
56
87
|
`${cloudUrl}/v1/releases/?${filter}&limit=${RELEASES_FETCH_LIMIT}`,
|
|
57
88
|
{ signal: AbortSignal.timeout(10_000) },
|
|
58
89
|
);
|
|
59
|
-
if (!response.ok)
|
|
60
|
-
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const parsed = z
|
|
94
|
+
.array(ReleaseListItemSchema)
|
|
95
|
+
.safeParse(await response.json());
|
|
96
|
+
return parsed.success ? parsed.data : null;
|
|
61
97
|
} catch {
|
|
62
98
|
return null;
|
|
63
99
|
}
|
|
64
100
|
}
|
|
65
101
|
|
|
66
102
|
/**
|
|
67
|
-
* Fetch the latest release version
|
|
103
|
+
* Fetch the latest release version from Cadet Cloud, falling back to GitHub
|
|
104
|
+
* for stable releases when the cloud has no usable result.
|
|
68
105
|
* Returns the version string (e.g. "0.7.0") or null if unavailable.
|
|
69
106
|
* The releases endpoint returns entries ordered newest-first, so the first
|
|
70
107
|
* entry is the latest for the requested channel.
|
|
@@ -73,7 +110,14 @@ export async function fetchLatestVersion(
|
|
|
73
110
|
channel: ReleaseChannel = "stable",
|
|
74
111
|
): Promise<string | null> {
|
|
75
112
|
const releases = await fetchReleases(releasesOptsFor(channel));
|
|
76
|
-
|
|
113
|
+
if (channel === "preview") {
|
|
114
|
+
return releases?.[0]?.version ?? null;
|
|
115
|
+
}
|
|
116
|
+
const latest = releases?.find(
|
|
117
|
+
(release) =>
|
|
118
|
+
release.is_stable !== false && STABLE_VERSION.test(release.version),
|
|
119
|
+
);
|
|
120
|
+
return latest?.version ?? fetchLatestGitHubStableVersion();
|
|
77
121
|
}
|
|
78
122
|
|
|
79
123
|
/**
|
package/src/lib/cloud-values.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared interactive confirmation for destructive CLI commands (retire,
|
|
3
|
-
* …). Per cli/AGENTS.md, a command that removes assistant state must print the
|
|
2
|
+
* Shared interactive confirmation for destructive CLI commands (retire,
|
|
3
|
+
* devices revoke, …). Per cli/AGENTS.md, a command that removes assistant state must print the
|
|
4
4
|
* resolved identity and require confirmation, with a `--yes` bypass for
|
|
5
5
|
* automation.
|
|
6
6
|
*/
|
package/src/lib/docker.ts
CHANGED
|
@@ -296,6 +296,8 @@ export interface HatchDockerParams {
|
|
|
296
296
|
/** Extra env vars forwarded into the assistant container. */
|
|
297
297
|
flagEnvVars?: Record<string, string>;
|
|
298
298
|
setupProviderCredentials?: boolean;
|
|
299
|
+
/** Whole-installation state installed into stopped containers before startup. */
|
|
300
|
+
initialArchive?: string;
|
|
299
301
|
/**
|
|
300
302
|
* Path to a local source tree to build images from before hatching. When
|
|
301
303
|
* provided, this path is used directly as the repo root and no file
|
|
@@ -812,6 +814,7 @@ export const SERVICE_START_ORDER: ServiceName[] = [
|
|
|
812
814
|
/** Start all three containers in dependency order. */
|
|
813
815
|
export async function startContainers(
|
|
814
816
|
opts: {
|
|
817
|
+
createOnly?: boolean;
|
|
815
818
|
signingKey?: string;
|
|
816
819
|
bootstrapSecret?: string;
|
|
817
820
|
cesServiceToken?: string;
|
|
@@ -832,8 +835,13 @@ export async function startContainers(
|
|
|
832
835
|
avatarDevicePath: resolveAvatarDevicePath(),
|
|
833
836
|
});
|
|
834
837
|
for (const service of SERVICE_START_ORDER) {
|
|
835
|
-
|
|
836
|
-
|
|
838
|
+
const args = runArgs[service]();
|
|
839
|
+
if (opts.createOnly) {
|
|
840
|
+
args[0] = "create";
|
|
841
|
+
args.splice(args.indexOf("-d"), 1);
|
|
842
|
+
}
|
|
843
|
+
log(`${opts.createOnly ? "Creating" : "Starting"} ${service} container...`);
|
|
844
|
+
await exec("docker", args);
|
|
837
845
|
}
|
|
838
846
|
}
|
|
839
847
|
|
|
@@ -849,14 +857,23 @@ export async function stopContainers(
|
|
|
849
857
|
/** Stop containers without removing them (preserves state for `docker start`). */
|
|
850
858
|
export async function sleepContainers(
|
|
851
859
|
res: ReturnType<typeof dockerResourceNames>,
|
|
860
|
+
options: { timeoutMs?: number } = {},
|
|
852
861
|
): Promise<void> {
|
|
853
862
|
for (const container of [
|
|
854
|
-
res.cesContainer,
|
|
855
|
-
res.gatewayContainer,
|
|
856
863
|
res.assistantContainer,
|
|
864
|
+
res.gatewayContainer,
|
|
865
|
+
res.cesContainer,
|
|
857
866
|
]) {
|
|
858
867
|
try {
|
|
859
|
-
await
|
|
868
|
+
const status = await execOutput("docker", ["inspect", "--format", "{{.State.Status}}", container]);
|
|
869
|
+
if (status === "exited" || status === "created") continue;
|
|
870
|
+
if (status !== "running") throw new Error(`${container} is ${status}; cannot stop gracefully.`);
|
|
871
|
+
await exec("docker", ["kill", "--signal=SIGTERM", container]);
|
|
872
|
+
const deadline = Date.now() + (options.timeoutMs ?? 60_000);
|
|
873
|
+
while (await execOutput("docker", ["inspect", "--format", "{{.State.Running}}", container]) === "true") {
|
|
874
|
+
if (Date.now() >= deadline) throw new Error(`${container} did not stop gracefully.`);
|
|
875
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
876
|
+
}
|
|
860
877
|
} catch (err) {
|
|
861
878
|
const msg =
|
|
862
879
|
err instanceof Error ? err.message.toLowerCase() : String(err);
|
|
@@ -1630,7 +1647,9 @@ export async function hatchDocker(params: HatchDockerParams): Promise<void> {
|
|
|
1630
1647
|
}
|
|
1631
1648
|
|
|
1632
1649
|
const cesServiceToken = randomBytes(32).toString("hex");
|
|
1633
|
-
const signingKey =
|
|
1650
|
+
const signingKey = params.initialArchive
|
|
1651
|
+
? undefined
|
|
1652
|
+
: randomBytes(32).toString("hex");
|
|
1634
1653
|
|
|
1635
1654
|
// When launched by a remote hatch startup script, the env var
|
|
1636
1655
|
// GUARDIAN_BOOTSTRAP_SECRET is already set with the laptop's secret.
|
|
@@ -1669,8 +1688,26 @@ export async function hatchDocker(params: HatchDockerParams): Promise<void> {
|
|
|
1669
1688
|
...flagEnvVars,
|
|
1670
1689
|
CADET_DEVICE_ID: hostDeviceId,
|
|
1671
1690
|
};
|
|
1691
|
+
const runtimeUrl = `http://localhost:${gatewayPort}`;
|
|
1692
|
+
const dockerEntry: AssistantEntry = {
|
|
1693
|
+
assistantId: instanceName,
|
|
1694
|
+
runtimeUrl,
|
|
1695
|
+
cloud: "docker",
|
|
1696
|
+
hatchedAt: new Date().toISOString(),
|
|
1697
|
+
guardianBootstrapSecret: ownSecret,
|
|
1698
|
+
containerInfo: {
|
|
1699
|
+
assistantImage: imageTags.assistant,
|
|
1700
|
+
gatewayImage: imageTags.gateway,
|
|
1701
|
+
cesImage: imageTags["credential-executor"],
|
|
1702
|
+
assistantPort,
|
|
1703
|
+
},
|
|
1704
|
+
};
|
|
1705
|
+
if (params.initialArchive) {
|
|
1706
|
+
saveAssistantEntry(dockerEntry);
|
|
1707
|
+
}
|
|
1672
1708
|
await startContainers(
|
|
1673
1709
|
{
|
|
1710
|
+
createOnly: !!params.initialArchive,
|
|
1674
1711
|
signingKey,
|
|
1675
1712
|
bootstrapSecret,
|
|
1676
1713
|
cesServiceToken,
|
|
@@ -1688,29 +1725,29 @@ export async function hatchDocker(params: HatchDockerParams): Promise<void> {
|
|
|
1688
1725
|
);
|
|
1689
1726
|
const containersUpAt = Date.now();
|
|
1690
1727
|
|
|
1691
|
-
const runtimeUrl = `http://localhost:${gatewayPort}`;
|
|
1692
|
-
const dockerEntry: AssistantEntry = {
|
|
1693
|
-
assistantId: instanceName,
|
|
1694
|
-
runtimeUrl,
|
|
1695
|
-
cloud: "docker",
|
|
1696
|
-
hatchedAt: new Date().toISOString(),
|
|
1697
|
-
guardianBootstrapSecret: ownSecret,
|
|
1698
|
-
containerInfo: {
|
|
1699
|
-
assistantImage: imageTags.assistant,
|
|
1700
|
-
gatewayImage: imageTags.gateway,
|
|
1701
|
-
cesImage: imageTags["credential-executor"],
|
|
1702
|
-
assistantPort,
|
|
1703
|
-
},
|
|
1704
|
-
};
|
|
1705
1728
|
emitProgress(5, 6, "Saving configuration...");
|
|
1706
1729
|
saveAssistantEntry(dockerEntry);
|
|
1707
1730
|
setActiveAssistant(instanceName);
|
|
1708
1731
|
|
|
1732
|
+
if (params.initialArchive) {
|
|
1733
|
+
const { initializeRootArchive, DockerRootStorage } =
|
|
1734
|
+
await import("./root-backup.js");
|
|
1735
|
+
await initializeRootArchive(dockerEntry, params.initialArchive);
|
|
1736
|
+
const storage = new DockerRootStorage(dockerEntry);
|
|
1737
|
+
storage.initialRunning = new Set(Object.values(storage.containers));
|
|
1738
|
+
await storage.resume();
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1709
1741
|
emitProgress(6, 6, "Waiting for services...");
|
|
1710
1742
|
// Hatch always detaches; only watch mode keeps the log stream attached.
|
|
1711
1743
|
const waitDetached = !watch;
|
|
1712
|
-
const { ready, guardianAccessToken, notReadyReason } =
|
|
1713
|
-
|
|
1744
|
+
const { ready, guardianAccessToken, notReadyReason } = params.initialArchive
|
|
1745
|
+
? {
|
|
1746
|
+
ready: true,
|
|
1747
|
+
guardianAccessToken: undefined,
|
|
1748
|
+
notReadyReason: undefined,
|
|
1749
|
+
}
|
|
1750
|
+
: await waitForGatewayAndLease({
|
|
1714
1751
|
bootstrapSecret: ownSecret,
|
|
1715
1752
|
containerName: res.assistantContainer,
|
|
1716
1753
|
detached: waitDetached,
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
mkdirSync,
|
|
8
8
|
openSync,
|
|
9
9
|
readFileSync,
|
|
10
|
-
rmdirSync,
|
|
11
10
|
statSync,
|
|
12
11
|
unlinkSync,
|
|
13
12
|
writeFileSync,
|
|
@@ -79,27 +78,6 @@ function getGuardianTokenPath(assistantId: string): string {
|
|
|
79
78
|
return guardianTokenPath(resolveConfigDir(process.env), assistantId);
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
/**
|
|
83
|
-
* Best-effort removal of an assistant's stored guardian token (used by
|
|
84
|
-
* `cadet unpair` to forget a paired connection). Never throws if the token
|
|
85
|
-
* file or its per-assistant directory is already absent.
|
|
86
|
-
*/
|
|
87
|
-
export function deleteGuardianToken(assistantId: string): void {
|
|
88
|
-
const tokenPath = getGuardianTokenPath(assistantId);
|
|
89
|
-
try {
|
|
90
|
-
unlinkSync(tokenPath);
|
|
91
|
-
} catch {
|
|
92
|
-
/* already gone */
|
|
93
|
-
}
|
|
94
|
-
// Clean up the now-empty per-assistant directory; rmdir throws if it still
|
|
95
|
-
// holds other files, in which case we leave it.
|
|
96
|
-
try {
|
|
97
|
-
rmdirSync(dirname(tokenPath));
|
|
98
|
-
} catch {
|
|
99
|
-
/* not empty or absent */
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
81
|
function getPersistedDeviceIdPath(): string {
|
|
104
82
|
return join(getConfigDir(getCurrentEnvironment()), "device-id");
|
|
105
83
|
}
|
|
@@ -454,7 +432,7 @@ export async function refreshGuardianTokenResult(
|
|
|
454
432
|
return {
|
|
455
433
|
ok: false,
|
|
456
434
|
status: 401,
|
|
457
|
-
error: "Guardian token expired
|
|
435
|
+
error: "Guardian refresh token expired",
|
|
458
436
|
};
|
|
459
437
|
}
|
|
460
438
|
|
package/src/lib/hatch-local.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { persistRootSigningKey } from "./root-signing-key.js";
|
|
1
2
|
import {
|
|
2
3
|
existsSync,
|
|
3
4
|
lstatSync,
|
|
@@ -160,6 +161,8 @@ export interface HatchLocalParams {
|
|
|
160
161
|
/** Extra env vars forwarded into the assistant process. */
|
|
161
162
|
flagEnvVars?: Record<string, string>;
|
|
162
163
|
setupProviderCredentials?: boolean;
|
|
164
|
+
/** Whole-installation state installed before the first service startup. */
|
|
165
|
+
initialArchive?: string;
|
|
163
166
|
/**
|
|
164
167
|
* Show the per-service startup logs (assistant, credential executor,
|
|
165
168
|
* gateway) and the extended next steps. Off by default — the happy path
|
|
@@ -291,7 +294,20 @@ export async function hatchLocal(
|
|
|
291
294
|
const defaultWorkspaceConfigPath = writeInitialConfig(hatchConfigValues);
|
|
292
295
|
|
|
293
296
|
reporter.progress(3, 6, "Starting assistant...");
|
|
294
|
-
const signingKey =
|
|
297
|
+
const signingKey = params.initialArchive
|
|
298
|
+
? await (
|
|
299
|
+
await import("./root-backup.js")
|
|
300
|
+
).initializeRootArchive(
|
|
301
|
+
{
|
|
302
|
+
assistantId: instanceName,
|
|
303
|
+
cloud: "local",
|
|
304
|
+
runtimeUrl: "",
|
|
305
|
+
resources,
|
|
306
|
+
},
|
|
307
|
+
params.initialArchive,
|
|
308
|
+
)
|
|
309
|
+
: generateLocalSigningKey();
|
|
310
|
+
persistRootSigningKey(resources.instanceDir, signingKey);
|
|
295
311
|
const bootstrapSecret = generateLocalSigningKey();
|
|
296
312
|
let runtimeUrl = `http://127.0.0.1:${resources.gatewayPort}`;
|
|
297
313
|
try {
|