@arcadiasystems/morse-cli 0.5.0 → 0.6.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.
- package/CHANGELOG.md +66 -0
- package/README.md +42 -15
- package/dist/index.js +72 -25
- package/docs/QUICKSTART.md +11 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,72 @@ All notable changes to `@arcadiasystems/morse-cli` are documented here. The
|
|
|
4
4
|
format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.6.1] - 2026-09-09
|
|
8
|
+
|
|
9
|
+
Picks up `@arcadiasystems/morse-sdk@0.6.0` and fixes a download regression it exposed. Upgrade if you use mainnet at all.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`file download` worked on no mainnet file, public or not.** `buildFileDownloadContext` constructed the Seal adapter eagerly, so once SDK 0.6.0 stopped pinning mainnet key servers, every download failed at context build with a Seal error before the file was even looked up. Downloading a public file never touches Seal, so the adapter is now built on first use and memoized, matching the lazy-signer pattern the same context already used. Regression covered in `test/program.test.ts` against the real context builder.
|
|
14
|
+
- **Encrypted commands now explain themselves on mainnet.** They previously surfaced the SDK's `ConfigurationError`, whose advice ("pass seal.serverConfigs", "start from MAINNET_SEAL_COMMITTEE") names an API the CLI does not expose: there is no flag or env var for key servers, so on mainnet these commands cannot work at all. `entry add-encrypted`, `entry decrypt`, `file upload --encrypt` / `--recipient`, and file decryption now exit 2 with a message that says that and points at testnet or the SDK.
|
|
15
|
+
- **`file upload --public` is not caught by that guard.** It shares the encrypt context but never encrypts, so `EncryptContext.seal` became a lazy factory too. Building the adapter eagerly there would have blocked public mainnet uploads the same way the eager build blocked public downloads. Both the refusal and the exemption are covered in `test/program.test.ts`.
|
|
16
|
+
- **`file download` refused every public file, on every network.** The check asked whether the file had recipients, on the assumption that "a public file has no recipients". The Move contract auto-includes the owner on creation, so `members` is non-empty for every file that exists, public or not: a public file and an encrypted one both report exactly one member. The result was that `file upload --public` produced a file the CLI would not download, telling the user their plaintext was "unusable ciphertext" and pointing at `--raw`, which then wrote the correct bytes anyway.
|
|
17
|
+
|
|
18
|
+
Encryption is now determined by the Seal id prefix via the new `getRecipientFileSealPrefix` in SDK 0.7.0, which is the only reliable signal. This bug predates the mainnet work and shipped in 0.6.0 and earlier.
|
|
19
|
+
|
|
20
|
+
It escaped the tests because the download fixture defaulted to `members: []`, modelling an object the contract cannot produce. The fixture now carries a realistic owner-member, and a regression test covers a public file that has members.
|
|
21
|
+
- Dependency moves to `@arcadiasystems/morse-sdk@^0.7.0`. The published 0.6.0 declared `^0.5.0`, which resolves to the SDK release whose mainnet Seal default could produce permanently unreadable ciphertext.
|
|
22
|
+
|
|
23
|
+
### Known gaps
|
|
24
|
+
|
|
25
|
+
- `file list` still needs `--indexer-url` on every network; public Sui fullnodes have retired the JSON-RPC event query it walks.
|
|
26
|
+
- Encrypted mainnet stays blocked until Seal operator credentials are available and the CLI grows a way to pass them.
|
|
27
|
+
|
|
28
|
+
## [0.6.0] - 2026-09-09
|
|
29
|
+
|
|
30
|
+
Mainnet support, consuming `@arcadiasystems/morse-sdk@0.5.0`. The Move contracts
|
|
31
|
+
went live on Sui mainnet on 2026-09-09; the CLI rejected `mainnet` outright until
|
|
32
|
+
now.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`--network mainnet` works**, as do `MORSE_NETWORK=mainnet` and
|
|
37
|
+
`morse config add <name> --network mainnet`. `coerceNetwork` no longer
|
|
38
|
+
special-cases mainnet, and `localnet` is accepted too (it still fails at
|
|
39
|
+
startup unless you point the SDK at your own package and registry, which is
|
|
40
|
+
`morseConfig`'s error to raise, not the flag parser's).
|
|
41
|
+
- **Commands that spend gas now name the network they spent on.** The resolved
|
|
42
|
+
network appears as the first detail line under the result headline, and as a
|
|
43
|
+
`network` field in `--json` output. Applied on the write contexts only, so read
|
|
44
|
+
and list output is byte-for-byte unchanged. Nothing selects mainnet implicitly
|
|
45
|
+
(with no flag, env var, or profile the CLI targets testnet), so this is there
|
|
46
|
+
to make a mainnet spend visible after the fact rather than to gate it.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Status banner claimed v0.3.0, two releases stale, and said mainnet support was
|
|
51
|
+
pending.
|
|
52
|
+
- `--network` help text listed only testnet and localnet, in both the global
|
|
53
|
+
flag (`morse --help`) and `config add --network`, as did the `MORSE_NETWORK`
|
|
54
|
+
row of the README env table.
|
|
55
|
+
- Seven README links pointed into `examples/`, which is not in the package
|
|
56
|
+
`files` array, so they 404'd on the npm page. Now absolute GitHub URLs;
|
|
57
|
+
`docs/QUICKSTART.md` stays relative because `docs` does ship.
|
|
58
|
+
- Dead Walrus faucet link in `docs/QUICKSTART.md`. `docs.walrus.site` no longer
|
|
59
|
+
resolves; WAL acquisition now points at the working stake-wal.wal.app swap,
|
|
60
|
+
matching the README and morse-sdk.
|
|
61
|
+
- Known limitations now record that `file list` requires `--indexer-url`, since
|
|
62
|
+
public Sui fullnodes have retired the JSON-RPC event query it walks. This
|
|
63
|
+
affects testnet as well as mainnet and predates this release.
|
|
64
|
+
|
|
65
|
+
### Known gaps
|
|
66
|
+
|
|
67
|
+
- `file list` is unusable without `--indexer-url` on every network, per above.
|
|
68
|
+
Porting the event source off `suix_queryEvents` is tracked separately.
|
|
69
|
+
- No mainnet run of the opt-in `bun run test:e2e` lifecycle; it stays pointed at
|
|
70
|
+
testnet, where it costs nothing real.
|
|
71
|
+
- The Move contracts remain unaudited.
|
|
72
|
+
|
|
7
73
|
## [0.5.0] - 2026-06-09
|
|
8
74
|
|
|
9
75
|
Hardening pass from CLI testing: fail fast before paying for Walrus storage,
|
package/README.md
CHANGED
|
@@ -6,16 +6,36 @@ content entries from your terminal, signing with a locally encrypted key.
|
|
|
6
6
|
Content is stored on [Walrus](https://walrus.xyz); private entries are encrypted
|
|
7
7
|
with [Seal](https://github.com/MystenLabs/seal).
|
|
8
8
|
|
|
9
|
-
> Status: v0.
|
|
10
|
-
>
|
|
9
|
+
> Status: v0.6.1. Mainnet and testnet are both supported for public content;
|
|
10
|
+
> the command surface is stable.
|
|
11
|
+
>
|
|
12
|
+
> **Encrypted commands are testnet-only.** `entry add-encrypted`, `entry
|
|
13
|
+
> decrypt`, `file upload --encrypt` (and `--recipient`, which implies it), and
|
|
14
|
+
> decrypting a downloaded file all need Seal key servers. Testnet has an open
|
|
15
|
+
> set; mainnet does not, because every mainnet Seal operator is commercial, and
|
|
16
|
+
> the CLI has no flag or env var for supplying one. These commands exit 2 on
|
|
17
|
+
> mainnet with an explanation.
|
|
18
|
+
>
|
|
19
|
+
> Everything that does not encrypt works on both networks, including
|
|
20
|
+
> `file upload --public`, `file download` of a public file, and the whole
|
|
21
|
+
> publication, collection and entry surface. All of that is verified against
|
|
22
|
+
> live mainnet, including a public upload and download round trip.
|
|
23
|
+
>
|
|
24
|
+
> **The Move contracts are unaudited.** On mainnet, gas and Walrus storage cost
|
|
25
|
+
> real SUI and WAL, there is no faucet, and deletions are irreversible. Nothing
|
|
26
|
+
> selects mainnet for you: with no `--network`, `MORSE_NETWORK`, or profile
|
|
27
|
+
> setting, the CLI targets testnet. Every command that spends gas prints the
|
|
28
|
+
> network it used, and `--json` output carries a `network` field.
|
|
11
29
|
|
|
12
30
|
## Requirements
|
|
13
31
|
|
|
14
32
|
- [Node.js](https://nodejs.org) >= 18, or [Bun](https://bun.sh) >= 1.2. The
|
|
15
33
|
published CLI runs under either.
|
|
16
|
-
- A funded Sui
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
- A funded Sui address for gas, and WAL for Walrus storage when adding content.
|
|
35
|
+
On testnet, get SUI from the [Sui faucet](https://faucet.sui.io/) and swap some
|
|
36
|
+
for WAL at [stake-wal.wal.app](https://stake-wal.wal.app/?network=testnet). On
|
|
37
|
+
mainnet there is no faucet: both have to be acquired, and every write spends
|
|
38
|
+
real value.
|
|
19
39
|
|
|
20
40
|
## Install
|
|
21
41
|
|
|
@@ -91,7 +111,7 @@ Environment variables (override the config file, overridden by flags):
|
|
|
91
111
|
| Variable | Overrides | Notes |
|
|
92
112
|
| --- | --- | --- |
|
|
93
113
|
| `MORSE_PROFILE` | `--profile` | Profile to use. |
|
|
94
|
-
| `MORSE_NETWORK` | `--network` | `testnet
|
|
114
|
+
| `MORSE_NETWORK` | `--network` | `mainnet`, `testnet`, or `localnet`. |
|
|
95
115
|
| `MORSE_RPC_URL` | `--rpc` | Sui RPC URL override. |
|
|
96
116
|
| `MORSE_ADDRESS` | (no flag) | Active account address, selecting which keystore to use. |
|
|
97
117
|
| `MORSE_PUBLICATION` | `-P, --publication` | Active publication id. |
|
|
@@ -123,7 +143,7 @@ Global options apply to every command and must appear before the subcommand
|
|
|
123
143
|
|
|
124
144
|
| Global option | Purpose |
|
|
125
145
|
| --- | --- |
|
|
126
|
-
| `--network <testnet\|localnet>` | Network to target (default: testnet). |
|
|
146
|
+
| `--network <mainnet\|testnet\|localnet>` | Network to target (default: testnet). `localnet` needs a custom deployment. |
|
|
127
147
|
| `-p, --profile <name>` | Config profile to use. |
|
|
128
148
|
| `--rpc <url>` | Override the Sui RPC URL. |
|
|
129
149
|
| `--json` | Machine-readable JSON on stdout. |
|
|
@@ -290,13 +310,13 @@ to fetch the full record per file (one read each) when you need them.
|
|
|
290
310
|
## Examples
|
|
291
311
|
|
|
292
312
|
- [docs/QUICKSTART.md](./docs/QUICKSTART.md): a full, copy-pasteable walkthrough.
|
|
293
|
-
- Runnable shell recipes in [examples/](
|
|
294
|
-
- [`lifecycle.sh`](
|
|
295
|
-
- [`content.sh`](
|
|
296
|
-
- [`encrypt-decrypt.sh`](
|
|
297
|
-
- [`delegation.sh`](
|
|
298
|
-
- [`ci-noninteractive.sh`](
|
|
299
|
-
- [`files.sh`](
|
|
313
|
+
- Runnable shell recipes in [examples/](https://github.com/arcadiasystems/morse-dcms/tree/main/morse-cli/examples):
|
|
314
|
+
- [`lifecycle.sh`](https://github.com/arcadiasystems/morse-dcms/blob/main/morse-cli/examples/lifecycle.sh): create, add an entry, read, revise, tear down.
|
|
315
|
+
- [`content.sh`](https://github.com/arcadiasystems/morse-dcms/blob/main/morse-cli/examples/content.sh): upload an image and a post, publish a revision, fetch content back, get a link, remove a collection.
|
|
316
|
+
- [`encrypt-decrypt.sh`](https://github.com/arcadiasystems/morse-dcms/blob/main/morse-cli/examples/encrypt-decrypt.sh): encrypt with Seal and decrypt back.
|
|
317
|
+
- [`delegation.sh`](https://github.com/arcadiasystems/morse-dcms/blob/main/morse-cli/examples/delegation.sh): issue a PublisherCap to a delegate, then revoke it.
|
|
318
|
+
- [`ci-noninteractive.sh`](https://github.com/arcadiasystems/morse-dcms/blob/main/morse-cli/examples/ci-noninteractive.sh): env-var auth, `--yes`, and `--json` parsing.
|
|
319
|
+
- [`files.sh`](https://github.com/arcadiasystems/morse-dcms/blob/main/morse-cli/examples/files.sh): RecipientFile round-trip (upload an encrypted file, download/decrypt via its share string, manage recipients, plus a public file).
|
|
300
320
|
|
|
301
321
|
## Limitations
|
|
302
322
|
|
|
@@ -319,7 +339,14 @@ to fetch the full record per file (one read each) when you need them.
|
|
|
319
339
|
created but not yet populated through the CLI; `entry add` refuses them.
|
|
320
340
|
- There is no command to query Walrus blob storage expiry; `--epochs` sets the
|
|
321
341
|
lease at upload time, but remaining lease time is not exposed by the SDK.
|
|
322
|
-
-
|
|
342
|
+
- Encrypted commands do not work on mainnet, per the status note above. There
|
|
343
|
+
is no way to supply Seal key servers to the CLI; if you have credentials from
|
|
344
|
+
an operator, use the SDK directly for now.
|
|
345
|
+
- `localnet` is accepted as a network but has no canonical deployment, so it
|
|
346
|
+
fails at startup unless you point the SDK at your own package and registry.
|
|
347
|
+
- `file list` walks a JSON-RPC event query that public Sui fullnodes have
|
|
348
|
+
retired, so it needs `--indexer-url` pointing at a source that serves the same
|
|
349
|
+
query. This affects testnet as well as mainnet.
|
|
323
350
|
|
|
324
351
|
## Publishing
|
|
325
352
|
|
package/dist/index.js
CHANGED
|
@@ -148,7 +148,7 @@ import { Command } from "commander";
|
|
|
148
148
|
// package.json
|
|
149
149
|
var package_default = {
|
|
150
150
|
name: "@arcadiasystems/morse-cli",
|
|
151
|
-
version: "0.
|
|
151
|
+
version: "0.6.1",
|
|
152
152
|
description: "Command-line interface for the Morse decentralized CMS on Sui.",
|
|
153
153
|
license: "MIT",
|
|
154
154
|
type: "module",
|
|
@@ -203,7 +203,7 @@ var package_default = {
|
|
|
203
203
|
prepublishOnly: "bun run check && bun run build"
|
|
204
204
|
},
|
|
205
205
|
dependencies: {
|
|
206
|
-
"@arcadiasystems/morse-sdk": "^0.
|
|
206
|
+
"@arcadiasystems/morse-sdk": "^0.7.0",
|
|
207
207
|
"@mysten/seal": "1.1.3",
|
|
208
208
|
"@mysten/sui": "2.16.2",
|
|
209
209
|
"@mysten/walrus": "1.1.6",
|
|
@@ -219,7 +219,7 @@ var package_default = {
|
|
|
219
219
|
// src/cli/program.ts
|
|
220
220
|
function buildProgram() {
|
|
221
221
|
const program = new Command;
|
|
222
|
-
program.name("morse").description("Command-line interface for the Morse decentralized CMS on Sui.").version(package_default.version, "-V, --version", "Print the version and exit").option("--network <network>", "Sui network: testnet or localnet [env: MORSE_NETWORK]").option("-p, --profile <name>", "Config profile to use [env: MORSE_PROFILE]").option("--rpc <url>", "Override the Sui RPC URL [env: MORSE_RPC_URL]").option("--json", "Output machine-readable JSON on stdout").option("-q, --quiet", "Suppress progress and informational output").option("-y, --yes", "Assume yes for confirmation prompts").option("--debug", "Print stack traces on error").enablePositionalOptions().showHelpAfterError().exitOverride();
|
|
222
|
+
program.name("morse").description("Command-line interface for the Morse decentralized CMS on Sui.").version(package_default.version, "-V, --version", "Print the version and exit").option("--network <network>", "Sui network: mainnet, testnet, or localnet [env: MORSE_NETWORK]").option("-p, --profile <name>", "Config profile to use [env: MORSE_PROFILE]").option("--rpc <url>", "Override the Sui RPC URL [env: MORSE_RPC_URL]").option("--json", "Output machine-readable JSON on stdout").option("-q, --quiet", "Suppress progress and informational output").option("-y, --yes", "Assume yes for confirmation prompts").option("--debug", "Print stack traces on error").enablePositionalOptions().showHelpAfterError().exitOverride();
|
|
223
223
|
return program;
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -316,14 +316,39 @@ class Output {
|
|
|
316
316
|
get isJson() {
|
|
317
317
|
return this.options.json;
|
|
318
318
|
}
|
|
319
|
+
withNetwork(network) {
|
|
320
|
+
return new Output({ ...this.options, network });
|
|
321
|
+
}
|
|
319
322
|
result(human, data) {
|
|
320
323
|
if (this.options.json) {
|
|
321
|
-
this.out(`${toJson(data)}
|
|
324
|
+
this.out(`${toJson(this.stampJson(data))}
|
|
322
325
|
`);
|
|
323
326
|
return;
|
|
324
327
|
}
|
|
325
|
-
this.out(`${human}
|
|
328
|
+
this.out(`${this.stampHuman(human)}
|
|
329
|
+
`);
|
|
330
|
+
}
|
|
331
|
+
stampJson(data) {
|
|
332
|
+
const network = this.options.network;
|
|
333
|
+
if (network === undefined || !isPlainObject(data) || "network" in data) {
|
|
334
|
+
return data;
|
|
335
|
+
}
|
|
336
|
+
return { network, ...data };
|
|
337
|
+
}
|
|
338
|
+
stampHuman(human) {
|
|
339
|
+
const network = this.options.network;
|
|
340
|
+
if (network === undefined) {
|
|
341
|
+
return human;
|
|
342
|
+
}
|
|
343
|
+
const line = ` network: ${network}`;
|
|
344
|
+
const breakAt = human.indexOf(`
|
|
326
345
|
`);
|
|
346
|
+
if (breakAt === -1) {
|
|
347
|
+
return `${human}
|
|
348
|
+
${line}`;
|
|
349
|
+
}
|
|
350
|
+
return `${human.slice(0, breakAt)}
|
|
351
|
+
${line}${human.slice(breakAt)}`;
|
|
327
352
|
}
|
|
328
353
|
info(message) {
|
|
329
354
|
if (this.options.quiet || this.options.json) {
|
|
@@ -369,6 +394,9 @@ function resolveColor(json) {
|
|
|
369
394
|
}
|
|
370
395
|
return Boolean(process.stderr.isTTY);
|
|
371
396
|
}
|
|
397
|
+
function isPlainObject(value) {
|
|
398
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
399
|
+
}
|
|
372
400
|
function createOutput(opts) {
|
|
373
401
|
const json = Boolean(opts.json);
|
|
374
402
|
return new Output({
|
|
@@ -397,13 +425,10 @@ function isNetwork(value) {
|
|
|
397
425
|
return typeof value === "string" && NETWORKS.has(value);
|
|
398
426
|
}
|
|
399
427
|
function coerceNetwork(value) {
|
|
400
|
-
if (value === Network.Mainnet) {
|
|
401
|
-
throw new UsageError("Morse is not yet deployed on mainnet. Use testnet or localnet.");
|
|
402
|
-
}
|
|
403
428
|
if (isNetwork(value)) {
|
|
404
429
|
return value;
|
|
405
430
|
}
|
|
406
|
-
throw new UsageError(`Unknown network "${value}". Use one of: testnet, localnet.`);
|
|
431
|
+
throw new UsageError(`Unknown network "${value}". Use one of: mainnet, testnet, localnet.`);
|
|
407
432
|
}
|
|
408
433
|
function parseConfig(raw, source) {
|
|
409
434
|
if (!isRecord(raw)) {
|
|
@@ -945,6 +970,7 @@ async function buildWriteContext(command) {
|
|
|
945
970
|
const { base, keypair, address } = await buildSignedBase(command);
|
|
946
971
|
return {
|
|
947
972
|
...base,
|
|
973
|
+
output: base.output.withNetwork(base.settings.network),
|
|
948
974
|
adapter: new KeypairAdapter(keypair, base.client),
|
|
949
975
|
address
|
|
950
976
|
};
|
|
@@ -966,12 +992,23 @@ async function buildContentContext(command) {
|
|
|
966
992
|
}
|
|
967
993
|
const adapter = new KeypairAdapter(keypair, base.client);
|
|
968
994
|
const walrus = DefaultWalrusWriteAdapter.fromConfig({ network, suiClient: base.client }, keypair);
|
|
969
|
-
return {
|
|
995
|
+
return {
|
|
996
|
+
...base,
|
|
997
|
+
output: base.output.withNetwork(network),
|
|
998
|
+
adapter,
|
|
999
|
+
address,
|
|
1000
|
+
walrus
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
function assertSealAvailable(config) {
|
|
1004
|
+
if (config.sealKeyServers.length > 0) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
throw new CliError(`Encrypted commands are not available on ${config.network}. Seal key servers there are operated commercially and the CLI cannot yet be pointed at one. Use --network testnet, or drive @arcadiasystems/morse-sdk directly with your own seal.serverConfigs.`, ExitCode.Usage);
|
|
970
1008
|
}
|
|
971
1009
|
async function buildEncryptContext(command) {
|
|
972
1010
|
const ctx = await buildContentContext(command);
|
|
973
|
-
|
|
974
|
-
return { ...ctx, seal };
|
|
1011
|
+
return { ...ctx, seal: lazySeal(ctx) };
|
|
975
1012
|
}
|
|
976
1013
|
function walrusReadAdapter(base, network, viaAggregator) {
|
|
977
1014
|
if (viaAggregator) {
|
|
@@ -999,6 +1036,7 @@ async function buildDecryptContext(command, opts = {}) {
|
|
|
999
1036
|
if (network === "localnet") {
|
|
1000
1037
|
throw new CliError("Seal decryption is not available on localnet. Use testnet or mainnet.", ExitCode.Usage);
|
|
1001
1038
|
}
|
|
1039
|
+
assertSealAvailable(base.config);
|
|
1002
1040
|
const seal = DefaultSealAdapter.fromMorseConfig(base.config, {}, base.client);
|
|
1003
1041
|
return {
|
|
1004
1042
|
...base,
|
|
@@ -1020,10 +1058,18 @@ async function buildFileDownloadContext(command, opts = {}) {
|
|
|
1020
1058
|
packageId: base.config.packageId
|
|
1021
1059
|
}),
|
|
1022
1060
|
walrusRead: walrusReadAdapter(base, network, Boolean(opts.viaAggregator)),
|
|
1023
|
-
seal:
|
|
1061
|
+
seal: lazySeal(base),
|
|
1024
1062
|
unlockSigner: () => resolveSigner(base.settings.account, process.env, base.signal)
|
|
1025
1063
|
};
|
|
1026
1064
|
}
|
|
1065
|
+
function lazySeal(base) {
|
|
1066
|
+
let adapter;
|
|
1067
|
+
return () => {
|
|
1068
|
+
assertSealAvailable(base.config);
|
|
1069
|
+
adapter ??= DefaultSealAdapter.fromMorseConfig(base.config, {}, base.client);
|
|
1070
|
+
return adapter;
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1027
1073
|
async function buildFileListContext(command, opts = {}) {
|
|
1028
1074
|
const base = await buildReadContext(command);
|
|
1029
1075
|
const originPackageId = base.config.recipientFileEventOriginPackageId;
|
|
@@ -1536,7 +1582,7 @@ function registerConfigCommands(program) {
|
|
|
1536
1582
|
config.command("list").description("List profiles and show the default").action(async (_options, command) => {
|
|
1537
1583
|
await runConfigList(outputFor(command));
|
|
1538
1584
|
});
|
|
1539
|
-
config.command("add <name>").description("Create or update a profile").requiredOption("--network <network>", "Sui network: testnet or localnet").option("--rpc <url>", "RPC URL override for this profile").action(async (name, options, command) => {
|
|
1585
|
+
config.command("add <name>").description("Create or update a profile").requiredOption("--network <network>", "Sui network: mainnet, testnet, or localnet").option("--rpc <url>", "RPC URL override for this profile").action(async (name, options, command) => {
|
|
1540
1586
|
await runConfigAdd(outputFor(command), name, options);
|
|
1541
1587
|
});
|
|
1542
1588
|
config.command("use <name>").description("Set the default profile").action(async (name, _options, command) => {
|
|
@@ -1649,7 +1695,7 @@ async function runEntryAddEncrypted(ctx, name, options) {
|
|
|
1649
1695
|
ctx.output.info(`Encrypting and uploading ${plaintext.length} bytes...`);
|
|
1650
1696
|
const result = await addEncryptedEntryFromBytes(ctx.adapter, ctx.config, {
|
|
1651
1697
|
walrus: ctx.walrus,
|
|
1652
|
-
seal: ctx.seal,
|
|
1698
|
+
seal: ctx.seal(),
|
|
1653
1699
|
publicationId: id,
|
|
1654
1700
|
publisherCapId,
|
|
1655
1701
|
collectionName: collection,
|
|
@@ -2117,7 +2163,7 @@ async function runFileUpload(ctx, path, options) {
|
|
|
2117
2163
|
const upload = { epochs, deletable: true };
|
|
2118
2164
|
const onProgress = uploadProgress(ctx.output);
|
|
2119
2165
|
if (options.public) {
|
|
2120
|
-
const
|
|
2166
|
+
const result = await uploadRecipientFileFromBytes(ctx.adapter, ctx.config, {
|
|
2121
2167
|
walrus: ctx.walrus,
|
|
2122
2168
|
bytes,
|
|
2123
2169
|
recipients: [],
|
|
@@ -2128,15 +2174,15 @@ async function runFileUpload(ctx, path, options) {
|
|
|
2128
2174
|
onProgress
|
|
2129
2175
|
});
|
|
2130
2176
|
const aggregator = ctx.config.walrusEndpoints.aggregator;
|
|
2131
|
-
const viewUrl = aggregator.length > 0 ? `${aggregator}/v1/blobs/${
|
|
2132
|
-
const
|
|
2177
|
+
const viewUrl = aggregator.length > 0 ? `${aggregator}/v1/blobs/${result.blobId}` : undefined;
|
|
2178
|
+
const human = viewUrl === undefined ? `Uploaded public file ${result.fileId}. (tx: ${result.digest})` : `Uploaded public file ${result.fileId}. (tx: ${result.digest})
|
|
2133
2179
|
view: ${viewUrl}`;
|
|
2134
|
-
ctx.output.result(
|
|
2180
|
+
ctx.output.result(human, { ...result, viewUrl: viewUrl ?? null });
|
|
2135
2181
|
return;
|
|
2136
2182
|
}
|
|
2137
2183
|
const result = await uploadEncryptedRecipientFileFromBytes(ctx.adapter, ctx.config, {
|
|
2138
2184
|
walrus: ctx.walrus,
|
|
2139
|
-
seal: ctx.seal,
|
|
2185
|
+
seal: ctx.seal(),
|
|
2140
2186
|
plaintext: bytes,
|
|
2141
2187
|
recipients,
|
|
2142
2188
|
name: options.name,
|
|
@@ -2206,7 +2252,7 @@ async function decryptRecipientFile(ctx, fileId, ciphertext, prefix, nonce) {
|
|
|
2206
2252
|
signer: keypair,
|
|
2207
2253
|
suiClient: ctx.client
|
|
2208
2254
|
});
|
|
2209
|
-
return ctx.seal.decryptUnderRecipientFile(ciphertext, {
|
|
2255
|
+
return ctx.seal().decryptUnderRecipientFile(ciphertext, {
|
|
2210
2256
|
sessionKey,
|
|
2211
2257
|
sealId,
|
|
2212
2258
|
fileId
|
|
@@ -2222,9 +2268,10 @@ async function runFileDownload(ctx, positional, options) {
|
|
|
2222
2268
|
}
|
|
2223
2269
|
const fileId = toRecipientFileId(target.fileId);
|
|
2224
2270
|
const file = await ctx.filesReader.getRecipientFile(fileId, ctx.signal);
|
|
2225
|
-
|
|
2271
|
+
const encrypted = target.decrypt !== undefined || await ctx.filesReader.getRecipientFileSealPrefix(fileId, ctx.signal) !== null;
|
|
2272
|
+
if (target.decrypt === undefined && encrypted) {
|
|
2226
2273
|
if (!options.raw) {
|
|
2227
|
-
throw new UsageError("This file
|
|
2274
|
+
throw new UsageError("This file is encrypted. Pass --share (or --prefix and --nonce) to decrypt, or --raw to write the raw ciphertext anyway.");
|
|
2228
2275
|
}
|
|
2229
2276
|
ctx.output.warn("Writing raw ciphertext (--raw); the output is unusable without decryption.");
|
|
2230
2277
|
}
|
|
@@ -2588,11 +2635,11 @@ function registerRevisionCommands(program) {
|
|
|
2588
2635
|
// src/commands/use.ts
|
|
2589
2636
|
async function runUse(output, gopts, publication, collection, options, makeContext) {
|
|
2590
2637
|
if (options.clear) {
|
|
2591
|
-
const
|
|
2638
|
+
const profile = await updateActiveProfile(gopts, {
|
|
2592
2639
|
publication: undefined,
|
|
2593
2640
|
collection: undefined
|
|
2594
2641
|
});
|
|
2595
|
-
output.result(`Cleared the active publication and collection (profile "${
|
|
2642
|
+
output.result(`Cleared the active publication and collection (profile "${profile}").`, { profile, publication: null, collection: null });
|
|
2596
2643
|
return;
|
|
2597
2644
|
}
|
|
2598
2645
|
if (publication === undefined) {
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -5,14 +5,21 @@ Sui testnet, then tears it down. Every command is copy-pasteable; expected
|
|
|
5
5
|
output is shown beneath each step. Replace IDs in later steps with the ones your
|
|
6
6
|
own runs print.
|
|
7
7
|
|
|
8
|
+
The walkthrough uses testnet on purpose: it creates a publication, uploads
|
|
9
|
+
content to Walrus, and deletes it all again. On mainnet that sequence costs real
|
|
10
|
+
SUI and WAL. Once it works for you, the only change needed is the network on the
|
|
11
|
+
profile in step 1 (`--network mainnet`); every later command reads the network
|
|
12
|
+
from the profile and prints which one it spent on.
|
|
13
|
+
|
|
8
14
|
## 0. Prerequisites
|
|
9
15
|
|
|
10
16
|
- Install [Bun](https://bun.sh) >= 1.2.
|
|
11
17
|
- Have a Sui testnet keypair. If you use the Sui CLI, export one with
|
|
12
18
|
`sui keytool export --key-identity <alias>` to get a `suiprivkey1...` string.
|
|
13
19
|
- Fund the address with testnet SUI ([faucet](https://faucet.sui.io/)) for gas,
|
|
14
|
-
and with WAL
|
|
15
|
-
for content
|
|
20
|
+
and with WAL by swapping some SUI at
|
|
21
|
+
[stake-wal.wal.app](https://stake-wal.wal.app/?network=testnet) for content
|
|
22
|
+
uploads.
|
|
16
23
|
|
|
17
24
|
Install the CLI:
|
|
18
25
|
|
|
@@ -24,6 +31,8 @@ morse --version
|
|
|
24
31
|
## 1. Create a profile
|
|
25
32
|
|
|
26
33
|
A profile pins the network. The first profile you add becomes the default.
|
|
34
|
+
`--network` takes `mainnet`, `testnet`, or `localnet`; with no profile, flag, or
|
|
35
|
+
`MORSE_NETWORK`, the CLI targets testnet.
|
|
27
36
|
|
|
28
37
|
```sh
|
|
29
38
|
morse config add testnet --network testnet
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcadiasystems/morse-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Command-line interface for the Morse decentralized CMS on Sui.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"prepublishOnly": "bun run check && bun run build"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@arcadiasystems/morse-sdk": "^0.
|
|
58
|
+
"@arcadiasystems/morse-sdk": "^0.7.0",
|
|
59
59
|
"@mysten/seal": "1.1.3",
|
|
60
60
|
"@mysten/sui": "2.16.2",
|
|
61
61
|
"@mysten/walrus": "1.1.6",
|