@attocash/cli 0.1.0
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/LICENSE +28 -0
- package/README.md +707 -0
- package/dist/application/app.d.ts +138 -0
- package/dist/application/app.js +654 -0
- package/dist/application/operations.d.ts +9 -0
- package/dist/application/operations.js +96 -0
- package/dist/cli/cli.d.ts +1 -0
- package/dist/cli/cli.js +460 -0
- package/dist/cli/help.d.ts +4 -0
- package/dist/cli/help.js +49 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +13 -0
- package/dist/cli/onboarding.d.ts +14 -0
- package/dist/cli/onboarding.js +144 -0
- package/dist/cli/output.d.ts +2 -0
- package/dist/cli/output.js +231 -0
- package/dist/cli/terminal.d.ts +6 -0
- package/dist/cli/terminal.js +92 -0
- package/dist/cli/update-check.d.ts +1 -0
- package/dist/cli/update-check.js +12 -0
- package/dist/cli/updates.d.ts +2 -0
- package/dist/cli/updates.js +98 -0
- package/dist/core.d.ts +14 -0
- package/dist/core.js +14 -0
- package/dist/doctor/credential-probe.d.ts +7 -0
- package/dist/doctor/credential-probe.js +49 -0
- package/dist/doctor/doctor.d.ts +4 -0
- package/dist/doctor/doctor.js +213 -0
- package/dist/doctor/keyring.d.ts +7 -0
- package/dist/doctor/keyring.js +150 -0
- package/dist/doctor/profile.d.ts +19 -0
- package/dist/doctor/profile.js +73 -0
- package/dist/doctor/types.d.ts +40 -0
- package/dist/doctor/types.js +1 -0
- package/dist/domain/amount.d.ts +6 -0
- package/dist/domain/amount.js +27 -0
- package/dist/domain/errors.d.ts +10 -0
- package/dist/domain/errors.js +16 -0
- package/dist/labels/directory.d.ts +87 -0
- package/dist/labels/directory.js +140 -0
- package/dist/labels/personal.d.ts +17 -0
- package/dist/labels/personal.js +56 -0
- package/dist/labels/presentation.d.ts +154 -0
- package/dist/labels/presentation.js +90 -0
- package/dist/network/reader.d.ts +31 -0
- package/dist/network/reader.js +370 -0
- package/dist/network/retry.d.ts +7 -0
- package/dist/network/retry.js +53 -0
- package/dist/network/work.d.ts +3 -0
- package/dist/network/work.js +66 -0
- package/dist/pricing/market.d.ts +29 -0
- package/dist/pricing/market.js +150 -0
- package/dist/pricing/terms.d.ts +6 -0
- package/dist/pricing/terms.js +12 -0
- package/dist/spending/destination.d.ts +16 -0
- package/dist/spending/destination.js +42 -0
- package/dist/spending/journal.d.ts +28 -0
- package/dist/spending/journal.js +69 -0
- package/dist/spending/ledger.d.ts +118 -0
- package/dist/spending/ledger.js +444 -0
- package/dist/spending/payments.d.ts +67 -0
- package/dist/spending/payments.js +424 -0
- package/dist/spending/pool.d.ts +13 -0
- package/dist/spending/pool.js +38 -0
- package/dist/spending/reconcile.d.ts +18 -0
- package/dist/spending/reconcile.js +64 -0
- package/dist/storage/profiles.d.ts +13 -0
- package/dist/storage/profiles.js +86 -0
- package/dist/storage/secrets.d.ts +16 -0
- package/dist/storage/secrets.js +104 -0
- package/dist/storage/state.d.ts +32 -0
- package/dist/storage/state.js +255 -0
- package/dist/wallet/auto-receive.d.ts +57 -0
- package/dist/wallet/auto-receive.js +140 -0
- package/dist/wallet/defaults.d.ts +3 -0
- package/dist/wallet/defaults.js +26 -0
- package/dist/wallet/signing.d.ts +17 -0
- package/dist/wallet/signing.js +128 -0
- package/dist/wallet/types.d.ts +65 -0
- package/dist/wallet/types.js +1 -0
- package/dist/wallet/work.d.ts +29 -0
- package/dist/wallet/work.js +154 -0
- package/dist/watches/manager.d.ts +75 -0
- package/dist/watches/manager.js +197 -0
- package/node_modules/@attocash/commons-core/Kotlin-DateTime-library-kotlinx-datetime.mjs +6678 -0
- package/node_modules/@attocash/commons-core/Kotlin-DateTime-library-kotlinx-datetime.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/LICENSE +28 -0
- package/node_modules/@attocash/commons-core/README.md +118 -0
- package/node_modules/@attocash/commons-core/commons-commons-core.d.mts +1036 -0
- package/node_modules/@attocash/commons-core/commons-commons-core.mjs +7701 -0
- package/node_modules/@attocash/commons-core/commons-commons-core.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/kotlin-kotlin-stdlib.mjs +17681 -0
- package/node_modules/@attocash/commons-core/kotlin-kotlin-stdlib.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/kotlin_org_jetbrains_kotlin_kotlin_dom_api_compat.mjs +4 -0
- package/node_modules/@attocash/commons-core/kotlin_org_jetbrains_kotlin_kotlin_dom_api_compat.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-bytestring.mjs +209 -0
- package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-bytestring.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-core.mjs +2284 -0
- package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-core.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-core.mjs +6042 -0
- package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-core.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
- package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-core/package.json +27 -0
- package/node_modules/@attocash/commons-node/LICENSE +28 -0
- package/node_modules/@attocash/commons-node/README.md +92 -0
- package/node_modules/@attocash/commons-node/commons-commons-node.d.mts +255 -0
- package/node_modules/@attocash/commons-node/commons-commons-node.mjs +2977 -0
- package/node_modules/@attocash/commons-node/commons-commons-node.mjs.map +1 -0
- package/node_modules/@attocash/commons-node/kotlin-logging.mjs +432 -0
- package/node_modules/@attocash/commons-node/kotlin-logging.mjs.map +1 -0
- package/node_modules/@attocash/commons-node/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
- package/node_modules/@attocash/commons-node/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-node/package.json +29 -0
- package/node_modules/@attocash/commons-node-remote/LICENSE +28 -0
- package/node_modules/@attocash/commons-node-remote/README.md +61 -0
- package/node_modules/@attocash/commons-node-remote/commons-commons-node-remote.d.mts +122 -0
- package/node_modules/@attocash/commons-node-remote/commons-commons-node-remote.mjs +1810 -0
- package/node_modules/@attocash/commons-node-remote/commons-commons-node-remote.mjs.map +1 -0
- package/node_modules/@attocash/commons-node-remote/kotlin-logging.mjs +432 -0
- package/node_modules/@attocash/commons-node-remote/kotlin-logging.mjs.map +1 -0
- package/node_modules/@attocash/commons-node-remote/kotlinx-browser.mjs +8 -0
- package/node_modules/@attocash/commons-node-remote/kotlinx-browser.mjs.map +1 -0
- package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs +4 -0
- package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs.map +1 -0
- package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
- package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-node-remote/package.json +31 -0
- package/node_modules/@attocash/commons-transport/LICENSE +28 -0
- package/node_modules/@attocash/commons-transport/README.md +5 -0
- package/node_modules/@attocash/commons-transport/commons-commons-transport.d.mts +64 -0
- package/node_modules/@attocash/commons-transport/commons-commons-transport.mjs +249 -0
- package/node_modules/@attocash/commons-transport/commons-commons-transport.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/kotlinx-atomicfu.mjs +263 -0
- package/node_modules/@attocash/commons-transport/kotlinx-atomicfu.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/kotlinx-browser.mjs +8 -0
- package/node_modules/@attocash/commons-transport/kotlinx-browser.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/kotlinx-coroutines-core.mjs +11604 -0
- package/node_modules/@attocash/commons-transport/kotlinx-coroutines-core.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json-io.mjs +4 -0
- package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json-io.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
- package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-client-content-negotiation.mjs +499 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-client-content-negotiation.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-client-core.mjs +6456 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-client-core.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-events.mjs +83 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-events.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-http-cio.mjs +1714 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-http-cio.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-http.mjs +3197 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-http.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-io.mjs +2290 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-io.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx-json.mjs +46 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx.mjs +482 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-serialization.mjs +165 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-serialization.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-sse.mjs +4 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-sse.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-utils.mjs +3222 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-utils.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-websocket-serialization.mjs +4 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-websocket-serialization.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-websockets.mjs +1485 -0
- package/node_modules/@attocash/commons-transport/ktor-ktor-websockets.mjs.map +1 -0
- package/node_modules/@attocash/commons-transport/package.json +29 -0
- package/node_modules/@attocash/commons-worker/LICENSE +28 -0
- package/node_modules/@attocash/commons-worker/README.md +48 -0
- package/node_modules/@attocash/commons-worker/commons-commons-worker.d.mts +73 -0
- package/node_modules/@attocash/commons-worker/commons-commons-worker.mjs +702 -0
- package/node_modules/@attocash/commons-worker/commons-commons-worker.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker/kotlin-logging.mjs +432 -0
- package/node_modules/@attocash/commons-worker/kotlin-logging.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
- package/node_modules/@attocash/commons-worker/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker/package.json +29 -0
- package/node_modules/@attocash/commons-worker-remote/LICENSE +28 -0
- package/node_modules/@attocash/commons-worker-remote/README.md +60 -0
- package/node_modules/@attocash/commons-worker-remote/commons-commons-worker-remote.d.mts +93 -0
- package/node_modules/@attocash/commons-worker-remote/commons-commons-worker-remote.mjs +289 -0
- package/node_modules/@attocash/commons-worker-remote/commons-commons-worker-remote.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker-remote/kotlin-logging.mjs +432 -0
- package/node_modules/@attocash/commons-worker-remote/kotlin-logging.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker-remote/kotlinx-browser.mjs +8 -0
- package/node_modules/@attocash/commons-worker-remote/kotlinx-browser.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs +4 -0
- package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
- package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
- package/node_modules/@attocash/commons-worker-remote/package.json +31 -0
- package/node_modules/@js-joda/core/CHANGELOG.md +363 -0
- package/node_modules/@js-joda/core/CheatSheet.md +947 -0
- package/node_modules/@js-joda/core/LICENSE +34 -0
- package/node_modules/@js-joda/core/README.md +155 -0
- package/node_modules/@js-joda/core/bower.json +21 -0
- package/node_modules/@js-joda/core/dist/js-joda.d.ts +2452 -0
- package/node_modules/@js-joda/core/dist/js-joda.esm.js +13564 -0
- package/node_modules/@js-joda/core/dist/js-joda.flow.js +949 -0
- package/node_modules/@js-joda/core/dist/js-joda.js +13630 -0
- package/node_modules/@js-joda/core/dist/js-joda.js.map +1 -0
- package/node_modules/@js-joda/core/dist/js-joda.min.js +5 -0
- package/node_modules/@js-joda/core/package.json +76 -0
- package/node_modules/@js-joda/core/src/Clock.js +330 -0
- package/node_modules/@js-joda/core/src/DayOfWeek.js +451 -0
- package/node_modules/@js-joda/core/src/Duration.js +1205 -0
- package/node_modules/@js-joda/core/src/Enum.js +30 -0
- package/node_modules/@js-joda/core/src/Instant.js +1012 -0
- package/node_modules/@js-joda/core/src/LocalDate.js +1703 -0
- package/node_modules/@js-joda/core/src/LocalDateTime.js +1686 -0
- package/node_modules/@js-joda/core/src/LocalTime.js +1415 -0
- package/node_modules/@js-joda/core/src/MathUtil.js +236 -0
- package/node_modules/@js-joda/core/src/Month.js +600 -0
- package/node_modules/@js-joda/core/src/MonthDay.js +730 -0
- package/node_modules/@js-joda/core/src/OffsetDateTime.js +757 -0
- package/node_modules/@js-joda/core/src/OffsetTime.js +621 -0
- package/node_modules/@js-joda/core/src/Period.js +933 -0
- package/node_modules/@js-joda/core/src/StringUtil.js +43 -0
- package/node_modules/@js-joda/core/src/Year.js +1015 -0
- package/node_modules/@js-joda/core/src/YearConstants.js +21 -0
- package/node_modules/@js-joda/core/src/YearMonth.js +1096 -0
- package/node_modules/@js-joda/core/src/ZoneId.js +236 -0
- package/node_modules/@js-joda/core/src/ZoneIdFactory.js +201 -0
- package/node_modules/@js-joda/core/src/ZoneOffset.js +486 -0
- package/node_modules/@js-joda/core/src/ZoneRegion.js +72 -0
- package/node_modules/@js-joda/core/src/ZonedDateTime.js +2007 -0
- package/node_modules/@js-joda/core/src/_init.js +68 -0
- package/node_modules/@js-joda/core/src/assert.js +33 -0
- package/node_modules/@js-joda/core/src/chrono/ChronoLocalDate.js +230 -0
- package/node_modules/@js-joda/core/src/chrono/ChronoLocalDateTime.js +130 -0
- package/node_modules/@js-joda/core/src/chrono/ChronoZonedDateTime.js +194 -0
- package/node_modules/@js-joda/core/src/chrono/IsoChronology.js +235 -0
- package/node_modules/@js-joda/core/src/convert.js +83 -0
- package/node_modules/@js-joda/core/src/errors.js +54 -0
- package/node_modules/@js-joda/core/src/format/DateTimeBuilder.js +535 -0
- package/node_modules/@js-joda/core/src/format/DateTimeFormatter.js +728 -0
- package/node_modules/@js-joda/core/src/format/DateTimeFormatterBuilder.js +1545 -0
- package/node_modules/@js-joda/core/src/format/DateTimeParseContext.js +297 -0
- package/node_modules/@js-joda/core/src/format/DateTimePrintContext.js +138 -0
- package/node_modules/@js-joda/core/src/format/DecimalStyle.js +113 -0
- package/node_modules/@js-joda/core/src/format/EnumMap.js +70 -0
- package/node_modules/@js-joda/core/src/format/ParsePosition.js +31 -0
- package/node_modules/@js-joda/core/src/format/ResolverStyle.js +93 -0
- package/node_modules/@js-joda/core/src/format/SignStyle.js +38 -0
- package/node_modules/@js-joda/core/src/format/StringBuilder.js +47 -0
- package/node_modules/@js-joda/core/src/format/TextStyle.js +114 -0
- package/node_modules/@js-joda/core/src/format/parser/CharLiteralPrinterParser.js +46 -0
- package/node_modules/@js-joda/core/src/format/parser/CompositePrinterParser.js +89 -0
- package/node_modules/@js-joda/core/src/format/parser/FractionPrinterParser.js +162 -0
- package/node_modules/@js-joda/core/src/format/parser/NumberPrinterParser.js +367 -0
- package/node_modules/@js-joda/core/src/format/parser/OffsetIdPrinterParser.js +177 -0
- package/node_modules/@js-joda/core/src/format/parser/PadPrinterParserDecorator.js +81 -0
- package/node_modules/@js-joda/core/src/format/parser/SettingsParser.js +44 -0
- package/node_modules/@js-joda/core/src/format/parser/StringLiteralPrinterParser.js +39 -0
- package/node_modules/@js-joda/core/src/format/parser/ZoneIdPrinterParser.js +218 -0
- package/node_modules/@js-joda/core/src/js-joda.js +210 -0
- package/node_modules/@js-joda/core/src/license-preamble.js +3 -0
- package/node_modules/@js-joda/core/src/temporal/ChronoField.js +470 -0
- package/node_modules/@js-joda/core/src/temporal/ChronoUnit.js +310 -0
- package/node_modules/@js-joda/core/src/temporal/DefaultInterfaceTemporal.js +118 -0
- package/node_modules/@js-joda/core/src/temporal/IsoFields.js +859 -0
- package/node_modules/@js-joda/core/src/temporal/NativeJsTemporal.js +105 -0
- package/node_modules/@js-joda/core/src/temporal/Temporal.js +346 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalAccessor.js +125 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalAdjuster.js +92 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalAdjusters.js +464 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalAmount.js +166 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalField.js +248 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalQueries.js +223 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalQueriesFactory.js +77 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalQuery.js +107 -0
- package/node_modules/@js-joda/core/src/temporal/TemporalUnit.js +179 -0
- package/node_modules/@js-joda/core/src/temporal/ValueRange.js +257 -0
- package/node_modules/@js-joda/core/src/use.js +20 -0
- package/node_modules/@js-joda/core/src/zone/SystemDefaultZoneId.js +31 -0
- package/node_modules/@js-joda/core/src/zone/SystemDefaultZoneRules.js +162 -0
- package/node_modules/@js-joda/core/src/zone/ZoneOffsetTransition.js +289 -0
- package/node_modules/@js-joda/core/src/zone/ZoneRules.js +464 -0
- package/node_modules/@js-joda/core/src/zone/ZoneRulesProvider.js +36 -0
- package/node_modules/@noble/hashes/LICENSE +21 -0
- package/node_modules/@noble/hashes/README.md +563 -0
- package/node_modules/@noble/hashes/_blake.d.ts +21 -0
- package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_blake.js +52 -0
- package/node_modules/@noble/hashes/_blake.js.map +1 -0
- package/node_modules/@noble/hashes/_md.d.ts +99 -0
- package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_md.js +202 -0
- package/node_modules/@noble/hashes/_md.js.map +1 -0
- package/node_modules/@noble/hashes/_u64.d.ts +63 -0
- package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_u64.js +84 -0
- package/node_modules/@noble/hashes/_u64.js.map +1 -0
- package/node_modules/@noble/hashes/argon2.d.ts +112 -0
- package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/argon2.js +517 -0
- package/node_modules/@noble/hashes/argon2.js.map +1 -0
- package/node_modules/@noble/hashes/blake1.d.ts +156 -0
- package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake1.js +560 -0
- package/node_modules/@noble/hashes/blake1.js.map +1 -0
- package/node_modules/@noble/hashes/blake2.d.ts +184 -0
- package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2.js +501 -0
- package/node_modules/@noble/hashes/blake2.js.map +1 -0
- package/node_modules/@noble/hashes/blake3.d.ts +68 -0
- package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake3.js +284 -0
- package/node_modules/@noble/hashes/blake3.js.map +1 -0
- package/node_modules/@noble/hashes/eskdf.d.ts +92 -0
- package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/eskdf.js +218 -0
- package/node_modules/@noble/hashes/eskdf.js.map +1 -0
- package/node_modules/@noble/hashes/hkdf.d.ts +64 -0
- package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/hkdf.js +120 -0
- package/node_modules/@noble/hashes/hkdf.js.map +1 -0
- package/node_modules/@noble/hashes/hmac.d.ts +47 -0
- package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
- package/node_modules/@noble/hashes/hmac.js +93 -0
- package/node_modules/@noble/hashes/hmac.js.map +1 -0
- package/node_modules/@noble/hashes/index.d.ts +2 -0
- package/node_modules/@noble/hashes/index.d.ts.map +1 -0
- package/node_modules/@noble/hashes/index.js +35 -0
- package/node_modules/@noble/hashes/index.js.map +1 -0
- package/node_modules/@noble/hashes/legacy.d.ts +102 -0
- package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
- package/node_modules/@noble/hashes/legacy.js +327 -0
- package/node_modules/@noble/hashes/legacy.js.map +1 -0
- package/node_modules/@noble/hashes/package.json +97 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts +56 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/pbkdf2.js +135 -0
- package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
- package/node_modules/@noble/hashes/scrypt.d.ts +65 -0
- package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
- package/node_modules/@noble/hashes/scrypt.js +257 -0
- package/node_modules/@noble/hashes/scrypt.js.map +1 -0
- package/node_modules/@noble/hashes/sha2.d.ts +249 -0
- package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha2.js +458 -0
- package/node_modules/@noble/hashes/sha2.js.map +1 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts +461 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha3-addons.js +783 -0
- package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
- package/node_modules/@noble/hashes/sha3.d.ts +199 -0
- package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha3.js +431 -0
- package/node_modules/@noble/hashes/sha3.js.map +1 -0
- package/node_modules/@noble/hashes/src/_blake.ts +57 -0
- package/node_modules/@noble/hashes/src/_md.ts +222 -0
- package/node_modules/@noble/hashes/src/_u64.ts +117 -0
- package/node_modules/@noble/hashes/src/argon2.ts +667 -0
- package/node_modules/@noble/hashes/src/blake1.ts +614 -0
- package/node_modules/@noble/hashes/src/blake2.ts +582 -0
- package/node_modules/@noble/hashes/src/blake3.ts +317 -0
- package/node_modules/@noble/hashes/src/eskdf.ts +259 -0
- package/node_modules/@noble/hashes/src/hkdf.ts +133 -0
- package/node_modules/@noble/hashes/src/hmac.ts +126 -0
- package/node_modules/@noble/hashes/src/index.ts +33 -0
- package/node_modules/@noble/hashes/src/legacy.ts +339 -0
- package/node_modules/@noble/hashes/src/pbkdf2.ts +175 -0
- package/node_modules/@noble/hashes/src/scrypt.ts +315 -0
- package/node_modules/@noble/hashes/src/sha2.ts +530 -0
- package/node_modules/@noble/hashes/src/sha3-addons.ts +983 -0
- package/node_modules/@noble/hashes/src/sha3.ts +485 -0
- package/node_modules/@noble/hashes/src/utils.ts +848 -0
- package/node_modules/@noble/hashes/src/webcrypto.ts +239 -0
- package/node_modules/@noble/hashes/utils.d.ts +519 -0
- package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
- package/node_modules/@noble/hashes/utils.js +578 -0
- package/node_modules/@noble/hashes/utils.js.map +1 -0
- package/node_modules/@noble/hashes/webcrypto.d.ts +120 -0
- package/node_modules/@noble/hashes/webcrypto.d.ts.map +1 -0
- package/node_modules/@noble/hashes/webcrypto.js +165 -0
- package/node_modules/@noble/hashes/webcrypto.js.map +1 -0
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1407 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/package.json +53 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { personalNameSchema } from '../labels/personal.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { AttoError } from '../domain/errors.js';
|
|
4
|
+
import { paymentMetadataSchema } from '../spending/journal.js';
|
|
5
|
+
const index = z.number().int().min(0).max(2_147_483_647);
|
|
6
|
+
const address = z.string().min(1).max(128).describe('Atto address.');
|
|
7
|
+
const addresses = z.array(address).min(1).max(100);
|
|
8
|
+
const hash = z.string().regex(/^[a-fA-F0-9]{64}$/).describe('64-character hexadecimal transaction hash.');
|
|
9
|
+
const integer = z.string().regex(/^\d+$/).max(40);
|
|
10
|
+
const decimal = z.string().regex(/^\d+(?:\.\d+)?$/).max(80).describe('Exact decimal amount; never a JSON number.');
|
|
11
|
+
const unit = z.enum(['ATTO', 'RAW']);
|
|
12
|
+
const cursor = z.string().min(1).max(16_384);
|
|
13
|
+
const limit = z.number().int().min(1).max(1000).describe('Maximum returned records; defaults to 100.');
|
|
14
|
+
const timeoutMs = z.number().int().min(100).max(30_000).describe('Scan window in milliseconds; list queries default to 3000.');
|
|
15
|
+
const endpoint = z.url().max(2048).refine(value => {
|
|
16
|
+
const url = new URL(value);
|
|
17
|
+
return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash;
|
|
18
|
+
}, 'Use an HTTP(S) endpoint without credentials, query parameters, or fragments.');
|
|
19
|
+
const amount = z.strictObject({ amount: decimal, unit });
|
|
20
|
+
const policy = z.strictObject({
|
|
21
|
+
perRequest: amount.nullable(),
|
|
22
|
+
rolling: z.array(amount.extend({ days: z.number().int().positive().max(36_500) })).max(100),
|
|
23
|
+
});
|
|
24
|
+
const pool = z.strictObject({
|
|
25
|
+
indexes: z.array(index).min(1).max(100).refine(indexes => new Set(indexes).size === indexes.length, 'Pool indexes must be unique.'),
|
|
26
|
+
consolidate: z.boolean(),
|
|
27
|
+
});
|
|
28
|
+
const streamFields = {
|
|
29
|
+
addresses: addresses.optional(),
|
|
30
|
+
hash: hash.optional(),
|
|
31
|
+
fromHeight: integer.optional(),
|
|
32
|
+
toHeight: integer.optional(),
|
|
33
|
+
minAmountRaw: integer.optional(),
|
|
34
|
+
};
|
|
35
|
+
const listFields = { limit: limit.optional(), timeoutMs: timeoutMs.optional(), cursor: cursor.optional() };
|
|
36
|
+
const labelTarget = { address: address.optional(), index: index.optional() };
|
|
37
|
+
const oneLabelTarget = (value) => (value.address !== undefined) !== (value.index !== undefined);
|
|
38
|
+
const accountFields = { index: index.optional(), addresses: addresses.optional() };
|
|
39
|
+
function singleSelection(value) {
|
|
40
|
+
return [value.index !== undefined, value.addresses !== undefined, value.all === true, value.networkWide === true, value.hash !== undefined].filter(Boolean).length <= 1;
|
|
41
|
+
}
|
|
42
|
+
export const operations = [
|
|
43
|
+
{ name: 'doctor', description: 'Diagnose this process environment, profile, keyring credential access, node APIs and streaming, worker proof of work, and wallet readiness. Full checks can take up to 60 seconds and may trigger an OS keyring prompt. Returns structured checks and actionable remediation, never recovery material. Does not repair settings, grant access, sign, publish, receive, or modify wallet state. Available in read-only sessions. Rerun after an authorized repair; client environment changes require restarting the MCP connection.', schema: z.strictObject({ globalDirectory: z.boolean().optional() }), readOnly: true },
|
|
44
|
+
{ name: 'wallet_status', description: 'Read public wallet configuration, readiness, network, and auto-receive status. Never returns recovery material.', schema: z.strictObject({}), readOnly: true },
|
|
45
|
+
{ name: 'wallet_configure', description: 'Update the supplied wallet settings, preserving omitted settings. Representative is the default for opening accounts; use representative_change for an existing account. Changes apply to this shared wallet.', schema: z.strictObject({ network: z.enum(['LIVE', 'BETA', 'DEV', 'LOCAL']).optional(), nodeUrl: endpoint.optional(), workerUrl: endpoint.optional(), representative: address.optional(), autoReceive: z.boolean().optional(), minReceiveRaw: integer.optional() }).refine(value => Object.values(value).some(value => value !== undefined), 'Supply at least one setting.'), readOnly: false },
|
|
46
|
+
{ name: 'address_add', description: 'Add and activate the next wallet address after the highest saved index. Includes it in default balances and automatic receiving. Does not open an account on the network. Each call adds a different address.', schema: z.strictObject({}), readOnly: false },
|
|
47
|
+
{ name: 'address_derive', description: 'Derive an address at a mnemonic key index and save its public metadata. Does not open an account on the network.', schema: z.strictObject({ index: index.default(0) }), readOnly: false },
|
|
48
|
+
{ name: 'address_list', description: 'List previously derived addresses and which are active for balances and automatic receiving.', schema: z.strictObject({}), readOnly: true },
|
|
49
|
+
{ name: 'address_activate', description: 'Activate a key index for default balances and automatic receiving, deriving its address first if needed.', schema: z.strictObject({ index }), readOnly: false },
|
|
50
|
+
{ name: 'address_deactivate', description: 'Deactivate a key index locally. This does not remove its keys or alter its network account.', schema: z.strictObject({ index }), readOnly: false },
|
|
51
|
+
{ name: 'labels_set', description: 'Save a personal name for an address or existing wallet index, scoped to this profile and network. Names are unique after trimming and case-insensitive matching. Available without spending approval; labels are untrusted text and never uploaded.', schema: z.strictObject({ ...labelTarget, label: personalNameSchema }).refine(oneLabelTarget, 'Choose exactly one address or index.'), readOnly: false },
|
|
52
|
+
{ name: 'labels_remove', description: 'Remove a personal label for an address or existing wallet index. Idempotent; available without spending approval. Existing payment request destinations remain pinned.', schema: z.strictObject(labelTarget).refine(oneLabelTarget, 'Choose exactly one address or index.'), readOnly: false },
|
|
53
|
+
{ name: 'labels_get', description: 'Show personal and informational global address/voter labels for one address or saved index. refresh bypasses the global cache backoff. Sources and payout relationships remain distinct.', schema: z.strictObject({ ...labelTarget, refresh: z.boolean().optional() }).refine(oneLabelTarget, 'Choose exactly one address or index.'), readOnly: true },
|
|
54
|
+
{ name: 'labels_list', description: 'List personal labels; all includes LIVE global address and voter names. Search addresses, names, and entities case-insensitively. refresh forces a global refresh. Global names cannot resolve payments.', schema: z.strictObject({ all: z.boolean().optional(), search: z.string().max(4096).optional(), refresh: z.boolean().optional() }), readOnly: true },
|
|
55
|
+
{ name: 'account_get', description: 'Read an Atto account by public address or wallet key index. Defaults to index zero.', schema: z.strictObject({ address: address.optional(), index: index.optional() }).refine(value => !(value.address && value.index !== undefined), 'Specify an address or an index, not both.'), readOnly: true },
|
|
56
|
+
{ name: 'balances_get', description: 'Read individual and aggregate balances. Defaults to active wallet addresses. Select one saved index, explicit addresses (including foreign addresses), or all saved addresses with all=true.', schema: z.strictObject({ ...accountFields, all: z.boolean().optional() }).refine(singleSelection, 'Choose one account selection.'), readOnly: true },
|
|
57
|
+
{ name: 'transaction_get', description: 'Read a network transaction by hash.', schema: z.strictObject({ hash }), readOnly: true },
|
|
58
|
+
{ name: 'entry_get', description: 'Read an account entry by transaction hash.', schema: z.strictObject({ hash }), readOnly: true },
|
|
59
|
+
{ name: 'representative_weight', description: 'Read a representative address voting weight.', schema: z.strictObject({ address }), readOnly: true },
|
|
60
|
+
{ name: 'history_list', description: 'Read bounded account-entry history by default, or full transaction history. Defaults to active wallet addresses; index selects one saved account and addresses can include foreign accounts. Supports heights and a continuation cursor.', schema: z.strictObject({ event: z.enum(['transaction', 'entry']).default('entry'), ...accountFields, fromHeight: integer.optional(), toHeight: integer.optional(), ...listFields }).refine(singleSelection, 'Choose an index or addresses.'), readOnly: true },
|
|
61
|
+
{ name: 'receivables_list', description: 'Scan pending incoming payments, defaulting to active wallet addresses. Select a saved index or explicit addresses, including foreign accounts. This is a bounded scan, without continuation cursors; timedOut means the scan window ended and more payments may exist.', schema: z.strictObject({ ...accountFields, minAmountRaw: integer.optional(), limit: limit.optional(), timeoutMs: timeoutMs.optional() }).refine(singleSelection, 'Choose an index or addresses.'), readOnly: true },
|
|
62
|
+
{ name: 'send', description: 'Send funds to exactly one destination address, saved destinationIndex, or personal destinationLabel, subject to shared spending limits. Omit index to select an account from the approved pool. Explicit MCP indexes must belong to that pool and hold the full amount. Approved consolidation may combine pool funds only when index is omitted. Metadata is optional local journal data (up to 4096 UTF-8 bytes) and is never published on the network. USD uses an indicative price and requires accepted terms. Reuse requestId for retries; the original account, metadata, and USD conversion are preserved. Omit metadata to retain it; changed metadata is rejected.', schema: z.strictObject({ index: index.optional(), destination: address.optional(), destinationLabel: personalNameSchema.optional().describe('Exact personal name; global-only names fail locally. The original label and resolved address are pinned per request ID before network access.'), destinationIndex: index.optional().describe('Existing saved destination account index, instead of destination.'), amount: decimal, unit: z.enum(['ATTO', 'RAW', 'USD']).default('ATTO'), requestId: z.string().min(1).max(128), metadata: paymentMetadataSchema.optional() }).refine(value => [value.destination, value.destinationIndex, value.destinationLabel].filter(value => value !== undefined).length === 1, 'Choose exactly one destination address, destination index, or personal label.'), readOnly: false },
|
|
63
|
+
{ name: 'pool_get', description: 'Read the approved send pool, its account balances, and readiness for payments. Does not reserve an account or move funds.', schema: z.strictObject({}), readOnly: true },
|
|
64
|
+
{ name: 'journal_list', description: 'List local payment journal records, newest first, with an optional status filter. Includes caller metadata and recorded payment progress; metadata is not an authenticated statement from the network.', schema: z.strictObject({ limit: z.number().int().min(1).max(100).default(50), cursor: cursor.optional(), status: z.enum(['reserved', 'signed', 'published', 'unknown', 'failed']).optional() }), readOnly: true },
|
|
65
|
+
{ name: 'journal_get', description: 'Read a local payment journal record by its request ID, including caller metadata and recorded payment progress.', schema: z.strictObject({ requestId: z.string().min(1).max(128) }), readOnly: true },
|
|
66
|
+
{ name: 'metrics_get', description: 'Read public Atto market metrics and their observation date. Market prices are indicative, not executable exchange quotes.', schema: z.strictObject({}), readOnly: true },
|
|
67
|
+
{ name: 'price_quote', description: 'Convert a USD decimal amount to Atto using the public indicative price. Returns the price observation date and rejects data older than 72 hours. Does not send funds.', schema: z.strictObject({ amount: decimal }), readOnly: true },
|
|
68
|
+
{ name: 'terms_get', description: 'Read the current terms and version for USD-priced payments. Present the terms to the user before requesting acknowledgement.', schema: z.strictObject({}), readOnly: true },
|
|
69
|
+
{ name: 'terms_accept', description: 'Persist acknowledgement of the specified terms version for USD-priced payments. Call only after the user has read and explicitly accepted those terms.', schema: z.strictObject({ version: z.string().min(1).max(128), accepted: z.literal(true) }), readOnly: false },
|
|
70
|
+
{ name: 'receive', description: 'Receive one pending payment by hash, opening the account if needed.', schema: z.strictObject({ index: index.default(0), hash, representative: address.optional() }), readOnly: false },
|
|
71
|
+
{ name: 'receive_all', description: 'Receive a bounded set of pending payments for a key index, opening the account if needed.', schema: z.strictObject({ index: index.default(0), limit: limit.default(100), timeoutMs: timeoutMs.default(2000), representative: address.optional() }), readOnly: false },
|
|
72
|
+
{ name: 'representative_change', description: 'Publish a representative change for an existing account.', schema: z.strictObject({ index: index.default(0), representative: address }), readOnly: false },
|
|
73
|
+
{ name: 'limits_get', description: 'Read spending policy, approved send pool, MCP access, proposal status, historical usage, reservations, and remaining allowances.', schema: z.strictObject({}), readOnly: true },
|
|
74
|
+
{ name: 'limits_propose', description: 'Propose spending limits, MCP access, and optional send-pool settings for local terminal approval. Omitted pool preserves the approved pool. Does not change active settings or grant access. A new proposal replaces the previous proposal and expires after 24 hours. Read limits_get for active settings and proposal status. Amounts are ATTO or RAW; null perRequest with empty rolling means unlimited spending if approved.', schema: z.strictObject({ policy, access: z.enum(['read-only', 'spend']).default('spend'), pool: pool.optional() }), readOnly: false },
|
|
75
|
+
{ name: 'watch_start', description: 'Start an observational, reconnecting watch for this session, defaulting to active wallet addresses. Choose one saved index, explicit addresses (including foreign accounts), a transaction/entry hash, or networkWide=true. Network-wide receivables are unsupported. Read buffered events and connection status with watch_read. Does not enable automatic receiving.', schema: z.strictObject({ event: z.enum(['account', 'transaction', 'entry', 'receivable']), ...streamFields, index: index.optional(), networkWide: z.boolean().optional() }).refine(singleSelection, 'Choose one watch selection.').refine(value => !(value.networkWide && value.event === 'receivable'), 'Receivable watches require addresses.'), readOnly: false },
|
|
76
|
+
{ name: 'watch_list', description: 'List watches owned by this process.', schema: z.strictObject({}), readOnly: true },
|
|
77
|
+
{ name: 'watch_read', description: 'Read buffered events after an optional numeric cursor. Gaps are reported if bounded event retention was exceeded.', schema: z.strictObject({ id: z.string().min(1).max(128), cursor: z.number().int().nonnegative().optional(), limit: limit.optional() }), readOnly: true },
|
|
78
|
+
{ name: 'watch_stop', description: 'Stop a watch owned by this process and cancel its network subscription.', schema: z.strictObject({ id: z.string().min(1).max(128) }), readOnly: false },
|
|
79
|
+
];
|
|
80
|
+
export function parseOperation(name, input = {}) {
|
|
81
|
+
const operation = operations.find(operation => operation.name === name);
|
|
82
|
+
if (!operation)
|
|
83
|
+
throw new AttoError('UNKNOWN_OPERATION', 'Unknown Atto operation.');
|
|
84
|
+
const parsed = operation.schema.safeParse(input);
|
|
85
|
+
if (!parsed.success) {
|
|
86
|
+
// Do not echo invalid values: callers may accidentally supply a secret.
|
|
87
|
+
const selectionError = parsed.error.issues.find(issue => issue.code === 'custom' && issue.path.length === 0);
|
|
88
|
+
throw new AttoError('INVALID_INPUT', selectionError?.message ?? 'Invalid operation input. Inspect the operation schema or CLI help.', {
|
|
89
|
+
fields: [...new Set(parsed.error.issues.map(issue => {
|
|
90
|
+
const field = String(issue.path[0] ?? 'input');
|
|
91
|
+
return Object.hasOwn(operation.schema.shape, field) ? field : 'input';
|
|
92
|
+
}))],
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return parsed.data;
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runCli(argv?: string[]): Promise<void>;
|
package/dist/cli/cli.js
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
|
+
import { Command, CommanderError, InvalidArgumentError } from 'commander';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { AttoApplication } from '../application/app.js';
|
|
7
|
+
import { operations, parseOperation } from '../application/operations.js';
|
|
8
|
+
import { AttoError, errorResult } from '../domain/errors.js';
|
|
9
|
+
import { hiddenPrompt, requireTerminal, showRecovery, terminalPrompt } from './terminal.js';
|
|
10
|
+
import { notifyUpdate } from './updates.js';
|
|
11
|
+
import { approveLimitsProposal, rejectLimitsProposal } from './onboarding.js';
|
|
12
|
+
import { formatHumanResult } from './output.js';
|
|
13
|
+
import { configureHelp, commandErrorMessage } from './help.js';
|
|
14
|
+
import { runDoctor } from '../doctor/doctor.js';
|
|
15
|
+
function number(value) {
|
|
16
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value)))
|
|
17
|
+
throw new InvalidArgumentError('Use an unsigned integer.');
|
|
18
|
+
return Number(value);
|
|
19
|
+
}
|
|
20
|
+
function json(value) {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(value);
|
|
23
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
24
|
+
throw new Error();
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new AttoError('INVALID_INPUT', 'Input must be a JSON object.');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function compact(input) {
|
|
32
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
33
|
+
}
|
|
34
|
+
function addressOptions(command) {
|
|
35
|
+
return command.option('--index <index>', 'Select one saved wallet account', number)
|
|
36
|
+
.option('--addresses <addresses>', 'Comma-separated addresses, including accounts outside this wallet', value => value.split(',').map(value => value.trim()));
|
|
37
|
+
}
|
|
38
|
+
function listOptions(command) {
|
|
39
|
+
return addressOptions(command)
|
|
40
|
+
.option('--limit <count>', 'Maximum returned records (1-1000; default: 100)', number)
|
|
41
|
+
.option('--timeout-ms <milliseconds>', 'Scan window (100-30000 ms; default: 3000)', number);
|
|
42
|
+
}
|
|
43
|
+
function streamOptions(command) {
|
|
44
|
+
return addressOptions(command)
|
|
45
|
+
.option('--hash <hash>', 'Select one transaction or entry by hash; cannot combine with an account selector')
|
|
46
|
+
.option('--from-height <height>', 'Inclusive starting height for account transaction/entry streams (default: 1)')
|
|
47
|
+
.option('--to-height <height>', 'Inclusive ending height for account transaction/entry streams')
|
|
48
|
+
.option('--min-amount-raw <raw>', 'Minimum receivable amount in RAW (default: 0; receivable events only)');
|
|
49
|
+
}
|
|
50
|
+
export async function runCli(argv = process.argv) {
|
|
51
|
+
const program = new Command();
|
|
52
|
+
let activeCommand = program;
|
|
53
|
+
const currentVersion = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version;
|
|
54
|
+
let application;
|
|
55
|
+
let sendRequestId;
|
|
56
|
+
const app = (onReceiveProgress, sendRetry, onDestination) => application ??= new AttoApplication({ directory: program.opts().dataDir, onReceiveProgress, sendRetry, onDestination });
|
|
57
|
+
const jsonOutput = (value) => { process.stdout.write(`${JSON.stringify(value, (_, value) => typeof value === 'bigint' ? value.toString() : value)}\n`); };
|
|
58
|
+
const output = (result, operation) => {
|
|
59
|
+
if (program.opts().json)
|
|
60
|
+
jsonOutput({ result });
|
|
61
|
+
else
|
|
62
|
+
process.stdout.write(formatHumanResult(result, operation));
|
|
63
|
+
};
|
|
64
|
+
const call = async (name, input = {}) => {
|
|
65
|
+
if (name === 'doctor') {
|
|
66
|
+
parseOperation(name, input);
|
|
67
|
+
return diagnose(input);
|
|
68
|
+
}
|
|
69
|
+
const result = await app().call(name, compact(input));
|
|
70
|
+
output(result, name);
|
|
71
|
+
};
|
|
72
|
+
const waitForSignal = async (work) => {
|
|
73
|
+
const controller = new AbortController();
|
|
74
|
+
const stop = () => controller.abort();
|
|
75
|
+
process.once('SIGINT', stop);
|
|
76
|
+
process.once('SIGTERM', stop);
|
|
77
|
+
try {
|
|
78
|
+
await work(controller.signal);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
controller.abort();
|
|
82
|
+
process.removeListener('SIGINT', stop);
|
|
83
|
+
process.removeListener('SIGTERM', stop);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const diagnose = (options = {}) => waitForSignal(async (signal) => {
|
|
87
|
+
const report = await runDoctor({ directory: program.opts().dataDir, signal, globalDirectory: options.globalDirectory });
|
|
88
|
+
output(report, 'doctor');
|
|
89
|
+
if (report.status === 'fail')
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
});
|
|
92
|
+
program.name('atto').description('Atto wallet: manage accounts, send funds, and receive payments.')
|
|
93
|
+
.version(currentVersion)
|
|
94
|
+
.option('--json', 'Print machine-readable JSON instead of plain text')
|
|
95
|
+
.option('--no-update-notifier', 'Skip update checks and notifications')
|
|
96
|
+
.option('--data-dir <directory>', 'Public wallet state directory; secrets remain in the OS password store')
|
|
97
|
+
.configureOutput({ writeErr: () => { } })
|
|
98
|
+
.hook('preAction', (_command, command) => {
|
|
99
|
+
activeCommand = command;
|
|
100
|
+
if (!['doctor', 'send'].includes(command.name()) && !(command.name() === 'call' && ['doctor', 'send'].includes(command.args[0] ?? '')) && !program.opts().json && program.opts().updateNotifier !== false)
|
|
101
|
+
notifyUpdate(currentVersion);
|
|
102
|
+
});
|
|
103
|
+
program.command('doctor').description('Check runtime, profile, keyring, node, worker, and readiness; diagnose without repairs (up to 60s)')
|
|
104
|
+
.option('--global-directory', 'Also check the public LIVE address directory without updating its cache')
|
|
105
|
+
.action(options => diagnose(options));
|
|
106
|
+
const wallet = program.command('wallet').description('Wallet setup, settings, and automatic receiving');
|
|
107
|
+
wallet.command('status').description('Read public wallet status').action(() => call('wallet_status'));
|
|
108
|
+
wallet.command('configure').description('Update public settings')
|
|
109
|
+
.option('--network <name>', 'LIVE, BETA, DEV, or LOCAL')
|
|
110
|
+
.option('--node-url <url>', 'Node HTTP(S) URL')
|
|
111
|
+
.option('--worker-url <url>', 'Work server HTTP(S) URL')
|
|
112
|
+
.option('--representative <address>', 'Default representative for opening accounts; use representative change for existing accounts')
|
|
113
|
+
.option('--auto-receive', 'Enable automatic receiving in receiving sessions')
|
|
114
|
+
.option('--no-auto-receive', 'Disable automatic receiving')
|
|
115
|
+
.option('--min-receive-raw <amount>', 'Minimum automatically received amount in RAW')
|
|
116
|
+
.action(options => call('wallet_configure', options));
|
|
117
|
+
wallet.command('create').description('Create and store a new mnemonic; display recovery words in this terminal')
|
|
118
|
+
.action(async () => {
|
|
119
|
+
requireTerminal();
|
|
120
|
+
const result = await app().createWallet();
|
|
121
|
+
showRecovery(await app().backupMnemonic());
|
|
122
|
+
output(result, 'wallet_create');
|
|
123
|
+
});
|
|
124
|
+
wallet.command('import').description('Import a mnemonic through a hidden terminal prompt')
|
|
125
|
+
.action(async () => {
|
|
126
|
+
requireTerminal();
|
|
127
|
+
const status = await app().call('wallet_status');
|
|
128
|
+
if (status.resetPending) {
|
|
129
|
+
throw new AttoError('WALLET_RESET_REQUIRED', 'Wallet reset is unfinished. Run wallet reset for this profile before importing a wallet.');
|
|
130
|
+
}
|
|
131
|
+
if (status.initialized) {
|
|
132
|
+
throw new AttoError('WALLET_EXISTS', 'This wallet is already initialized. Use wallet reset for this profile to clear it, or --data-dir to choose another profile.');
|
|
133
|
+
}
|
|
134
|
+
const mnemonic = await hiddenPrompt('Recovery phrase (hidden): ');
|
|
135
|
+
output(await app().createWallet(mnemonic), 'wallet_import');
|
|
136
|
+
});
|
|
137
|
+
wallet.command('reset').description('Delete this local wallet and its stored recovery phrase after terminal confirmation')
|
|
138
|
+
.action(async () => {
|
|
139
|
+
requireTerminal();
|
|
140
|
+
const review = await app().reviewWalletReset();
|
|
141
|
+
process.stderr.write([
|
|
142
|
+
`Profile: ${JSON.stringify(review.directory)}`,
|
|
143
|
+
`Wallet: ${review.identity ? JSON.stringify(review.identity.address) : 'Not initialized'}`,
|
|
144
|
+
`Network: ${JSON.stringify(review.network)}`,
|
|
145
|
+
'',
|
|
146
|
+
'This removes the recovery phrase from the OS password store and clears this profile\'s',
|
|
147
|
+
'addresses, personal labels, payment journal, spending limits, and access permissions.',
|
|
148
|
+
'Back up your recovery phrase and public wallet profile before continuing.',
|
|
149
|
+
'You need an offline backup of the recovery phrase to recover any funds.',
|
|
150
|
+
'Stop other sessions using this wallet before resetting.',
|
|
151
|
+
'',
|
|
152
|
+
].join('\n'));
|
|
153
|
+
if (await terminalPrompt('Type reset to delete this local wallet: ') !== 'reset') {
|
|
154
|
+
throw new AttoError('CANCELLED', 'Wallet reset cancelled.');
|
|
155
|
+
}
|
|
156
|
+
const result = await app().resetWallet(review.identity?.fingerprint ?? null);
|
|
157
|
+
if (program.opts().json)
|
|
158
|
+
output(result);
|
|
159
|
+
else
|
|
160
|
+
process.stderr.write('Local wallet reset. You can now create or import a wallet.\n');
|
|
161
|
+
});
|
|
162
|
+
wallet.command('backup').description('Display recovery words in this terminal only')
|
|
163
|
+
.action(async () => {
|
|
164
|
+
requireTerminal();
|
|
165
|
+
showRecovery(await app().backupMnemonic());
|
|
166
|
+
if (program.opts().json)
|
|
167
|
+
output({ displayed: true });
|
|
168
|
+
});
|
|
169
|
+
wallet.command('receive').description('Keep automatic receiving running until Ctrl+C')
|
|
170
|
+
.action(async () => {
|
|
171
|
+
const receiving = app(event => output(event, 'receive_progress'));
|
|
172
|
+
const status = await receiving.call('wallet_status');
|
|
173
|
+
if (!status.initialized)
|
|
174
|
+
throw new AttoError('WALLET_NOT_INITIALIZED', 'Create or import a wallet before receiving: atto wallet create or atto wallet import.');
|
|
175
|
+
if (!status.settings.autoReceive)
|
|
176
|
+
throw new AttoError('AUTO_RECEIVE_DISABLED', 'Automatic receiving is disabled. Enable it with atto wallet configure --auto-receive.');
|
|
177
|
+
if (!status.addresses.some(address => address.active))
|
|
178
|
+
throw new AttoError('NO_ACTIVE_ACCOUNTS', 'Add an active address with atto address add, or activate a saved account with atto address activate <index>.');
|
|
179
|
+
output(status, 'wallet_receive');
|
|
180
|
+
await waitForSignal(async (signal) => {
|
|
181
|
+
await receiving.start();
|
|
182
|
+
if (!signal.aborted)
|
|
183
|
+
await new Promise(resolve => signal.addEventListener('abort', () => resolve(), { once: true }));
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
const address = program.command('address').description('Manage mnemonic-derived public addresses');
|
|
187
|
+
address.command('list').description('List all saved addresses and their activation state').action(() => call('address_list'));
|
|
188
|
+
address.command('add').description('Add and activate the next account address; receiving funds opens it on the network').action(() => call('address_add'));
|
|
189
|
+
address.command('derive').description('Save a specific address without activating it; an existing address keeps its state').argument('[index]', 'Key index (defaults to zero)', number).action(index => call('address_derive', { index }));
|
|
190
|
+
address.command('activate').description('Include an account in default balances and receiving; derive it if needed').argument('<index>', 'Key index', number).action(index => call('address_activate', { index }));
|
|
191
|
+
address.command('deactivate').description('Exclude an account from default balances and receiving; funds remain visible with balances --all').argument('<index>', 'Key index', number).action(index => call('address_deactivate', { index }));
|
|
192
|
+
const labels = program.command('labels').description('Personal address names and informational global labels');
|
|
193
|
+
const labelTarget = (target) => /^\d+$/.test(target) ? { index: number(target) } : { address: target };
|
|
194
|
+
labels.command('set <address-or-index> <name>').description('Set a unique personal name in this profile and network')
|
|
195
|
+
.action((target, label) => call('labels_set', { ...labelTarget(target), label }));
|
|
196
|
+
labels.command('remove <address-or-index>').description('Remove a personal name; existing request IDs keep their destination')
|
|
197
|
+
.action(target => call('labels_remove', labelTarget(target)));
|
|
198
|
+
labels.command('show <address-or-index>').description('Show personal and global labels with their sources')
|
|
199
|
+
.option('--refresh', 'Refresh global data, bypassing freshness and retry backoff')
|
|
200
|
+
.action((target, options) => call('labels_get', { ...labelTarget(target), ...options }));
|
|
201
|
+
labels.command('list').description('List personal labels in this profile and network')
|
|
202
|
+
.option('--all', 'Include informational LIVE global address and voter names')
|
|
203
|
+
.option('--search <text>', 'Match addresses, names, and entity names case-insensitively')
|
|
204
|
+
.option('--refresh', 'Refresh global data, bypassing freshness and retry backoff')
|
|
205
|
+
.action(options => call('labels_list', options));
|
|
206
|
+
program.command('account [address]').description('Read an account by address or key index')
|
|
207
|
+
.option('--index <index>', 'Saved wallet account index (default: 0)', number).action((address, options) => call('account_get', { address, ...options }));
|
|
208
|
+
addressOptions(program.command('balances').description('Read balances and total for active wallet accounts by default'))
|
|
209
|
+
.option('--all', 'Include every saved address, including inactive accounts').action(options => call('balances_get', options));
|
|
210
|
+
program.command('transaction <hash>').description('Read a network transaction').action(hash => call('transaction_get', { hash }));
|
|
211
|
+
program.command('entry <hash>').description('Read an account entry').action(hash => call('entry_get', { hash }));
|
|
212
|
+
listOptions(program.command('history [event]').description('Read wallet account entries by default; event can be entry or transaction'))
|
|
213
|
+
.option('--cursor <cursor>', 'Continue a previous history page with the same filters')
|
|
214
|
+
.option('--from-height <height>', 'Inclusive starting height')
|
|
215
|
+
.option('--to-height <height>', 'Inclusive ending height')
|
|
216
|
+
.action((event, options) => call('history_list', { event, ...options }));
|
|
217
|
+
listOptions(program.command('receivables').description('Scan pending payments for active wallet accounts by default; does not receive them'))
|
|
218
|
+
.option('--min-amount-raw <raw>', 'Minimum amount in RAW (default: 0)').action(options => call('receivables_list', options));
|
|
219
|
+
program.command('send [destination] [amount]').description('Send funds; retry temporary network failures until success, HTTP 4xx, or Ctrl+C')
|
|
220
|
+
.option('--request-id <id>', 'Payment ID (generated if omitted); reuse to check an existing payment')
|
|
221
|
+
.option('--index <index>', 'Source account index (defaults to 0 unless --pool is used)', number)
|
|
222
|
+
.option('--pool', 'Automatically select a source from the approved account pool; cannot combine with --index')
|
|
223
|
+
.option('--destination <address>', 'Destination instead of a positional argument')
|
|
224
|
+
.option('--to-index <index>', 'Send to an existing saved account instead of a destination address; use --amount or --usd', number)
|
|
225
|
+
.option('--to-label <name>', 'Send to an exact personal name in this profile and network; use --amount or --usd')
|
|
226
|
+
.option('--amount <amount>', 'Exact amount instead of a positional argument')
|
|
227
|
+
.option('--usd <amount>', 'USD amount converted using the indicative market price')
|
|
228
|
+
.option('--unit <unit>', 'ATTO (default), RAW, or USD')
|
|
229
|
+
.option('--metadata <json>', 'Local payment metadata as a JSON object; never published on the network')
|
|
230
|
+
.option('--reason <text>', 'Local payment reason; shorthand for metadata.reason')
|
|
231
|
+
.action((destination, amount, options) => {
|
|
232
|
+
if (options.pool && options.index !== undefined) {
|
|
233
|
+
throw new AttoError('INVALID_INPUT', 'Choose either --pool or --index.');
|
|
234
|
+
}
|
|
235
|
+
if ([destination, options.destination, options.toIndex, options.toLabel].filter(value => value !== undefined).length !== 1 || [amount, options.amount, options.usd].filter(value => value !== undefined).length !== 1 || (options.usd && options.unit)) {
|
|
236
|
+
throw new AttoError('INVALID_INPUT', 'Specify one destination and one amount: positional, --amount with --unit, or --usd.');
|
|
237
|
+
}
|
|
238
|
+
let metadata = options.metadata === undefined ? undefined : json(options.metadata);
|
|
239
|
+
if (options.reason !== undefined) {
|
|
240
|
+
if (Object.hasOwn(metadata ?? {}, 'reason'))
|
|
241
|
+
throw new AttoError('INVALID_INPUT', 'Specify the payment reason once: --reason or metadata.reason.');
|
|
242
|
+
metadata = { ...metadata, reason: options.reason };
|
|
243
|
+
}
|
|
244
|
+
const input = compact({ destination: destination ?? options.destination, destinationIndex: options.toIndex, destinationLabel: options.toLabel, amount: options.usd ?? options.amount ?? amount, unit: options.usd !== undefined ? 'USD' : options.unit, index: options.pool ? undefined : options.index ?? 0, requestId: options.requestId ?? randomUUID(), metadata });
|
|
245
|
+
parseOperation('send', input);
|
|
246
|
+
sendRequestId = input.requestId;
|
|
247
|
+
const progress = (value, message) => {
|
|
248
|
+
process.stderr.write(program.opts().json ? `${JSON.stringify({ progress: value })}\n`
|
|
249
|
+
: message ?? formatHumanResult(value));
|
|
250
|
+
};
|
|
251
|
+
progress({ requestId: sendRequestId });
|
|
252
|
+
return waitForSignal(async (signal) => {
|
|
253
|
+
const result = await app(undefined, {
|
|
254
|
+
signal,
|
|
255
|
+
onRetry: (error, delayMs) => progress({ requestId: sendRequestId, error: errorResult(error), retryInMs: delayMs }, `${error.message} Retrying in ${delayMs / 1000}s. Press Ctrl+C to stop.\n`),
|
|
256
|
+
}, binding => progress({ requestId: sendRequestId, destination: binding.address, network: binding.network, ...(binding.label ? { personalName: binding.label } : {}) })).call('send', input);
|
|
257
|
+
output(result, 'send');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
program.command('metrics').description('Read public Atto market metrics').action(() => call('metrics_get'));
|
|
261
|
+
program.command('quote').description('Preview a USD-to-Atto conversion using the indicative market price; does not send funds')
|
|
262
|
+
.requiredOption('--usd <amount>', 'Exact USD amount')
|
|
263
|
+
.action(options => call('price_quote', { amount: options.usd }));
|
|
264
|
+
const terms = program.command('terms').description('Terms for USD-priced payments');
|
|
265
|
+
terms.command('show').description('Read current terms and required acceptance version').action(() => call('terms_get'));
|
|
266
|
+
terms.command('accept').description('Record explicit acceptance after reading the current terms')
|
|
267
|
+
.requiredOption('--version <version>', 'Exact version returned by terms show')
|
|
268
|
+
.requiredOption('--accepted', 'Confirm that you read and accept those terms')
|
|
269
|
+
.action(options => call('terms_accept', { version: options.version, accepted: options.accepted }));
|
|
270
|
+
program.command('receive <hash>').description('Receive one pending payment')
|
|
271
|
+
.option('--index <index>', 'Receiving wallet account index (default: 0)', number)
|
|
272
|
+
.option('--representative <address>', 'Representative when opening an account')
|
|
273
|
+
.action((hash, options) => call('receive', { hash, ...options }));
|
|
274
|
+
program.command('receive-all').description('Receive a bounded batch for account 0 by default; use wallet receive for continuous receiving')
|
|
275
|
+
.option('--index <index>', 'Receiving wallet account index (default: 0)', number)
|
|
276
|
+
.option('--limit <count>', 'Maximum payments to receive (1-1000; default: 100)', number)
|
|
277
|
+
.option('--timeout-ms <milliseconds>', 'Pending-payment scan window (100-30000 ms; default: 2000)', number)
|
|
278
|
+
.option('--representative <address>', 'Representative when opening an account')
|
|
279
|
+
.action(options => call('receive_all', options));
|
|
280
|
+
const representative = program.command('representative').description('Account representatives and voting weight');
|
|
281
|
+
representative.command('weight <address>').description('Read any representative address voting weight').action(address => call('representative_weight', { address }));
|
|
282
|
+
representative.command('change <address>').description('Publish a representative change for an existing account')
|
|
283
|
+
.option('--index <index>', 'Wallet account index (default: 0)', number)
|
|
284
|
+
.action((representative, options) => call('representative_change', { representative, ...options }));
|
|
285
|
+
const limits = program.command('limits').description('Shared spending budgets and usage');
|
|
286
|
+
limits.command('status').description('Read active limits, spending usage, and pending proposals').action(() => call('limits_get'));
|
|
287
|
+
const proposeAndApprove = async (update) => {
|
|
288
|
+
requireTerminal();
|
|
289
|
+
const current = await app().call('limits_get');
|
|
290
|
+
const changes = update(current);
|
|
291
|
+
const { proposal } = await app().call('limits_propose', compact({ policy: changes.policy ?? current.policy, access: changes.access ?? current.mcpAccess, pool: changes.pool }));
|
|
292
|
+
output(await approveLimitsProposal({ id: proposal.id, directory: app().store.directory }));
|
|
293
|
+
};
|
|
294
|
+
limits.command('set').description('Review and confirm limit changes; omitted rules are preserved')
|
|
295
|
+
.option('--per-payment <amount>', 'Maximum amount per payment')
|
|
296
|
+
.option('--daily <amount>', 'Maximum total across a rolling 24 hours; preserves other windows')
|
|
297
|
+
.option('--unit <unit>', 'ATTO (default) or RAW for the supplied amount flags')
|
|
298
|
+
.option('--input <json>', 'Replace the entire policy using JSON: {"perRequest":null,"rolling":[]}')
|
|
299
|
+
.option('--access <mode>', 'MCP access: read-only or spend; defaults to current access')
|
|
300
|
+
.action(async (options) => {
|
|
301
|
+
if (options.input !== undefined && [options.perPayment, options.daily, options.unit].some(value => value !== undefined))
|
|
302
|
+
throw new AttoError('INVALID_INPUT', 'Choose policy JSON or amount flags, not both.');
|
|
303
|
+
if ([options.input, options.perPayment, options.daily, options.access].every(value => value === undefined))
|
|
304
|
+
throw new AttoError('INVALID_INPUT', 'Supply --per-payment, --daily, --input, or --access.');
|
|
305
|
+
if (options.unit !== undefined && options.perPayment === undefined && options.daily === undefined)
|
|
306
|
+
throw new AttoError('INVALID_INPUT', '--unit requires --per-payment or --daily.');
|
|
307
|
+
return proposeAndApprove(current => ({
|
|
308
|
+
policy: options.input !== undefined ? json(options.input) : {
|
|
309
|
+
perRequest: options.perPayment === undefined ? current.policy.perRequest : { amount: options.perPayment, unit: options.unit ?? 'ATTO' },
|
|
310
|
+
rolling: options.daily === undefined ? current.policy.rolling : [...current.policy.rolling.filter(rule => rule.days !== 1), { days: 1, amount: options.daily, unit: options.unit ?? 'ATTO' }],
|
|
311
|
+
}, access: options.access,
|
|
312
|
+
}));
|
|
313
|
+
});
|
|
314
|
+
limits.command('clear').description('Remove all spending limits, preserving historical usage')
|
|
315
|
+
.action(() => proposeAndApprove(() => ({ policy: { perRequest: null, rolling: [] } })));
|
|
316
|
+
limits.command('approve <id>').description('Review and approve an immutable proposal in this terminal')
|
|
317
|
+
.action(async (id) => output(await approveLimitsProposal({ id, directory: program.opts().dataDir })));
|
|
318
|
+
limits.command('reject <id>').description('Review and reject an immutable proposal in this terminal')
|
|
319
|
+
.action(async (id) => output(await rejectLimitsProposal({ id, directory: program.opts().dataDir })));
|
|
320
|
+
const pool = program.command('pool').description('Accounts available for automatic payment selection');
|
|
321
|
+
pool.command('status').description('Read pool membership, balances, and readiness').action(() => call('pool_get'));
|
|
322
|
+
pool.command('configure').description('Propose and locally approve account-pool settings')
|
|
323
|
+
.option('--indexes <indexes>', 'Unique comma-separated account indexes, up to 100', value => value.split(',').map(value => number(value.trim())))
|
|
324
|
+
.option('--consolidate', 'Allow moving pool funds together before an automatic payment')
|
|
325
|
+
.option('--no-consolidate', 'Disable consolidation; omission preserves the current setting')
|
|
326
|
+
.action(async (options) => {
|
|
327
|
+
if (options.indexes === undefined && options.consolidate === undefined)
|
|
328
|
+
throw new AttoError('INVALID_INPUT', 'Supply --indexes, --consolidate, or --no-consolidate.');
|
|
329
|
+
return proposeAndApprove(current => ({ pool: { indexes: options.indexes ?? current.pool.indexes, consolidate: options.consolidate ?? current.pool.consolidate } }));
|
|
330
|
+
});
|
|
331
|
+
const journal = program.command('journal').description('Local payment history, metadata, and progress');
|
|
332
|
+
journal.command('list').description('List newest payment records first')
|
|
333
|
+
.option('--status <status>', 'reserved, signed, published, unknown, or failed')
|
|
334
|
+
.option('--limit <count>', 'Maximum returned records (1-100; defaults to 50)', number)
|
|
335
|
+
.option('--cursor <cursor>', 'Continue a previous journal page with the same status filter')
|
|
336
|
+
.action(options => call('journal_list', options));
|
|
337
|
+
journal.command('show <request-id>').description('Read one payment record by request ID')
|
|
338
|
+
.action(requestId => call('journal_get', { requestId }));
|
|
339
|
+
streamOptions(program.command('watch <event>').description('Observe events for active wallet accounts by default until Ctrl+C; does not receive funds'))
|
|
340
|
+
.option('--network-wide', 'Watch the whole network instead of active wallet accounts; unavailable for receivables')
|
|
341
|
+
.action(async (event, options) => {
|
|
342
|
+
const watch = await app().call('watch_start', compact({ event, ...options }));
|
|
343
|
+
output(watch, 'watch_start');
|
|
344
|
+
try {
|
|
345
|
+
await waitForSignal(async (signal) => {
|
|
346
|
+
let cursor;
|
|
347
|
+
let lastStatus;
|
|
348
|
+
while (!signal.aborted) {
|
|
349
|
+
const result = await app().call('watch_read', compact({ id: watch.id, cursor }));
|
|
350
|
+
const status = JSON.stringify([result.status, result.lastError]);
|
|
351
|
+
if (result.events.length || result.gapDetected || status !== lastStatus)
|
|
352
|
+
output(result, 'watch_read');
|
|
353
|
+
lastStatus = status;
|
|
354
|
+
cursor = result.nextCursor;
|
|
355
|
+
if (['stopped', 'completed'].includes(result.status) && result.events.length === 0)
|
|
356
|
+
break;
|
|
357
|
+
await delay(250, undefined, { signal }).catch(error => { if (!signal.aborted)
|
|
358
|
+
throw error; });
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
await app().call('watch_stop', { id: watch.id });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
program.command('operations [name]').description('List shared operations or inspect one input schema')
|
|
367
|
+
.action(name => {
|
|
368
|
+
if (name === undefined)
|
|
369
|
+
return output(operations.map(({ name, description, readOnly }) => ({ name, description, readOnly })), 'operations');
|
|
370
|
+
const operation = operations.find(operation => operation.name === name);
|
|
371
|
+
if (!operation)
|
|
372
|
+
throw new AttoError('UNKNOWN_OPERATION', 'Unknown Atto operation. Run atto operations to list available names.');
|
|
373
|
+
output({ name: operation.name, description: operation.description, readOnly: operation.readOnly, inputSchema: z.toJSONSchema(operation.schema, { io: 'input' }) });
|
|
374
|
+
});
|
|
375
|
+
program.command('call <operation>').description('Call a single operation with JSON input; use watch for streaming')
|
|
376
|
+
.option('--input <json>', 'Operation arguments as JSON', '{}')
|
|
377
|
+
.action((operation, options) => {
|
|
378
|
+
if (['watch_start', 'watch_list', 'watch_read', 'watch_stop'].includes(operation))
|
|
379
|
+
throw new AttoError('SESSION_REQUIRED', 'Use atto watch for terminal streaming, or MCP for persistent watch IDs.');
|
|
380
|
+
return call(operation, json(options.input));
|
|
381
|
+
});
|
|
382
|
+
const parserCommand = configureHelp(program, {
|
|
383
|
+
atto: 'atto wallet create\n atto balances\n atto send <address> 1\n atto wallet receive',
|
|
384
|
+
'atto doctor': 'atto doctor\n atto --json --data-dir <wallet-directory> doctor',
|
|
385
|
+
'atto wallet': 'atto wallet status', 'atto wallet status': 'atto wallet status',
|
|
386
|
+
'atto wallet configure': 'atto wallet configure --node-url https://node-public.live.application.atto.cash --auto-receive',
|
|
387
|
+
'atto wallet create': 'atto wallet create', 'atto wallet import': 'atto wallet import',
|
|
388
|
+
'atto wallet reset': 'atto wallet reset', 'atto wallet backup': 'atto wallet backup', 'atto wallet receive': 'atto wallet receive',
|
|
389
|
+
'atto address': 'atto address add\n atto address list', 'atto address list': 'atto address list',
|
|
390
|
+
'atto address add': 'atto address add', 'atto address derive': 'atto address derive 3',
|
|
391
|
+
'atto address activate': 'atto address activate 1', 'atto address deactivate': 'atto address deactivate 1',
|
|
392
|
+
'atto account': 'atto account --index 0', 'atto balances': 'atto balances\n atto balances --all',
|
|
393
|
+
'atto transaction': 'atto transaction <hash>', 'atto entry': 'atto entry <hash>',
|
|
394
|
+
'atto history': 'atto history --index 0\n atto history transaction --addresses <address>',
|
|
395
|
+
'atto receivables': 'atto receivables --index 1',
|
|
396
|
+
'atto labels': 'atto labels set 1 \"Savings\"\n atto labels list --all',
|
|
397
|
+
'atto labels set': 'atto labels set 1 \"Savings\"', 'atto labels remove': 'atto labels remove 1',
|
|
398
|
+
'atto labels show': 'atto labels show 1', 'atto labels list': 'atto labels list --all --search treasury',
|
|
399
|
+
'atto send': 'atto send <address> 1\n atto send --to-index 1 --amount 1\n atto send --to-label "Savings" --amount 1\n atto send <address> --usd 1',
|
|
400
|
+
'atto metrics': 'atto metrics', 'atto quote': 'atto quote --usd 1',
|
|
401
|
+
'atto terms': 'atto terms show', 'atto terms show': 'atto terms show',
|
|
402
|
+
'atto terms accept': 'atto terms accept --version <version-shown-by-terms-show> --accepted',
|
|
403
|
+
'atto receive': 'atto receive <send-hash> --index 1', 'atto receive-all': 'atto receive-all --index 1',
|
|
404
|
+
'atto representative': 'atto representative weight <address>',
|
|
405
|
+
'atto representative weight': 'atto representative weight <address>',
|
|
406
|
+
'atto representative change': 'atto representative change <address> --index 0',
|
|
407
|
+
'atto limits': 'atto limits status', 'atto limits status': 'atto limits status',
|
|
408
|
+
'atto limits set': 'atto limits set --per-payment 10 --daily 25', 'atto limits clear': 'atto limits clear',
|
|
409
|
+
'atto limits approve': 'atto limits approve <proposal-id>', 'atto limits reject': 'atto limits reject <proposal-id>',
|
|
410
|
+
'atto pool': 'atto pool status', 'atto pool status': 'atto pool status',
|
|
411
|
+
'atto pool configure': 'atto pool configure --indexes 0,1 --consolidate\n atto pool configure --no-consolidate',
|
|
412
|
+
'atto journal': 'atto journal list', 'atto journal list': 'atto journal list --status unknown',
|
|
413
|
+
'atto journal show': 'atto journal show <request-id>',
|
|
414
|
+
'atto watch': 'atto watch transaction\n atto watch account --addresses <address>\n atto watch transaction --network-wide',
|
|
415
|
+
'atto operations': 'atto operations send',
|
|
416
|
+
'atto call': 'atto --json call account_get --input \'{"index":0}\'',
|
|
417
|
+
});
|
|
418
|
+
try {
|
|
419
|
+
await program.parseAsync(argv);
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
if (error instanceof CommanderError) {
|
|
423
|
+
process.exitCode = error.exitCode;
|
|
424
|
+
if (!error.exitCode)
|
|
425
|
+
return;
|
|
426
|
+
if (error.code === 'commander.help' && parserCommand() === program && !program.opts().json) {
|
|
427
|
+
program.outputHelp();
|
|
428
|
+
process.exitCode = 0;
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const message = commandErrorMessage(error);
|
|
432
|
+
if (program.opts().json) {
|
|
433
|
+
jsonOutput({ error: { code: 'INVALID_INPUT', message } });
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
process.stderr.write(`Error: ${message}\n\n${parserCommand().helpInformation({ error: true })}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
const failure = errorResult(error);
|
|
441
|
+
if (sendRequestId)
|
|
442
|
+
failure.details = { ...failure.details, requestId: sendRequestId };
|
|
443
|
+
if (program.opts().json)
|
|
444
|
+
jsonOutput({ error: failure });
|
|
445
|
+
else {
|
|
446
|
+
process.stderr.write(`${failure.code === 'CANCELLED' ? '' : 'Error: '}${failure.message}\n`);
|
|
447
|
+
if (['INVALID_INPUT', 'INVALID_ADDRESS', 'INVALID_AMOUNT', 'INVALID_POLICY', 'INVALID_FILTER', 'INVALID_HEIGHT', 'INVALID_CURSOR'].includes(failure.code)) {
|
|
448
|
+
const fields = failure.details?.fields;
|
|
449
|
+
if (fields?.length)
|
|
450
|
+
process.stderr.write(`Check fields: ${fields.join(', ')}.\n`);
|
|
451
|
+
process.stderr.write(`\n${activeCommand.helpInformation()}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
process.exitCode = 1;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
finally {
|
|
458
|
+
await application?.close();
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Command, CommanderError } from 'commander';
|
|
2
|
+
/** Keep nested parser errors attached to the command whose input was invalid. */
|
|
3
|
+
export declare function configureHelp(program: Command, examples: Record<string, string>): () => Command;
|
|
4
|
+
export declare function commandErrorMessage(error: CommanderError): string;
|