@forgezero/agent 0.1.67 → 0.1.68
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 +7 -1
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +1 -1
- package/dist/fz-agent.js +1 -1
- package/dist/fz.js +135 -14
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.d.ts +28 -1
- package/dist/operator-bootstrap.js +167 -4
- package/dist/platform-fleet-verification.js +1 -1
- package/dist/provision.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -532,22 +532,26 @@ await readBootstrapBundle(built.bundlePath, built.manifestPath);
|
|
|
532
532
|
<a id="forgezero-agent-operator-bootstrap"></a>
|
|
533
533
|
## @forgezero/agent/operator-bootstrap
|
|
534
534
|
|
|
535
|
-
Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. This entry exposes
|
|
535
|
+
Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. This entry exposes 19 named value exports and 17 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
536
536
|
|
|
537
537
|
```text
|
|
538
538
|
import {
|
|
539
539
|
applyOperatorMetalBootstrap,
|
|
540
540
|
applyOperatorPlatformBootstrap,
|
|
541
541
|
applyOperatorPlatformFleetBootstrap,
|
|
542
|
+
collectOperatorGuestHostKeys,
|
|
542
543
|
createOperatorSshHop,
|
|
543
544
|
operatorPlatformFleetCoordinates,
|
|
544
545
|
planOperatorMetalBootstrap,
|
|
545
546
|
planOperatorPlatformBootstrap,
|
|
546
547
|
planOperatorPlatformFleetBootstrap,
|
|
548
|
+
readOperatorGuestHostKeyEvidence,
|
|
547
549
|
readOperatorMetalBootstrapRequest,
|
|
548
550
|
readOperatorPlatformBootstrapFleetRequest,
|
|
549
551
|
readOperatorPlatformBootstrapRequest,
|
|
550
552
|
verifyOperatorPlatformFleet,
|
|
553
|
+
withOperatorGuestTransport,
|
|
554
|
+
writeOperatorGuestHostKeyEvidence,
|
|
551
555
|
writeOperatorMetalBootstrapRequest,
|
|
552
556
|
writeOperatorPlatformBootstrapFleetRequest,
|
|
553
557
|
writeOperatorPlatformBootstrapRequest,
|
|
@@ -556,6 +560,8 @@ import {
|
|
|
556
560
|
import type {
|
|
557
561
|
OperatorCommand,
|
|
558
562
|
OperatorCommandResult,
|
|
563
|
+
OperatorGuestHostKeyEvidence,
|
|
564
|
+
OperatorGuestTransport,
|
|
559
565
|
OperatorMetalBootstrapMode,
|
|
560
566
|
OperatorMetalBootstrapPlan,
|
|
561
567
|
OperatorMetalBootstrapRequest,
|
package/dist/agent-heartbeat.js
CHANGED
package/dist/bootstrap.js
CHANGED
package/dist/fz-agent.js
CHANGED
|
@@ -8588,7 +8588,7 @@ import { join as join3 } from "path";
|
|
|
8588
8588
|
import { fileURLToPath } from "url";
|
|
8589
8589
|
|
|
8590
8590
|
// src/version.ts
|
|
8591
|
-
var VERSION2 = "0.1.
|
|
8591
|
+
var VERSION2 = "0.1.68";
|
|
8592
8592
|
|
|
8593
8593
|
// src/ssh-bootstrap.ts
|
|
8594
8594
|
class SshBootstrapError extends Error {
|
package/dist/fz.js
CHANGED
|
@@ -4829,7 +4829,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
4829
4829
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
4830
4830
|
|
|
4831
4831
|
// src/version.ts
|
|
4832
|
-
var VERSION2 = "0.1.
|
|
4832
|
+
var VERSION2 = "0.1.68";
|
|
4833
4833
|
|
|
4834
4834
|
// src/software.ts
|
|
4835
4835
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -16237,6 +16237,48 @@ function writeOperatorMetalBootstrapRequest(path, request) {
|
|
|
16237
16237
|
validateMetalRequestValue(request);
|
|
16238
16238
|
return writeOwnerJson2(path, request, "operator metal request");
|
|
16239
16239
|
}
|
|
16240
|
+
var validateGuestHostKeyEvidenceValue = (value, request) => {
|
|
16241
|
+
const root = exactKeys5(value, ["format", "kind", "metalHostKeySha256", "nodes"], "operator guest host-key evidence");
|
|
16242
|
+
if (root.format !== 1 || root.kind !== "forgezero-operator-guest-host-keys" || typeof root.metalHostKeySha256 !== "string" || !Array.isArray(root.nodes)) {
|
|
16243
|
+
throw new Error("operator guest host-key evidence is malformed");
|
|
16244
|
+
}
|
|
16245
|
+
const nodes = root.nodes.map((value2, index) => {
|
|
16246
|
+
const row2 = exactKeys5(value2, ["name", "address", "hostKey", "hostKeySha256"], `operator guest host key ${index + 1}`);
|
|
16247
|
+
if (typeof row2.name !== "string" || typeof row2.address !== "string") {
|
|
16248
|
+
throw new Error("operator guest host-key identity is malformed");
|
|
16249
|
+
}
|
|
16250
|
+
const hop = validateHop({
|
|
16251
|
+
address: row2.address,
|
|
16252
|
+
port: 22,
|
|
16253
|
+
user: "forgezero",
|
|
16254
|
+
hostKey: row2.hostKey,
|
|
16255
|
+
hostKeySha256: row2.hostKeySha256
|
|
16256
|
+
}, `operator guest host key ${row2.name}`);
|
|
16257
|
+
return { name: row2.name, address: hop.address, hostKey: hop.hostKey, hostKeySha256: hop.hostKeySha256 };
|
|
16258
|
+
});
|
|
16259
|
+
if (new Set(nodes.map(({ name }) => name)).size !== nodes.length || new Set(nodes.map(({ address }) => address)).size !== nodes.length) {
|
|
16260
|
+
throw new Error("operator guest host-key identities must be unique");
|
|
16261
|
+
}
|
|
16262
|
+
if (request) {
|
|
16263
|
+
const expected = nodes.length === request.genesis.nodes.length ? [...request.genesis.nodes] : [...request.genesis.nodes, ...request.genesis.rehearsalNode ? [request.genesis.rehearsalNode] : []];
|
|
16264
|
+
if (root.metalHostKeySha256 !== request.target.hostKeySha256 || ![request.genesis.nodes.length, request.genesis.nodes.length + (request.genesis.rehearsalNode ? 1 : 0)].includes(nodes.length) || nodes.length !== expected.length || expected.some((guest, index) => nodes[index]?.name !== guest.name || nodes[index]?.address !== guest.address)) {
|
|
16265
|
+
throw new Error("operator guest host-key evidence does not match the reviewed Metal request");
|
|
16266
|
+
}
|
|
16267
|
+
}
|
|
16268
|
+
return {
|
|
16269
|
+
format: 1,
|
|
16270
|
+
kind: "forgezero-operator-guest-host-keys",
|
|
16271
|
+
metalHostKeySha256: root.metalHostKeySha256,
|
|
16272
|
+
nodes
|
|
16273
|
+
};
|
|
16274
|
+
};
|
|
16275
|
+
function readOperatorGuestHostKeyEvidence(path, request) {
|
|
16276
|
+
const value = JSON.parse(new TextDecoder().decode(ownerFile(path, REQUEST_LIMIT, "operator guest host-key evidence")));
|
|
16277
|
+
return validateGuestHostKeyEvidenceValue(value, request);
|
|
16278
|
+
}
|
|
16279
|
+
function writeOperatorGuestHostKeyEvidence(path, evidence, request) {
|
|
16280
|
+
return writeOwnerJson2(path, validateGuestHostKeyEvidenceValue(evidence, request), "operator guest host-key evidence");
|
|
16281
|
+
}
|
|
16240
16282
|
function planOperatorMetalBootstrap(request, mode) {
|
|
16241
16283
|
const config = readMetalBootstrapConfig(request.metalConfigFile);
|
|
16242
16284
|
const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
|
|
@@ -16244,19 +16286,20 @@ function planOperatorMetalBootstrap(request, mode) {
|
|
|
16244
16286
|
throw new Error("the reviewed metal request has no rehearsal node");
|
|
16245
16287
|
}
|
|
16246
16288
|
const claims = mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
|
|
16289
|
+
const genesisNodes = mode === "host-keys" ? request.genesis.nodes.map(({ name }) => name) : mode === "rehearsal-host-keys" ? guestRows(request).map(({ name }) => name) : claims.map((claim) => claim.spec.guestName).filter((name) => Boolean(name));
|
|
16247
16290
|
return {
|
|
16248
16291
|
kind: "metal-remote",
|
|
16249
16292
|
mode,
|
|
16250
|
-
mutation:
|
|
16293
|
+
mutation: !["status", "host-keys", "rehearsal-host-keys"].includes(mode),
|
|
16251
16294
|
target: { address: request.target.address, port: request.target.port, user: request.target.user },
|
|
16252
|
-
steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
|
|
16295
|
+
steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "host-keys" || mode === "rehearsal-host-keys" ? ["verify pinned SSH transport", "collect exact Ed25519 host keys for the reviewed guest addresses"] : mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
|
|
16253
16296
|
"verify pinned SSH transport and caller-approved SSH agent",
|
|
16254
16297
|
"copy pinned Bun and packaged fz artifacts to private staging",
|
|
16255
16298
|
"stage the strict owner-only metal config and optional seed handoff",
|
|
16256
16299
|
"run typed fz bootstrap metal --apply",
|
|
16257
16300
|
"remove transient local and remote staging data"
|
|
16258
16301
|
],
|
|
16259
|
-
genesisNodes
|
|
16302
|
+
genesisNodes,
|
|
16260
16303
|
secretInputs: config.agentSeedFile ? ["metal-agent-seed"] : []
|
|
16261
16304
|
};
|
|
16262
16305
|
}
|
|
@@ -16479,6 +16522,53 @@ var copy = async (exec, request, knownHosts, local, remotePath, secret = false)
|
|
|
16479
16522
|
`${destination2(request.target)}:${remotePath}`
|
|
16480
16523
|
], "secure copy", { secret });
|
|
16481
16524
|
};
|
|
16525
|
+
var guestRows = (request) => [
|
|
16526
|
+
...request.genesis.nodes,
|
|
16527
|
+
...request.genesis.rehearsalNode ? [request.genesis.rehearsalNode] : []
|
|
16528
|
+
];
|
|
16529
|
+
var parsedScannedHostKey = (output, address) => {
|
|
16530
|
+
const keys = output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith(`${address} ssh-ed25519 `)).map((line) => line.split(/\s+/).slice(1, 3).join(" "));
|
|
16531
|
+
const unique = [...new Set(keys)];
|
|
16532
|
+
if (unique.length !== 1 || !/^ssh-ed25519 [A-Za-z0-9+/]+={0,3}$/.test(unique[0])) {
|
|
16533
|
+
throw new Error(`guest ${address} did not return one exact Ed25519 host key`);
|
|
16534
|
+
}
|
|
16535
|
+
return unique[0];
|
|
16536
|
+
};
|
|
16537
|
+
async function collectOperatorGuestHostKeys(request, options = {}, includeRehearsal = false) {
|
|
16538
|
+
publicIdentity(request.target.identityPublicKeyFile);
|
|
16539
|
+
socketPath(request.target.agentSocket);
|
|
16540
|
+
const exec = options.exec ?? defaultExec3;
|
|
16541
|
+
const directory = mkdtempSync(join10(tmpdir(), "forgezero-operator-host-keys-"));
|
|
16542
|
+
try {
|
|
16543
|
+
const knownHosts = writeKnownHosts(request, directory);
|
|
16544
|
+
const nodes = [];
|
|
16545
|
+
for (const guest of includeRehearsal ? guestRows(request) : request.genesis.nodes) {
|
|
16546
|
+
const output = await remote(exec, request, knownHosts, [
|
|
16547
|
+
"/usr/bin/ssh-keyscan",
|
|
16548
|
+
"-T",
|
|
16549
|
+
"10",
|
|
16550
|
+
"-t",
|
|
16551
|
+
"ed25519",
|
|
16552
|
+
guest.address
|
|
16553
|
+
], `collect ${guest.name} SSH host key`);
|
|
16554
|
+
const hostKey = parsedScannedHostKey(output, guest.address);
|
|
16555
|
+
nodes.push({
|
|
16556
|
+
name: guest.name,
|
|
16557
|
+
address: guest.address,
|
|
16558
|
+
hostKey,
|
|
16559
|
+
hostKeySha256: fingerprint(hostKey)
|
|
16560
|
+
});
|
|
16561
|
+
}
|
|
16562
|
+
return validateGuestHostKeyEvidenceValue({
|
|
16563
|
+
format: 1,
|
|
16564
|
+
kind: "forgezero-operator-guest-host-keys",
|
|
16565
|
+
metalHostKeySha256: request.target.hostKeySha256,
|
|
16566
|
+
nodes
|
|
16567
|
+
}, request);
|
|
16568
|
+
} finally {
|
|
16569
|
+
rmSync7(directory, { recursive: true, force: true });
|
|
16570
|
+
}
|
|
16571
|
+
}
|
|
16482
16572
|
async function stageConfig(config, directory) {
|
|
16483
16573
|
const rewritten = structuredClone(config);
|
|
16484
16574
|
const staged = [];
|
|
@@ -16658,6 +16748,12 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
16658
16748
|
const output2 = await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/local/bin/fz", "bootstrap", "status"], "remote metal status");
|
|
16659
16749
|
return { plan, output: output2 };
|
|
16660
16750
|
}
|
|
16751
|
+
if (mode === "host-keys" || mode === "rehearsal-host-keys") {
|
|
16752
|
+
return {
|
|
16753
|
+
plan,
|
|
16754
|
+
output: JSON.stringify(await collectOperatorGuestHostKeys(request, { ...options, exec }, mode === "rehearsal-host-keys"))
|
|
16755
|
+
};
|
|
16756
|
+
}
|
|
16661
16757
|
if (mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup") {
|
|
16662
16758
|
const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
|
|
16663
16759
|
if ((mode === "rehearsal" || mode === "rehearsal-cleanup") && !request.genesis.rehearsalNode) {
|
|
@@ -18002,6 +18098,8 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18002
18098
|
const output = genesisOutputDirectory(directory);
|
|
18003
18099
|
const metalRequestFile = bootstrapAnswer("Owner-only metal remote request containing the reviewed genesis guests");
|
|
18004
18100
|
const metalRequest = readOperatorMetalBootstrapRequest(metalRequestFile);
|
|
18101
|
+
const guestHostKeysFile = bootstrapAnswer("Owner-only guest host-key evidence file");
|
|
18102
|
+
const guestHostKeys = readOperatorGuestHostKeyEvidence(guestHostKeysFile, metalRequest);
|
|
18005
18103
|
const fleet = metalRequest.genesis.nodes;
|
|
18006
18104
|
const metalHostname = readMetalBootstrapConfig(metalRequest.metalConfigFile).metalHostname;
|
|
18007
18105
|
const template = interactiveBootstrap("platform", fleet);
|
|
@@ -18022,16 +18120,28 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18022
18120
|
writeBootstrapConfig(path, config);
|
|
18023
18121
|
return path;
|
|
18024
18122
|
});
|
|
18025
|
-
const identity = operatorIdentityCoordinates();
|
|
18026
|
-
const useJump = bootstrapBoolean("Reach genesis computes through the Metal SSH jump?", "yes");
|
|
18027
|
-
const jump = useJump ? interactiveOperatorHop("Metal jump", { user: "root" }) : undefined;
|
|
18028
18123
|
const requests = configs.map((platformConfigFile, index) => {
|
|
18029
18124
|
const guest = fleet[index];
|
|
18030
|
-
const
|
|
18125
|
+
const host = guestHostKeys.nodes.find(({ name }) => name === guest.name);
|
|
18031
18126
|
return writeOperatorPlatformBootstrapRequest(join12(output, `${guest.name}-remote.json`), {
|
|
18032
18127
|
kind: "platform-remote",
|
|
18033
18128
|
platformConfigFile,
|
|
18034
|
-
target: {
|
|
18129
|
+
target: {
|
|
18130
|
+
address: guest.address,
|
|
18131
|
+
port: 22,
|
|
18132
|
+
user: "forgezero",
|
|
18133
|
+
hostKey: host.hostKey,
|
|
18134
|
+
hostKeySha256: host.hostKeySha256,
|
|
18135
|
+
identityPublicKeyFile: metalRequest.target.identityPublicKeyFile,
|
|
18136
|
+
agentSocket: metalRequest.target.agentSocket,
|
|
18137
|
+
jump: {
|
|
18138
|
+
address: metalRequest.target.address,
|
|
18139
|
+
port: metalRequest.target.port,
|
|
18140
|
+
user: metalRequest.target.user,
|
|
18141
|
+
hostKey: metalRequest.target.hostKey,
|
|
18142
|
+
hostKeySha256: metalRequest.target.hostKeySha256
|
|
18143
|
+
}
|
|
18144
|
+
}
|
|
18035
18145
|
});
|
|
18036
18146
|
});
|
|
18037
18147
|
const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join12(output, "platform-fleet-remote.json"), requests);
|
|
@@ -18232,11 +18342,14 @@ async function cmdBootstrap(options, args) {
|
|
|
18232
18342
|
}
|
|
18233
18343
|
if (operation === "metal" && args[1] === "remote") {
|
|
18234
18344
|
const mode = args[2];
|
|
18235
|
-
if (!mode || !["apply", "genesis", "rehearsal", "rehearsal-cleanup", "status"].includes(mode) || args[3] !== undefined) {
|
|
18236
|
-
throw new Error("Usage: fz bootstrap metal remote <apply|genesis|rehearsal|rehearsal-cleanup|status> --bootstrap-config <owner-only-request.json> [--apply]");
|
|
18345
|
+
if (!mode || !["apply", "genesis", "rehearsal", "rehearsal-cleanup", "host-keys", "rehearsal-host-keys", "status"].includes(mode) || args[3] !== undefined) {
|
|
18346
|
+
throw new Error("Usage: fz bootstrap metal remote <apply|genesis|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status> --bootstrap-config <owner-only-request.json> [--output <owner-only-evidence.json>] [--apply]");
|
|
18237
18347
|
}
|
|
18238
18348
|
if (!options.bootstrapConfigPath)
|
|
18239
18349
|
throw new Error("remote metal bootstrap requires --bootstrap-config");
|
|
18350
|
+
if ((mode === "host-keys" || mode === "rehearsal-host-keys") && !options.outputPath) {
|
|
18351
|
+
throw new Error(`${mode} requires --output <absolute-owner-only-evidence.json>`);
|
|
18352
|
+
}
|
|
18240
18353
|
const request = readOperatorMetalBootstrapRequest(options.bootstrapConfigPath);
|
|
18241
18354
|
const plan2 = planOperatorMetalBootstrap(request, mode);
|
|
18242
18355
|
if (!options.apply) {
|
|
@@ -18244,7 +18357,15 @@ async function cmdBootstrap(options, args) {
|
|
|
18244
18357
|
out.step("Review the pinned metal target and exact genesis nodes, then repeat with --apply to contact the host.");
|
|
18245
18358
|
return 0;
|
|
18246
18359
|
}
|
|
18247
|
-
|
|
18360
|
+
const result2 = await applyOperatorMetalBootstrap(request, mode);
|
|
18361
|
+
if (mode === "host-keys" || mode === "rehearsal-host-keys") {
|
|
18362
|
+
const evidence = JSON.parse(result2.output);
|
|
18363
|
+
const path = writeOperatorGuestHostKeyEvidence(options.outputPath, evidence, request);
|
|
18364
|
+
readOperatorGuestHostKeyEvidence(path, request);
|
|
18365
|
+
out.line(JSON.stringify({ plan: result2.plan, evidencePath: path, mode: "0600" }, null, 2));
|
|
18366
|
+
return 0;
|
|
18367
|
+
}
|
|
18368
|
+
out.line(JSON.stringify(result2, null, 2));
|
|
18248
18369
|
return 0;
|
|
18249
18370
|
}
|
|
18250
18371
|
if (operation === "platform" && args[1] === "cloudflare") {
|
|
@@ -18332,7 +18453,7 @@ async function cmdBootstrap(options, args) {
|
|
|
18332
18453
|
return 0;
|
|
18333
18454
|
}
|
|
18334
18455
|
if (!["platform", "repair"].includes(operation)) {
|
|
18335
|
-
throw new Error("Usage: fz bootstrap config <metal|platform|cloudflare>|platform [bundle|fleet <apply|status>|remote <apply|status>|cloudflare [verify|finalize]]|metal [remote <apply|genesis|rehearsal|rehearsal-cleanup|status>]|status|repair [--bootstrap-config <path>] [--apply]");
|
|
18456
|
+
throw new Error("Usage: fz bootstrap config <metal|platform|cloudflare>|platform [bundle|fleet <apply|status>|remote <apply|status>|cloudflare [verify|finalize]]|metal [remote <apply|genesis|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status>]|status|repair [--bootstrap-config <path>] [--apply]");
|
|
18336
18457
|
}
|
|
18337
18458
|
const config = options.bootstrapConfigPath ? readBootstrapConfig(options.bootstrapConfigPath) : operation === "repair" ? (() => {
|
|
18338
18459
|
throw new Error("repair requires --bootstrap-config so immutable coordinates are revalidated");
|
|
@@ -18927,7 +19048,7 @@ function usage() {
|
|
|
18927
19048
|
Persist secret-free public-node acceptance, then remove
|
|
18928
19049
|
the completed non-secret checkpoint and node handoffs
|
|
18929
19050
|
fz bootstrap metal Plan/install an identity-only physical provisioner
|
|
18930
|
-
fz bootstrap metal remote <apply|genesis|rehearsal|rehearsal-cleanup|status>
|
|
19051
|
+
fz bootstrap metal remote <apply|genesis|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status>
|
|
18931
19052
|
Install/bootstrap one pinned blank metal target, then
|
|
18932
19053
|
provision the exact selected three-node genesis seed
|
|
18933
19054
|
fz bootstrap status Verify persisted profile and supervised units
|
package/dist/metal-bootstrap.js
CHANGED
|
@@ -354,7 +354,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
// src/version.ts
|
|
357
|
-
var VERSION = "0.1.
|
|
357
|
+
var VERSION = "0.1.68";
|
|
358
358
|
|
|
359
359
|
// src/otel-collector.ts
|
|
360
360
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -26,6 +26,17 @@ export interface OperatorMetalBootstrapRequest {
|
|
|
26
26
|
sshPublicKeyFiles: string[];
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
+
export interface OperatorGuestHostKeyEvidence {
|
|
30
|
+
format: 1;
|
|
31
|
+
kind: 'forgezero-operator-guest-host-keys';
|
|
32
|
+
metalHostKeySha256: string;
|
|
33
|
+
nodes: readonly {
|
|
34
|
+
name: string;
|
|
35
|
+
address: string;
|
|
36
|
+
hostKey: string;
|
|
37
|
+
hostKeySha256: string;
|
|
38
|
+
}[];
|
|
39
|
+
}
|
|
29
40
|
export interface OperatorPlatformBootstrapFleetRequest {
|
|
30
41
|
kind: 'platform-fleet-remote';
|
|
31
42
|
requests: readonly [string, string, string];
|
|
@@ -66,7 +77,7 @@ export interface OperatorPlatformFleetCoordinates {
|
|
|
66
77
|
}[];
|
|
67
78
|
}
|
|
68
79
|
export type OperatorPlatformBootstrapMode = 'apply' | 'status';
|
|
69
|
-
export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'rehearsal' | 'rehearsal-cleanup' | 'status';
|
|
80
|
+
export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'rehearsal' | 'rehearsal-cleanup' | 'host-keys' | 'rehearsal-host-keys' | 'status';
|
|
70
81
|
export interface OperatorPlatformBootstrapPlan {
|
|
71
82
|
kind: 'platform-remote';
|
|
72
83
|
mode: OperatorPlatformBootstrapMode;
|
|
@@ -115,11 +126,27 @@ export declare function readOperatorPlatformBootstrapFleetRequest(path: string):
|
|
|
115
126
|
export declare function writeOperatorPlatformBootstrapFleetRequest(path: string, requestFiles: readonly [string, string, string]): string;
|
|
116
127
|
export declare function readOperatorMetalBootstrapRequest(path: string): OperatorMetalBootstrapRequest;
|
|
117
128
|
export declare function writeOperatorMetalBootstrapRequest(path: string, request: OperatorMetalBootstrapRequest): string;
|
|
129
|
+
export declare function readOperatorGuestHostKeyEvidence(path: string, request?: OperatorMetalBootstrapRequest): OperatorGuestHostKeyEvidence;
|
|
130
|
+
export declare function writeOperatorGuestHostKeyEvidence(path: string, evidence: OperatorGuestHostKeyEvidence, request?: OperatorMetalBootstrapRequest): string;
|
|
118
131
|
export declare function planOperatorMetalBootstrap(request: OperatorMetalBootstrapRequest, mode: OperatorMetalBootstrapMode): OperatorMetalBootstrapPlan;
|
|
119
132
|
export declare function planOperatorPlatformBootstrap(request: OperatorPlatformBootstrapRequest, mode: OperatorPlatformBootstrapMode): OperatorPlatformBootstrapPlan;
|
|
120
133
|
/** Exact non-secret coordinates derived from the same reviewed files bootstrap applies. */
|
|
121
134
|
export declare function operatorPlatformFleetCoordinates(fleet: OperatorPlatformBootstrapFleetRequest): OperatorPlatformFleetCoordinates;
|
|
122
135
|
export declare function planOperatorPlatformFleetBootstrap(fleet: OperatorPlatformBootstrapFleetRequest, mode: OperatorPlatformBootstrapMode): OperatorPlatformBootstrapFleetPlan;
|
|
136
|
+
export declare function collectOperatorGuestHostKeys(request: OperatorMetalBootstrapRequest, options?: OperatorPlatformBootstrapOptions, includeRehearsal?: boolean): Promise<OperatorGuestHostKeyEvidence>;
|
|
137
|
+
export interface OperatorGuestTransport {
|
|
138
|
+
run(nodeName: string, argv: readonly string[], options?: {
|
|
139
|
+
stdin?: string;
|
|
140
|
+
secret?: boolean;
|
|
141
|
+
allowFailure?: boolean;
|
|
142
|
+
}): Promise<OperatorCommandResult>;
|
|
143
|
+
copy(nodeName: string, localPath: string, remotePath: string): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Execute a bounded callback through the same pinned Metal jump, operator
|
|
147
|
+
* public identity selector and reviewed guest host keys used by bootstrap.
|
|
148
|
+
*/
|
|
149
|
+
export declare function withOperatorGuestTransport<T>(request: OperatorMetalBootstrapRequest, evidence: OperatorGuestHostKeyEvidence, callback: (transport: OperatorGuestTransport) => Promise<T>, options?: OperatorPlatformBootstrapOptions): Promise<T>;
|
|
123
150
|
/**
|
|
124
151
|
* Run one reviewed operator action. No private SSH key, credential value or
|
|
125
152
|
* management token is placed in argv; OpenSSH selects the matching private key
|
|
@@ -1396,7 +1396,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1396
1396
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1397
1397
|
|
|
1398
1398
|
// src/version.ts
|
|
1399
|
-
var VERSION = "0.1.
|
|
1399
|
+
var VERSION = "0.1.68";
|
|
1400
1400
|
|
|
1401
1401
|
// src/software.ts
|
|
1402
1402
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -6079,6 +6079,48 @@ function writeOperatorMetalBootstrapRequest(path, request) {
|
|
|
6079
6079
|
validateMetalRequestValue(request);
|
|
6080
6080
|
return writeOwnerJson2(path, request, "operator metal request");
|
|
6081
6081
|
}
|
|
6082
|
+
var validateGuestHostKeyEvidenceValue = (value, request) => {
|
|
6083
|
+
const root = exactKeys3(value, ["format", "kind", "metalHostKeySha256", "nodes"], "operator guest host-key evidence");
|
|
6084
|
+
if (root.format !== 1 || root.kind !== "forgezero-operator-guest-host-keys" || typeof root.metalHostKeySha256 !== "string" || !Array.isArray(root.nodes)) {
|
|
6085
|
+
throw new Error("operator guest host-key evidence is malformed");
|
|
6086
|
+
}
|
|
6087
|
+
const nodes = root.nodes.map((value2, index) => {
|
|
6088
|
+
const row = exactKeys3(value2, ["name", "address", "hostKey", "hostKeySha256"], `operator guest host key ${index + 1}`);
|
|
6089
|
+
if (typeof row.name !== "string" || typeof row.address !== "string") {
|
|
6090
|
+
throw new Error("operator guest host-key identity is malformed");
|
|
6091
|
+
}
|
|
6092
|
+
const hop = validateHop({
|
|
6093
|
+
address: row.address,
|
|
6094
|
+
port: 22,
|
|
6095
|
+
user: "forgezero",
|
|
6096
|
+
hostKey: row.hostKey,
|
|
6097
|
+
hostKeySha256: row.hostKeySha256
|
|
6098
|
+
}, `operator guest host key ${row.name}`);
|
|
6099
|
+
return { name: row.name, address: hop.address, hostKey: hop.hostKey, hostKeySha256: hop.hostKeySha256 };
|
|
6100
|
+
});
|
|
6101
|
+
if (new Set(nodes.map(({ name }) => name)).size !== nodes.length || new Set(nodes.map(({ address }) => address)).size !== nodes.length) {
|
|
6102
|
+
throw new Error("operator guest host-key identities must be unique");
|
|
6103
|
+
}
|
|
6104
|
+
if (request) {
|
|
6105
|
+
const expected = nodes.length === request.genesis.nodes.length ? [...request.genesis.nodes] : [...request.genesis.nodes, ...request.genesis.rehearsalNode ? [request.genesis.rehearsalNode] : []];
|
|
6106
|
+
if (root.metalHostKeySha256 !== request.target.hostKeySha256 || ![request.genesis.nodes.length, request.genesis.nodes.length + (request.genesis.rehearsalNode ? 1 : 0)].includes(nodes.length) || nodes.length !== expected.length || expected.some((guest, index) => nodes[index]?.name !== guest.name || nodes[index]?.address !== guest.address)) {
|
|
6107
|
+
throw new Error("operator guest host-key evidence does not match the reviewed Metal request");
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
return {
|
|
6111
|
+
format: 1,
|
|
6112
|
+
kind: "forgezero-operator-guest-host-keys",
|
|
6113
|
+
metalHostKeySha256: root.metalHostKeySha256,
|
|
6114
|
+
nodes
|
|
6115
|
+
};
|
|
6116
|
+
};
|
|
6117
|
+
function readOperatorGuestHostKeyEvidence(path, request) {
|
|
6118
|
+
const value = JSON.parse(new TextDecoder().decode(ownerFile(path, REQUEST_LIMIT, "operator guest host-key evidence")));
|
|
6119
|
+
return validateGuestHostKeyEvidenceValue(value, request);
|
|
6120
|
+
}
|
|
6121
|
+
function writeOperatorGuestHostKeyEvidence(path, evidence, request) {
|
|
6122
|
+
return writeOwnerJson2(path, validateGuestHostKeyEvidenceValue(evidence, request), "operator guest host-key evidence");
|
|
6123
|
+
}
|
|
6082
6124
|
function planOperatorMetalBootstrap(request, mode) {
|
|
6083
6125
|
const config = readMetalBootstrapConfig(request.metalConfigFile);
|
|
6084
6126
|
const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
|
|
@@ -6086,19 +6128,20 @@ function planOperatorMetalBootstrap(request, mode) {
|
|
|
6086
6128
|
throw new Error("the reviewed metal request has no rehearsal node");
|
|
6087
6129
|
}
|
|
6088
6130
|
const claims = mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
|
|
6131
|
+
const genesisNodes = mode === "host-keys" ? request.genesis.nodes.map(({ name }) => name) : mode === "rehearsal-host-keys" ? guestRows(request).map(({ name }) => name) : claims.map((claim) => claim.spec.guestName).filter((name) => Boolean(name));
|
|
6089
6132
|
return {
|
|
6090
6133
|
kind: "metal-remote",
|
|
6091
6134
|
mode,
|
|
6092
|
-
mutation:
|
|
6135
|
+
mutation: !["status", "host-keys", "rehearsal-host-keys"].includes(mode),
|
|
6093
6136
|
target: { address: request.target.address, port: request.target.port, user: request.target.user },
|
|
6094
|
-
steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
|
|
6137
|
+
steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "host-keys" || mode === "rehearsal-host-keys" ? ["verify pinned SSH transport", "collect exact Ed25519 host keys for the reviewed guest addresses"] : mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
|
|
6095
6138
|
"verify pinned SSH transport and caller-approved SSH agent",
|
|
6096
6139
|
"copy pinned Bun and packaged fz artifacts to private staging",
|
|
6097
6140
|
"stage the strict owner-only metal config and optional seed handoff",
|
|
6098
6141
|
"run typed fz bootstrap metal --apply",
|
|
6099
6142
|
"remove transient local and remote staging data"
|
|
6100
6143
|
],
|
|
6101
|
-
genesisNodes
|
|
6144
|
+
genesisNodes,
|
|
6102
6145
|
secretInputs: config.agentSeedFile ? ["metal-agent-seed"] : []
|
|
6103
6146
|
};
|
|
6104
6147
|
}
|
|
@@ -6335,6 +6378,116 @@ var copy = async (exec, request, knownHosts, local, remotePath, secret = false)
|
|
|
6335
6378
|
`${destination2(request.target)}:${remotePath}`
|
|
6336
6379
|
], "secure copy", { secret });
|
|
6337
6380
|
};
|
|
6381
|
+
var guestRows = (request) => [
|
|
6382
|
+
...request.genesis.nodes,
|
|
6383
|
+
...request.genesis.rehearsalNode ? [request.genesis.rehearsalNode] : []
|
|
6384
|
+
];
|
|
6385
|
+
var parsedScannedHostKey = (output, address) => {
|
|
6386
|
+
const keys = output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith(`${address} ssh-ed25519 `)).map((line) => line.split(/\s+/).slice(1, 3).join(" "));
|
|
6387
|
+
const unique = [...new Set(keys)];
|
|
6388
|
+
if (unique.length !== 1 || !/^ssh-ed25519 [A-Za-z0-9+/]+={0,3}$/.test(unique[0])) {
|
|
6389
|
+
throw new Error(`guest ${address} did not return one exact Ed25519 host key`);
|
|
6390
|
+
}
|
|
6391
|
+
return unique[0];
|
|
6392
|
+
};
|
|
6393
|
+
async function collectOperatorGuestHostKeys(request, options = {}, includeRehearsal = false) {
|
|
6394
|
+
publicIdentity(request.target.identityPublicKeyFile);
|
|
6395
|
+
socketPath(request.target.agentSocket);
|
|
6396
|
+
const exec = options.exec ?? defaultExec3;
|
|
6397
|
+
const directory = mkdtempSync(join6(tmpdir(), "forgezero-operator-host-keys-"));
|
|
6398
|
+
try {
|
|
6399
|
+
const knownHosts = writeKnownHosts(request, directory);
|
|
6400
|
+
const nodes = [];
|
|
6401
|
+
for (const guest of includeRehearsal ? guestRows(request) : request.genesis.nodes) {
|
|
6402
|
+
const output = await remote(exec, request, knownHosts, [
|
|
6403
|
+
"/usr/bin/ssh-keyscan",
|
|
6404
|
+
"-T",
|
|
6405
|
+
"10",
|
|
6406
|
+
"-t",
|
|
6407
|
+
"ed25519",
|
|
6408
|
+
guest.address
|
|
6409
|
+
], `collect ${guest.name} SSH host key`);
|
|
6410
|
+
const hostKey = parsedScannedHostKey(output, guest.address);
|
|
6411
|
+
nodes.push({
|
|
6412
|
+
name: guest.name,
|
|
6413
|
+
address: guest.address,
|
|
6414
|
+
hostKey,
|
|
6415
|
+
hostKeySha256: fingerprint(hostKey)
|
|
6416
|
+
});
|
|
6417
|
+
}
|
|
6418
|
+
return validateGuestHostKeyEvidenceValue({
|
|
6419
|
+
format: 1,
|
|
6420
|
+
kind: "forgezero-operator-guest-host-keys",
|
|
6421
|
+
metalHostKeySha256: request.target.hostKeySha256,
|
|
6422
|
+
nodes
|
|
6423
|
+
}, request);
|
|
6424
|
+
} finally {
|
|
6425
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
async function withOperatorGuestTransport(request, evidence, callback, options = {}) {
|
|
6429
|
+
const reviewed = validateGuestHostKeyEvidenceValue(evidence, request);
|
|
6430
|
+
publicIdentity(request.target.identityPublicKeyFile);
|
|
6431
|
+
socketPath(request.target.agentSocket);
|
|
6432
|
+
const exec = options.exec ?? defaultExec3;
|
|
6433
|
+
const directory = mkdtempSync(join6(tmpdir(), "forgezero-operator-guest-transport-"));
|
|
6434
|
+
try {
|
|
6435
|
+
const byName = new Map(reviewed.nodes.map((node) => [node.name, node]));
|
|
6436
|
+
const requests = new Map;
|
|
6437
|
+
const knownHosts = new Map;
|
|
6438
|
+
for (const node of reviewed.nodes) {
|
|
6439
|
+
const nodeDirectory = join6(directory, node.name);
|
|
6440
|
+
mkdirSync8(nodeDirectory, { mode: 448 });
|
|
6441
|
+
const guestRequest = {
|
|
6442
|
+
target: {
|
|
6443
|
+
address: node.address,
|
|
6444
|
+
port: 22,
|
|
6445
|
+
user: "forgezero",
|
|
6446
|
+
hostKey: node.hostKey,
|
|
6447
|
+
hostKeySha256: node.hostKeySha256,
|
|
6448
|
+
identityPublicKeyFile: request.target.identityPublicKeyFile,
|
|
6449
|
+
agentSocket: request.target.agentSocket,
|
|
6450
|
+
jump: {
|
|
6451
|
+
address: request.target.address,
|
|
6452
|
+
port: request.target.port,
|
|
6453
|
+
user: request.target.user,
|
|
6454
|
+
hostKey: request.target.hostKey,
|
|
6455
|
+
hostKeySha256: request.target.hostKeySha256
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6458
|
+
};
|
|
6459
|
+
requests.set(node.name, guestRequest);
|
|
6460
|
+
knownHosts.set(node.name, writeKnownHosts(guestRequest, nodeDirectory));
|
|
6461
|
+
}
|
|
6462
|
+
const transport = {
|
|
6463
|
+
async run(nodeName, argv, runOptions = {}) {
|
|
6464
|
+
if (!byName.has(nodeName))
|
|
6465
|
+
throw new Error(`guest transport node ${nodeName} is not reviewed`);
|
|
6466
|
+
argv.forEach(safeRemoteArg);
|
|
6467
|
+
const nodeRequest = requests.get(nodeName);
|
|
6468
|
+
const result = await exec([
|
|
6469
|
+
"ssh",
|
|
6470
|
+
...sshOptions(nodeRequest, knownHosts.get(nodeName)),
|
|
6471
|
+
destination2(nodeRequest.target),
|
|
6472
|
+
"--",
|
|
6473
|
+
...argv
|
|
6474
|
+
], { stdin: runOptions.stdin, secret: runOptions.secret });
|
|
6475
|
+
if (result.exitCode !== 0 && !runOptions.allowFailure) {
|
|
6476
|
+
throw new Error(`${nodeName} remote operation failed${result.output ? `: ${result.output.trim()}` : ""}`);
|
|
6477
|
+
}
|
|
6478
|
+
return result;
|
|
6479
|
+
},
|
|
6480
|
+
async copy(nodeName, localPath, remotePath) {
|
|
6481
|
+
if (!byName.has(nodeName))
|
|
6482
|
+
throw new Error(`guest transport node ${nodeName} is not reviewed`);
|
|
6483
|
+
await copy(exec, requests.get(nodeName), knownHosts.get(nodeName), localPath, remotePath);
|
|
6484
|
+
}
|
|
6485
|
+
};
|
|
6486
|
+
return await callback(transport);
|
|
6487
|
+
} finally {
|
|
6488
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6338
6491
|
async function stageConfig(config, directory) {
|
|
6339
6492
|
const rewritten = structuredClone(config);
|
|
6340
6493
|
const staged = [];
|
|
@@ -6546,6 +6699,12 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
6546
6699
|
const output2 = await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/local/bin/fz", "bootstrap", "status"], "remote metal status");
|
|
6547
6700
|
return { plan, output: output2 };
|
|
6548
6701
|
}
|
|
6702
|
+
if (mode === "host-keys" || mode === "rehearsal-host-keys") {
|
|
6703
|
+
return {
|
|
6704
|
+
plan,
|
|
6705
|
+
output: JSON.stringify(await collectOperatorGuestHostKeys(request, { ...options, exec }, mode === "rehearsal-host-keys"))
|
|
6706
|
+
};
|
|
6707
|
+
}
|
|
6549
6708
|
if (mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup") {
|
|
6550
6709
|
const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
|
|
6551
6710
|
if ((mode === "rehearsal" || mode === "rehearsal-cleanup") && !request.genesis.rehearsalNode) {
|
|
@@ -6592,15 +6751,19 @@ export {
|
|
|
6592
6751
|
writeOperatorPlatformBootstrapRequest,
|
|
6593
6752
|
writeOperatorPlatformBootstrapFleetRequest,
|
|
6594
6753
|
writeOperatorMetalBootstrapRequest,
|
|
6754
|
+
writeOperatorGuestHostKeyEvidence,
|
|
6755
|
+
withOperatorGuestTransport,
|
|
6595
6756
|
verifyOperatorPlatformFleet,
|
|
6596
6757
|
readOperatorPlatformBootstrapRequest,
|
|
6597
6758
|
readOperatorPlatformBootstrapFleetRequest,
|
|
6598
6759
|
readOperatorMetalBootstrapRequest,
|
|
6760
|
+
readOperatorGuestHostKeyEvidence,
|
|
6599
6761
|
planOperatorPlatformFleetBootstrap,
|
|
6600
6762
|
planOperatorPlatformBootstrap,
|
|
6601
6763
|
planOperatorMetalBootstrap,
|
|
6602
6764
|
operatorPlatformFleetCoordinates,
|
|
6603
6765
|
createOperatorSshHop,
|
|
6766
|
+
collectOperatorGuestHostKeys,
|
|
6604
6767
|
applyOperatorPlatformFleetBootstrap,
|
|
6605
6768
|
applyOperatorPlatformBootstrap,
|
|
6606
6769
|
applyOperatorMetalBootstrap
|
|
@@ -2125,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2125
2125
|
}
|
|
2126
2126
|
|
|
2127
2127
|
// src/version.ts
|
|
2128
|
-
var VERSION3 = "0.1.
|
|
2128
|
+
var VERSION3 = "0.1.68";
|
|
2129
2129
|
|
|
2130
2130
|
// src/egress-policy.ts
|
|
2131
2131
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/provision.js
CHANGED
|
@@ -2125,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2125
2125
|
}
|
|
2126
2126
|
|
|
2127
2127
|
// src/version.ts
|
|
2128
|
-
var VERSION3 = "0.1.
|
|
2128
|
+
var VERSION3 = "0.1.68";
|
|
2129
2129
|
|
|
2130
2130
|
// src/egress-policy.ts
|
|
2131
2131
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.68";
|