@needmoretruth/nmts-cli 0.35.0 → 0.36.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/AGENTS.md +11 -12
- package/CHANGELOG.md +35 -0
- package/README.ko.md +19 -21
- package/README.md +21 -22
- package/dist/account.js +2 -1
- package/dist/arg-options.d.ts +3 -1
- package/dist/artifact-about.d.ts +1 -1
- package/dist/autonomy.d.ts +3 -10
- package/dist/autonomy.js +15 -17
- package/dist/bytes.d.ts +15 -0
- package/dist/bytes.js +63 -0
- package/dist/collision.d.ts +5 -5
- package/dist/collision.js +19 -22
- package/dist/commands/delete-account.js +1 -1
- package/dist/commands/erase.js +1 -1
- package/dist/commands/get.js +1 -1
- package/dist/commands/listfile.js +1 -1
- package/dist/commands/mcp.js +5 -1
- package/dist/commands/mode.js +2 -2
- package/dist/commands/on-collision.d.ts +1 -1
- package/dist/commands/on-collision.js +3 -3
- package/dist/commands/platform.d.ts +9 -0
- package/dist/commands/platform.js +153 -0
- package/dist/commands/pull.js +1 -1
- package/dist/commands/push-wallet.js +2 -1
- package/dist/commands/push.js +6 -4
- package/dist/commands/put-wallet.js +5 -3
- package/dist/commands/put.js +8 -5
- package/dist/commands/receive.js +1 -1
- package/dist/commands/settings.js +1 -1
- package/dist/commands/share.js +1 -1
- package/dist/commands/support.js +3 -3
- package/dist/commands/unlock.js +2 -2
- package/dist/commands/wallet-hall.d.ts +4 -1
- package/dist/commands/wallet-hall.js +16 -8
- package/dist/commands/wallet-storage-ops.d.ts +4 -1
- package/dist/commands/wallet-storage-ops.js +7 -2
- package/dist/commands/wallet-swap.d.ts +3 -0
- package/dist/commands/wallet-swap.js +8 -4
- package/dist/commands/wallet.d.ts +1 -1
- package/dist/crypto.d.ts +8 -5
- package/dist/crypto.js +15 -61
- package/dist/download-sink-node.d.ts +49 -0
- package/dist/download-sink-node.js +204 -0
- package/dist/download-sink.d.ts +0 -48
- package/dist/download-sink.js +6 -199
- package/dist/download.js +5 -4
- package/dist/engine-node.d.ts +19 -0
- package/dist/engine-node.js +77 -0
- package/dist/env-vars.d.ts +20 -0
- package/dist/env-vars.js +27 -0
- package/dist/gate.js +1 -1
- package/dist/help.js +9 -4
- package/dist/host-contract.d.ts +7 -0
- package/dist/host-contract.js +95 -0
- package/dist/host-node.d.ts +27 -0
- package/dist/host-node.js +192 -0
- package/dist/host.d.ts +99 -0
- package/dist/host.js +59 -0
- package/dist/index.d.ts +6 -49
- package/dist/index.js +20 -45
- package/dist/main.js +7 -6
- package/dist/manifest-chunk-cache.d.ts +3 -3
- package/dist/manifest-chunk-cache.js +39 -44
- package/dist/manifest-chunk-flow.js +15 -14
- package/dist/manifest-write.js +1 -1
- package/dist/manifest.d.ts +2 -2
- package/dist/manifest.js +37 -34
- package/dist/mcp-tools/tiers.js +1 -1
- package/dist/network.d.ts +1 -1
- package/dist/network.js +6 -2
- package/dist/platform-sign.d.ts +143 -0
- package/dist/platform-sign.js +220 -0
- package/dist/portable.d.ts +59 -0
- package/dist/portable.js +82 -0
- package/dist/product.d.ts +1 -1
- package/dist/product.js +1 -1
- package/dist/progress-node.d.ts +2 -0
- package/dist/progress-node.js +14 -0
- package/dist/progress.d.ts +0 -1
- package/dist/progress.js +4 -8
- package/dist/redact.js +3 -2
- package/dist/registration.js +5 -2
- package/dist/risk.d.ts +7 -0
- package/dist/risk.js +10 -0
- package/dist/run-log.d.ts +4 -4
- package/dist/run-log.js +29 -36
- package/dist/seal.js +5 -4
- package/dist/server.d.ts +1 -1
- package/dist/server.js +6 -2
- package/dist/setup-questions.js +2 -2
- package/dist/upload-file-node.d.ts +3 -0
- package/dist/upload-file-node.js +38 -0
- package/dist/upload-file.d.ts +2 -2
- package/dist/upload-file.js +15 -45
- package/dist/upload-price-node.d.ts +8 -0
- package/dist/upload-price-node.js +37 -0
- package/dist/upload-price.d.ts +0 -8
- package/dist/upload-price.js +4 -33
- package/dist/upload-steps.js +4 -4
- package/dist/upload-store.d.ts +14 -14
- package/dist/upload-store.js +68 -75
- package/dist/upload-wallet-chain.js +3 -2
- package/dist/upload-wallet-put.js +2 -2
- package/dist/upload-wallet.js +16 -15
- package/dist/upload.js +16 -15
- package/dist/wallet-grant.js +1 -1
- package/dist/wallet-sign-seams.d.ts +12 -0
- package/dist/wallet-sign.js +10 -13
- package/dist/walrus-write.js +3 -2
- package/dist/walrus.d.ts +1 -16
- package/dist/walrus.js +11 -22
- package/docs/commands/credits.md +1 -1
- package/docs/commands/platform.md +15 -0
- package/docs/commands/support.md +1 -1
- package/docs/commands/trash.md +1 -1
- package/docs/commands/wallet.md +11 -7
- package/package.json +13 -2
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// The Node host: the five things `host.ts` names, done the way this tool has always done them.
|
|
2
|
+
//
|
|
3
|
+
// ⛔ THIS FILE IS WHERE THE `node:` IMPORTS WENT. They used to be spread over a dozen modules, and
|
|
4
|
+
// that is what stopped the package running anywhere else. Nothing moved in behaviour: the
|
|
5
|
+
// engine is found on disk the same way, the state is the same files under the same names, and
|
|
6
|
+
// an installation made by an earlier version is read without noticing the change.
|
|
7
|
+
//
|
|
8
|
+
// ⛔ THE FILE NAMES ARE A COMPATIBILITY PROMISE, not a mapping somebody is free to tidy. A kept
|
|
9
|
+
// file list, a chunk of one and an unfinished upload all sit in a home directory that this
|
|
10
|
+
// version did not create. `placeOf` is the whole of that promise, and it is a table so a reader
|
|
11
|
+
// can check it against a real `~/.nmts` in one glance.
|
|
12
|
+
//
|
|
13
|
+
// ⛔ EVERY WRITE IS ASIDE-AND-RENAME, 0600, under a 0700 directory. A rename within one directory
|
|
14
|
+
// is atomic, so a reader meets the old bytes or the new ones and never half of either — which
|
|
15
|
+
// matters most for the one record that says an upload was already paid for.
|
|
16
|
+
//
|
|
17
|
+
// ⚠ NOTHING HERE THROWS ON A FAILED WRITE. Every caller in the package treats missing state as
|
|
18
|
+
// "do the work again", so a full disk, a read-only home directory or a directory owned by
|
|
19
|
+
// somebody else costs a slower command rather than a failed one.
|
|
20
|
+
//
|
|
21
|
+
// ⛔ THE ENGINE AND THE ZSTD ENCODER ARE LOADED WHEN THEY ARE ASKED FOR, not when the host is made.
|
|
22
|
+
// `nmts --help` registers a host and uses none of it, and an agent runs this tool in a loop —
|
|
23
|
+
// `check:cli-startup` measures exactly this and refuses a build that widened it.
|
|
24
|
+
import { chmodSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
25
|
+
import { join, sep } from "node:path";
|
|
26
|
+
import { configDir, modesAreEnforced } from "./credentials.js";
|
|
27
|
+
import { registerHost } from "./host.js";
|
|
28
|
+
/**
|
|
29
|
+
* The file one state key names — the table an earlier installation's layout is read through.
|
|
30
|
+
*
|
|
31
|
+
* Anything not named here is spelled out as it reads: the slashes become directories and the last
|
|
32
|
+
* segment is the file name, which is what `uploads/…` and the host-contract check already are.
|
|
33
|
+
*/
|
|
34
|
+
function placeOf(key) {
|
|
35
|
+
if (key === "runlog")
|
|
36
|
+
return { dir: "", file: "runs.jsonl" };
|
|
37
|
+
if (key === "collision")
|
|
38
|
+
return { dir: "", file: "collision.json" };
|
|
39
|
+
if (key === "autonomy")
|
|
40
|
+
return { dir: "", file: "autonomy.json" };
|
|
41
|
+
if (key === "manifest/state")
|
|
42
|
+
return { dir: "", file: "file-list-state.json" };
|
|
43
|
+
const manifest = after(key, "manifest/");
|
|
44
|
+
if (manifest !== null)
|
|
45
|
+
return { dir: "", file: `file-list-${manifest}.json` };
|
|
46
|
+
const chunk = after(key, "chunks/");
|
|
47
|
+
if (chunk !== null) {
|
|
48
|
+
const cut = chunk.lastIndexOf("/");
|
|
49
|
+
return { dir: join("file-list-chunks", chunk.slice(0, cut)), file: `${chunk.slice(cut + 1)}.ct` };
|
|
50
|
+
}
|
|
51
|
+
const cut = key.lastIndexOf("/");
|
|
52
|
+
return cut < 0 ? { dir: "", file: key } : { dir: key.slice(0, cut), file: key.slice(cut + 1) };
|
|
53
|
+
}
|
|
54
|
+
function after(key, prefix) {
|
|
55
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : null;
|
|
56
|
+
}
|
|
57
|
+
/** The key a file stands for, or null when this tool keeps no key for it. The inverse of `placeOf`. */
|
|
58
|
+
function keyOf(place) {
|
|
59
|
+
if (place.dir === "") {
|
|
60
|
+
if (place.file === "runs.jsonl")
|
|
61
|
+
return "runlog";
|
|
62
|
+
if (place.file === "collision.json")
|
|
63
|
+
return "collision";
|
|
64
|
+
if (place.file === "autonomy.json")
|
|
65
|
+
return "autonomy";
|
|
66
|
+
if (place.file === "file-list-state.json")
|
|
67
|
+
return "manifest/state";
|
|
68
|
+
const listed = place.file.match(/^file-list-(.+)\.json$/);
|
|
69
|
+
return listed?.[1] === undefined ? null : `manifest/${listed[1]}`;
|
|
70
|
+
}
|
|
71
|
+
const parts = place.dir.split(sep);
|
|
72
|
+
if (parts[0] === "file-list-chunks") {
|
|
73
|
+
if (!place.file.endsWith(".ct"))
|
|
74
|
+
return null;
|
|
75
|
+
return ["chunks", ...parts.slice(1), place.file.slice(0, -".ct".length)].join("/");
|
|
76
|
+
}
|
|
77
|
+
return [...parts, place.file].join("/");
|
|
78
|
+
}
|
|
79
|
+
/** Every file under the config directory, as `Place`s. A missing directory is no files. */
|
|
80
|
+
function walk(root, dir, out) {
|
|
81
|
+
let names;
|
|
82
|
+
try {
|
|
83
|
+
names = readdirSync(join(root, dir));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
for (const name of names) {
|
|
89
|
+
const full = join(root, dir, name);
|
|
90
|
+
let isDirectory = false;
|
|
91
|
+
try {
|
|
92
|
+
isDirectory = statSync(full).isDirectory();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (isDirectory)
|
|
98
|
+
walk(root, join(dir, name), out);
|
|
99
|
+
else
|
|
100
|
+
out.push({ dir, file: name });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function atomically(target, bytes) {
|
|
104
|
+
const scratch = `${target}.${process.pid}.tmp`;
|
|
105
|
+
writeFileSync(scratch, bytes, { mode: 0o600 });
|
|
106
|
+
if (modesAreEnforced())
|
|
107
|
+
chmodSync(scratch, 0o600);
|
|
108
|
+
renameSync(scratch, target);
|
|
109
|
+
}
|
|
110
|
+
/** Files under the person's own config directory, named by `placeOf`. */
|
|
111
|
+
export function nodeState() {
|
|
112
|
+
return {
|
|
113
|
+
durable: true,
|
|
114
|
+
async read(key) {
|
|
115
|
+
const place = placeOf(key);
|
|
116
|
+
try {
|
|
117
|
+
return new Uint8Array(readFileSync(join(configDir(), place.dir, place.file)));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
async write(key, bytes) {
|
|
124
|
+
const place = placeOf(key);
|
|
125
|
+
const dir = join(configDir(), place.dir);
|
|
126
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
127
|
+
if (modesAreEnforced())
|
|
128
|
+
chmodSync(configDir(), 0o700);
|
|
129
|
+
atomically(join(dir, place.file), bytes);
|
|
130
|
+
},
|
|
131
|
+
async remove(key) {
|
|
132
|
+
const place = placeOf(key);
|
|
133
|
+
rmSync(join(configDir(), place.dir, place.file), { force: true });
|
|
134
|
+
},
|
|
135
|
+
async keys(prefix) {
|
|
136
|
+
const found = [];
|
|
137
|
+
walk(configDir(), "", found);
|
|
138
|
+
const keys = [];
|
|
139
|
+
for (const place of found) {
|
|
140
|
+
const key = keyOf(place);
|
|
141
|
+
if (key !== null && key.startsWith(prefix))
|
|
142
|
+
keys.push(key);
|
|
143
|
+
}
|
|
144
|
+
return keys;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Where a state key lands on this machine. For tests, and for messages that name a file. */
|
|
149
|
+
export function statePath(key) {
|
|
150
|
+
const place = placeOf(key);
|
|
151
|
+
return join(configDir(), place.dir, place.file);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Every `NMTS_*` variable this process is holding.
|
|
155
|
+
*
|
|
156
|
+
* ⛔ THE ONE PLACE THE WHOLE ENVIRONMENT IS READ. `redact.ts` needs the values to label them out of
|
|
157
|
+
* a report, and a browser has none — so it is a host call rather than a `process.env` walk in a
|
|
158
|
+
* module that has to run in both.
|
|
159
|
+
*/
|
|
160
|
+
export function nodeEnvEntries() {
|
|
161
|
+
const out = [];
|
|
162
|
+
for (const [name, value] of Object.entries(process.env)) {
|
|
163
|
+
if (value !== undefined)
|
|
164
|
+
out.push({ name, value });
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
/** This machine, as the package's host. */
|
|
169
|
+
export function nodeHost() {
|
|
170
|
+
return {
|
|
171
|
+
name: "node",
|
|
172
|
+
engine: { load: async () => (await import("./engine-node.js")).loadEngine() },
|
|
173
|
+
state: nodeState(),
|
|
174
|
+
env: (name) => process.env[name],
|
|
175
|
+
envEntries: nodeEnvEntries,
|
|
176
|
+
// ⛔ STDERR, NOT STDOUT. Progress is not the answer, and a caller redirecting the answer to a
|
|
177
|
+
// file must not find it interleaved with percentages.
|
|
178
|
+
log: (line) => void process.stderr.write(line),
|
|
179
|
+
zstd: { register: async () => (await import("./zstd-node.js")).registerNodeZstd() },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Put this machine in the register: what the two Node doors — `index.ts` and the command's own
|
|
184
|
+
* entry point — call before anything that loads the engine, keeps state or reads the environment.
|
|
185
|
+
*
|
|
186
|
+
* ⛔ THE COMMAND LOADS THIS FILE LATE, not at the top of `main.ts`. The chain below reaches the
|
|
187
|
+
* credentials module, and a fixed cost at the head of `nmts --help` is one an agent running
|
|
188
|
+
* this tool in a loop pays thousands of times (`check:cli-startup`).
|
|
189
|
+
*/
|
|
190
|
+
export function registerNodeHost() {
|
|
191
|
+
registerHost(nodeHost());
|
|
192
|
+
}
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { CryptoGlue } from "./crypto-surface.ts";
|
|
2
|
+
/** Which runtime put the host in the register. Reported; nothing branches on it. */
|
|
3
|
+
export type HostName = "node" | "browser";
|
|
4
|
+
/** Loading the WebAssembly engine that derives this account's keys. */
|
|
5
|
+
export interface EngineHost {
|
|
6
|
+
/**
|
|
7
|
+
* The engine, made once and kept.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ THE SHAPE CHECK IS NOT THE HOST'S. `crypto.ts` runs `isCryptoGlue` over whatever comes
|
|
10
|
+
* back, so both hosts pass the same gate and a build that renamed an export is named at load
|
|
11
|
+
* time whichever runtime met it.
|
|
12
|
+
*/
|
|
13
|
+
load(): Promise<CryptoGlue>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* What this installation keeps between runs, as bytes under a key.
|
|
17
|
+
*
|
|
18
|
+
* ⛔ KEYS ARE `<area>/<name>`, and the area is what a reader of the key can see at a glance:
|
|
19
|
+
* `manifest/…` is a kept file list, `chunks/…` a copy of one of its chunks, `uploads/…` a
|
|
20
|
+
* reservation a half-finished upload can be resumed from, `runlog` this machine's record of
|
|
21
|
+
* what it did, `collision` and `autonomy` the two answers a person gave at setup.
|
|
22
|
+
*
|
|
23
|
+
* ⛔ NOTHING HERE IS A CACHE THE ACCOUNT DEPENDS ON. Every reader treats "no bytes" as "fetch it
|
|
24
|
+
* again", so a host whose store is empty, full or unavailable is a slower program and never a
|
|
25
|
+
* broken one.
|
|
26
|
+
*/
|
|
27
|
+
export interface StateHost {
|
|
28
|
+
/**
|
|
29
|
+
* False when what is written will not survive the page or the process — a browser with no
|
|
30
|
+
* IndexedDB, or one that would not open it. Reported so a caller can say so rather than
|
|
31
|
+
* promising a resumable upload it cannot resume.
|
|
32
|
+
*/
|
|
33
|
+
readonly durable: boolean;
|
|
34
|
+
read(key: string): Promise<Uint8Array | undefined>;
|
|
35
|
+
write(key: string, bytes: Uint8Array): Promise<void>;
|
|
36
|
+
remove(key: string): Promise<void>;
|
|
37
|
+
/** Every key that starts with `prefix`, in no particular order. */
|
|
38
|
+
keys(prefix: string): Promise<string[]>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Filling the file-list codec's zstd register (NCF-3 §6.3.4). Safe to call more than once.
|
|
42
|
+
*
|
|
43
|
+
* ⛔ IT IS AWAITED. Both hosts fetch their encoder rather than carrying it — Node's `zlib` and a
|
|
44
|
+
* WebAssembly build respectively — and a list read that did not wait for it would fall back to
|
|
45
|
+
* gzip for a frame the browser wrote in zstd.
|
|
46
|
+
*/
|
|
47
|
+
export interface ZstdHost {
|
|
48
|
+
register(): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
/** Everything this package needs from the runtime it was loaded into. */
|
|
51
|
+
export interface Host {
|
|
52
|
+
readonly name: HostName;
|
|
53
|
+
readonly engine: EngineHost;
|
|
54
|
+
readonly state: StateHost;
|
|
55
|
+
/**
|
|
56
|
+
* A named value from the environment, or undefined.
|
|
57
|
+
*
|
|
58
|
+
* ⚠ A PAGE HAS NO ENVIRONMENT, so the addresses a variable would have carried — the relay, the
|
|
59
|
+
* Sui endpoint, the aggregators — arrive as options on the client, and the browser host
|
|
60
|
+
* answers those three under the same variable names and undefined to everything else. Asking
|
|
61
|
+
* through here rather than `process.env` is what lets one module do both without knowing which
|
|
62
|
+
* runtime it is in.
|
|
63
|
+
*/
|
|
64
|
+
env(name: string): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Every variable this process is holding, for the one caller that needs the VALUES rather than
|
|
67
|
+
* one of them: `redact.ts` labels them out of a report before it is written down.
|
|
68
|
+
*
|
|
69
|
+
* ⚠ A BROWSER ANSWERS AN EMPTY LIST, which is the truth rather than a stub: a page holds no
|
|
70
|
+
* environment, so there is nothing of that kind to take out of a report.
|
|
71
|
+
*/
|
|
72
|
+
envEntries(): {
|
|
73
|
+
name: string;
|
|
74
|
+
value: string;
|
|
75
|
+
}[];
|
|
76
|
+
/** One line about how far along something is. Where it goes is the host's business. */
|
|
77
|
+
log(line: string): void;
|
|
78
|
+
readonly zstd: ZstdHost;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Put the host for this runtime in the register.
|
|
82
|
+
*
|
|
83
|
+
* Called once, from the entry point a program imported, before anything else runs. Calling it
|
|
84
|
+
* again replaces what is there, which is what a test that swaps a host needs and what nothing else
|
|
85
|
+
* should do.
|
|
86
|
+
*/
|
|
87
|
+
export declare function registerHost(next: Host): void;
|
|
88
|
+
/** Empty the register. For tests that need to see what an unhosted module does. */
|
|
89
|
+
export declare function forgetHost(): void;
|
|
90
|
+
/** True when something has registered one. Lets a caller ask instead of catching. */
|
|
91
|
+
export declare function hostIsRegistered(): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The host for this runtime.
|
|
94
|
+
*
|
|
95
|
+
* ⛔ IT REFUSES RATHER THAN GUESSING. A program that imported `@needmoretruth/nmts-cli/portable`
|
|
96
|
+
* and registered nothing has one thing wrong with it, and naming that is worth more than a
|
|
97
|
+
* default host that half-works and fails somewhere further in.
|
|
98
|
+
*/
|
|
99
|
+
export declare function host(): Host;
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// The five things this package cannot do by itself, in one register — so that everything else in
|
|
2
|
+
// it is code that runs anywhere a `fetch` does.
|
|
3
|
+
//
|
|
4
|
+
// ⛔ A REGISTER, NOT A BRANCH. Nothing below asks which runtime it is in. The entry point that a
|
|
5
|
+
// program imports puts a host in here — `index.ts` registers the Node one, the SDK's `/browser`
|
|
6
|
+
// entry registers a browser one — and every portable module afterwards asks for the one that is
|
|
7
|
+
// there. A `typeof window === "undefined"` test in each of the modules instead would be the
|
|
8
|
+
// same decision made twenty times, and the twenty-first is the one that gets it wrong on a
|
|
9
|
+
// runtime nobody was thinking about.
|
|
10
|
+
//
|
|
11
|
+
// ⛔ FIVE, AND THEY ARE THE WHOLE LIST. Loading the engine, keeping state, reading the
|
|
12
|
+
// environment, saying how far along something is, and having a zstd encoder: those are what a
|
|
13
|
+
// browser cannot do the way Node does. Anything a sixth entry would be asked to carry is a sign
|
|
14
|
+
// that a module is doing something it should be handed instead.
|
|
15
|
+
//
|
|
16
|
+
// ⛔ STATE IS ASYNCHRONOUS. A browser's store is a database with transactions and there is no
|
|
17
|
+
// honest way to read one without waiting. Node's is a file and could have stayed synchronous;
|
|
18
|
+
// making it look synchronous in a browser would take a worker and a shared buffer to fake, and
|
|
19
|
+
// a fake that blocks the page is worse than the `await` it saves.
|
|
20
|
+
//
|
|
21
|
+
// ⛔ NO `node:` IMPORT MAY EVER APPEAR IN THIS FILE, or in anything it reaches. `portable.ts` is
|
|
22
|
+
// the entry that promises it and `test/portable-closure.test.ts` is what holds the promise.
|
|
23
|
+
import { NmtsError } from "./errors.js";
|
|
24
|
+
let registered = null;
|
|
25
|
+
/**
|
|
26
|
+
* Put the host for this runtime in the register.
|
|
27
|
+
*
|
|
28
|
+
* Called once, from the entry point a program imported, before anything else runs. Calling it
|
|
29
|
+
* again replaces what is there, which is what a test that swaps a host needs and what nothing else
|
|
30
|
+
* should do.
|
|
31
|
+
*/
|
|
32
|
+
export function registerHost(next) {
|
|
33
|
+
registered = next;
|
|
34
|
+
}
|
|
35
|
+
/** Empty the register. For tests that need to see what an unhosted module does. */
|
|
36
|
+
export function forgetHost() {
|
|
37
|
+
registered = null;
|
|
38
|
+
}
|
|
39
|
+
/** True when something has registered one. Lets a caller ask instead of catching. */
|
|
40
|
+
export function hostIsRegistered() {
|
|
41
|
+
return registered !== null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The host for this runtime.
|
|
45
|
+
*
|
|
46
|
+
* ⛔ IT REFUSES RATHER THAN GUESSING. A program that imported `@needmoretruth/nmts-cli/portable`
|
|
47
|
+
* and registered nothing has one thing wrong with it, and naming that is worth more than a
|
|
48
|
+
* default host that half-works and fails somewhere further in.
|
|
49
|
+
*/
|
|
50
|
+
export function host() {
|
|
51
|
+
if (registered === null) {
|
|
52
|
+
throw new NmtsError("HOST_MISSING: no runtime host has been registered.", {
|
|
53
|
+
exitCode: 1,
|
|
54
|
+
nextStep: "Import `@needmoretruth/nmts-cli` (which registers the Node host) or the SDK's `/browser` " +
|
|
55
|
+
"entry (which registers a browser one) before calling anything that keeps state.",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return registered;
|
|
59
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,51 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { request, ServerError, DEFAULT_TIMEOUT_MS } from "./api.ts";
|
|
3
|
-
export type { RequestOptions, ServerRefusal } from "./api.ts";
|
|
4
|
-
export { DEFAULT_SERVER, SERVER_ENV_VAR, resolveServer } from "./server.ts";
|
|
5
|
-
export { NETWORKS, NETWORK_ENV_VAR, resolveNetwork } from "./network.ts";
|
|
6
|
-
export type { Network } from "./network.ts";
|
|
1
|
+
export * from "./portable.ts";
|
|
7
2
|
export { API_KEY_ENV_VAR, API_KEY_FILE_ENV_VAR, CODE_ENV_VAR, CODE_FILE_ENV_VAR, configDir, readSecretFile, resolveAccountCode, resolveApiKey, } from "./credentials.ts";
|
|
8
3
|
export type { CredentialSource, ResolvedCode } from "./credentials.ts";
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
13
|
-
export {
|
|
14
|
-
export type { FileList } from "./manifest.ts";
|
|
15
|
-
export { addEntry, applyManyToList, applyToList, planAddition } from "./manifest-write.ts";
|
|
16
|
-
export type { AddEntryInput, AddEntryResult, ListEditInput, ListEditResult } from "./manifest-write.ts";
|
|
17
|
-
export type { Manifest, ManifestEntry } from "./shared/lib/drive/manifest-codec.ts";
|
|
18
|
-
export { buildIndex, entryAt, folderIdFor, fullPathOf, isLive, KIND_FILE, KIND_FOLDER, namesIn, normalisePath, trashedAt, } from "./drive-paths.ts";
|
|
19
|
-
export type { FindOptions, ManifestIndex } from "./drive-paths.ts";
|
|
20
|
-
export { setTrashed } from "./item-trash.ts";
|
|
21
|
-
export { fileSource, partKeysOf, uploadFile } from "./upload-file.ts";
|
|
22
|
-
export type { FileUploadInput, FileUploadStep, PlaintextSource } from "./upload-file.ts";
|
|
23
|
-
export { createUploadApi } from "./upload-api.ts";
|
|
24
|
-
export { CREDIT_BYTES, creditsFor, measureLocal, partSizeFor, planAndPrice, UPLOAD_EPOCHS, } from "./upload-price.ts";
|
|
25
|
-
export { clearItemRecord, clearReservation } from "./upload-store.ts";
|
|
26
|
-
export { UploadError } from "./upload-wire.ts";
|
|
27
|
-
export type { BlobProtocol, UploadApi, UploadResult, UploadStep } from "./upload-wire.ts";
|
|
28
|
-
export type { PaddingRule } from "./shared/lib/crypto/size-padding.ts";
|
|
29
|
-
export { DEFAULT_PART_BYTES } from "./seal.ts";
|
|
30
|
-
export { createBlobProtocol, readCurrentEpoch } from "./walrus-write.ts";
|
|
31
|
-
export { walletPut } from "./upload-wallet-put.ts";
|
|
32
|
-
export type { WalletPutContext, WalletPutFile, WalletPutOutcome, WalletPutReview, WalletPutSeams, } from "./upload-wallet-put.ts";
|
|
33
|
-
export { DEFAULT_UPLOAD_EPOCHS } from "./upload-wallet-plan.ts";
|
|
34
|
-
export type { PartQuote, StorageChoice, UploadBudget, WalletUploadReads } from "./upload-wallet-plan.ts";
|
|
35
|
-
export type { Spend } from "./wallet-grant.ts";
|
|
36
|
-
export { fetchFile, fetchWithKey } from "./download.ts";
|
|
37
|
-
export type { FetchedFile, FetchInput } from "./download.ts";
|
|
38
|
-
export { fileSink } from "./download-sink.ts";
|
|
39
|
-
export type { PlaintextSink } from "./download-sink.ts";
|
|
40
|
-
export { AGGREGATOR_ENV_VAR, readBlob, RELAY_ENV_VAR, SUI_RPC_ENV_VAR } from "./walrus.ts";
|
|
41
|
-
export type { ReadOptions } from "./walrus.ts";
|
|
42
|
-
export { coinAmount, readBalances, walCoinType, walletAddress } from "./wallet.ts";
|
|
43
|
-
export type { ChainReader, CoinBalance, WalletBalances } from "./wallet.ts";
|
|
44
|
-
export { chainReader } from "./wallet-chain.ts";
|
|
45
|
-
export { signerAddress, signExtension, signTransfer } from "./wallet-sign.ts";
|
|
46
|
-
export type { SignTransfer } from "./wallet-sign.ts";
|
|
47
|
-
export { activeWalletOf, walletCountOf, WALLET_INDEX_LIMIT } from "./shared/lib/drive/manifest-settings.ts";
|
|
48
|
-
export { discoverWallets, WALLET_SCAN_GAP } from "./shared/lib/wallet/discover.ts";
|
|
49
|
-
export type { WalletProbe, WalletScan } from "./shared/lib/wallet/discover.ts";
|
|
50
|
-
export { hasHistory } from "./wallet-list-chain.ts";
|
|
51
|
-
export { HOME_URL, PRODUCT_NAME, SOURCE_URL, SUPPORT_EMAIL, VERSION } from "./product.ts";
|
|
4
|
+
export { nodeHost, registerNodeHost, statePath } from "./host-node.ts";
|
|
5
|
+
export { engineDir } from "./engine-node.ts";
|
|
6
|
+
export { fileSource } from "./upload-file-node.ts";
|
|
7
|
+
export { measureLocal } from "./upload-price-node.ts";
|
|
8
|
+
export { fileSink } from "./download-sink-node.ts";
|
package/dist/index.js
CHANGED
|
@@ -10,53 +10,28 @@
|
|
|
10
10
|
// that stops to ask on somebody's server is a library that hangs. A program that wants the
|
|
11
11
|
// NMTS key from the environment reads `credentials.ts` and decides for itself.
|
|
12
12
|
//
|
|
13
|
+
// ⛔ THIS ENTRY POINT IS NODE'S, AND IT SAYS SO BY REGISTERING THE NODE HOST ON THE WAY IN. Every
|
|
14
|
+
// program that imports this package is unchanged by the split: the engine is still found on
|
|
15
|
+
// disk, state is still the same files under the same names, the environment is still read. What
|
|
16
|
+
// is new is that the same modules also run in a page, through `portable.ts` and a host
|
|
17
|
+
// registered there.
|
|
18
|
+
//
|
|
13
19
|
// ⚠ THE PACKAGE'S `exports` MAP NAMES THIS FILE AND A FEW OF THE MODULES BELOW BY SUBPATH. A
|
|
14
20
|
// module not named there is reachable through this file only; adding a subpath is a promise
|
|
15
21
|
// that its shape stays put, so it is done on purpose and one at a time.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
import { registerNodeHost } from "./host-node.js";
|
|
23
|
+
// ⛔ BEFORE ANYTHING BELOW CAN BE CALLED, AND THAT IS WHY IT IS A STATEMENT RATHER THAN SOMETHING A
|
|
24
|
+
// CALLER DOES. A module's imports run before its body, so by the time a program holds anything
|
|
25
|
+
// from this file the host is in place — and no existing caller had to learn that there is one.
|
|
26
|
+
registerNodeHost();
|
|
27
|
+
// Everything that runs anywhere, with the reasons: `portable.ts`.
|
|
28
|
+
export * from "./portable.js";
|
|
22
29
|
// Where a credential can come from on this machine. Reading is here; ASKING is not.
|
|
23
30
|
export { API_KEY_ENV_VAR, API_KEY_FILE_ENV_VAR, CODE_ENV_VAR, CODE_FILE_ENV_VAR, configDir, readSecretFile, resolveAccountCode, resolveApiKey, } from "./credentials.js";
|
|
24
|
-
//
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
27
|
-
//
|
|
28
|
-
export {
|
|
29
|
-
export {
|
|
30
|
-
export {
|
|
31
|
-
export { setTrashed } from "./item-trash.js";
|
|
32
|
-
// Uploading: sealing here, buying storage with credits, pushing the sealed bytes.
|
|
33
|
-
export { fileSource, partKeysOf, uploadFile } from "./upload-file.js";
|
|
34
|
-
export { createUploadApi } from "./upload-api.js";
|
|
35
|
-
export { CREDIT_BYTES, creditsFor, measureLocal, partSizeFor, planAndPrice, UPLOAD_EPOCHS, } from "./upload-price.js";
|
|
36
|
-
export { clearItemRecord, clearReservation } from "./upload-store.js";
|
|
37
|
-
export { UploadError } from "./upload-wire.js";
|
|
38
|
-
export { DEFAULT_PART_BYTES } from "./seal.js";
|
|
39
|
-
export { createBlobProtocol, readCurrentEpoch } from "./walrus-write.js";
|
|
40
|
-
// Uploading paid by the account's OWN WALLET instead of credits: the same seal-buy-push-record
|
|
41
|
-
// path, priced and refused before anything is signed. `nmts put --pay wallet` is this plus a
|
|
42
|
-
// terminal; the spending ledger and the standing gift are the command's and are not here.
|
|
43
|
-
export { walletPut } from "./upload-wallet-put.js";
|
|
44
|
-
export { DEFAULT_UPLOAD_EPOCHS } from "./upload-wallet-plan.js";
|
|
45
|
-
// Downloading: fetching sealed parts from the storage network and opening them here.
|
|
46
|
-
export { fetchFile, fetchWithKey } from "./download.js";
|
|
47
|
-
export { fileSink } from "./download-sink.js";
|
|
48
|
-
export { AGGREGATOR_ENV_VAR, readBlob, RELAY_ENV_VAR, SUI_RPC_ENV_VAR } from "./walrus.js";
|
|
49
|
-
// The wallet the NMTS key derives: reading it, and signing with it.
|
|
50
|
-
export { coinAmount, readBalances, walCoinType, walletAddress } from "./wallet.js";
|
|
51
|
-
export { chainReader } from "./wallet-chain.js";
|
|
52
|
-
export { signerAddress, signExtension, signTransfer } from "./wallet-sign.js";
|
|
53
|
-
// WHICH of this key's wallets, and which of them have been used. One key derives a wallet at every
|
|
54
|
-
// index (NCF-3 §1.3), so the account's own number is read out of the sealed list and the rest is a
|
|
55
|
-
// walk. Both are here so that a library built on this package finds the SAME wallets under the same
|
|
56
|
-
// numbers as `nmts wallet list` — a second walk, or a second reading of the setting, would be a
|
|
57
|
-
// second answer to a question that has to have exactly one.
|
|
58
|
-
export { activeWalletOf, walletCountOf, WALLET_INDEX_LIMIT } from "./shared/lib/drive/manifest-settings.js";
|
|
59
|
-
export { discoverWallets, WALLET_SCAN_GAP } from "./shared/lib/wallet/discover.js";
|
|
60
|
-
export { hasHistory } from "./wallet-list-chain.js";
|
|
61
|
-
// What this package is.
|
|
62
|
-
export { HOME_URL, PRODUCT_NAME, SOURCE_URL, SUPPORT_EMAIL, VERSION } from "./product.js";
|
|
31
|
+
// This machine as a host: the engine on disk, state in the config directory, the real environment.
|
|
32
|
+
export { nodeHost, registerNodeHost, statePath } from "./host-node.js";
|
|
33
|
+
export { engineDir } from "./engine-node.js";
|
|
34
|
+
// Bytes in and out of the places only Node has: a file on a disk, and this process's own stdout.
|
|
35
|
+
export { fileSource } from "./upload-file-node.js";
|
|
36
|
+
export { measureLocal } from "./upload-price-node.js";
|
|
37
|
+
export { fileSink } from "./download-sink-node.js";
|
package/dist/main.js
CHANGED
|
@@ -40,6 +40,7 @@ import { BINARY_NAME, VERSION } from "./product.js";
|
|
|
40
40
|
*/
|
|
41
41
|
export const NOT_BUILT_YET = [];
|
|
42
42
|
export async function run(argv) {
|
|
43
|
+
(await import("./host-node.js")).registerNodeHost();
|
|
43
44
|
const args = parseArgs(argv);
|
|
44
45
|
if (args.version) {
|
|
45
46
|
process.stdout.write(`${VERSION}\n`);
|
|
@@ -54,7 +55,7 @@ export async function run(argv) {
|
|
|
54
55
|
// ⚠ `mode` itself is exempt: the command that prints the setting does not need it twice.
|
|
55
56
|
if (args.command !== "mode") {
|
|
56
57
|
const { announcement, currentMode } = await import("./autonomy.js");
|
|
57
|
-
const line = announcement(currentMode());
|
|
58
|
+
const line = announcement(await currentMode());
|
|
58
59
|
if (line !== null)
|
|
59
60
|
process.stderr.write(`${line}\n`);
|
|
60
61
|
}
|
|
@@ -76,10 +77,8 @@ export async function run(argv) {
|
|
|
76
77
|
env: args.env,
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
|
-
case "logout":
|
|
80
|
-
|
|
81
|
-
return logout();
|
|
82
|
-
}
|
|
80
|
+
case "logout":
|
|
81
|
+
return (await import("./commands/logout.js")).logout();
|
|
83
82
|
case "whoami": {
|
|
84
83
|
const { whoami } = await import("./commands/whoami.js");
|
|
85
84
|
return await whoami({
|
|
@@ -352,6 +351,8 @@ export async function run(argv) {
|
|
|
352
351
|
const { mcp } = await import("./commands/mcp.js");
|
|
353
352
|
return await mcp({ server: args.server, network: args.network, out: args.out });
|
|
354
353
|
}
|
|
354
|
+
case "platform":
|
|
355
|
+
return (await import("./commands/platform.js")).platform(args.operands[0], { out: args.out });
|
|
355
356
|
case "s3": {
|
|
356
357
|
const { s3 } = await import("./commands/s3.js");
|
|
357
358
|
return await s3({ server: args.server, network: args.network, port: args.port, json: args.json });
|
|
@@ -385,7 +386,7 @@ async function main() {
|
|
|
385
386
|
const { noteFailure, recordRun } = await import("./run-log.js");
|
|
386
387
|
if (failed !== null)
|
|
387
388
|
noteFailure(failed);
|
|
388
|
-
recordRun(argv, typeof process.exitCode === "number" ? process.exitCode : 0, Date.now() - started);
|
|
389
|
+
await recordRun(argv, typeof process.exitCode === "number" ? process.exitCode : 0, Date.now() - started);
|
|
389
390
|
await noteUpdateAfter(argv, VERSION);
|
|
390
391
|
}
|
|
391
392
|
if (invokedDirectly(import.meta.filename)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The sealed bytes this machine holds under that name, or null when it holds none. */
|
|
2
|
-
export declare function readCachedChunk(accountId: string, hash: string): string | null
|
|
2
|
+
export declare function readCachedChunk(accountId: string, hash: string): Promise<string | null>;
|
|
3
3
|
/** Keep these sealed bytes under that name. Silent when the machine will not take them. */
|
|
4
|
-
export declare function writeCachedChunk(accountId: string, hash: string, ct: string): void
|
|
4
|
+
export declare function writeCachedChunk(accountId: string, hash: string, ct: string): Promise<void>;
|
|
5
5
|
/**
|
|
6
6
|
* Drop every copy this account holds that the given list does not name.
|
|
7
7
|
*
|
|
@@ -11,4 +11,4 @@ export declare function writeCachedChunk(accountId: string, hash: string, ct: st
|
|
|
11
11
|
* abandoned. Called after every complete read and every successful write, which is exactly when
|
|
12
12
|
* "what the list names" is known.
|
|
13
13
|
*/
|
|
14
|
-
export declare function pruneChunkCache(accountId: string, keep: ReadonlySet<string>): void
|
|
14
|
+
export declare function pruneChunkCache(accountId: string, keep: ReadonlySet<string>): Promise<void>;
|