@flop-labs/cli 0.4.1 → 0.4.2
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/CHANGELOG.md +17 -0
- package/dist/cli.js +362 -125
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this package follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.2] - 2026-11-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `flop model backup <model_hash>` streams a registered `content-v1` checkpoint from its
|
|
15
|
+
Hugging Face mirror into the validators' DA copy under the running box's own DA key,
|
|
16
|
+
piping the packed image into `da-validator` instead of writing it out, so the box needs
|
|
17
|
+
room for the checkpoint rather than twice it. It refuses a root the hub table does not
|
|
18
|
+
reproduce, prints the plan and refundable DA deposit under `--dry-run`, and leaves the
|
|
19
|
+
`ModelWeightsRef` binding to the model publisher. Both the download and the DA push
|
|
20
|
+
resume on a rerun. Metadata-only onboarding now prints this command as its next step.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Wallet enrollment now waits up to 120 seconds for the gateway's answer, so a slow enrollment
|
|
25
|
+
completes and a refusal such as a full daily cap is reported instead of a timeout.
|
|
26
|
+
|
|
10
27
|
## [0.4.1] - 2026-11-08
|
|
11
28
|
|
|
12
29
|
### Added
|
package/dist/cli.js
CHANGED
|
@@ -724,14 +724,14 @@ var init_task_runner = __esm({
|
|
|
724
724
|
}
|
|
725
725
|
}
|
|
726
726
|
withTimeout(promise, timeoutMs) {
|
|
727
|
-
return new Promise((
|
|
727
|
+
return new Promise((resolve10, reject) => {
|
|
728
728
|
const timer = setTimeout(
|
|
729
729
|
() => reject(new AgentError("T002" /* TASK_TIMEOUT */, `Task timed out after ${timeoutMs}ms`, true)),
|
|
730
730
|
timeoutMs
|
|
731
731
|
);
|
|
732
732
|
promise.then((result) => {
|
|
733
733
|
clearTimeout(timer);
|
|
734
|
-
|
|
734
|
+
resolve10(result);
|
|
735
735
|
}).catch((err) => {
|
|
736
736
|
clearTimeout(timer);
|
|
737
737
|
reject(err);
|
|
@@ -739,7 +739,7 @@ var init_task_runner = __esm({
|
|
|
739
739
|
});
|
|
740
740
|
}
|
|
741
741
|
sleep(ms) {
|
|
742
|
-
return new Promise((
|
|
742
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
743
743
|
}
|
|
744
744
|
};
|
|
745
745
|
}
|
|
@@ -904,7 +904,7 @@ var init_worker = __esm({
|
|
|
904
904
|
return this.identity;
|
|
905
905
|
}
|
|
906
906
|
sleep(ms) {
|
|
907
|
-
return new Promise((
|
|
907
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
908
908
|
}
|
|
909
909
|
};
|
|
910
910
|
}
|
|
@@ -912,10 +912,10 @@ var init_worker = __esm({
|
|
|
912
912
|
|
|
913
913
|
// ../agent/src/chain-apis.ts
|
|
914
914
|
async function submitTx(tx, signer) {
|
|
915
|
-
return new Promise((
|
|
915
|
+
return new Promise((resolve10, reject) => {
|
|
916
916
|
tx.signAndSend(signer, ({ status, dispatchError }) => {
|
|
917
917
|
if (dispatchError) reject(new Error(`Tx failed: ${dispatchError.toString()}`));
|
|
918
|
-
else if (status.isInBlock || status.isFinalized)
|
|
918
|
+
else if (status.isInBlock || status.isFinalized) resolve10(status.asFinalized.toString());
|
|
919
919
|
}).catch(reject);
|
|
920
920
|
});
|
|
921
921
|
}
|
|
@@ -1219,7 +1219,7 @@ async function waitForTaskCompletion(api, taskId, timeoutMs = DEFAULT_TIMEOUT, p
|
|
|
1219
1219
|
return receipt;
|
|
1220
1220
|
}
|
|
1221
1221
|
}
|
|
1222
|
-
await new Promise((
|
|
1222
|
+
await new Promise((resolve10) => setTimeout(resolve10, pollIntervalMs));
|
|
1223
1223
|
}
|
|
1224
1224
|
throw new AgentError(
|
|
1225
1225
|
"T002" /* TASK_TIMEOUT */,
|
|
@@ -1878,7 +1878,7 @@ async function waitForBalance(api, address, minBalance, timeoutMs = 6e4) {
|
|
|
1878
1878
|
}
|
|
1879
1879
|
} catch {
|
|
1880
1880
|
}
|
|
1881
|
-
await new Promise((
|
|
1881
|
+
await new Promise((resolve10) => setTimeout(resolve10, pollInterval));
|
|
1882
1882
|
}
|
|
1883
1883
|
return false;
|
|
1884
1884
|
}
|
|
@@ -4941,9 +4941,9 @@ var init_src = __esm({
|
|
|
4941
4941
|
});
|
|
4942
4942
|
|
|
4943
4943
|
// src/cli.ts
|
|
4944
|
-
import { Command as
|
|
4944
|
+
import { Command as Command49, Option as Option8 } from "commander";
|
|
4945
4945
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
4946
|
-
import { join as
|
|
4946
|
+
import { join as join11 } from "node:path";
|
|
4947
4947
|
|
|
4948
4948
|
// src/output.ts
|
|
4949
4949
|
var activeOutputMode = "human";
|
|
@@ -6127,12 +6127,12 @@ function pathCheck(id, target, required = true) {
|
|
|
6127
6127
|
};
|
|
6128
6128
|
}
|
|
6129
6129
|
async function probeHttpUrl(id, url, required = true, timeoutMs = 3e3) {
|
|
6130
|
-
return new Promise((
|
|
6130
|
+
return new Promise((resolve10) => {
|
|
6131
6131
|
let parsed;
|
|
6132
6132
|
try {
|
|
6133
6133
|
parsed = new URL(url);
|
|
6134
6134
|
} catch {
|
|
6135
|
-
|
|
6135
|
+
resolve10({
|
|
6136
6136
|
id,
|
|
6137
6137
|
status: "fail",
|
|
6138
6138
|
required,
|
|
@@ -6145,7 +6145,7 @@ async function probeHttpUrl(id, url, required = true, timeoutMs = 3e3) {
|
|
|
6145
6145
|
const request3 = transport.request(parsed, { method: "GET", timeout: timeoutMs }, (response) => {
|
|
6146
6146
|
response.resume();
|
|
6147
6147
|
const reachable = (response.statusCode ?? 500) < 500;
|
|
6148
|
-
|
|
6148
|
+
resolve10({
|
|
6149
6149
|
id,
|
|
6150
6150
|
status: reachable ? "pass" : required ? "fail" : "warn",
|
|
6151
6151
|
required,
|
|
@@ -6155,7 +6155,7 @@ async function probeHttpUrl(id, url, required = true, timeoutMs = 3e3) {
|
|
|
6155
6155
|
});
|
|
6156
6156
|
request3.on("timeout", () => request3.destroy(new Error("timeout")));
|
|
6157
6157
|
request3.on("error", (error) => {
|
|
6158
|
-
|
|
6158
|
+
resolve10({
|
|
6159
6159
|
id,
|
|
6160
6160
|
status: required ? "fail" : "warn",
|
|
6161
6161
|
required,
|
|
@@ -6199,7 +6199,7 @@ function unwrapStorage(raw) {
|
|
|
6199
6199
|
|
|
6200
6200
|
// src/commands/launch.ts
|
|
6201
6201
|
function checkGateway(url) {
|
|
6202
|
-
return new Promise((
|
|
6202
|
+
return new Promise((resolve10) => {
|
|
6203
6203
|
try {
|
|
6204
6204
|
const parsedUrl = new URL(url);
|
|
6205
6205
|
const req = http2.request(
|
|
@@ -6211,17 +6211,17 @@ function checkGateway(url) {
|
|
|
6211
6211
|
timeout: 1e3
|
|
6212
6212
|
},
|
|
6213
6213
|
(res) => {
|
|
6214
|
-
|
|
6214
|
+
resolve10(res.statusCode === 200);
|
|
6215
6215
|
}
|
|
6216
6216
|
);
|
|
6217
|
-
req.on("error", () =>
|
|
6217
|
+
req.on("error", () => resolve10(false));
|
|
6218
6218
|
req.on("timeout", () => {
|
|
6219
6219
|
req.destroy();
|
|
6220
|
-
|
|
6220
|
+
resolve10(false);
|
|
6221
6221
|
});
|
|
6222
6222
|
req.end();
|
|
6223
6223
|
} catch {
|
|
6224
|
-
|
|
6224
|
+
resolve10(false);
|
|
6225
6225
|
}
|
|
6226
6226
|
});
|
|
6227
6227
|
}
|
|
@@ -6529,7 +6529,7 @@ async function withCredentialLock(path5, action) {
|
|
|
6529
6529
|
continue;
|
|
6530
6530
|
}
|
|
6531
6531
|
if (Date.now() >= deadline) throw new Error(`FLOP credential lock is busy at ${lock}`);
|
|
6532
|
-
await new Promise((
|
|
6532
|
+
await new Promise((resolve10) => setTimeout(resolve10, 100));
|
|
6533
6533
|
}
|
|
6534
6534
|
}
|
|
6535
6535
|
try {
|
|
@@ -6589,6 +6589,8 @@ function validateWalletEnrollmentChallenge(message, address, gateway, genesis, n
|
|
|
6589
6589
|
const issued = Date.parse(issuedText), expires = Date.parse(expiresText);
|
|
6590
6590
|
if (!Number.isFinite(issued) || !Number.isFinite(expires) || lines[6] !== `Issued At: ${new Date(issued).toISOString()}` || lines[7] !== `Expiration Time: ${new Date(expires).toISOString()}` || issued > now || expires <= now || expires <= issued || expires - issued > 5 * 6e4) throw invalid();
|
|
6591
6591
|
}
|
|
6592
|
+
var ENROLLMENT_CHALLENGE_TIMEOUT_MS = 3e4;
|
|
6593
|
+
var ENROLLMENT_SUBMIT_TIMEOUT_MS = 12e4;
|
|
6592
6594
|
function walletEnrollment(options) {
|
|
6593
6595
|
return async () => {
|
|
6594
6596
|
if (options.purpose) await hermesEnrollmentOffer(options.gateway);
|
|
@@ -6605,12 +6607,12 @@ function walletEnrollment(options) {
|
|
|
6605
6607
|
const deadline = Date.now() + (options.fundTimeoutMs ?? 12e4);
|
|
6606
6608
|
while (await options.balance(pair.address) < minBalance) {
|
|
6607
6609
|
if (Date.now() >= deadline) throw new Error("devnet faucet grant did not finalize in time; retry later");
|
|
6608
|
-
await new Promise((
|
|
6610
|
+
await new Promise((resolve10) => setTimeout(resolve10, options.pollMs ?? 3e3));
|
|
6609
6611
|
}
|
|
6610
6612
|
}
|
|
6611
6613
|
const challenge = await fetch(
|
|
6612
6614
|
`${options.gateway}/v1/enroll/wallet/challenge?address=${encodeURIComponent(pair.address)}${options.purpose ? `&purpose=${options.purpose}` : ""}`,
|
|
6613
|
-
{ redirect: "error", signal: AbortSignal.timeout(
|
|
6615
|
+
{ redirect: "error", signal: AbortSignal.timeout(ENROLLMENT_CHALLENGE_TIMEOUT_MS) }
|
|
6614
6616
|
);
|
|
6615
6617
|
if (!challenge.ok) throw await failure(challenge, "devnet enrollment challenge");
|
|
6616
6618
|
const { message } = await challenge.json();
|
|
@@ -6621,7 +6623,7 @@ function walletEnrollment(options) {
|
|
|
6621
6623
|
headers: { "content-type": "application/json" },
|
|
6622
6624
|
body: JSON.stringify({ address: pair.address, message, signature }),
|
|
6623
6625
|
redirect: "error",
|
|
6624
|
-
signal: AbortSignal.timeout(
|
|
6626
|
+
signal: AbortSignal.timeout(ENROLLMENT_SUBMIT_TIMEOUT_MS)
|
|
6625
6627
|
});
|
|
6626
6628
|
};
|
|
6627
6629
|
}
|
|
@@ -7012,14 +7014,14 @@ var CORS_HEADERS = {
|
|
|
7012
7014
|
"Access-Control-Allow-Headers": "Authorization, Content-Type"
|
|
7013
7015
|
};
|
|
7014
7016
|
function readBody(req) {
|
|
7015
|
-
return new Promise((
|
|
7017
|
+
return new Promise((resolve10) => {
|
|
7016
7018
|
let data = "";
|
|
7017
7019
|
req.on("data", (c) => data += c);
|
|
7018
7020
|
req.on("end", () => {
|
|
7019
7021
|
try {
|
|
7020
|
-
|
|
7022
|
+
resolve10(data ? JSON.parse(data) : {});
|
|
7021
7023
|
} catch {
|
|
7022
|
-
|
|
7024
|
+
resolve10({});
|
|
7023
7025
|
}
|
|
7024
7026
|
});
|
|
7025
7027
|
});
|
|
@@ -7256,7 +7258,7 @@ async function startProxyServer(o) {
|
|
|
7256
7258
|
embeddingModel: o.embeddingModel,
|
|
7257
7259
|
embed: createLocalEmbedder(o.embeddingModel)
|
|
7258
7260
|
});
|
|
7259
|
-
await new Promise((
|
|
7261
|
+
await new Promise((resolve10) => server.listen(o.port, o.host, () => resolve10()));
|
|
7260
7262
|
const addr = server.address();
|
|
7261
7263
|
const url = `http://${o.host}:${addr.port}`;
|
|
7262
7264
|
const models = await listModels();
|
|
@@ -7393,9 +7395,9 @@ function launchExternal2(command, args, envOverrides) {
|
|
|
7393
7395
|
function runHermesConfig(launcher, commands) {
|
|
7394
7396
|
return commands.reduce(
|
|
7395
7397
|
(p, args) => p.then(
|
|
7396
|
-
() => new Promise((
|
|
7398
|
+
() => new Promise((resolve10, reject) => {
|
|
7397
7399
|
const c = spawnLauncher(launcher, args, { stdio: "inherit" });
|
|
7398
|
-
c.on("exit", (code) => code === 0 ?
|
|
7400
|
+
c.on("exit", (code) => code === 0 ? resolve10() : reject(new Error(`hermes ${args.join(" ")} exited ${code}`)));
|
|
7399
7401
|
c.on("error", reject);
|
|
7400
7402
|
})
|
|
7401
7403
|
),
|
|
@@ -7630,6 +7632,7 @@ import { basename as basename2, join as join6, resolve as resolve8 } from "node:
|
|
|
7630
7632
|
import { resolve as resolve5, relative, sep } from "node:path";
|
|
7631
7633
|
import { mkdir as mkdir7, readFile as readFile4, readdir, lstat as lstat3, writeFile as writeFile7 } from "node:fs/promises";
|
|
7632
7634
|
import { execFile as execFile3, spawn as spawn4 } from "node:child_process";
|
|
7635
|
+
import { pipeline } from "node:stream/promises";
|
|
7633
7636
|
import { promisify as promisify3 } from "node:util";
|
|
7634
7637
|
function modelToOutput(model) {
|
|
7635
7638
|
return {
|
|
@@ -7752,6 +7755,9 @@ async function daCommit(executable, blobFile) {
|
|
|
7752
7755
|
const { stdout: stdout2 } = await run2(executable, ["commit", "--blob-file", blobFile, "--json"], {
|
|
7753
7756
|
maxBuffer: 1024 * 1024
|
|
7754
7757
|
});
|
|
7758
|
+
return parseDaLayout(stdout2);
|
|
7759
|
+
}
|
|
7760
|
+
function parseDaLayout(stdout2) {
|
|
7755
7761
|
const line = lastJsonLine(stdout2);
|
|
7756
7762
|
if (!line) throw new Error("da-validator commit returned no JSON result");
|
|
7757
7763
|
const result = JSON.parse(line);
|
|
@@ -7765,6 +7771,62 @@ async function daCommit(executable, blobFile) {
|
|
|
7765
7771
|
chunks: result.chunks
|
|
7766
7772
|
};
|
|
7767
7773
|
}
|
|
7774
|
+
async function runWithBlobOnStdin(executable, args, env, body) {
|
|
7775
|
+
const child = spawn4(executable, args, { stdio: ["pipe", "pipe", "inherit"], env });
|
|
7776
|
+
let stdout2 = "";
|
|
7777
|
+
child.stdout.setEncoding("utf8");
|
|
7778
|
+
child.stdout.on("data", (part) => {
|
|
7779
|
+
stdout2 += part;
|
|
7780
|
+
});
|
|
7781
|
+
let streamError;
|
|
7782
|
+
const fed = pipeline(body, child.stdin).catch((error) => {
|
|
7783
|
+
if (error.code === "EPIPE") return;
|
|
7784
|
+
streamError = error;
|
|
7785
|
+
child.kill("SIGKILL");
|
|
7786
|
+
});
|
|
7787
|
+
const code = await new Promise((resolveExit, rejectExit) => {
|
|
7788
|
+
child.on("error", rejectExit);
|
|
7789
|
+
child.on("close", resolveExit);
|
|
7790
|
+
});
|
|
7791
|
+
await fed;
|
|
7792
|
+
if (streamError) throw streamError;
|
|
7793
|
+
return { code, stdout: stdout2 };
|
|
7794
|
+
}
|
|
7795
|
+
async function daCommitStream(executable, blob) {
|
|
7796
|
+
const { code, stdout: stdout2 } = await runWithBlobOnStdin(
|
|
7797
|
+
executable,
|
|
7798
|
+
["commit", "--blob-stdin", "--size", String(blob.sizeBytes), "--manifest-out", blob.manifestFile, "--json"],
|
|
7799
|
+
process.env,
|
|
7800
|
+
blob.open()
|
|
7801
|
+
);
|
|
7802
|
+
if (code !== 0) throw new Error(`da-validator commit failed (exit ${code}); see its log above`);
|
|
7803
|
+
return parseDaLayout(stdout2);
|
|
7804
|
+
}
|
|
7805
|
+
async function submitDaBlobStream(executable, rpc, blob, publisherKey) {
|
|
7806
|
+
const env = { ...process.env };
|
|
7807
|
+
if (publisherKey) env.DA_PUBLISHER_KEY = publisherKey;
|
|
7808
|
+
else delete env.DA_PUBLISHER_KEY;
|
|
7809
|
+
const { code, stdout: stdout2 } = await runWithBlobOnStdin(
|
|
7810
|
+
executable,
|
|
7811
|
+
[
|
|
7812
|
+
"submit",
|
|
7813
|
+
"--chain-rpc",
|
|
7814
|
+
rpc,
|
|
7815
|
+
"--blob-stdin",
|
|
7816
|
+
"--size",
|
|
7817
|
+
String(blob.sizeBytes),
|
|
7818
|
+
"--manifest-file",
|
|
7819
|
+
blob.manifestFile,
|
|
7820
|
+
"--json"
|
|
7821
|
+
],
|
|
7822
|
+
env,
|
|
7823
|
+
blob.open()
|
|
7824
|
+
);
|
|
7825
|
+
if (code !== 0 && !lastJsonLine(stdout2)) {
|
|
7826
|
+
throw new Error(`da-validator submit failed (exit ${code}); see its log above`);
|
|
7827
|
+
}
|
|
7828
|
+
return parseCustodyResult(stdout2);
|
|
7829
|
+
}
|
|
7768
7830
|
function parseCustodyResult(stdout2) {
|
|
7769
7831
|
const line = lastJsonLine(stdout2);
|
|
7770
7832
|
if (!line) throw new Error("da-validator returned no JSON custody result");
|
|
@@ -9289,7 +9351,9 @@ function modelOnboardCmd() {
|
|
|
9289
9351
|
if (opts.approve && !state.approved) throw new Error("approval is not present in finalized registry state");
|
|
9290
9352
|
if (metadataOnly && !state.weightsRef) {
|
|
9291
9353
|
process.stderr.write(
|
|
9292
|
-
`next: approval needs the DA backup of ${identity.modelHash} (packed image + file table, ${measured.imageSize} bytes in ${Math.max(1, Math.ceil(measured.imageSize / sdk.DA_CHUNK_SIZE))} chunk(s))
|
|
9354
|
+
`next: approval needs the DA backup of ${identity.modelHash} (packed image + file table, ${measured.imageSize} bytes in ${Math.max(1, Math.ceil(measured.imageSize / sdk.DA_CHUNK_SIZE))} chunk(s)). On a box with room for the checkpoint, run
|
|
9355
|
+
flop model backup ${identity.modelHash} --model-dir <path>${preflight ? ` --revision ${preflight.revision}` : ""}
|
|
9356
|
+
then bind the DataRef it prints. Rerunning this command with --download packs the image from disk instead, which needs about twice the checkpoint's size.
|
|
9293
9357
|
`
|
|
9294
9358
|
);
|
|
9295
9359
|
}
|
|
@@ -9489,10 +9553,167 @@ function modelPreflightCmd() {
|
|
|
9489
9553
|
});
|
|
9490
9554
|
}
|
|
9491
9555
|
|
|
9556
|
+
// src/commands/model-backup.ts
|
|
9557
|
+
import { Command as Command33, Option as Option3 } from "commander";
|
|
9558
|
+
import { mkdir as mkdir12, rm as rm4, writeFile as writeFile10 } from "node:fs/promises";
|
|
9559
|
+
import { join as join8, resolve as resolve9 } from "node:path";
|
|
9560
|
+
var PRECISIONS3 = ["FP32", "FP16", "BF16", "INT8", "INT4", "Mixed"];
|
|
9561
|
+
async function finalizedLeasedRef2(api, commitment, size) {
|
|
9562
|
+
const hash2 = await api.rpc.chain.getFinalizedHead();
|
|
9563
|
+
const [at, header] = await Promise.all([api.at(hash2), api.rpc.chain.getHeader(hash2)]);
|
|
9564
|
+
return existingLeasedRef({ query: at.query, rpc: { chain: { getHeader: async () => header } } }, commitment, size);
|
|
9565
|
+
}
|
|
9566
|
+
var REGISTRATION_WAIT_POLLS = 30;
|
|
9567
|
+
var REGISTRATION_POLL_MS = 2e3;
|
|
9568
|
+
async function awaitFinalizedLeasedRef(api, commitment, size) {
|
|
9569
|
+
for (let poll = 0; ; poll++) {
|
|
9570
|
+
const ref = await finalizedLeasedRef2(api, commitment, size);
|
|
9571
|
+
if (ref || poll >= REGISTRATION_WAIT_POLLS) return ref;
|
|
9572
|
+
await new Promise((wake) => setTimeout(wake, REGISTRATION_POLL_MS));
|
|
9573
|
+
}
|
|
9574
|
+
}
|
|
9575
|
+
function registeredHubRepo(mirrors) {
|
|
9576
|
+
const repos = [...new Set(mirrors.filter((m) => m.kind === "huggingface").map((m) => m.repo))];
|
|
9577
|
+
if (repos.length > 1) {
|
|
9578
|
+
throw new Error(
|
|
9579
|
+
`model advertises ${repos.length} Hugging Face mirrors (${repos.join(", ")}); name the one to back up with --source`
|
|
9580
|
+
);
|
|
9581
|
+
}
|
|
9582
|
+
return repos[0];
|
|
9583
|
+
}
|
|
9584
|
+
function modelBackupCmd() {
|
|
9585
|
+
return new Command33("backup").description(
|
|
9586
|
+
"Stream a registered content-v1 checkpoint from Hugging Face into validator DA under this box's DA key, without ever writing the packed image out, and print the DataRefs its publisher then binds"
|
|
9587
|
+
).argument("<model_hash>", "Registered model hash (0x-hex)").requiredOption("--model-dir <path>", "Directory the checkpoint streams into (needs room for it)").option("--source <hf-id>", "Hugging Face id (owner/model or hf:owner/model); the model's registered mirror when omitted").option("--revision <sha>", "Immutable 40-hex commit; main is resolved to one when omitted").addOption(new Option3("--precision <precision>", "Measured-root precision").choices(PRECISIONS3).default("FP16")).option("--da-validator <path>", "da-validator executable", "da-validator").option("--token <token>", "Hugging Face bearer token (prefer HF_TOKEN)").option("--state-dir <path>", "Download resume receipts (outside the checkpoint tree)").option("--dry-run", "Print the plan and the DA deposit it would lock, and stop before any transfer").option("--json", "Output as JSON").action(async (modelHash, opts, command) => {
|
|
9588
|
+
let client;
|
|
9589
|
+
let workDir;
|
|
9590
|
+
try {
|
|
9591
|
+
if (opts.revision && !/^[0-9a-f]{40}$/.test(opts.revision)) throw new Error("--revision must be an immutable 40-hex commit");
|
|
9592
|
+
const sdk = await import("@flop-labs/sdk");
|
|
9593
|
+
const rpc = getRpc(command);
|
|
9594
|
+
const precision = opts.precision;
|
|
9595
|
+
client = await createClient(rpc, getSigner(command));
|
|
9596
|
+
const api = client.api;
|
|
9597
|
+
const registered = await new sdk.OnChainModelRegistry(api).getModel(modelHash);
|
|
9598
|
+
if (!registered) throw new Error(`model not found: ${modelHash}`);
|
|
9599
|
+
const expected = await sdk.readMeasuredRoot(api, modelHash, precision);
|
|
9600
|
+
if (!expected) throw new Error(`no measured root for ${modelHash} at ${opts.precision}`);
|
|
9601
|
+
const hub = { revision: opts.revision, token: opts.token ?? process.env.HF_TOKEN };
|
|
9602
|
+
const repo = opts.source ? opts.source.startsWith("hf:") ? opts.source.slice(3) : opts.source : registeredHubRepo(await sdk.readModelMirrors(api, modelHash));
|
|
9603
|
+
if (!repo) {
|
|
9604
|
+
throw new Error(
|
|
9605
|
+
`model ${modelHash} advertises no Hugging Face mirror; pass --source <owner/model>, or back it up from disk with \`flop model upload --model-hash <hash> --path <dir>\``
|
|
9606
|
+
);
|
|
9607
|
+
}
|
|
9608
|
+
const preflight = await sdk.preflightHuggingFace(repo, hub);
|
|
9609
|
+
if (sdk.normalizeRoot(preflight.contentRoot) !== sdk.normalizeRoot(expected)) {
|
|
9610
|
+
throw new Error(
|
|
9611
|
+
`${repo} at ${preflight.revision} has content_root 0x${preflight.contentRoot}, not the registered root ${expected}. A verity-v1 model's root cannot be checked from hub metadata, and a repo that moved needs the registered --revision; back such a model up from disk with \`flop model upload --model-hash ${modelHash} --path <dir>\``
|
|
9612
|
+
);
|
|
9613
|
+
}
|
|
9614
|
+
const imageSize = sdk.packedImageSize(preflight.files);
|
|
9615
|
+
if (imageSize !== preflight.imageSize) throw new Error("packed image size disagrees with the preflight table");
|
|
9616
|
+
const tableBytes = sdk.encodeModelFileTable("0".repeat(64), preflight.files).length;
|
|
9617
|
+
const deposit = await sdk.da.leaseDeposit(api, imageSize + tableBytes);
|
|
9618
|
+
const plan = {
|
|
9619
|
+
model_hash: modelHash,
|
|
9620
|
+
precision: opts.precision,
|
|
9621
|
+
repo,
|
|
9622
|
+
revision: preflight.revision,
|
|
9623
|
+
content_root: `0x${preflight.contentRoot}`,
|
|
9624
|
+
scheme: "content-v1",
|
|
9625
|
+
files: preflight.files.length,
|
|
9626
|
+
image_size: imageSize,
|
|
9627
|
+
da_chunks: preflight.daChunks,
|
|
9628
|
+
da_deposit_plancks: deposit.toString(),
|
|
9629
|
+
publisher: client.signer.address
|
|
9630
|
+
};
|
|
9631
|
+
if (opts.dryRun) {
|
|
9632
|
+
print({ ...plan, dry_run: true, next: "rerun without --dry-run to transfer and register" }, getJson(command));
|
|
9633
|
+
return;
|
|
9634
|
+
}
|
|
9635
|
+
const publisherKey = await sr25519KeyHex(getSigner(command));
|
|
9636
|
+
const modelDir = resolve9(opts.modelDir);
|
|
9637
|
+
workDir = join8(resolve9(modelDir, ".."), `.flop-backup-${modelHash.replace(/^0x/, "").slice(0, 16)}`);
|
|
9638
|
+
await mkdir12(workDir, { recursive: true, mode: 448 });
|
|
9639
|
+
await mkdir12(modelDir, { recursive: true });
|
|
9640
|
+
const resumeState = opts.stateDir ? join8(resolve9(opts.stateDir), "download.json") : void 0;
|
|
9641
|
+
if (resumeState) await mkdir12(resolve9(opts.stateDir), { recursive: true, mode: 448 });
|
|
9642
|
+
process.stderr.write(
|
|
9643
|
+
`backing up ${repo}@${preflight.revision} (${preflight.files.length} files, image ${imageSize} bytes, ${preflight.daChunks} DA chunk(s)) as ${modelHash}
|
|
9644
|
+
`
|
|
9645
|
+
);
|
|
9646
|
+
await sdk.fetchHuggingFaceToDir(repo, modelDir, {
|
|
9647
|
+
...hub,
|
|
9648
|
+
revision: preflight.revision,
|
|
9649
|
+
files: preflight.files,
|
|
9650
|
+
resumeState
|
|
9651
|
+
});
|
|
9652
|
+
const manifestFile = join8(workDir, "layout.bin");
|
|
9653
|
+
const image = {
|
|
9654
|
+
sizeBytes: imageSize,
|
|
9655
|
+
manifestFile,
|
|
9656
|
+
open: () => sdk.packedImageStream(modelDir, preflight.files)
|
|
9657
|
+
};
|
|
9658
|
+
const imageLayout = await daCommitStream(opts.daValidator, image);
|
|
9659
|
+
if (imageLayout.sizeBytes !== imageSize) throw new Error("streamed DA commitment covers the wrong image size");
|
|
9660
|
+
const tablePath = join8(workDir, "files.json");
|
|
9661
|
+
await writeFile10(tablePath, sdk.encodeModelFileTable(imageLayout.commitment, preflight.files), { mode: 384 });
|
|
9662
|
+
const tableLayout = await daCommit(opts.daValidator, tablePath);
|
|
9663
|
+
const refs = [];
|
|
9664
|
+
for (const blob of [
|
|
9665
|
+
{ commitment: imageLayout.commitment, sizeBytes: imageLayout.sizeBytes, stream: image },
|
|
9666
|
+
{ commitment: tableLayout.commitment, sizeBytes: tableLayout.sizeBytes, stream: void 0 }
|
|
9667
|
+
]) {
|
|
9668
|
+
let ref = await finalizedLeasedRef2(api, blob.commitment, blob.sizeBytes);
|
|
9669
|
+
let registerTx;
|
|
9670
|
+
if (!ref) {
|
|
9671
|
+
const reg = await sdk.da.registerBlob(api, client.signer, {
|
|
9672
|
+
commitment: blob.commitment,
|
|
9673
|
+
sizeBytes: blob.sizeBytes
|
|
9674
|
+
});
|
|
9675
|
+
registerTx = reg.txHash;
|
|
9676
|
+
ref = await awaitFinalizedLeasedRef(api, blob.commitment, blob.sizeBytes);
|
|
9677
|
+
}
|
|
9678
|
+
if (!ref) {
|
|
9679
|
+
throw new Error(
|
|
9680
|
+
`DA registration for ${blob.commitment} did not finalize within ${REGISTRATION_WAIT_POLLS * REGISTRATION_POLL_MS / 1e3}s; rerun the same command once it has`
|
|
9681
|
+
);
|
|
9682
|
+
}
|
|
9683
|
+
const mine = sameAccount(ref.publisher, client.signer.address);
|
|
9684
|
+
const custody = blob.stream ? await submitDaBlobStream(opts.daValidator, rpc, blob.stream, mine ? publisherKey : void 0) : await submitDaBlob(opts.daValidator, rpc, tablePath, mine ? publisherKey : void 0);
|
|
9685
|
+
process.stderr.write(`${custodySummary(custody)}
|
|
9686
|
+
`);
|
|
9687
|
+
refs.push({ ref, custody, registerTx });
|
|
9688
|
+
}
|
|
9689
|
+
const [weights, table2] = refs;
|
|
9690
|
+
print({
|
|
9691
|
+
...plan,
|
|
9692
|
+
weights_ref: weights.ref.commitment,
|
|
9693
|
+
weights_register_tx: weights.registerTx,
|
|
9694
|
+
weights_custody_shards: weights.custody.confirmedShards,
|
|
9695
|
+
table_ref: table2.ref.commitment,
|
|
9696
|
+
table_register_tx: table2.registerTx,
|
|
9697
|
+
// The binding is the model publisher's own transaction: this box holds no
|
|
9698
|
+
// model-registry authority, and `set_model_weights_ref` takes any Leased ref from it.
|
|
9699
|
+
next: `the model publisher binds the backup with \`flop model upload --model-hash ${modelHash} --commitment ${weights.ref.commitment} --size ${imageSize}\`, then advertises the file table as a da:${table2.ref.commitment} mirror with \`modelRegistry.setModelMirrors\``,
|
|
9700
|
+
recovery: `flop da withdraw --commitment ${weights.ref.commitment} (and ${table2.ref.commitment}) refunds both deposits while nothing pins them`
|
|
9701
|
+
}, getJson(command));
|
|
9702
|
+
} catch (error) {
|
|
9703
|
+
commandError(error, {
|
|
9704
|
+
next: "rerun the same command to resume the download and the DA push; to abandon a registered blob, run the printed `flop da withdraw` recovery"
|
|
9705
|
+
});
|
|
9706
|
+
} finally {
|
|
9707
|
+
if (workDir) await rm4(workDir, { recursive: true, force: true });
|
|
9708
|
+
await client?.disconnect();
|
|
9709
|
+
}
|
|
9710
|
+
});
|
|
9711
|
+
}
|
|
9712
|
+
|
|
9492
9713
|
// src/commands/model-list.ts
|
|
9493
|
-
import { Command as
|
|
9714
|
+
import { Command as Command34 } from "commander";
|
|
9494
9715
|
function modelListCmd() {
|
|
9495
|
-
return new
|
|
9716
|
+
return new Command34("list").description("List models registered on the selected FLOP chain").option("--name <text>", "Filter model names").option("--limit <n>", "Maximum rows returned", "100").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
9496
9717
|
let client;
|
|
9497
9718
|
try {
|
|
9498
9719
|
const sdk = await import("@flop-labs/sdk");
|
|
@@ -9522,9 +9743,9 @@ function modelListCmd() {
|
|
|
9522
9743
|
}
|
|
9523
9744
|
|
|
9524
9745
|
// src/commands/model-get.ts
|
|
9525
|
-
import { Command as
|
|
9746
|
+
import { Command as Command35 } from "commander";
|
|
9526
9747
|
function modelGetCmd() {
|
|
9527
|
-
return new
|
|
9748
|
+
return new Command35("get").description("Get a registered model by hash or exact name").argument("<identifier>", "Model hash or name").option("--precision <precision>", "Measured-root precision", "FP16").option("--json", "Output as JSON").action(async (identifier, opts, command) => {
|
|
9528
9749
|
let client;
|
|
9529
9750
|
try {
|
|
9530
9751
|
const sdk = await import("@flop-labs/sdk");
|
|
@@ -9555,10 +9776,10 @@ function modelGetCmd() {
|
|
|
9555
9776
|
}
|
|
9556
9777
|
|
|
9557
9778
|
// src/commands/model-fetch.ts
|
|
9558
|
-
import { Command as
|
|
9559
|
-
var
|
|
9779
|
+
import { Command as Command36, Option as Option4 } from "commander";
|
|
9780
|
+
var PRECISIONS4 = ["FP32", "FP16", "BF16", "INT8", "INT4", "Mixed"];
|
|
9560
9781
|
function modelFetchCmd() {
|
|
9561
|
-
return new
|
|
9782
|
+
return new Command36("fetch").description("Fetch model weights and accept only a registered dm-verity root").argument("<identifier>", "Registered model hash or exact name").addOption(new Option4("--precision <precision>", "Weights precision").choices(PRECISIONS4).default("FP16")).option("--source <source...>", "Override mirrors: hf:<repo>, modelscope:<repo>, https://..., da:<commitment>").option("--da-endpoint <url>", "DA blob gateway exposing GET /blob/<commitment>").option("--output-dir <path>", "Destination directory", "./flop-model").option("--token <token>", "HF/ModelScope bearer token (prefer HF_TOKEN)").option("--json", "Output as JSON").action(async (identifier, opts, command) => {
|
|
9562
9783
|
let client;
|
|
9563
9784
|
try {
|
|
9564
9785
|
const sdk = await import("@flop-labs/sdk");
|
|
@@ -9621,7 +9842,7 @@ function modelFetchCmd() {
|
|
|
9621
9842
|
|
|
9622
9843
|
// src/commands/agent.ts
|
|
9623
9844
|
import path2 from "node:path";
|
|
9624
|
-
import { Command as
|
|
9845
|
+
import { Command as Command37, Option as Option5 } from "commander";
|
|
9625
9846
|
import { getFlopNetworkProfile as getFlopNetworkProfile3 } from "@flop-labs/sdk";
|
|
9626
9847
|
var DEFAULT_MCP_URL = "https://mcp.flop.network/mcp";
|
|
9627
9848
|
var DEVNET_MCP_URL = "https://mcp.flop.network/mcp";
|
|
@@ -9709,14 +9930,14 @@ function buildAgentInitPlan(client, mode, mcpUrl2 = DEFAULT_MCP_URL) {
|
|
|
9709
9930
|
};
|
|
9710
9931
|
}
|
|
9711
9932
|
function agentInitCmd() {
|
|
9712
|
-
return new
|
|
9933
|
+
return new Command37("init").description("Print a safe agent onboarding plan and MCP configuration").addOption(new Option5("--client <client>", "Agent client").choices([
|
|
9713
9934
|
"claude-code",
|
|
9714
9935
|
"codex",
|
|
9715
9936
|
"cursor",
|
|
9716
9937
|
"windsurf",
|
|
9717
9938
|
"zed",
|
|
9718
9939
|
"generic"
|
|
9719
|
-
]).default("generic")).addOption(new
|
|
9940
|
+
]).default("generic")).addOption(new Option5("--mode <mode>", "MCP mode").choices(["remote", "local"]).default("remote")).option("--mcp-url <url>", "Hosted MCP URL").option("--json", "Output as JSON").action((opts, command) => {
|
|
9720
9941
|
const plan = buildAgentInitPlan(opts.client, opts.mode, mcpUrl(command, opts.mcpUrl));
|
|
9721
9942
|
const output = getOutputMode(command);
|
|
9722
9943
|
printRolePlan(plan, output);
|
|
@@ -9727,7 +9948,7 @@ function agentInitCmd() {
|
|
|
9727
9948
|
});
|
|
9728
9949
|
}
|
|
9729
9950
|
function agentDoctorCmd() {
|
|
9730
|
-
return new
|
|
9951
|
+
return new Command37("doctor").description("Check agent MCP, gateway, and optional chain connectivity").addOption(new Option5("--mode <mode>", "MCP mode").choices(["remote", "local"]).default("remote")).option("--mcp-url <url>", "MCP URL").option("--gateway <url>", "Optional OpenAI-compatible gateway URL").option("--repo <path>", "flop-core checkout for local mode", process.cwd()).option("--check-rpc", "Also require the configured chain RPC").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
9731
9952
|
try {
|
|
9732
9953
|
const checks = [executableCheck("node")];
|
|
9733
9954
|
if (opts.mode === "remote") {
|
|
@@ -9754,7 +9975,7 @@ function agentDoctorCmd() {
|
|
|
9754
9975
|
});
|
|
9755
9976
|
}
|
|
9756
9977
|
function agentCredentialCmd() {
|
|
9757
|
-
return new
|
|
9978
|
+
return new Command37("credential").description("Provision or reuse a devnet gateway key for agent clients (wallet-signed by default)").option("--print-key", "Print only the bearer key for a local client auth helper").option("--tailnet", "Enroll through the private tailnet lane (approved Tailscale users only)").option("--json", "Output non-secret credential metadata as JSON").action(async (opts, command) => {
|
|
9758
9979
|
try {
|
|
9759
9980
|
if (getNetwork(command) !== "devnet") {
|
|
9760
9981
|
throw new Error("agent credential requires --network devnet");
|
|
@@ -9787,20 +10008,20 @@ function agentCredentialCmd() {
|
|
|
9787
10008
|
});
|
|
9788
10009
|
}
|
|
9789
10010
|
function agentCmd() {
|
|
9790
|
-
const command = new
|
|
10011
|
+
const command = new Command37("agent").description("Onboard and operate FLOP-backed agents");
|
|
9791
10012
|
command.addCommand(agentInitCmd());
|
|
9792
10013
|
command.addCommand(agentDoctorCmd());
|
|
9793
10014
|
command.addCommand(agentCredentialCmd());
|
|
9794
10015
|
command.addCommand(launchCmd());
|
|
9795
10016
|
command.addCommand(proxyCmd());
|
|
9796
10017
|
command.addCommand(hermesCmd());
|
|
9797
|
-
const keys = new
|
|
10018
|
+
const keys = new Command37("keys").description("Manage agent keys");
|
|
9798
10019
|
keys.addCommand(keysGenerateCmd());
|
|
9799
10020
|
command.addCommand(keys);
|
|
9800
|
-
const wallet = new
|
|
10021
|
+
const wallet = new Command37("wallet").description("Manage agent wallets");
|
|
9801
10022
|
wallet.addCommand(walletAgentCreateCmd());
|
|
9802
10023
|
command.addCommand(wallet);
|
|
9803
|
-
const session = new
|
|
10024
|
+
const session = new Command37("session").description("Manage agent session keys");
|
|
9804
10025
|
session.addCommand(sessionCreateCmd());
|
|
9805
10026
|
session.addCommand(sessionInfoCmd());
|
|
9806
10027
|
session.addCommand(sessionRevokeCmd());
|
|
@@ -9812,7 +10033,7 @@ function agentCmd() {
|
|
|
9812
10033
|
// src/commands/miner.ts
|
|
9813
10034
|
import { readFileSync } from "node:fs";
|
|
9814
10035
|
import path3 from "node:path";
|
|
9815
|
-
import { Command as
|
|
10036
|
+
import { Command as Command38, Option as Option6 } from "commander";
|
|
9816
10037
|
import { formatFlop as formatFlop2 } from "@flop-labs/sdk";
|
|
9817
10038
|
|
|
9818
10039
|
// src/gpu-inventory-remark.ts
|
|
@@ -10030,7 +10251,7 @@ function buildMinerInitPlan(options) {
|
|
|
10030
10251
|
};
|
|
10031
10252
|
}
|
|
10032
10253
|
function minerInitCmd() {
|
|
10033
|
-
return new
|
|
10254
|
+
return new Command38("init").description("Print a phased miner onboarding plan").addOption(new Option6("--mode <mode>", "Miner mode").choices(["sim", "tdx", "cloud"]).default("sim")).option("--instance <number>", "Local guest instance number", "0").option("--provider <name>", "Cloud provider", "runpod").option(
|
|
10034
10255
|
"--gpu <SKU[:COUNT]>",
|
|
10035
10256
|
"Expected GPU inventory; repeat for mixed-SKU hosts",
|
|
10036
10257
|
collectGpu,
|
|
@@ -10073,7 +10294,7 @@ async function gpuCheck(mode) {
|
|
|
10073
10294
|
}
|
|
10074
10295
|
}
|
|
10075
10296
|
function minerDoctorCmd() {
|
|
10076
|
-
return new
|
|
10297
|
+
return new Command38("doctor").description("Check miner runtime, hardware, chain, and serving prerequisites").addOption(new Option6("--mode <mode>", "Miner mode").choices(["sim", "tdx", "cloud"]).default("sim")).option("--repo <path>", "flop-core checkout", process.cwd()).option("--session-url <url>", "Optional miner session info URL").option("--offline", "Skip chain RPC check").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10077
10298
|
try {
|
|
10078
10299
|
const checks = [];
|
|
10079
10300
|
if (opts.mode === "cloud") {
|
|
@@ -10110,7 +10331,7 @@ function minerDoctorCmd() {
|
|
|
10110
10331
|
});
|
|
10111
10332
|
}
|
|
10112
10333
|
function minerStatusCmd() {
|
|
10113
|
-
return new
|
|
10334
|
+
return new Command38("status").description("Show miner registration, calibration, and optional session status").argument("[address]", "Miner SS58 address; defaults to the configured signer").option("--session-url <url>", "Optional miner session info URL").option(
|
|
10114
10335
|
"--expect-gpu <SKU[:COUNT]>",
|
|
10115
10336
|
"Expected inventory; repeat to check attested state",
|
|
10116
10337
|
collectGpu,
|
|
@@ -10160,7 +10381,7 @@ function minerStatusCmd() {
|
|
|
10160
10381
|
});
|
|
10161
10382
|
}
|
|
10162
10383
|
function minerSkusCmd() {
|
|
10163
|
-
return new
|
|
10384
|
+
return new Command38("skus").description("List GPU SKUs with active governance calibration ceilings").option("--json", "Output as JSON").action(async (_opts, command) => {
|
|
10164
10385
|
let client;
|
|
10165
10386
|
try {
|
|
10166
10387
|
const rpc = getRpc(command);
|
|
@@ -10184,7 +10405,7 @@ function minerSkusCmd() {
|
|
|
10184
10405
|
});
|
|
10185
10406
|
}
|
|
10186
10407
|
function minerRegisterCmd() {
|
|
10187
|
-
return new
|
|
10408
|
+
return new Command38("register").description("Register the signer as a miner and lock the runtime-defined self-stake").option("--commission <percent>", "Commission percent (0-20)", "10").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10188
10409
|
let client;
|
|
10189
10410
|
try {
|
|
10190
10411
|
const commission = Number(opts.commission);
|
|
@@ -10211,7 +10432,7 @@ function minerRegisterCmd() {
|
|
|
10211
10432
|
});
|
|
10212
10433
|
}
|
|
10213
10434
|
function minerDeclareGpuCmd() {
|
|
10214
|
-
return new
|
|
10435
|
+
return new Command38("declare-gpu").description("Publish the signer's self-reported GPU inventory as a flop.gpu-inventory.v1 remark (display-only)").option("--nvidia-smi <path>", "Output of `nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits` (- for stdin)").option("--detect", "Run nvidia-smi here (on the GPU host)").option("--retract", "Publish an empty inventory, withdrawing an earlier declaration").option("--dry-run", "Print the remark without signing").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10215
10436
|
let client;
|
|
10216
10437
|
try {
|
|
10217
10438
|
if ([opts.nvidiaSmi, opts.detect, opts.retract].filter(Boolean).length !== 1) {
|
|
@@ -10247,7 +10468,7 @@ function minerDeclareGpuCmd() {
|
|
|
10247
10468
|
});
|
|
10248
10469
|
}
|
|
10249
10470
|
function minerCmd() {
|
|
10250
|
-
const command = new
|
|
10471
|
+
const command = new Command38("miner").description("Onboard and operate inference miners");
|
|
10251
10472
|
command.addCommand(minerInitCmd());
|
|
10252
10473
|
command.addCommand(minerDoctorCmd());
|
|
10253
10474
|
command.addCommand(minerStatusCmd());
|
|
@@ -10260,7 +10481,7 @@ function minerCmd() {
|
|
|
10260
10481
|
|
|
10261
10482
|
// src/commands/validator.ts
|
|
10262
10483
|
import path4 from "node:path";
|
|
10263
|
-
import { Command as
|
|
10484
|
+
import { Command as Command39, Option as Option7 } from "commander";
|
|
10264
10485
|
import { formatFlop as formatFlop3 } from "@flop-labs/sdk";
|
|
10265
10486
|
function buildValidatorInitPlan(mode, minimumStake) {
|
|
10266
10487
|
const bringUp = mode === "local" ? "just run-node" : "infra/vultr/create-vm.sh <args>; infra/vultr/deploy-core.sh --vm-ip <TAILSCALE-IP>";
|
|
@@ -10310,7 +10531,7 @@ function buildValidatorInitPlan(mode, minimumStake) {
|
|
|
10310
10531
|
};
|
|
10311
10532
|
}
|
|
10312
10533
|
function validatorInitCmd() {
|
|
10313
|
-
return new
|
|
10534
|
+
return new Command39("init").description("Print a validator onboarding plan with the live minimum stake").addOption(new Option7("--mode <mode>", "Validator deployment mode").choices(["local", "vultr"]).default("local")).option("--offline", "Do not query the configured chain").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10314
10535
|
let client;
|
|
10315
10536
|
try {
|
|
10316
10537
|
let minimumStake;
|
|
@@ -10329,7 +10550,7 @@ function validatorInitCmd() {
|
|
|
10329
10550
|
});
|
|
10330
10551
|
}
|
|
10331
10552
|
function validatorDoctorCmd() {
|
|
10332
|
-
return new
|
|
10553
|
+
return new Command39("doctor").description("Check validator node and on-chain eligibility prerequisites").addOption(new Option7("--mode <mode>", "Validator deployment mode").choices(["local", "vultr"]).default("local")).option("--repo <path>", "flop-core checkout", process.cwd()).option("--offline", "Skip chain and account checks").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10333
10554
|
let client;
|
|
10334
10555
|
try {
|
|
10335
10556
|
const checks = [
|
|
@@ -10400,7 +10621,7 @@ function validatorDoctorCmd() {
|
|
|
10400
10621
|
});
|
|
10401
10622
|
}
|
|
10402
10623
|
function validatorStatusCmd() {
|
|
10403
|
-
return new
|
|
10624
|
+
return new Command39("status").description("Show validator stake, jail, performance, and verified-work state").argument("[address]", "Validator SS58 address; defaults to the configured signer").option("--json", "Output as JSON").action(async (address, opts, command) => {
|
|
10404
10625
|
let client;
|
|
10405
10626
|
try {
|
|
10406
10627
|
const rpc = getRpc(command);
|
|
@@ -10445,7 +10666,7 @@ function validatorStatusCmd() {
|
|
|
10445
10666
|
});
|
|
10446
10667
|
}
|
|
10447
10668
|
function validatorRegisterCmd() {
|
|
10448
|
-
return new
|
|
10669
|
+
return new Command39("register").description("Register the signer as a validator, bonding the effective minimum").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10449
10670
|
let client;
|
|
10450
10671
|
try {
|
|
10451
10672
|
const rpc = getRpc(command);
|
|
@@ -10475,7 +10696,7 @@ function validatorRegisterCmd() {
|
|
|
10475
10696
|
});
|
|
10476
10697
|
}
|
|
10477
10698
|
function validatorCommissionCmd() {
|
|
10478
|
-
return new
|
|
10699
|
+
return new Command39("commission").description("Set validator commission").argument("<percent>", "Commission percent (0-100)").option("--json", "Output as JSON").action(async (percent, opts, command) => {
|
|
10479
10700
|
let client;
|
|
10480
10701
|
try {
|
|
10481
10702
|
const value = Number(percent);
|
|
@@ -10495,7 +10716,7 @@ function validatorCommissionCmd() {
|
|
|
10495
10716
|
});
|
|
10496
10717
|
}
|
|
10497
10718
|
function validatorLifecycleCmd(name, description, sdkMethod) {
|
|
10498
|
-
return new
|
|
10719
|
+
return new Command39(name).description(description).option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10499
10720
|
let client;
|
|
10500
10721
|
try {
|
|
10501
10722
|
const rpc = getRpc(command);
|
|
@@ -10511,7 +10732,7 @@ function validatorLifecycleCmd(name, description, sdkMethod) {
|
|
|
10511
10732
|
});
|
|
10512
10733
|
}
|
|
10513
10734
|
function validatorCmd() {
|
|
10514
|
-
const command = new
|
|
10735
|
+
const command = new Command39("validator").description("Onboard and operate validator nodes");
|
|
10515
10736
|
command.addCommand(validatorInitCmd());
|
|
10516
10737
|
command.addCommand(validatorDoctorCmd());
|
|
10517
10738
|
command.addCommand(validatorStatusCmd());
|
|
@@ -10524,7 +10745,7 @@ function validatorCmd() {
|
|
|
10524
10745
|
}
|
|
10525
10746
|
|
|
10526
10747
|
// src/commands/network-status.ts
|
|
10527
|
-
import { Command as
|
|
10748
|
+
import { Command as Command40 } from "commander";
|
|
10528
10749
|
|
|
10529
10750
|
// src/network.ts
|
|
10530
10751
|
async function probeRpcChain(network, rpc) {
|
|
@@ -10558,7 +10779,7 @@ function networkStatusCmd(probeRpc2 = probeRpcChain, probeFaucet = async (endpoi
|
|
|
10558
10779
|
return { ...identity, ready: null, readinessError: error instanceof Error ? error.message : String(error) };
|
|
10559
10780
|
}
|
|
10560
10781
|
}) {
|
|
10561
|
-
return new
|
|
10782
|
+
return new Command40("status").description("Show resolved network endpoints and live chain identity").option("--offline", "Resolve configured endpoints without connecting").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10562
10783
|
const resolved = resolveNetwork(command);
|
|
10563
10784
|
let rpcInfo = null;
|
|
10564
10785
|
let rpcError;
|
|
@@ -10612,9 +10833,9 @@ function networkStatusCmd(probeRpc2 = probeRpcChain, probeFaucet = async (endpoi
|
|
|
10612
10833
|
}
|
|
10613
10834
|
|
|
10614
10835
|
// src/commands/faucet-request.ts
|
|
10615
|
-
import { Command as
|
|
10836
|
+
import { Command as Command41 } from "commander";
|
|
10616
10837
|
function faucetRequestCmd(probeRpc2 = probeRpcChain) {
|
|
10617
|
-
return new
|
|
10838
|
+
return new Command41("request").description("Request dev/test FLOP after verifying faucet and RPC chain identity").argument("<address>", "SS58 address to fund").option("--amount <flop>", "Requested FLOP amount; server policy may cap it").option("--endpoint <url>", "Explicit faucet endpoint override").option("--json", "Output as JSON").action(async (address, opts, command) => {
|
|
10618
10839
|
try {
|
|
10619
10840
|
const resolved = resolveNetwork(command);
|
|
10620
10841
|
const endpoint = opts.endpoint ?? resolved.profile.faucet;
|
|
@@ -10667,15 +10888,15 @@ function faucetRequestCmd(probeRpc2 = probeRpcChain) {
|
|
|
10667
10888
|
}
|
|
10668
10889
|
|
|
10669
10890
|
// src/commands/chat.ts
|
|
10670
|
-
import { Command as
|
|
10891
|
+
import { Command as Command42 } from "commander";
|
|
10671
10892
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
10672
10893
|
import { homedir as homedir4 } from "node:os";
|
|
10673
|
-
import { join as
|
|
10894
|
+
import { join as join9 } from "node:path";
|
|
10674
10895
|
import { createInterface } from "node:readline/promises";
|
|
10675
10896
|
import { stdin, stdout } from "node:process";
|
|
10676
10897
|
|
|
10677
10898
|
// src/conversations.ts
|
|
10678
|
-
import { mkdir as
|
|
10899
|
+
import { mkdir as mkdir13, readFile as readFile8, rename as rename8, writeFile as writeFile11 } from "node:fs/promises";
|
|
10679
10900
|
import { dirname as dirname6 } from "node:path";
|
|
10680
10901
|
var MAX_CONVERSATIONS = 100;
|
|
10681
10902
|
var MAX_CONVERSATION_MESSAGES = 100;
|
|
@@ -10695,9 +10916,9 @@ var ConversationStore = class {
|
|
|
10695
10916
|
}
|
|
10696
10917
|
}
|
|
10697
10918
|
async write(data) {
|
|
10698
|
-
await
|
|
10919
|
+
await mkdir13(dirname6(this.path), { recursive: true, mode: 448 });
|
|
10699
10920
|
const temp = `${this.path}.${process.pid}.tmp`;
|
|
10700
|
-
await
|
|
10921
|
+
await writeFile11(temp, `${JSON.stringify(data, null, 2)}
|
|
10701
10922
|
`, { mode: 384 });
|
|
10702
10923
|
await rename8(temp, this.path);
|
|
10703
10924
|
}
|
|
@@ -10734,7 +10955,7 @@ function conversationPrompt(messages) {
|
|
|
10734
10955
|
|
|
10735
10956
|
// src/commands/chat.ts
|
|
10736
10957
|
function chatCmd() {
|
|
10737
|
-
return new
|
|
10958
|
+
return new Command42("chat").description("Start or resume a persistent FLOP chat, optionally through a bounded session").argument("[prompt]", "One-shot prompt; omit for interactive chat").option("--conversation <id>", "Resume a conversation ID").option("--model <id>", "32-byte model hash or exact registered name from flop model list").option("--session-owner <address>", "Use --signer as this owner's session delegate").option("--max-flop <n>", "Per-turn FLOP cap", "5").option("--tee-requirements <hex>", "Registered HTLC appraisal commitments (100-byte SCALE hex)", process.env.FLOP_HTLC_TEE_REQUIREMENTS).option("--skip-model-check", "Allow an unregistered model (local development only)").option("--history", "List stored conversations without connecting").option("--delete <id>", "Delete a stored conversation without connecting").option("--store <path>", "Conversation store path", join9(homedir4(), ".flop", "conversations.json")).option("--json", "Output as JSON").action(async (prompt, opts, command) => {
|
|
10738
10959
|
const store = new ConversationStore(opts.store);
|
|
10739
10960
|
let agent;
|
|
10740
10961
|
try {
|
|
@@ -10859,8 +11080,8 @@ function chatCmd() {
|
|
|
10859
11080
|
}
|
|
10860
11081
|
|
|
10861
11082
|
// src/commands/tx-build.ts
|
|
10862
|
-
import { Command as
|
|
10863
|
-
import { writeFile as
|
|
11083
|
+
import { Command as Command43 } from "commander";
|
|
11084
|
+
import { writeFile as writeFile12 } from "node:fs/promises";
|
|
10864
11085
|
import { execFile as execFile5 } from "node:child_process";
|
|
10865
11086
|
import { promisify as promisify5 } from "node:util";
|
|
10866
11087
|
|
|
@@ -10926,7 +11147,7 @@ function assertEnvelopeChain(api, envelope) {
|
|
|
10926
11147
|
|
|
10927
11148
|
// src/commands/tx-build.ts
|
|
10928
11149
|
function txBuildCmd() {
|
|
10929
|
-
return new
|
|
11150
|
+
return new Command43("build").description("Build an immortal unsigned transaction envelope for air-gapped signing").requiredOption("--address <address>", "Signing account").requiredOption("--call <hex>", "SCALE call hex").option("--out <path>", "Write envelope JSON").option("--qr", "Render the signing payload using installed qrencode").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
10930
11151
|
let client;
|
|
10931
11152
|
try {
|
|
10932
11153
|
const resolved = resolveNetwork(command);
|
|
@@ -10939,7 +11160,7 @@ function txBuildCmd() {
|
|
|
10939
11160
|
opts.call
|
|
10940
11161
|
);
|
|
10941
11162
|
if (opts.out) {
|
|
10942
|
-
await
|
|
11163
|
+
await writeFile12(opts.out, `${JSON.stringify(envelope, null, 2)}
|
|
10943
11164
|
`, { mode: 384 });
|
|
10944
11165
|
}
|
|
10945
11166
|
if (opts.qr) {
|
|
@@ -10971,10 +11192,10 @@ function txBuildCmd() {
|
|
|
10971
11192
|
}
|
|
10972
11193
|
|
|
10973
11194
|
// src/commands/tx-broadcast.ts
|
|
10974
|
-
import { Command as
|
|
11195
|
+
import { Command as Command44 } from "commander";
|
|
10975
11196
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
10976
11197
|
function txBroadcastCmd() {
|
|
10977
|
-
return new
|
|
11198
|
+
return new Command44("broadcast").description("Import an offline signature, reject chain drift, and broadcast").argument("<envelope>", "Unsigned envelope JSON").requiredOption("--signature <hex>", "SCALE MultiSignature from the offline signer").option("--allow-nonce-drift", "Broadcast even if account nonce has changed").option("--json", "Output as JSON").action(async (path5, opts, command) => {
|
|
10978
11199
|
let client;
|
|
10979
11200
|
try {
|
|
10980
11201
|
if (!/^0x[0-9a-f]+$/i.test(opts.signature)) throw new Error("--signature must be 0x hex");
|
|
@@ -11010,7 +11231,7 @@ function txBroadcastCmd() {
|
|
|
11010
11231
|
}
|
|
11011
11232
|
|
|
11012
11233
|
// src/commands/multisig.ts
|
|
11013
|
-
import { Command as
|
|
11234
|
+
import { Command as Command45 } from "commander";
|
|
11014
11235
|
import { createKeyMulti, encodeAddress, sortAddresses } from "@polkadot/util-crypto";
|
|
11015
11236
|
import { blake2AsHex } from "@polkadot/util-crypto";
|
|
11016
11237
|
function otherSignatories(signer, values) {
|
|
@@ -11031,8 +11252,8 @@ function timepoint(opts) {
|
|
|
11031
11252
|
return { height: parsePositiveInt(opts.height, "--height"), index };
|
|
11032
11253
|
}
|
|
11033
11254
|
function multisigCmd() {
|
|
11034
|
-
const command = new
|
|
11035
|
-
command.addCommand(new
|
|
11255
|
+
const command = new Command45("multisig").description("Participate in Substrate multisig transactions");
|
|
11256
|
+
command.addCommand(new Command45("address").description("Derive the deterministic multisig address").requiredOption("--threshold <n>", "Approval threshold").requiredOption("--signatory <address...>", "All signatory addresses").option("--json", "Output as JSON").action(async (opts, cmd) => {
|
|
11036
11257
|
try {
|
|
11037
11258
|
const threshold = parsePositiveInt(opts.threshold, "--threshold");
|
|
11038
11259
|
const sorted = sortAddresses(opts.signatory, 42);
|
|
@@ -11047,7 +11268,7 @@ function multisigCmd() {
|
|
|
11047
11268
|
commandError(error);
|
|
11048
11269
|
}
|
|
11049
11270
|
}));
|
|
11050
|
-
command.addCommand(new
|
|
11271
|
+
command.addCommand(new Command45("propose").description("Create or execute a multisig call").requiredOption("--threshold <n>", "Approval threshold").requiredOption("--other <address...>", "Other signatories (exclude configured signer)").requiredOption("--call <hex>", "SCALE call hex").option("--max-ref-time <n>", "Maximum call refTime").option("--max-proof-size <n>", "Maximum call proofSize").option("--json", "Output as JSON").action(async (opts, cmd) => {
|
|
11051
11272
|
let client;
|
|
11052
11273
|
try {
|
|
11053
11274
|
client = await createClient(getRpc(cmd), getSigner(cmd));
|
|
@@ -11070,7 +11291,7 @@ function multisigCmd() {
|
|
|
11070
11291
|
await client?.disconnect();
|
|
11071
11292
|
}
|
|
11072
11293
|
}));
|
|
11073
|
-
command.addCommand(new
|
|
11294
|
+
command.addCommand(new Command45("approve").description("Approve a pending multisig call hash").requiredOption("--threshold <n>", "Approval threshold").requiredOption("--other <address...>", "Other signatories").requiredOption("--call-hash <hex>", "Pending call hash").requiredOption("--height <n>", "Timepoint block height").requiredOption("--index <n>", "Timepoint extrinsic index").option("--max-ref-time <n>", "Maximum call refTime").option("--max-proof-size <n>", "Maximum call proofSize").option("--json", "Output as JSON").action(async (opts, cmd) => {
|
|
11074
11295
|
let client;
|
|
11075
11296
|
try {
|
|
11076
11297
|
client = await createClient(getRpc(cmd), getSigner(cmd));
|
|
@@ -11089,7 +11310,7 @@ function multisigCmd() {
|
|
|
11089
11310
|
await client?.disconnect();
|
|
11090
11311
|
}
|
|
11091
11312
|
}));
|
|
11092
|
-
command.addCommand(new
|
|
11313
|
+
command.addCommand(new Command45("cancel").description("Cancel a pending multisig as its depositor").requiredOption("--threshold <n>", "Approval threshold").requiredOption("--other <address...>", "Other signatories").requiredOption("--call-hash <hex>", "Pending call hash").requiredOption("--height <n>", "Timepoint block height").requiredOption("--index <n>", "Timepoint extrinsic index").option("--json", "Output as JSON").action(async (opts, cmd) => {
|
|
11093
11314
|
let client;
|
|
11094
11315
|
try {
|
|
11095
11316
|
client = await createClient(getRpc(cmd), getSigner(cmd));
|
|
@@ -11106,7 +11327,7 @@ function multisigCmd() {
|
|
|
11106
11327
|
await client?.disconnect();
|
|
11107
11328
|
}
|
|
11108
11329
|
}));
|
|
11109
|
-
command.addCommand(new
|
|
11330
|
+
command.addCommand(new Command45("list").alias("status").description("List pending calls for a multisig account").requiredOption("--account <address>", "Multisig account").option("--json", "Output as JSON").action(async (opts, cmd) => {
|
|
11110
11331
|
let client;
|
|
11111
11332
|
try {
|
|
11112
11333
|
client = await createClient(getRpc(cmd));
|
|
@@ -11126,10 +11347,10 @@ function multisigCmd() {
|
|
|
11126
11347
|
}
|
|
11127
11348
|
|
|
11128
11349
|
// src/commands/channel.ts
|
|
11129
|
-
import { Command as
|
|
11350
|
+
import { Command as Command46 } from "commander";
|
|
11130
11351
|
function channelCmd() {
|
|
11131
|
-
const command = new
|
|
11132
|
-
command.addCommand(new
|
|
11352
|
+
const command = new Command46("channel").description("Inspect and finalize compute channels");
|
|
11353
|
+
command.addCommand(new Command46("list").description("List on-chain compute channels").option("--agent <address>", "Filter by paying agent").option("--miner <address>", "Filter by serving miner").option("--include-closed", "Include terminal channels").option("--json", "Output as JSON").action(async (opts, cmd) => {
|
|
11133
11354
|
let client;
|
|
11134
11355
|
try {
|
|
11135
11356
|
client = await createClient(getRpc(cmd));
|
|
@@ -11157,7 +11378,7 @@ function channelCmd() {
|
|
|
11157
11378
|
await client?.disconnect();
|
|
11158
11379
|
}
|
|
11159
11380
|
}));
|
|
11160
|
-
command.addCommand(new
|
|
11381
|
+
command.addCommand(new Command46("finalize").description("Finalize a force-settled channel after its challenge window").argument("<channel-id>", "0x channel ID").option("--json", "Output as JSON").action(async (channelId, _opts, cmd) => {
|
|
11161
11382
|
let client;
|
|
11162
11383
|
try {
|
|
11163
11384
|
client = await createClient(getRpc(cmd), getSigner(cmd));
|
|
@@ -11603,8 +11824,8 @@ function generateGatewayConfig(input) {
|
|
|
11603
11824
|
}
|
|
11604
11825
|
|
|
11605
11826
|
// src/commands/gateway-allowlist.ts
|
|
11606
|
-
import { Command as
|
|
11607
|
-
var
|
|
11827
|
+
import { Command as Command47 } from "commander";
|
|
11828
|
+
var PRECISIONS5 = ["FP32", "FP16", "BF16", "INT8", "INT4", "Mixed"];
|
|
11608
11829
|
function field(value, camel, snake) {
|
|
11609
11830
|
return value?.[camel] ?? value?.[snake];
|
|
11610
11831
|
}
|
|
@@ -11641,7 +11862,7 @@ async function fetchMinerSessionInfo(endpoint, authToken, signal) {
|
|
|
11641
11862
|
}
|
|
11642
11863
|
async function readChainSnapshot(api, miner, pinnedHead) {
|
|
11643
11864
|
const identity = miner.modelIdentity;
|
|
11644
|
-
const precision =
|
|
11865
|
+
const precision = PRECISIONS5[identity.precision];
|
|
11645
11866
|
if (!precision) throw new Error(`miner precision is invalid: ${identity.precision}`);
|
|
11646
11867
|
const finalizedHead = pinnedHead ?? await api.rpc.chain.getFinalizedHead();
|
|
11647
11868
|
const at = await api.at(finalizedHead);
|
|
@@ -11692,7 +11913,7 @@ async function readChainSnapshot(api, miner, pinnedHead) {
|
|
|
11692
11913
|
};
|
|
11693
11914
|
}
|
|
11694
11915
|
function gatewayAllowlistCmd() {
|
|
11695
|
-
return new
|
|
11916
|
+
return new Command47("allowlist").description("Generate gateway allowlist and model catalog from a live miner and chain").requiredOption("--miner <url>", "Authenticated miner base URL").requiredOption("--auth-token-env <name>", "Environment variable containing the miner bearer token").requiredOption("--attestation <path>", "Operator/provider deployment, catalog, and price evidence JSON").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
11696
11917
|
let client;
|
|
11697
11918
|
try {
|
|
11698
11919
|
const authToken = process.env[opts.authTokenEnv];
|
|
@@ -11718,7 +11939,7 @@ function gatewayAllowlistCmd() {
|
|
|
11718
11939
|
}
|
|
11719
11940
|
|
|
11720
11941
|
// src/commands/gateway-renew.ts
|
|
11721
|
-
import { open as open3, readFile as readFile11, rename as rename9, rm as
|
|
11942
|
+
import { open as open3, readFile as readFile11, rename as rename9, rm as rm5, writeFile as writeFile13 } from "node:fs/promises";
|
|
11722
11943
|
import { createHash as createHash6 } from "node:crypto";
|
|
11723
11944
|
import { request as request2 } from "node:http";
|
|
11724
11945
|
import { setTimeout as delay } from "node:timers/promises";
|
|
@@ -12074,7 +12295,7 @@ var MinerAdmissions = class {
|
|
|
12074
12295
|
|
|
12075
12296
|
// ../gateway-core/src/miner-peer.ts
|
|
12076
12297
|
import { BlockList, isIP } from "node:net";
|
|
12077
|
-
import {
|
|
12298
|
+
import { resolve4 as resolve42, resolve6 as resolve62 } from "node:dns/promises";
|
|
12078
12299
|
import { request } from "node:https";
|
|
12079
12300
|
import { Readable } from "node:stream";
|
|
12080
12301
|
var NOT_PUBLIC = new BlockList();
|
|
@@ -12121,17 +12342,32 @@ function isPublicAddress(ip) {
|
|
|
12121
12342
|
}
|
|
12122
12343
|
var EndpointRefusal = class extends Error {
|
|
12123
12344
|
};
|
|
12124
|
-
|
|
12125
|
-
|
|
12345
|
+
async function resolveDnsAddresses(host, v4 = resolve42, v6 = resolve62) {
|
|
12346
|
+
for (let attempt = 0; ; attempt++) {
|
|
12347
|
+
const results = await Promise.allSettled([v4(host), v6(host)]);
|
|
12348
|
+
const errors = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
12349
|
+
const code = (error) => error?.code;
|
|
12350
|
+
const fatal = errors.find((error) => code(error) !== "ENODATA" && code(error) !== "ENOTFOUND");
|
|
12351
|
+
if (fatal) throw fatal;
|
|
12352
|
+
const notFound = errors.find((error) => code(error) === "ENOTFOUND");
|
|
12353
|
+
if (notFound) {
|
|
12354
|
+
if (attempt === 0) continue;
|
|
12355
|
+
throw notFound;
|
|
12356
|
+
}
|
|
12357
|
+
return results.flatMap((result, index) => result.status === "fulfilled" ? result.value.map((address) => ({ address, family: index === 0 ? 4 : 6 })) : []);
|
|
12358
|
+
}
|
|
12359
|
+
}
|
|
12360
|
+
var systemResolve = resolveDnsAddresses;
|
|
12361
|
+
async function resolvePublic(host, resolve10 = systemResolve) {
|
|
12126
12362
|
const family = isIP(host);
|
|
12127
|
-
const addresses = family ? [{ address: host, family }] : await
|
|
12363
|
+
const addresses = family ? [{ address: host, family }] : await resolve10(host);
|
|
12128
12364
|
if (addresses.length === 0 || !addresses.every((a) => isPublicAddress(a.address))) {
|
|
12129
12365
|
throw new EndpointRefusal(`${host} does not resolve to public addresses only`);
|
|
12130
12366
|
}
|
|
12131
12367
|
return addresses[0];
|
|
12132
12368
|
}
|
|
12133
12369
|
function minerPeerFetch(endpoint, dynamic, before = () => {
|
|
12134
|
-
},
|
|
12370
|
+
}, resolve10 = systemResolve, send2 = request) {
|
|
12135
12371
|
return async (input, init = {}) => {
|
|
12136
12372
|
before();
|
|
12137
12373
|
const url = new URL(String(input));
|
|
@@ -12142,7 +12378,7 @@ function minerPeerFetch(endpoint, dynamic, before = () => {
|
|
|
12142
12378
|
const pinned = await new Promise((accept, reject) => {
|
|
12143
12379
|
const abort = () => reject(signal.reason);
|
|
12144
12380
|
signal.addEventListener("abort", abort, { once: true });
|
|
12145
|
-
resolvePublic(url.hostname.replace(/^\[|\]$/g, ""),
|
|
12381
|
+
resolvePublic(url.hostname.replace(/^\[|\]$/g, ""), resolve10).then(accept, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
12146
12382
|
if (signal.aborted) abort();
|
|
12147
12383
|
});
|
|
12148
12384
|
before();
|
|
@@ -12178,7 +12414,7 @@ function minerPeerFetch(endpoint, dynamic, before = () => {
|
|
|
12178
12414
|
}
|
|
12179
12415
|
|
|
12180
12416
|
// src/commands/gateway-renew.ts
|
|
12181
|
-
import { Command as
|
|
12417
|
+
import { Command as Command48 } from "commander";
|
|
12182
12418
|
|
|
12183
12419
|
// src/commands/gateway-tools-qualification.ts
|
|
12184
12420
|
import { decodeAddress } from "@polkadot/util-crypto";
|
|
@@ -12442,7 +12678,7 @@ function tailnetNodeRef(source) {
|
|
|
12442
12678
|
return match ? { host: match[1], egress: match[2], asn: match[3] } : null;
|
|
12443
12679
|
}
|
|
12444
12680
|
function readTailscaleStatus(socketPath) {
|
|
12445
|
-
return new Promise((
|
|
12681
|
+
return new Promise((resolve10, reject) => {
|
|
12446
12682
|
const req = request2({
|
|
12447
12683
|
socketPath,
|
|
12448
12684
|
path: "/localapi/v0/status",
|
|
@@ -12457,7 +12693,7 @@ function readTailscaleStatus(socketPath) {
|
|
|
12457
12693
|
res.on("end", () => {
|
|
12458
12694
|
if (res.statusCode !== 200) return reject(new Error(`tailscaled status returned HTTP ${res.statusCode}`));
|
|
12459
12695
|
try {
|
|
12460
|
-
|
|
12696
|
+
resolve10(JSON.parse(body));
|
|
12461
12697
|
} catch {
|
|
12462
12698
|
reject(new Error("tailscaled status is not JSON"));
|
|
12463
12699
|
}
|
|
@@ -12671,12 +12907,12 @@ async function writeEvidenceFile(path5, evidence, appendQualification = false) {
|
|
|
12671
12907
|
evidence = { ...prior, generated_at: evidence.generated_at, conformance: [...conformance, ...latest.values()] };
|
|
12672
12908
|
}
|
|
12673
12909
|
parseGatewayEvidence(evidence);
|
|
12674
|
-
await
|
|
12910
|
+
await writeFile13(temporary, `${JSON.stringify(evidence, null, 2)}
|
|
12675
12911
|
`, { mode: 420 });
|
|
12676
12912
|
await rename9(temporary, path5);
|
|
12677
12913
|
} finally {
|
|
12678
12914
|
await lock.close();
|
|
12679
|
-
await
|
|
12915
|
+
await rm5(`${path5}.lock`);
|
|
12680
12916
|
}
|
|
12681
12917
|
}
|
|
12682
12918
|
async function finishRenewal(report, out, output) {
|
|
@@ -12957,7 +13193,7 @@ function envSecret(name) {
|
|
|
12957
13193
|
return value;
|
|
12958
13194
|
}
|
|
12959
13195
|
function gatewayRenewCmd() {
|
|
12960
|
-
return new
|
|
13196
|
+
return new Command48("renew").description("Re-observe allowlisted miners, re-run feature conformance, and write the gateway evidence file").option("--allowlist-env <name>", "Direct probe mode: environment variable holding GATEWAY_SESSION_ALLOWLIST (never printed)").requiredOption("--catalog-env <name>", "Environment variable holding GATEWAY_MODEL_CATALOG").option("--catalog-pricing <mode>", "The gateway's GATEWAY_CATALOG_PRICING: operator, or chain when the catalog omits prices", "operator").option("--provider-key-env <name>", "Direct probe mode: environment variable holding the GPU.ai API key").option("--tailscale-socket <path>", "tailscaled LocalAPI socket for tailnet-hosted miners", "/var/run/tailscale/tailscaled.sock").option("--probe-signer-env <name>", "Direct probe mode: environment variable holding the dedicated funded probe signer URI").option("--probe-escrow <flop>", "Direct probe mode: FLOP escrowed for each conformance probe channel").option("--qualification-receipt <path>", "Receipt-only mode: settled owner tools-qualification evidence; never runs another probe").option("--qualification-receipt-sha256 <hash>", "SHA-256 of the immutable owner qualification receipt").requiredOption("--out <path>", "Evidence file the gateway reads as GATEWAY_EVIDENCE_FILE").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
12961
13197
|
let client;
|
|
12962
13198
|
try {
|
|
12963
13199
|
if (opts.catalogPricing !== "operator" && opts.catalogPricing !== "chain") {
|
|
@@ -13051,9 +13287,9 @@ function gatewayRenewCmd() {
|
|
|
13051
13287
|
}
|
|
13052
13288
|
|
|
13053
13289
|
// src/release-check.ts
|
|
13054
|
-
import { mkdir as
|
|
13290
|
+
import { mkdir as mkdir14, readFile as readFile12, rename as rename10, writeFile as writeFile14 } from "node:fs/promises";
|
|
13055
13291
|
import { homedir as homedir5 } from "node:os";
|
|
13056
|
-
import { dirname as dirname7, join as
|
|
13292
|
+
import { dirname as dirname7, join as join10 } from "node:path";
|
|
13057
13293
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
13058
13294
|
var READ_ONLY = /* @__PURE__ */ new Set([
|
|
13059
13295
|
"health",
|
|
@@ -13097,7 +13333,7 @@ async function checkRelease(options) {
|
|
|
13097
13333
|
if (options.offline || (options.testing ?? (process.env.NODE_ENV === "test" || !!process.env.VITEST))) return;
|
|
13098
13334
|
const warn = options.warn ?? ((message) => console.error(message));
|
|
13099
13335
|
const now = options.now ?? Date.now();
|
|
13100
|
-
const cacheFile = options.cacheFile ??
|
|
13336
|
+
const cacheFile = options.cacheFile ?? join10(process.env.XDG_CACHE_HOME || join10(homedir5(), ".cache"), "flop", "releases.json");
|
|
13101
13337
|
let releases;
|
|
13102
13338
|
try {
|
|
13103
13339
|
const cached = JSON.parse(await readFile12(cacheFile, "utf8"));
|
|
@@ -13112,9 +13348,9 @@ async function checkRelease(options) {
|
|
|
13112
13348
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
13113
13349
|
releases = parseOperatorReleases(await response.json());
|
|
13114
13350
|
try {
|
|
13115
|
-
await
|
|
13351
|
+
await mkdir14(dirname7(cacheFile), { recursive: true });
|
|
13116
13352
|
const temporary = `${cacheFile}.${process.pid}.tmp`;
|
|
13117
|
-
await
|
|
13353
|
+
await writeFile14(temporary, JSON.stringify({ fetchedAt: now, releases }), { mode: 384 });
|
|
13118
13354
|
await rename10(temporary, cacheFile);
|
|
13119
13355
|
} catch {
|
|
13120
13356
|
warn("warning: could not cache operator releases");
|
|
@@ -13139,10 +13375,10 @@ async function checkRelease(options) {
|
|
|
13139
13375
|
}
|
|
13140
13376
|
|
|
13141
13377
|
// src/cli.ts
|
|
13142
|
-
var program = new
|
|
13378
|
+
var program = new Command49();
|
|
13143
13379
|
var outputMode = outputModeFromArgv(process.argv.slice(2));
|
|
13144
13380
|
setOutputMode(outputMode);
|
|
13145
|
-
program.name("flop").description("FLOP Network CLI").version(operatorVersion(JSON.parse(readFileSync2(
|
|
13381
|
+
program.name("flop").description("FLOP Network CLI").version(operatorVersion(JSON.parse(readFileSync2(join11(packageRoot, "package.json"), "utf8")).version)).option("--offline", "Skip the operator release check").addOption(new Option8("--network <network>", "Network profile").choices(["local", "devnet", "testnet", "mainnet"]).default("local")).option("--rpc <url>", "RPC endpoint override").option("--signer <uri>", "Signer URI (local/dev only; required outside local unless --keystore)").option("--keystore <path>", "Encrypted Polkadot JSON keystore").option("--password-env <name>", "Environment variable containing the keystore password").addOption(new Option8("--output <mode>", "Output mode").choices(["human", "json", "agent"]).default("human")).option("--json", "Output as JSON");
|
|
13146
13382
|
program.addCommand(healthCmd());
|
|
13147
13383
|
program.addCommand(balanceCmd());
|
|
13148
13384
|
program.addCommand(gpuCmd());
|
|
@@ -13154,13 +13390,13 @@ program.addCommand(chatCmd());
|
|
|
13154
13390
|
program.addCommand(agentCmd());
|
|
13155
13391
|
program.addCommand(minerCmd());
|
|
13156
13392
|
program.addCommand(validatorCmd());
|
|
13157
|
-
var networkCmd = new
|
|
13393
|
+
var networkCmd = new Command49("network").description("Resolve and inspect network profiles");
|
|
13158
13394
|
networkCmd.addCommand(networkStatusCmd());
|
|
13159
13395
|
program.addCommand(networkCmd);
|
|
13160
|
-
var faucetCmd = new
|
|
13396
|
+
var faucetCmd = new Command49("faucet").description("Request dev/test FLOP");
|
|
13161
13397
|
faucetCmd.addCommand(faucetRequestCmd());
|
|
13162
13398
|
program.addCommand(faucetCmd);
|
|
13163
|
-
var sessionCmd = new
|
|
13399
|
+
var sessionCmd = new Command49("session").description("Manage session keys");
|
|
13164
13400
|
sessionCmd.addCommand(sessionRunCmd());
|
|
13165
13401
|
sessionCmd.addCommand(sessionListCmd());
|
|
13166
13402
|
sessionCmd.addCommand(sessionCreateCmd());
|
|
@@ -13168,7 +13404,7 @@ sessionCmd.addCommand(sessionInfoCmd());
|
|
|
13168
13404
|
sessionCmd.addCommand(sessionRevokeCmd());
|
|
13169
13405
|
sessionCmd.addCommand(sessionLimitsCmd());
|
|
13170
13406
|
program.addCommand(sessionCmd);
|
|
13171
|
-
var keysCmd = new
|
|
13407
|
+
var keysCmd = new Command49("keys").description("Manage keys");
|
|
13172
13408
|
keysCmd.addCommand(keysGenerateCmd());
|
|
13173
13409
|
keysCmd.addCommand(keysInspectCmd());
|
|
13174
13410
|
keysCmd.addCommand(keysHardwareCmd());
|
|
@@ -13176,44 +13412,45 @@ keysCmd.addCommand(keysImportCmd());
|
|
|
13176
13412
|
keysCmd.addCommand(keysExportCmd());
|
|
13177
13413
|
keysCmd.addCommand(keysClaudeProfileCmd());
|
|
13178
13414
|
program.addCommand(keysCmd);
|
|
13179
|
-
var walletCmd = new
|
|
13180
|
-
var agentWalletCmd = new
|
|
13415
|
+
var walletCmd = new Command49("wallet").description("Manage wallets");
|
|
13416
|
+
var agentWalletCmd = new Command49("agent").description("Agent wallet operations");
|
|
13181
13417
|
agentWalletCmd.addCommand(walletAgentCreateCmd());
|
|
13182
13418
|
walletCmd.addCommand(agentWalletCmd);
|
|
13183
13419
|
program.addCommand(walletCmd);
|
|
13184
|
-
var txCmd = new
|
|
13420
|
+
var txCmd = new Command49("tx").description("Build, sign, and broadcast transactions");
|
|
13185
13421
|
txCmd.addCommand(txBuildCmd());
|
|
13186
13422
|
txCmd.addCommand(txBroadcastCmd());
|
|
13187
13423
|
program.addCommand(txCmd);
|
|
13188
13424
|
program.addCommand(multisigCmd());
|
|
13189
13425
|
program.addCommand(channelCmd());
|
|
13190
|
-
var gatewayCmd = new
|
|
13426
|
+
var gatewayCmd = new Command49("gateway").description("Generate verified gateway configuration");
|
|
13191
13427
|
gatewayCmd.addCommand(gatewayAllowlistCmd());
|
|
13192
13428
|
gatewayCmd.addCommand(gatewayRenewCmd());
|
|
13193
13429
|
program.addCommand(gatewayCmd);
|
|
13194
|
-
var x402Cmd = new
|
|
13430
|
+
var x402Cmd = new Command49("x402").description("x402 payment gateway operations");
|
|
13195
13431
|
x402Cmd.addCommand(x402SettleCmd());
|
|
13196
13432
|
program.addCommand(x402Cmd);
|
|
13197
|
-
var daCmd = new
|
|
13433
|
+
var daCmd = new Command49("da").description("Sovereign DA registry (leases, deposits)");
|
|
13198
13434
|
daCmd.addCommand(daDepositCmd());
|
|
13199
13435
|
daCmd.addCommand(daRegisterCmd());
|
|
13200
13436
|
daCmd.addCommand(daTopupCmd());
|
|
13201
13437
|
daCmd.addCommand(daWithdrawCmd());
|
|
13202
13438
|
program.addCommand(daCmd);
|
|
13203
|
-
var modelCmd = new
|
|
13439
|
+
var modelCmd = new Command49("model").description("Model registry operations");
|
|
13204
13440
|
modelCmd.addCommand(modelListCmd());
|
|
13205
13441
|
modelCmd.addCommand(modelGetCmd());
|
|
13206
13442
|
modelCmd.addCommand(modelFetchCmd());
|
|
13207
13443
|
modelCmd.addCommand(modelUploadCmd());
|
|
13208
13444
|
modelCmd.addCommand(modelPreflightCmd());
|
|
13209
13445
|
modelCmd.addCommand(modelOnboardCmd());
|
|
13446
|
+
modelCmd.addCommand(modelBackupCmd());
|
|
13210
13447
|
modelCmd.addCommand(modelAuditCmd());
|
|
13211
13448
|
program.addCommand(modelCmd);
|
|
13212
13449
|
program.hook("preAction", async (_root, action) => {
|
|
13213
13450
|
const path5 = [];
|
|
13214
13451
|
for (let command = action; command && command !== program; command = command.parent) path5.unshift(command.name());
|
|
13215
13452
|
await checkRelease({
|
|
13216
|
-
version: operatorVersion(JSON.parse(readFileSync2(
|
|
13453
|
+
version: operatorVersion(JSON.parse(readFileSync2(join11(packageRoot, "package.json"), "utf8")).version),
|
|
13217
13454
|
readOnly: isReadOnlyCommand(path5),
|
|
13218
13455
|
offline: program.opts().offline,
|
|
13219
13456
|
image: !!process.env.FLOP_IMAGE_VERSION
|