@needmoretruth/nmts-cli 0.35.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/AGENTS.md +11 -12
  2. package/CHANGELOG.md +26 -0
  3. package/README.ko.md +19 -21
  4. package/README.md +21 -22
  5. package/dist/account.js +2 -1
  6. package/dist/arg-options.d.ts +3 -1
  7. package/dist/artifact-about.d.ts +1 -1
  8. package/dist/autonomy.d.ts +3 -10
  9. package/dist/autonomy.js +15 -17
  10. package/dist/bytes.d.ts +15 -0
  11. package/dist/bytes.js +63 -0
  12. package/dist/collision.d.ts +5 -5
  13. package/dist/collision.js +19 -22
  14. package/dist/commands/delete-account.js +1 -1
  15. package/dist/commands/erase.js +1 -1
  16. package/dist/commands/get.js +1 -1
  17. package/dist/commands/listfile.js +1 -1
  18. package/dist/commands/mcp.js +5 -1
  19. package/dist/commands/mode.js +2 -2
  20. package/dist/commands/on-collision.d.ts +1 -1
  21. package/dist/commands/on-collision.js +3 -3
  22. package/dist/commands/platform.d.ts +9 -0
  23. package/dist/commands/platform.js +124 -0
  24. package/dist/commands/pull.js +1 -1
  25. package/dist/commands/push-wallet.js +2 -1
  26. package/dist/commands/push.js +6 -4
  27. package/dist/commands/put-wallet.js +5 -3
  28. package/dist/commands/put.js +8 -5
  29. package/dist/commands/receive.js +1 -1
  30. package/dist/commands/settings.js +1 -1
  31. package/dist/commands/support.js +3 -3
  32. package/dist/commands/unlock.js +2 -2
  33. package/dist/commands/wallet-hall.d.ts +4 -1
  34. package/dist/commands/wallet-hall.js +16 -8
  35. package/dist/commands/wallet-storage-ops.d.ts +4 -1
  36. package/dist/commands/wallet-storage-ops.js +7 -2
  37. package/dist/commands/wallet-swap.d.ts +3 -0
  38. package/dist/commands/wallet-swap.js +8 -4
  39. package/dist/commands/wallet.d.ts +1 -1
  40. package/dist/crypto.d.ts +8 -5
  41. package/dist/crypto.js +15 -61
  42. package/dist/download-sink-node.d.ts +49 -0
  43. package/dist/download-sink-node.js +204 -0
  44. package/dist/download-sink.d.ts +0 -48
  45. package/dist/download-sink.js +6 -199
  46. package/dist/download.js +5 -4
  47. package/dist/engine-node.d.ts +19 -0
  48. package/dist/engine-node.js +77 -0
  49. package/dist/env-vars.d.ts +20 -0
  50. package/dist/env-vars.js +27 -0
  51. package/dist/gate.js +1 -1
  52. package/dist/help.js +9 -4
  53. package/dist/host-contract.d.ts +7 -0
  54. package/dist/host-contract.js +95 -0
  55. package/dist/host-node.d.ts +27 -0
  56. package/dist/host-node.js +192 -0
  57. package/dist/host.d.ts +99 -0
  58. package/dist/host.js +59 -0
  59. package/dist/index.d.ts +6 -49
  60. package/dist/index.js +20 -45
  61. package/dist/main.js +7 -6
  62. package/dist/manifest-chunk-cache.d.ts +3 -3
  63. package/dist/manifest-chunk-cache.js +39 -44
  64. package/dist/manifest-chunk-flow.js +15 -14
  65. package/dist/manifest-write.js +1 -1
  66. package/dist/manifest.d.ts +2 -2
  67. package/dist/manifest.js +37 -34
  68. package/dist/mcp-tools/tiers.js +1 -1
  69. package/dist/network.d.ts +1 -1
  70. package/dist/network.js +6 -2
  71. package/dist/platform-sign.d.ts +143 -0
  72. package/dist/platform-sign.js +220 -0
  73. package/dist/portable.d.ts +59 -0
  74. package/dist/portable.js +82 -0
  75. package/dist/product.d.ts +1 -1
  76. package/dist/product.js +1 -1
  77. package/dist/progress-node.d.ts +2 -0
  78. package/dist/progress-node.js +14 -0
  79. package/dist/progress.d.ts +0 -1
  80. package/dist/progress.js +4 -8
  81. package/dist/redact.js +3 -2
  82. package/dist/registration.js +5 -2
  83. package/dist/risk.d.ts +7 -0
  84. package/dist/risk.js +10 -0
  85. package/dist/run-log.d.ts +4 -4
  86. package/dist/run-log.js +29 -36
  87. package/dist/seal.js +5 -4
  88. package/dist/server.d.ts +1 -1
  89. package/dist/server.js +6 -2
  90. package/dist/setup-questions.js +2 -2
  91. package/dist/upload-file-node.d.ts +3 -0
  92. package/dist/upload-file-node.js +38 -0
  93. package/dist/upload-file.d.ts +2 -2
  94. package/dist/upload-file.js +15 -45
  95. package/dist/upload-price-node.d.ts +8 -0
  96. package/dist/upload-price-node.js +37 -0
  97. package/dist/upload-price.d.ts +0 -8
  98. package/dist/upload-price.js +4 -33
  99. package/dist/upload-steps.js +3 -3
  100. package/dist/upload-store.d.ts +14 -14
  101. package/dist/upload-store.js +68 -75
  102. package/dist/upload-wallet-chain.js +3 -2
  103. package/dist/upload-wallet-put.js +2 -2
  104. package/dist/upload-wallet.js +16 -15
  105. package/dist/upload.js +16 -15
  106. package/dist/wallet-sign-seams.d.ts +12 -0
  107. package/dist/wallet-sign.js +10 -13
  108. package/dist/walrus-write.js +3 -2
  109. package/dist/walrus.d.ts +1 -16
  110. package/dist/walrus.js +11 -22
  111. package/docs/commands/credits.md +1 -1
  112. package/docs/commands/platform.md +14 -0
  113. package/docs/commands/support.md +1 -1
  114. package/docs/commands/trash.md +1 -1
  115. package/docs/commands/wallet.md +11 -7
  116. 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 { NmtsError, NotLoggedInError, renderError } from "./errors.ts";
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 { assertUsableCode, identityOf } from "./account.ts";
10
- export type { AccountIdentity } from "./account.ts";
11
- export { AAD, DERIVED, loadCrypto } from "./crypto.ts";
12
- export type { CryptoGlue } from "./crypto.ts";
13
- export { readFileList } from "./manifest.ts";
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
- // Errors the one shape every failure below arrives in.
17
- export { NmtsError, NotLoggedInError, renderError } from "./errors.js";
18
- // The server: one request function, and what a refusal looks like.
19
- export { request, ServerError, DEFAULT_TIMEOUT_MS } from "./api.js";
20
- export { DEFAULT_SERVER, SERVER_ENV_VAR, resolveServer } from "./server.js";
21
- export { NETWORKS, NETWORK_ENV_VAR, resolveNetwork } from "./network.js";
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
- // The account: what a code derives, and the engine that derives it.
25
- export { assertUsableCode, identityOf } from "./account.js";
26
- export { AAD, DERIVED, loadCrypto } from "./crypto.js";
27
- // The sealed file list: reading it, editing it, and walking it by path.
28
- export { readFileList } from "./manifest.js";
29
- export { addEntry, applyManyToList, applyToList, planAddition } from "./manifest-write.js";
30
- export { buildIndex, entryAt, folderIdFor, fullPathOf, isLive, KIND_FILE, KIND_FOLDER, namesIn, normalisePath, trashedAt, } from "./drive-paths.js";
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
- const { logout } = await import("./commands/logout.js");
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>;