@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,424 @@
|
|
|
1
|
+
import { bindDestination } from './destination.js';
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
+
import { AttoAmount, AttoOpenBlock, AttoReceiveBlock, AttoSendBlock, AttoUnit, toAttoIndex, } from '@attocash/commons-core';
|
|
5
|
+
import { amountOutput, amountRaw } from '../domain/amount.js';
|
|
6
|
+
import { AttoError } from '../domain/errors.js';
|
|
7
|
+
import { NodeReader, parseAddress, publicModel } from '../network/reader.js';
|
|
8
|
+
import { retryNetwork } from '../network/retry.js';
|
|
9
|
+
import { marketTerms } from '../pricing/terms.js';
|
|
10
|
+
import { derivedSigner, signingWallet } from '../wallet/signing.js';
|
|
11
|
+
import { planPayment } from './pool.js';
|
|
12
|
+
import { reconcileBlock, reconcileSend } from './reconcile.js';
|
|
13
|
+
/** Owns payment selection, reservations, publication and recovery as one workflow. */
|
|
14
|
+
export class Payments {
|
|
15
|
+
store;
|
|
16
|
+
ledger;
|
|
17
|
+
wallet;
|
|
18
|
+
market;
|
|
19
|
+
work;
|
|
20
|
+
retry;
|
|
21
|
+
onDestination;
|
|
22
|
+
constructor(store, ledger, wallet, market, work, retry, onDestination) {
|
|
23
|
+
this.store = store;
|
|
24
|
+
this.ledger = ledger;
|
|
25
|
+
this.wallet = wallet;
|
|
26
|
+
this.market = market;
|
|
27
|
+
this.work = work;
|
|
28
|
+
this.retry = retry;
|
|
29
|
+
this.onDestination = onDestination;
|
|
30
|
+
}
|
|
31
|
+
reader() { return new NodeReader(this.wallet.settings()); }
|
|
32
|
+
indexes(record) { return record.plan?.indexes ?? [record.index]; }
|
|
33
|
+
result(transaction) {
|
|
34
|
+
return { status: 'published', hash: transaction.hash.toString(), transaction: publicModel(transaction) };
|
|
35
|
+
}
|
|
36
|
+
paymentResult(record, transaction) {
|
|
37
|
+
const quote = record.quote ?? this.store.get(`send.quote.${record.id}`);
|
|
38
|
+
return { ...this.result(transaction), requestId: record.id, index: record.index,
|
|
39
|
+
sourceAddress: record.sourceAddress ?? transaction.address.value, destination: record.destination,
|
|
40
|
+
...(record.destinationBinding ? { destinationBinding: record.destinationBinding } : {}),
|
|
41
|
+
...(quote ? { quote } : {}), ...(record.metadata ? { metadata: record.metadata } : {}),
|
|
42
|
+
...(record.plan?.steps.length ? { consolidation: record.plan.steps.map(({ id, kind, index, hash, raw }) => ({ id, kind, index, hash, raw })) } : {}) };
|
|
43
|
+
}
|
|
44
|
+
checkRequest(record, request) {
|
|
45
|
+
const quote = (record.quote ?? this.store.get(`send.quote.${record.id}`));
|
|
46
|
+
const raw = request.unit === 'USD' ? quote?.amount.raw : amountRaw(request.amount, request.unit);
|
|
47
|
+
if (record.destination !== parseAddress(request.destination).value || record.raw !== raw
|
|
48
|
+
|| (request.index !== undefined && request.index !== record.index)
|
|
49
|
+
|| (request.unit === 'USD' ? quote?.usd !== request.amount : Boolean(quote))
|
|
50
|
+
|| (request.metadata !== undefined && !isDeepStrictEqual(record.metadata, request.metadata))) {
|
|
51
|
+
throw new AttoError('REQUEST_CONFLICT', 'This request ID belongs to a different payment. Keep the original destination, amount, source and metadata.');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
requireApproved(record) {
|
|
55
|
+
this.wallet.requireWrite();
|
|
56
|
+
this.ledger.assertReservationAllowed(record.id);
|
|
57
|
+
if (record.network && record.network !== this.wallet.settings().network)
|
|
58
|
+
throw new AttoError('NETWORK_MISMATCH', 'The payment belongs to a different network.');
|
|
59
|
+
const pool = this.ledger.pool();
|
|
60
|
+
if ((this.wallet.mcp || record.selection === 'automatic' || record.plan?.steps.length)
|
|
61
|
+
&& this.indexes(record).some(index => !pool.indexes.includes(index))) {
|
|
62
|
+
throw new AttoError('POOL_APPROVAL_REQUIRED', 'The payment uses an account outside the approved pool. Review the pool in a local terminal.');
|
|
63
|
+
}
|
|
64
|
+
if (record.plan?.steps.length && !pool.consolidate)
|
|
65
|
+
throw new AttoError('CONSOLIDATION_REQUIRED', 'Approve consolidation before continuing this payment.');
|
|
66
|
+
}
|
|
67
|
+
/** Caller owns this account's lock; unresolved plans keep their accounts reserved. */
|
|
68
|
+
requireAvailable(index) {
|
|
69
|
+
const record = this.ledger.pending().find(record => this.indexes(record).includes(index));
|
|
70
|
+
if (record)
|
|
71
|
+
throw new AttoError('ACCOUNT_RESERVED', 'This account belongs to an unfinished payment. Resume that request first.', { requestId: record.id });
|
|
72
|
+
}
|
|
73
|
+
async send(request) {
|
|
74
|
+
const binding = bindDestination(this.store, this.ledger, this.wallet.settings().network, request);
|
|
75
|
+
request = { ...request, destination: binding.address };
|
|
76
|
+
this.onDestination?.(binding);
|
|
77
|
+
const existing = this.ledger.get(request.requestId);
|
|
78
|
+
if (existing)
|
|
79
|
+
return this.resume(existing, request);
|
|
80
|
+
await this.reconcile();
|
|
81
|
+
let quote;
|
|
82
|
+
if (request.unit === 'USD') {
|
|
83
|
+
if (this.wallet.settings().network !== 'LIVE')
|
|
84
|
+
throw new AttoError('USD_NETWORK', 'The metrics USD price applies only to the LIVE network.');
|
|
85
|
+
if (this.store.get('market.terms')?.version !== marketTerms.version) {
|
|
86
|
+
throw new AttoError('TERMS_REQUIRED', 'Read and acknowledge the market-data terms before sending a USD-denominated amount.', marketTerms);
|
|
87
|
+
}
|
|
88
|
+
quote = await retryNetwork(() => this.market.quoteUsd(request.amount, this.retry?.signal), this.retry);
|
|
89
|
+
}
|
|
90
|
+
const raw = quote?.amount.raw ?? amountRaw(request.amount, request.unit === 'USD' ? 'RAW' : request.unit);
|
|
91
|
+
const destination = parseAddress(request.destination).value;
|
|
92
|
+
const deadline = Date.now() + 60_000;
|
|
93
|
+
for (;;) {
|
|
94
|
+
const releases = new Map();
|
|
95
|
+
let busy = false;
|
|
96
|
+
let reserved = false;
|
|
97
|
+
let retry = false;
|
|
98
|
+
try {
|
|
99
|
+
const snapshot = await this.store.withWalletLock(async () => {
|
|
100
|
+
this.wallet.requireWrite();
|
|
101
|
+
if (binding.network !== this.wallet.settings().network)
|
|
102
|
+
throw new AttoError('NETWORK_MISMATCH', 'The payment belongs to another network.');
|
|
103
|
+
const prior = this.ledger.get(request.requestId);
|
|
104
|
+
if (prior)
|
|
105
|
+
return { prior };
|
|
106
|
+
if (quote && this.wallet.settings().network !== 'LIVE')
|
|
107
|
+
throw new AttoError('USD_NETWORK', 'The metrics USD price applies only to the LIVE network.');
|
|
108
|
+
if (quote && this.store.get('market.terms')?.version !== marketTerms.version) {
|
|
109
|
+
throw new AttoError('TERMS_REQUIRED', 'Acknowledge the current market-data terms before sending a USD-denominated amount.');
|
|
110
|
+
}
|
|
111
|
+
this.ledger.assertCanReserve(raw);
|
|
112
|
+
const pool = this.ledger.pool();
|
|
113
|
+
const indexes = request.index === undefined ? pool.indexes : [request.index];
|
|
114
|
+
if (this.wallet.mcp && indexes.some(index => !pool.indexes.includes(index))) {
|
|
115
|
+
throw new AttoError('POOL_APPROVAL_REQUIRED', 'The source is outside the approved account pool.');
|
|
116
|
+
}
|
|
117
|
+
const addresses = indexes.map(index => {
|
|
118
|
+
const address = this.wallet.addresses().find(address => address.index === index);
|
|
119
|
+
if (!address)
|
|
120
|
+
throw new AttoError('ADDRESS_NOT_DERIVED', 'Derive the source index or approve the account pool first.');
|
|
121
|
+
return address;
|
|
122
|
+
}).filter(address => address.address !== destination);
|
|
123
|
+
if (!addresses.length)
|
|
124
|
+
throw new AttoError('SELF_SEND', 'Choose a destination different from the source address.');
|
|
125
|
+
const pending = new Set(this.ledger.pending().flatMap(record => this.indexes(record)));
|
|
126
|
+
for (const address of addresses) {
|
|
127
|
+
const release = this.store.tryAccountLocks([address.index]);
|
|
128
|
+
if (!release) {
|
|
129
|
+
busy = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (pending.has(address.index)) {
|
|
133
|
+
reserved = true;
|
|
134
|
+
release();
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
releases.set(address.index, release);
|
|
138
|
+
}
|
|
139
|
+
return { settings: this.wallet.settings(), pool, addresses: addresses.filter(address => releases.has(address.index)) };
|
|
140
|
+
});
|
|
141
|
+
if ('prior' in snapshot)
|
|
142
|
+
return this.resume(snapshot.prior, request);
|
|
143
|
+
const reader = new NodeReader(snapshot.settings);
|
|
144
|
+
const candidates = [];
|
|
145
|
+
for (const address of snapshot.addresses) {
|
|
146
|
+
const account = await retryNetwork(() => reader.account(address.address, this.retry?.signal), this.retry);
|
|
147
|
+
if (!account)
|
|
148
|
+
continue;
|
|
149
|
+
this.checkAccount(address, account, snapshot.settings);
|
|
150
|
+
candidates.push({ address, account, workReady: this.work.isReady(account) });
|
|
151
|
+
}
|
|
152
|
+
let chosen;
|
|
153
|
+
try {
|
|
154
|
+
if (request.index !== undefined && !candidates.length && !busy && !reserved)
|
|
155
|
+
throw new AttoError('ACCOUNT_NOT_OPEN', 'Receive funds to open this account before sending.');
|
|
156
|
+
chosen = planPayment(candidates, raw, request.index === undefined && snapshot.pool.consolidate);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (busy && Date.now() < deadline) {
|
|
160
|
+
retry = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (busy || reserved)
|
|
164
|
+
throw new AttoError('POOL_BUSY', 'Eligible funds are busy or reserved by unfinished payments. Retry after those payments settle.');
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
let raced = false;
|
|
168
|
+
const record = await this.store.withWalletLock(async () => {
|
|
169
|
+
this.wallet.requireWrite();
|
|
170
|
+
if (binding.network !== this.wallet.settings().network)
|
|
171
|
+
throw new AttoError('NETWORK_MISMATCH', 'The payment belongs to another network.');
|
|
172
|
+
const prior = this.ledger.get(request.requestId);
|
|
173
|
+
if (prior) {
|
|
174
|
+
raced = true;
|
|
175
|
+
return prior;
|
|
176
|
+
}
|
|
177
|
+
if (!isDeepStrictEqual(snapshot.settings, this.wallet.settings()) || !isDeepStrictEqual(snapshot.pool, this.ledger.pool())) {
|
|
178
|
+
throw new AttoError('POOL_CHANGED', 'Wallet configuration changed during account selection. Retry this request.');
|
|
179
|
+
}
|
|
180
|
+
return this.ledger.reserve({ id: request.requestId, index: chosen.source.address.index, destination, raw,
|
|
181
|
+
sourceAddress: chosen.source.address.address, network: snapshot.settings.network,
|
|
182
|
+
selection: request.index === undefined ? 'automatic' : 'explicit', plan: chosen.plan,
|
|
183
|
+
metadata: request.metadata, destinationBinding: binding, quote, createdAt: Date.now() });
|
|
184
|
+
});
|
|
185
|
+
if (raced) {
|
|
186
|
+
for (const release of releases.values())
|
|
187
|
+
release();
|
|
188
|
+
releases.clear();
|
|
189
|
+
return this.resume(record, request);
|
|
190
|
+
}
|
|
191
|
+
this.checkRequest(record, request);
|
|
192
|
+
// Release accounts that were considered but are not part of this payment.
|
|
193
|
+
for (const [index, release] of releases)
|
|
194
|
+
if (!this.indexes(record).includes(index)) {
|
|
195
|
+
release();
|
|
196
|
+
releases.delete(index);
|
|
197
|
+
}
|
|
198
|
+
if (this.indexes(record).some(index => !releases.has(index))) {
|
|
199
|
+
for (const release of releases.values())
|
|
200
|
+
release();
|
|
201
|
+
releases.clear();
|
|
202
|
+
return this.resume(record, request);
|
|
203
|
+
}
|
|
204
|
+
return await this.execute(record);
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
for (const release of releases.values())
|
|
208
|
+
release();
|
|
209
|
+
if (retry)
|
|
210
|
+
await delay(25);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
checkAccount(address, account, settings) {
|
|
215
|
+
if (account.address.value !== address.address || account.network.name !== settings.network) {
|
|
216
|
+
throw new AttoError('INVALID_NODE_RESPONSE', 'The node account does not match the requested source and network.');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async resume(record, request) {
|
|
220
|
+
this.checkRequest(record, request);
|
|
221
|
+
if (record.status === 'published')
|
|
222
|
+
return record.result;
|
|
223
|
+
if (record.status === 'failed')
|
|
224
|
+
throw new AttoError('REQUEST_FAILED', 'This payment failed. Inspect its journal before creating a new payment request.');
|
|
225
|
+
return this.store.withAccountLocks(this.indexes(record), async () => {
|
|
226
|
+
await this.reconcileRecord(this.ledger.get(record.id));
|
|
227
|
+
const current = this.ledger.get(record.id);
|
|
228
|
+
if (current.status === 'published')
|
|
229
|
+
return current.result;
|
|
230
|
+
if (current.status === 'failed')
|
|
231
|
+
throw new AttoError('REQUEST_FAILED', 'The payment was excluded from the account chain. Inspect its journal before creating a new request.');
|
|
232
|
+
if (current.hash || !current.plan || current.plan.steps.some(step => step.status === 'signed' || step.status === 'unknown'))
|
|
233
|
+
this.uncertain(current);
|
|
234
|
+
return this.execute(current);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
uncertain(record) {
|
|
238
|
+
throw new AttoError('PUBLICATION_UNCERTAIN', 'Publication is unresolved. Reuse this request ID to reconcile the existing payment.', { requestId: record.id, hash: record.hash, steps: record.plan?.steps.map(({ id, hash, status }) => ({ id, hash, status })) });
|
|
239
|
+
}
|
|
240
|
+
async execute(record) {
|
|
241
|
+
let seed;
|
|
242
|
+
try {
|
|
243
|
+
await this.store.withWalletLock(async () => this.requireApproved(record));
|
|
244
|
+
seed = await this.wallet.seed();
|
|
245
|
+
// Internal-transfer exemptions require ownership derived from the key,
|
|
246
|
+
// never metadata supplied by a caller or an address string alone.
|
|
247
|
+
for (const index of this.indexes(record)) {
|
|
248
|
+
const signer = await derivedSigner(seed, index);
|
|
249
|
+
const expected = index === record.index ? record.sourceAddress : record.plan?.steps.find(step => step.index === index)?.sourceAddress;
|
|
250
|
+
if (expected && signer.address.value !== expected)
|
|
251
|
+
throw new AttoError('WALLET_MISMATCH', 'A planned source does not match its wallet key.');
|
|
252
|
+
}
|
|
253
|
+
for (const step of record.plan?.steps ?? []) {
|
|
254
|
+
if (step.status === 'published')
|
|
255
|
+
continue;
|
|
256
|
+
if (step.hash)
|
|
257
|
+
this.uncertain(record);
|
|
258
|
+
await this.executeStep(record, step, seed);
|
|
259
|
+
}
|
|
260
|
+
const current = this.ledger.get(record.id);
|
|
261
|
+
const transaction = await this.publish(current, current.index, seed, undefined);
|
|
262
|
+
const result = this.paymentResult(this.ledger.get(record.id), transaction);
|
|
263
|
+
await this.store.withWalletLock(async () => this.ledger.complete(record.id, result, Number(transaction.block.timestamp.toEpochMilliseconds())));
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
const current = this.ledger.get(record.id);
|
|
268
|
+
const recorded = Boolean(current.hash || current.plan?.steps.some(step => step.hash));
|
|
269
|
+
await this.store.withWalletLock(async () => {
|
|
270
|
+
if (recorded)
|
|
271
|
+
this.ledger.uncertain(record.id);
|
|
272
|
+
else
|
|
273
|
+
this.ledger.fail(record.id);
|
|
274
|
+
});
|
|
275
|
+
if (current.hash || current.plan?.steps.some(step => step.status === 'signed' || step.status === 'unknown')) {
|
|
276
|
+
if (this.retry && error instanceof AttoError) {
|
|
277
|
+
throw new AttoError(error.code, `${error.message} Publication is unresolved; check the journal using this request ID.`, { ...error.details, requestId: record.id, hash: current.hash, publicationUncertain: true });
|
|
278
|
+
}
|
|
279
|
+
this.uncertain(this.ledger.get(record.id));
|
|
280
|
+
}
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
seed?.value.fill(0);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async executeStep(record, step, seed) {
|
|
288
|
+
try {
|
|
289
|
+
const transaction = await this.publish(record, step.index, seed, step);
|
|
290
|
+
await this.store.withWalletLock(async () => this.ledger.stepComplete(record.id, step.id, this.result(transaction), Number(transaction.block.timestamp.toEpochMilliseconds())));
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
await this.store.withWalletLock(async () => {
|
|
294
|
+
if (this.ledger.get(record.id).plan.steps.find(value => value.id === step.id).hash)
|
|
295
|
+
this.ledger.stepUncertain(record.id, step.id);
|
|
296
|
+
});
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async publish(record, index, seed, step) {
|
|
301
|
+
const settings = this.wallet.settings();
|
|
302
|
+
let sendHash;
|
|
303
|
+
if (step?.kind === 'receive') {
|
|
304
|
+
sendHash = this.ledger.get(record.id).plan.steps.find(value => value.id === step.sourceStepId).hash;
|
|
305
|
+
if (!sendHash)
|
|
306
|
+
throw new AttoError('SEND_STATE', 'The consolidation transfer has not been published.');
|
|
307
|
+
}
|
|
308
|
+
const execution = await signingWallet(seed, index, settings, async (block) => {
|
|
309
|
+
await this.store.withWalletLock(async () => {
|
|
310
|
+
this.requireApproved(record);
|
|
311
|
+
const source = step?.sourceAddress ?? record.sourceAddress;
|
|
312
|
+
if (source && block.address.value !== source)
|
|
313
|
+
throw new AttoError('TRANSACTION_MISMATCH', 'The transaction source differs from the payment plan.');
|
|
314
|
+
if (step?.kind === 'receive') {
|
|
315
|
+
if (!(block instanceof AttoReceiveBlock || block instanceof AttoOpenBlock) || block.sendHash.toString() !== sendHash) {
|
|
316
|
+
throw new AttoError('TRANSACTION_MISMATCH', 'The receive differs from the planned consolidation transfer.');
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else if (!(block instanceof AttoSendBlock) || block.amount.toString() !== (step?.raw ?? record.raw)
|
|
320
|
+
|| block.receiverAddress.value !== (step?.destination ?? record.destination)) {
|
|
321
|
+
throw new AttoError('TRANSACTION_MISMATCH', 'The constructed transaction differs from the payment plan.');
|
|
322
|
+
}
|
|
323
|
+
if (step)
|
|
324
|
+
this.ledger.stepSigned(record.id, step.id, block.hash.toString(), block.toJson());
|
|
325
|
+
else
|
|
326
|
+
this.ledger.signed(record.id, block.hash.toString(), block.toJson());
|
|
327
|
+
});
|
|
328
|
+
}, this.work.worker(), this.retry);
|
|
329
|
+
try {
|
|
330
|
+
if (step?.kind === 'receive') {
|
|
331
|
+
const receivable = await this.pendingReceivable(step.destination, sendHash);
|
|
332
|
+
if (!receivable || receivable.amount.toString() !== step.raw || receivable.network.name !== settings.network) {
|
|
333
|
+
throw new AttoError('RECEIVABLE_NOT_PENDING', 'The consolidation transfer is not available to receive yet. Resume this payment with the same request ID.');
|
|
334
|
+
}
|
|
335
|
+
const transaction = await execution.wallet.receive(receivable, parseAddress(settings.representative), null);
|
|
336
|
+
const account = await execution.wallet.getAccountByIndex(toAttoIndex(index));
|
|
337
|
+
if (account)
|
|
338
|
+
this.work.prepare([account]);
|
|
339
|
+
return transaction;
|
|
340
|
+
}
|
|
341
|
+
const transaction = await execution.wallet.sendByIndex(toAttoIndex(index), parseAddress(step?.destination ?? record.destination), AttoAmount.from(AttoUnit.RAW, step?.raw ?? record.raw), null);
|
|
342
|
+
const account = await execution.wallet.getAccountByIndex(toAttoIndex(index));
|
|
343
|
+
if (account)
|
|
344
|
+
this.work.prepare([account]);
|
|
345
|
+
return transaction;
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
execution.wallet.close();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async pendingReceivable(address, hash) {
|
|
352
|
+
const done = new AbortController();
|
|
353
|
+
let found;
|
|
354
|
+
await this.reader().stream({ event: 'receivable', addresses: [address], minAmountRaw: '1' }, model => {
|
|
355
|
+
const candidate = model;
|
|
356
|
+
if (candidate.hash.toString() === hash && candidate.receiverAddress.value === address) {
|
|
357
|
+
found = candidate;
|
|
358
|
+
done.abort();
|
|
359
|
+
}
|
|
360
|
+
}, AbortSignal.any([done.signal, AbortSignal.timeout(2000)]));
|
|
361
|
+
return found;
|
|
362
|
+
}
|
|
363
|
+
async reconcile() {
|
|
364
|
+
for (const record of this.ledger.pending()) {
|
|
365
|
+
const release = this.store.tryAccountLocks(this.indexes(record));
|
|
366
|
+
if (!release)
|
|
367
|
+
continue;
|
|
368
|
+
try {
|
|
369
|
+
await this.reconcileRecord(this.ledger.get(record.id));
|
|
370
|
+
}
|
|
371
|
+
finally {
|
|
372
|
+
release();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async reconcileRecord(record) {
|
|
377
|
+
if (record.status === 'published' || record.status === 'failed')
|
|
378
|
+
return;
|
|
379
|
+
for (const step of record.plan?.steps ?? []) {
|
|
380
|
+
if (!step.hash || step.status === 'published')
|
|
381
|
+
continue;
|
|
382
|
+
const outcome = await reconcileBlock(this.reader(), step);
|
|
383
|
+
if (outcome.status === 'unresolved')
|
|
384
|
+
return;
|
|
385
|
+
await this.store.withWalletLock(async () => {
|
|
386
|
+
if (outcome.status === 'rejected')
|
|
387
|
+
this.ledger.stepReject(record.id, step.id);
|
|
388
|
+
else
|
|
389
|
+
this.ledger.stepComplete(record.id, step.id, this.result(outcome.transaction), Number(outcome.transaction.block.timestamp.toEpochMilliseconds()));
|
|
390
|
+
});
|
|
391
|
+
if (outcome.status === 'rejected')
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!record.hash) {
|
|
395
|
+
if (!record.plan)
|
|
396
|
+
await this.store.withWalletLock(async () => this.ledger.fail(record.id));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const outcome = await reconcileSend(this.reader(), record);
|
|
400
|
+
await this.store.withWalletLock(async () => {
|
|
401
|
+
if (outcome.status === 'published')
|
|
402
|
+
this.ledger.complete(record.id, this.paymentResult(this.ledger.get(record.id), outcome.transaction), Number(outcome.transaction.block.timestamp.toEpochMilliseconds()));
|
|
403
|
+
else if (outcome.status === 'rejected')
|
|
404
|
+
this.ledger.reject(record.id);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
async poolStatus() {
|
|
408
|
+
const pool = this.ledger.pool();
|
|
409
|
+
const pending = new Set(this.ledger.pending().flatMap(record => this.indexes(record)));
|
|
410
|
+
const accounts = await Promise.all(pool.indexes.map(async (index) => {
|
|
411
|
+
const address = this.wallet.addresses().find(address => address.index === index);
|
|
412
|
+
const account = address ? await this.reader().account(address.address) : null;
|
|
413
|
+
if (address && account)
|
|
414
|
+
this.checkAccount(address, account, this.wallet.settings());
|
|
415
|
+
const release = this.store.tryAccountLocks([index]);
|
|
416
|
+
const busy = pending.has(index) || !release;
|
|
417
|
+
release?.();
|
|
418
|
+
return { index, address: address?.address ?? null, opened: Boolean(account), balance: amountOutput(account?.balance.toString() ?? '0'),
|
|
419
|
+
workReady: Boolean(account && this.work.isReady(account)), busy };
|
|
420
|
+
}));
|
|
421
|
+
return { pool, accounts, total: amountOutput(accounts.reduce((sum, account) => sum + BigInt(account.balance.raw), 0n).toString()),
|
|
422
|
+
available: amountOutput(accounts.reduce((sum, account) => sum + (account.busy ? 0n : BigInt(account.balance.raw)), 0n).toString()) };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AttoAccount } from '@attocash/commons-core';
|
|
2
|
+
import type { WalletAddress } from '../wallet/types.js';
|
|
3
|
+
import type { PaymentPlan } from './ledger.js';
|
|
4
|
+
export interface PoolAccount {
|
|
5
|
+
address: WalletAddress;
|
|
6
|
+
account: AttoAccount;
|
|
7
|
+
workReady: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Prefer a single debit; otherwise move only the shortfall from the fewest donors. */
|
|
10
|
+
export declare function planPayment(accounts: readonly PoolAccount[], raw: string, consolidate: boolean): {
|
|
11
|
+
source: PoolAccount;
|
|
12
|
+
plan: PaymentPlan;
|
|
13
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AttoError } from '../domain/errors.js';
|
|
2
|
+
/** Prefer a single debit; otherwise move only the shortfall from the fewest donors. */
|
|
3
|
+
export function planPayment(accounts, raw, consolidate) {
|
|
4
|
+
const amount = BigInt(raw);
|
|
5
|
+
const byBalance = (a, b) => {
|
|
6
|
+
const left = BigInt(a.account.balance.toString());
|
|
7
|
+
const right = BigInt(b.account.balance.toString());
|
|
8
|
+
return left === right ? a.address.index - b.address.index : left > right ? -1 : 1;
|
|
9
|
+
};
|
|
10
|
+
const funded = accounts.filter(candidate => BigInt(candidate.account.balance.toString()) >= amount)
|
|
11
|
+
.sort((a, b) => Number(b.workReady) - Number(a.workReady) || byBalance(a, b));
|
|
12
|
+
if (funded[0])
|
|
13
|
+
return { source: funded[0], plan: { indexes: [funded[0].address.index], steps: [] } };
|
|
14
|
+
const ordered = [...accounts].filter(candidate => BigInt(candidate.account.balance.toString()) > 0n).sort(byBalance);
|
|
15
|
+
if (ordered.reduce((sum, candidate) => sum + BigInt(candidate.account.balance.toString()), 0n) < amount) {
|
|
16
|
+
throw new AttoError('INSUFFICIENT_BALANCE', 'The available source accounts cannot cover this payment.');
|
|
17
|
+
}
|
|
18
|
+
if (!consolidate)
|
|
19
|
+
throw new AttoError('CONSOLIDATION_REQUIRED', 'The pool has enough funds across accounts. Approve consolidation or fund one account before sending.');
|
|
20
|
+
const source = ordered[0];
|
|
21
|
+
let missing = amount - BigInt(source.account.balance.toString());
|
|
22
|
+
const indexes = [source.address.index];
|
|
23
|
+
const steps = [];
|
|
24
|
+
for (const donor of ordered.slice(1)) {
|
|
25
|
+
if (missing === 0n)
|
|
26
|
+
break;
|
|
27
|
+
const balance = BigInt(donor.account.balance.toString());
|
|
28
|
+
const transfer = balance < missing ? balance : missing;
|
|
29
|
+
const id = `consolidate-${donor.address.index}`;
|
|
30
|
+
indexes.push(donor.address.index);
|
|
31
|
+
steps.push({ id, kind: 'send', index: donor.address.index, sourceAddress: donor.address.address,
|
|
32
|
+
destination: source.address.address, raw: transfer.toString(), status: 'planned' });
|
|
33
|
+
steps.push({ id: `${id}-receive`, kind: 'receive', index: source.address.index, sourceAddress: source.address.address,
|
|
34
|
+
destination: source.address.address, raw: transfer.toString(), sourceStepId: id, status: 'planned' });
|
|
35
|
+
missing -= transfer;
|
|
36
|
+
}
|
|
37
|
+
return { source, plan: { indexes: indexes.sort((a, b) => a - b), steps } };
|
|
38
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AttoTransaction } from '@attocash/commons-core';
|
|
2
|
+
import type { NodeReader } from '../network/reader.js';
|
|
3
|
+
import type { SendRecord } from './ledger.js';
|
|
4
|
+
export type SendResolution = {
|
|
5
|
+
status: 'published';
|
|
6
|
+
transaction: AttoTransaction;
|
|
7
|
+
} | {
|
|
8
|
+
status: 'rejected';
|
|
9
|
+
} | {
|
|
10
|
+
status: 'unresolved';
|
|
11
|
+
};
|
|
12
|
+
/** Release a reservation only when the configured node proves a canonical conflict. */
|
|
13
|
+
export declare function reconcileSend(reader: Pick<NodeReader, 'settings' | 'transaction' | 'stream'>, record: SendRecord, timeoutMs?: number): Promise<SendResolution>;
|
|
14
|
+
/** The same canonical-position check also protects consolidation receive steps. */
|
|
15
|
+
export declare function reconcileBlock(reader: Pick<NodeReader, 'settings' | 'transaction' | 'stream'>, record: {
|
|
16
|
+
hash?: string;
|
|
17
|
+
blockJson?: string;
|
|
18
|
+
}, timeoutMs?: number): Promise<SendResolution>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AttoBlock, AttoSendBlock, AttoTransaction } from '@attocash/commons-core';
|
|
2
|
+
/** Release a reservation only when the configured node proves a canonical conflict. */
|
|
3
|
+
export async function reconcileSend(reader, record, timeoutMs = 10_000) {
|
|
4
|
+
try {
|
|
5
|
+
if (!record.blockJson)
|
|
6
|
+
return { status: 'unresolved' };
|
|
7
|
+
const block = AttoBlock.fromJson(record.blockJson);
|
|
8
|
+
if (!(block instanceof AttoSendBlock) || block.amount.toString() !== record.raw
|
|
9
|
+
|| block.receiverAddress.value !== record.destination
|
|
10
|
+
|| (record.sourceAddress && block.address.value !== record.sourceAddress))
|
|
11
|
+
return { status: 'unresolved' };
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return { status: 'unresolved' };
|
|
15
|
+
}
|
|
16
|
+
return reconcileBlock(reader, record, timeoutMs);
|
|
17
|
+
}
|
|
18
|
+
/** The same canonical-position check also protects consolidation receive steps. */
|
|
19
|
+
export async function reconcileBlock(reader, record, timeoutMs = 10_000) {
|
|
20
|
+
const unresolved = { status: 'unresolved' };
|
|
21
|
+
const deadline = AbortSignal.timeout(timeoutMs);
|
|
22
|
+
try {
|
|
23
|
+
if (!record.hash || !record.blockJson)
|
|
24
|
+
return unresolved;
|
|
25
|
+
const block = AttoBlock.fromJson(record.blockJson);
|
|
26
|
+
if (!block.isValid()
|
|
27
|
+
|| block.hash.toString() !== record.hash.toUpperCase()
|
|
28
|
+
|| block.network.name !== reader.settings.network)
|
|
29
|
+
return unresolved;
|
|
30
|
+
const samePosition = (transaction) => transaction.address.value === block.address.value
|
|
31
|
+
&& transaction.block.network.name === block.network.name
|
|
32
|
+
&& transaction.height.toString() === block.height.toString();
|
|
33
|
+
const published = await reader.transaction(record.hash, deadline);
|
|
34
|
+
if (published) {
|
|
35
|
+
if (!samePosition(published) || published.hash.toString() !== block.hash.toString()
|
|
36
|
+
|| !await published.isValid() || deadline.aborted)
|
|
37
|
+
return unresolved;
|
|
38
|
+
return { status: 'published', transaction: published };
|
|
39
|
+
}
|
|
40
|
+
if (deadline.aborted)
|
|
41
|
+
return unresolved;
|
|
42
|
+
// The account's exact-height confirmed transaction stream supplies canonical
|
|
43
|
+
// evidence. A missing hash alone never proves that publication is impossible.
|
|
44
|
+
const received = new AbortController();
|
|
45
|
+
let canonical;
|
|
46
|
+
await reader.stream({
|
|
47
|
+
event: 'transaction', addresses: [block.address.value],
|
|
48
|
+
fromHeight: block.height.toString(), toHeight: block.height.toString(),
|
|
49
|
+
}, (model) => {
|
|
50
|
+
if (model instanceof AttoTransaction)
|
|
51
|
+
canonical = model;
|
|
52
|
+
received.abort();
|
|
53
|
+
}, AbortSignal.any([deadline, received.signal]));
|
|
54
|
+
if (!canonical || !samePosition(canonical) || !await canonical.isValid() || deadline.aborted)
|
|
55
|
+
return unresolved;
|
|
56
|
+
return canonical.hash.toString() === block.hash.toString()
|
|
57
|
+
? { status: 'published', transaction: canonical }
|
|
58
|
+
: { status: 'rejected' };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Missing, malformed, unavailable, and timed-out evidence retains allowance.
|
|
62
|
+
return unresolved;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface WalletProfile {
|
|
2
|
+
directory: string;
|
|
3
|
+
credentialAccount: string;
|
|
4
|
+
credentialService: 'Atto CLI' | 'Atto MCP';
|
|
5
|
+
}
|
|
6
|
+
/** Existing installations keep their original wallet directory. */
|
|
7
|
+
export declare function defaultCliDirectory(): string;
|
|
8
|
+
export declare function dedicatedMcpDirectory(): string;
|
|
9
|
+
/** Resolve one shared identity for CLI and MCP. A fresh standard CLI profile
|
|
10
|
+
* receives public metadata before StateStore creates its database. No keys move. */
|
|
11
|
+
export declare function resolveWalletProfile(directory?: string): WalletProfile;
|
|
12
|
+
/** Inspect the same profile selection without creating metadata or wallet state. */
|
|
13
|
+
export declare function inspectWalletProfile(directory?: string): WalletProfile;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { AttoError } from '../domain/errors.js';
|
|
6
|
+
import { defaultDataDirectory } from './state.js';
|
|
7
|
+
function standardCliDirectory() {
|
|
8
|
+
if (process.platform === 'win32')
|
|
9
|
+
return resolve(process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'), 'Atto CLI');
|
|
10
|
+
if (process.platform === 'darwin')
|
|
11
|
+
return resolve(homedir(), 'Library', 'Application Support', 'Atto CLI');
|
|
12
|
+
return resolve(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'atto-cli');
|
|
13
|
+
}
|
|
14
|
+
/** Existing installations keep their original wallet directory. */
|
|
15
|
+
export function defaultCliDirectory() {
|
|
16
|
+
const legacy = resolve(defaultDataDirectory());
|
|
17
|
+
const standard = standardCliDirectory();
|
|
18
|
+
if (readProfile(join(standard, 'profile.json'))?.defaultCli === true)
|
|
19
|
+
return standard;
|
|
20
|
+
return existsSync(join(legacy, 'state.sqlite')) ? legacy : standard;
|
|
21
|
+
}
|
|
22
|
+
export function dedicatedMcpDirectory() {
|
|
23
|
+
return resolve(defaultDataDirectory(), 'profiles', 'mcp');
|
|
24
|
+
}
|
|
25
|
+
function readProfile(file) {
|
|
26
|
+
try {
|
|
27
|
+
const info = statSync(file);
|
|
28
|
+
if (!info.isFile() || info.size > 4096)
|
|
29
|
+
throw new Error();
|
|
30
|
+
const profile = JSON.parse(readFileSync(file, 'utf8'));
|
|
31
|
+
if (profile?.version !== 1 || (profile.credentialService !== 'Atto CLI' && profile.credentialService !== 'Atto MCP')
|
|
32
|
+
|| (profile.defaultCli !== undefined && typeof profile.defaultCli !== 'boolean'))
|
|
33
|
+
throw new Error();
|
|
34
|
+
return { version: 1, credentialService: profile.credentialService,
|
|
35
|
+
...(profile.defaultCli === undefined ? {} : { defaultCli: profile.defaultCli }) };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code === 'ENOENT')
|
|
39
|
+
return undefined;
|
|
40
|
+
throw new AttoError('PROFILE_METADATA', 'The wallet profile metadata is invalid or unavailable. Restore it before opening this wallet.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function cliCredentialService(directory, initialize) {
|
|
44
|
+
const file = join(directory, 'profile.json');
|
|
45
|
+
const existing = readProfile(file);
|
|
46
|
+
if (existing)
|
|
47
|
+
return existing.credentialService;
|
|
48
|
+
// An older --data-dir may already equal the new CLI default. Its credential
|
|
49
|
+
// remains under the original service; never infer a migration from its path.
|
|
50
|
+
if (existsSync(join(directory, 'state.sqlite')))
|
|
51
|
+
return 'Atto MCP';
|
|
52
|
+
if (!initialize)
|
|
53
|
+
return 'Atto CLI';
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
56
|
+
const defaultCli = !existsSync(join(defaultDataDirectory(), 'state.sqlite'));
|
|
57
|
+
writeFileSync(file, JSON.stringify({ version: 1, credentialService: 'Atto CLI', defaultCli }), { flag: 'wx', mode: 0o600 });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error.code !== 'EEXIST') {
|
|
61
|
+
throw new AttoError('PROFILE_METADATA', 'The wallet profile metadata could not be saved. Check directory access before opening this wallet.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const profile = readProfile(file);
|
|
65
|
+
if (!profile)
|
|
66
|
+
throw new AttoError('PROFILE_METADATA', 'The wallet profile metadata disappeared before this wallet could be opened.');
|
|
67
|
+
return profile.credentialService;
|
|
68
|
+
}
|
|
69
|
+
/** Resolve one shared identity for CLI and MCP. A fresh standard CLI profile
|
|
70
|
+
* receives public metadata before StateStore creates its database. No keys move. */
|
|
71
|
+
export function resolveWalletProfile(directory = defaultCliDirectory()) {
|
|
72
|
+
return walletProfile(directory, true);
|
|
73
|
+
}
|
|
74
|
+
/** Inspect the same profile selection without creating metadata or wallet state. */
|
|
75
|
+
export function inspectWalletProfile(directory = defaultCliDirectory()) {
|
|
76
|
+
return walletProfile(directory, false);
|
|
77
|
+
}
|
|
78
|
+
function walletProfile(directory, initialize) {
|
|
79
|
+
const absolute = resolve(directory);
|
|
80
|
+
return {
|
|
81
|
+
directory: absolute,
|
|
82
|
+
credentialAccount: absolute === resolve(defaultDataDirectory())
|
|
83
|
+
? 'default' : createHash('sha256').update(absolute).digest('hex'),
|
|
84
|
+
credentialService: absolute === standardCliDirectory() ? cliCredentialService(absolute, initialize) : 'Atto MCP',
|
|
85
|
+
};
|
|
86
|
+
}
|