@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,98 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { dirname, isAbsolute, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { gt, prerelease, valid } from 'semver';
|
|
8
|
+
const checkInterval = 24 * 60 * 60 * 1000;
|
|
9
|
+
const maxCacheBytes = 4096;
|
|
10
|
+
function stableVersion(value) {
|
|
11
|
+
if (typeof value !== 'string')
|
|
12
|
+
return undefined;
|
|
13
|
+
const version = valid(value);
|
|
14
|
+
return version && prerelease(version) === null ? version : undefined;
|
|
15
|
+
}
|
|
16
|
+
function readCache(file) {
|
|
17
|
+
try {
|
|
18
|
+
if (statSync(file).size > maxCacheBytes)
|
|
19
|
+
return undefined;
|
|
20
|
+
const cache = JSON.parse(readFileSync(file, 'utf8'));
|
|
21
|
+
if (!cache || typeof cache.checkedAt !== 'number' || !Number.isFinite(cache.checkedAt) || cache.checkedAt < 0)
|
|
22
|
+
return undefined;
|
|
23
|
+
return { checkedAt: cache.checkedAt, latest: stableVersion(cache.latest) };
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function writeCache(file, cache) {
|
|
30
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
31
|
+
try {
|
|
32
|
+
mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
|
|
33
|
+
writeFileSync(temporary, JSON.stringify(cache), { mode: 0o600, flag: 'wx' });
|
|
34
|
+
renameSync(temporary, file);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
rmSync(temporary, { force: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Only the CLI calls this module; shared wallet operations and MCP have no
|
|
41
|
+
// update-check side effects. Network work runs in an independent, short-lived child.
|
|
42
|
+
export function notifyUpdate(currentVersion) {
|
|
43
|
+
try {
|
|
44
|
+
if (!process.stdout.isTTY || !process.stderr.isTTY ||
|
|
45
|
+
(process.env.CI && process.env.CI !== 'false') || process.env.NODE_ENV === 'test' ||
|
|
46
|
+
process.env.NO_UPDATE_NOTIFIER !== undefined || !valid(currentVersion))
|
|
47
|
+
return;
|
|
48
|
+
const directory = process.platform === 'win32'
|
|
49
|
+
? join(process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'), 'atto-cli', 'Cache')
|
|
50
|
+
: process.platform === 'darwin'
|
|
51
|
+
? join(homedir(), 'Library', 'Caches', 'atto-cli')
|
|
52
|
+
: join(process.env.XDG_CACHE_HOME && isAbsolute(process.env.XDG_CACHE_HOME)
|
|
53
|
+
? process.env.XDG_CACHE_HOME : join(homedir(), '.cache'), 'atto-cli');
|
|
54
|
+
const file = join(directory, 'update.json');
|
|
55
|
+
const cache = readCache(file);
|
|
56
|
+
if (cache?.latest && gt(cache.latest, currentVersion)) {
|
|
57
|
+
process.stderr.write(`Atto CLI update available: ${currentVersion} → ${cache.latest}\nUpdate: npm install --global @attocash/cli@${cache.latest}\n`);
|
|
58
|
+
}
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
const age = cache ? now - cache.checkedAt : undefined;
|
|
61
|
+
if (age !== undefined && age >= 0 && age < checkInterval)
|
|
62
|
+
return;
|
|
63
|
+
// Reserve the attempt before spawning, so an unavailable registry or a
|
|
64
|
+
// failed child does not cause a new request on every command invocation.
|
|
65
|
+
writeCache(file, { checkedAt: now, latest: cache?.latest });
|
|
66
|
+
const child = spawn(process.execPath, [fileURLToPath(new URL('./update-check.js', import.meta.url)), file], {
|
|
67
|
+
detached: true,
|
|
68
|
+
stdio: 'ignore',
|
|
69
|
+
windowsHide: true,
|
|
70
|
+
});
|
|
71
|
+
child.on('error', () => { });
|
|
72
|
+
child.unref();
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Update notices are optional, including when the cache cannot be written.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function refreshUpdateCache(file) {
|
|
79
|
+
try {
|
|
80
|
+
const cache = readCache(file);
|
|
81
|
+
if (!cache)
|
|
82
|
+
return;
|
|
83
|
+
const response = await fetch('https://registry.npmjs.org/@attocash%2fcli/latest', {
|
|
84
|
+
signal: AbortSignal.timeout(3000),
|
|
85
|
+
redirect: 'error',
|
|
86
|
+
headers: { Accept: 'application/json' },
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok)
|
|
89
|
+
return;
|
|
90
|
+
const metadata = await response.json();
|
|
91
|
+
const latest = metadata?.name === '@attocash/cli' ? stableVersion(metadata.version) : undefined;
|
|
92
|
+
if (latest)
|
|
93
|
+
writeCache(file, { checkedAt: cache.checkedAt, latest });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Keep the last known version and attempt timestamp after any failure.
|
|
97
|
+
}
|
|
98
|
+
}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { operations, type Operation } from './application/operations.js';
|
|
2
|
+
export { errorResult } from './domain/errors.js';
|
|
3
|
+
export { runDoctor, type DoctorOptions, type DoctorReport, type DoctorCheck, type DoctorStatus } from './doctor/doctor.js';
|
|
4
|
+
/** Public wallet operations and their session lifetime. Recovery stays in the CLI. */
|
|
5
|
+
export interface ApplicationSession {
|
|
6
|
+
call(name: string, input?: Record<string, unknown>): Promise<unknown>;
|
|
7
|
+
start(): Promise<void>;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface ApplicationOptions {
|
|
11
|
+
directory?: string;
|
|
12
|
+
access?: 'mcp';
|
|
13
|
+
}
|
|
14
|
+
export declare function createApplication(options?: ApplicationOptions): ApplicationSession;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AttoApplication } from './application/app.js';
|
|
2
|
+
export { operations } from './application/operations.js';
|
|
3
|
+
export { errorResult } from './domain/errors.js';
|
|
4
|
+
export { runDoctor } from './doctor/doctor.js';
|
|
5
|
+
export function createApplication(options = {}) {
|
|
6
|
+
const application = new AttoApplication(options);
|
|
7
|
+
// Expose only the supported session contract, including at runtime. Terminal
|
|
8
|
+
// recovery methods and mutable storage are not part of the library API.
|
|
9
|
+
return {
|
|
10
|
+
call: application.call.bind(application),
|
|
11
|
+
start: application.start.bind(application),
|
|
12
|
+
close: application.close.bind(application),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CredentialResult = 'matched' | 'readable' | 'missing' | 'invalid' | 'mismatch' | 'unavailable' | 'backend_unavailable' | 'timeout';
|
|
2
|
+
export interface CredentialRequest {
|
|
3
|
+
service: 'Atto CLI' | 'Atto MCP';
|
|
4
|
+
account: string;
|
|
5
|
+
address?: string;
|
|
6
|
+
fingerprint?: string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// This process receives public identifiers only. Recovery material never leaves it.
|
|
2
|
+
process.env.KOTLIN_LOGGING_STARTUP_MESSAGE = 'false';
|
|
3
|
+
console.log = console.info = console.debug = console.error = () => { };
|
|
4
|
+
process.once('message', async (request) => {
|
|
5
|
+
let result = 'unavailable';
|
|
6
|
+
try {
|
|
7
|
+
if (process.platform !== 'linux') {
|
|
8
|
+
try {
|
|
9
|
+
await import('@napi-rs/keyring');
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
process.send?.('backend_unavailable');
|
|
13
|
+
process.disconnect?.();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const { OsSecretStore } = await import('../storage/secrets.js');
|
|
18
|
+
const phrase = await new OsSecretStore(request.account, request.service).get();
|
|
19
|
+
if (phrase === null)
|
|
20
|
+
result = 'missing';
|
|
21
|
+
else {
|
|
22
|
+
result = 'invalid';
|
|
23
|
+
const { mnemonicSeed } = await import('../wallet/signing.js');
|
|
24
|
+
const { AttoAlgorithm, toAttoIndex } = await import('@attocash/commons-core');
|
|
25
|
+
const { createHash } = await import('node:crypto');
|
|
26
|
+
const seed = await mnemonicSeed(phrase);
|
|
27
|
+
try {
|
|
28
|
+
const key = await seed.toPrivateKey(toAttoIndex(0));
|
|
29
|
+
try {
|
|
30
|
+
const publicKey = await key.toPublicKey();
|
|
31
|
+
const address = publicKey.toAddress(AttoAlgorithm.V1).value;
|
|
32
|
+
const fingerprint = createHash('sha256').update(publicKey.toString()).digest('hex');
|
|
33
|
+
result = request.address === undefined ? 'readable'
|
|
34
|
+
: request.address === address && request.fingerprint === fingerprint ? 'matched' : 'mismatch';
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
key.value.fill(0);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
seed.value.fill(0);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch { /* Only the allowlisted result is returned, never dependency diagnostics. */ }
|
|
46
|
+
process.send?.(result);
|
|
47
|
+
process.disconnect?.();
|
|
48
|
+
});
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DoctorOptions, DoctorReport } from './types.js';
|
|
2
|
+
export type { DoctorCheck, DoctorOptions, DoctorReport, DoctorStatus } from './types.js';
|
|
3
|
+
/** Full diagnostics, independent of application startup and without wallet mutations. */
|
|
4
|
+
export declare function runDoctor(options?: DoctorOptions): Promise<DoctorReport>;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { fetchDirectory, DIRECTORY_URL } from '../labels/directory.js';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { AttoAccount, AttoBlock, AttoInstant } from '@attocash/commons-core';
|
|
6
|
+
import { satisfies } from 'semver';
|
|
7
|
+
import { AttoError } from '../domain/errors.js';
|
|
8
|
+
import { NodeReader } from '../network/reader.js';
|
|
9
|
+
import { requestWork } from '../network/work.js';
|
|
10
|
+
import { MarketData } from '../pricing/market.js';
|
|
11
|
+
import { dedicatedMcpDirectory, inspectWalletProfile } from '../storage/profiles.js';
|
|
12
|
+
import { checkKeyring } from './keyring.js';
|
|
13
|
+
import { readDoctorProfile } from './profile.js';
|
|
14
|
+
const skipped = (id, message) => ({ id, status: 'skipped', code: 'CHECK_PREREQUISITE_UNAVAILABLE', message });
|
|
15
|
+
function failure(id, error, signal, steps, warn = false) {
|
|
16
|
+
// Never include upstream messages or bodies; even SDK failures may contain sensitive data.
|
|
17
|
+
const denied = id === 'profile' && ['EACCES', 'EPERM'].includes(error?.code ?? '');
|
|
18
|
+
const code = signal.aborted ? 'CHECK_TIMEOUT' : error instanceof AttoError ? error.code : denied ? 'PROFILE_ACCESS_DENIED' : id === 'profile' ? 'PROFILE_STATE_INVALID' : 'CHECK_FAILED';
|
|
19
|
+
const httpStatus = error instanceof AttoError ? error.details?.httpStatus : undefined;
|
|
20
|
+
return { id, status: warn ? 'warn' : 'fail', code,
|
|
21
|
+
message: signal.aborted ? 'The check did not complete before its deadline or was cancelled.'
|
|
22
|
+
: denied ? 'This process lacks the file or directory access needed to use the profile.'
|
|
23
|
+
: code === 'STATE_VERSION' ? 'The state database version is not supported by this CLI.'
|
|
24
|
+
: code === 'PROFILE_STATE_INVALID' ? 'The selected state database or its public wallet data is invalid or unreadable.'
|
|
25
|
+
: 'The check failed. Follow the suggested steps and rerun doctor.',
|
|
26
|
+
...(typeof httpStatus === 'number' && Number.isInteger(httpStatus) ? { evidence: { httpStatus } } : {}), remediation: { steps } };
|
|
27
|
+
}
|
|
28
|
+
async function checkNode(profile, overall) {
|
|
29
|
+
const reader = new NodeReader(profile.settings);
|
|
30
|
+
const checks = [];
|
|
31
|
+
const signal = AbortSignal.any([overall, AbortSignal.timeout(10_000)]);
|
|
32
|
+
const fix = ['Verify nodeUrl with wallet status; use wallet configure --node-url only after selecting the intended endpoint.', 'Check network connectivity, TLS trust, and whether the server exposes the Atto public APIs.'];
|
|
33
|
+
let account = null;
|
|
34
|
+
const selectedAddress = profile.identity?.address ?? profile.settings.representative;
|
|
35
|
+
const lookupAccount = async (address) => {
|
|
36
|
+
const value = await reader.account(address, signal);
|
|
37
|
+
if (value && value.address.value !== address)
|
|
38
|
+
throw new AttoError('ACCOUNT_MISMATCH', 'The node returned another account.');
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
// Time and account APIs are independent; a clock endpoint alone does not prove account access.
|
|
42
|
+
const results = await Promise.allSettled([
|
|
43
|
+
reader.now(signal),
|
|
44
|
+
lookupAccount(selectedAddress),
|
|
45
|
+
]);
|
|
46
|
+
const time = results[0];
|
|
47
|
+
if (time.status === 'fulfilled') {
|
|
48
|
+
const offsetMs = Number(time.value.toEpochMilliseconds() - BigInt(Date.now()));
|
|
49
|
+
checks.push({ id: 'node.time', status: Math.abs(offsetMs) > 60_000 ? 'warn' : 'pass', code: Math.abs(offsetMs) > 60_000 ? 'CLOCK_SKEW' : 'NODE_TIME_OK',
|
|
50
|
+
message: 'The node time API returned a valid response.', evidence: { offsetMs },
|
|
51
|
+
...(Math.abs(offsetMs) > 60_000 ? { remediation: { steps: ['Check OS time synchronization and the configured node clock.'] } } : {}) });
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
checks.push(failure('node.time', time.reason, signal, fix));
|
|
55
|
+
const lookup = results[1];
|
|
56
|
+
if (lookup.status === 'fulfilled') {
|
|
57
|
+
account = lookup.value;
|
|
58
|
+
checks.push({ id: 'node.account', status: 'pass', code: account ? 'NODE_ACCOUNT_OK' : 'ACCOUNT_NOT_OPEN',
|
|
59
|
+
message: account ? 'The account API returned a valid account.' : 'The account API returned not found; the queried account may not be open.' });
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
checks.push(failure('node.account', lookup.reason, signal, fix));
|
|
63
|
+
if (lookup.status === 'fulfilled' && !account && selectedAddress !== profile.settings.representative) {
|
|
64
|
+
try {
|
|
65
|
+
account = await lookupAccount(profile.settings.representative);
|
|
66
|
+
checks.push({ id: 'node.representative', status: account ? 'pass' : 'warn', code: account ? 'NODE_REPRESENTATIVE_OK' : 'REPRESENTATIVE_NOT_OPEN',
|
|
67
|
+
message: account ? 'The configured representative provides an existing account for network and stream checks.' : 'The configured representative is also unopened; network and stream verification need an existing account.' });
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
checks.push(failure('node.representative', error, signal, fix));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!account)
|
|
74
|
+
return [...checks, skipped('node.network', 'No existing account was available to verify the node network.'), skipped('node.stream', 'No existing account was available for an observable account-stream snapshot.')];
|
|
75
|
+
checks.push({ id: 'node.network', status: account.network.name === profile.settings.network ? 'pass' : 'fail',
|
|
76
|
+
code: account.network.name === profile.settings.network ? 'NODE_NETWORK_MATCHED' : 'NETWORK_MISMATCH',
|
|
77
|
+
message: 'Compared the account network with the wallet configuration.', evidence: { configured: profile.settings.network, observed: account.network.name },
|
|
78
|
+
...(account.network.name === profile.settings.network ? {} : { remediation: { steps: ['Select node and worker endpoints for the intended network. A network setting alone does not change endpoints.'] } }) });
|
|
79
|
+
const streamDone = new AbortController();
|
|
80
|
+
const deadline = AbortSignal.any([overall, AbortSignal.timeout(10_000)]);
|
|
81
|
+
let observed = false;
|
|
82
|
+
try {
|
|
83
|
+
await reader.stream({ event: 'account', addresses: [account.address.value] }, model => {
|
|
84
|
+
if (!(model instanceof AttoAccount) || model.address.value !== account.address.value || model.network.name !== account.network.name)
|
|
85
|
+
throw new AttoError('ACCOUNT_MISMATCH', 'Unexpected account stream result.');
|
|
86
|
+
observed = true;
|
|
87
|
+
streamDone.abort();
|
|
88
|
+
}, AbortSignal.any([deadline, streamDone.signal]));
|
|
89
|
+
checks.push({ id: 'node.stream', status: observed ? 'pass' : 'warn', code: observed ? 'NODE_STREAM_OK' : 'NODE_STREAM_UNVERIFIED',
|
|
90
|
+
message: observed ? 'Commons received an account snapshot and closed the subscription.' : 'No account snapshot was observed during the check. An idle stream alone does not prove a failure.' });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
checks.push(failure('node.stream', error, deadline, fix));
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
streamDone.abort();
|
|
97
|
+
}
|
|
98
|
+
return checks;
|
|
99
|
+
}
|
|
100
|
+
async function checkWorker(profile, overall) {
|
|
101
|
+
const signal = AbortSignal.any([overall, AbortSignal.timeout(30_000)]);
|
|
102
|
+
try {
|
|
103
|
+
// A fresh public target ensures a real request; this block has no signer or funds.
|
|
104
|
+
const block = AttoBlock.fromJson(JSON.stringify({ type: 'CHANGE', network: profile.settings.network, version: 0, algorithm: 'V1',
|
|
105
|
+
publicKey: '11'.repeat(32), height: 2, balance: 0, timestamp: Number(AttoInstant.Companion.now().toEpochMilliseconds()),
|
|
106
|
+
previous: randomBytes(32).toString('hex'), representativeAlgorithm: 'V1', representativePublicKey: '11'.repeat(32) }));
|
|
107
|
+
await requestWork(block, profile.settings.workerUrl, signal);
|
|
108
|
+
return { id: 'worker.work', status: 'pass', code: 'WORKER_WORK_VALID', message: 'The worker generated fresh proof of work validated by Commons. Nothing was signed, published, or cached.' };
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return failure('worker.work', error, signal, ['Verify workerUrl and its network with wallet status.', 'Check worker connectivity and capacity. Run wallet configure --worker-url only after selecting the intended endpoint.']);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function checkPrice(profile, overall) {
|
|
115
|
+
if (profile.settings.network !== 'LIVE')
|
|
116
|
+
return skipped('usd.price', 'USD-priced payments are available on LIVE only.');
|
|
117
|
+
const signal = AbortSignal.any([overall, AbortSignal.timeout(10_000)]);
|
|
118
|
+
try {
|
|
119
|
+
const quote = await new MarketData().quoteUsd('1', signal);
|
|
120
|
+
return { id: 'usd.price', status: 'pass', code: 'USD_PRICE_AVAILABLE', message: 'An indicative USD conversion is available; it is not an executable exchange quote.', evidence: { source: quote.source, priceDate: quote.priceDate } };
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return failure('usd.price', error, signal, ['Check the market metrics service and price observation date. Ordinary ATTO payments do not require USD pricing.'], true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function checkDirectory(profile, signal) {
|
|
127
|
+
if (profile.settings.network !== 'LIVE')
|
|
128
|
+
return skipped('labels.directory', 'The public directory applies to LIVE only.');
|
|
129
|
+
try {
|
|
130
|
+
const snapshot = await fetchDirectory(fetch, signal);
|
|
131
|
+
return { id: 'labels.directory', status: 'pass', code: 'DIRECTORY_AVAILABLE', message: 'The global address directory is available. No cache was updated.',
|
|
132
|
+
evidence: { source: DIRECTORY_URL, addresses: snapshot.addresses.length, voters: snapshot.voters.length } };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return failure('labels.directory', error, signal, ['Check public directory connectivity. Personal-label payments use local storage and do not need this service.'], true);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function readiness(profile, options, directory) {
|
|
139
|
+
const checks = [{ id: 'wallet.initialization', status: profile.identity ? 'pass' : 'warn',
|
|
140
|
+
code: profile.identity ? 'WALLET_INITIALIZED' : 'WALLET_NOT_INITIALIZED', message: profile.identity ? 'A public wallet identity is saved.' : 'This profile has no initialized wallet.',
|
|
141
|
+
...(!profile.identity ? { remediation: { steps: ['Create or import a wallet in your local terminal using this data directory.'], command: ['atto', '--data-dir', directory, 'wallet', 'create'] } } : {}) }];
|
|
142
|
+
if (profile.resetPending)
|
|
143
|
+
checks.push({ id: 'wallet.reset', status: 'fail', code: 'WALLET_RESET_REQUIRED', message: 'A previously requested wallet reset is unfinished.', remediation: { steps: ['Review wallet status and your backups in a local terminal before explicitly resuming the reset. Doctor does not resume it.'] } });
|
|
144
|
+
checks.push({ id: 'wallet.receiving', status: 'pass', code: 'RECEIVING_CONFIGURATION', message: 'Receiving prerequisites are configuration, not a receiver started by doctor.',
|
|
145
|
+
evidence: { enabled: profile.settings.autoReceive, activeAccounts: profile.addresses.filter(address => address.active).length,
|
|
146
|
+
...(options.access === 'mcp' ? { approvedMcpAccess: profile.access, poolIndexes: profile.poolIndexes } : {}) } });
|
|
147
|
+
if (profile.identity && profile.settings.autoReceive && !profile.addresses.some(address => address.active))
|
|
148
|
+
checks.push({ id: 'wallet.activeAccounts', status: 'warn', code: 'NO_ACTIVE_ACCOUNTS', message: 'Receiving is enabled without any active accounts.', remediation: { steps: ['Activate a saved account to receive automatically.'], command: ['atto', '--data-dir', directory, 'address', 'activate', '0'] } });
|
|
149
|
+
if (options.access === 'mcp')
|
|
150
|
+
checks.push({ id: 'wallet.mcpAccess', status: 'pass', code: profile.access === 'spend' ? 'MCP_SPENDING_APPROVED' : 'MCP_READ_ONLY',
|
|
151
|
+
message: profile.access === 'spend' ? 'MCP spending access is approved, subject to the saved policy.' : 'Read-only MCP access is intentional. Spending needs a proposal and approval by the user in a local terminal; doctor cannot grant it.' });
|
|
152
|
+
checks.push({ id: 'wallet.pendingPayments', status: profile.pendingPayments ? 'warn' : 'pass', code: profile.pendingPayments ? 'PAYMENTS_UNFINISHED' : 'NO_PENDING_PAYMENTS',
|
|
153
|
+
message: profile.pendingPayments ? 'Unfinished payment records need inspection; doctor has not reconciled or retried them.' : 'No unfinished payment records are saved.', evidence: { count: profile.pendingPayments },
|
|
154
|
+
...(profile.pendingPayments ? { remediation: { steps: ['Inspect the journal and retain original request IDs when checking existing payments.'], command: ['atto', '--data-dir', directory, 'journal', 'list'] } } : {}) });
|
|
155
|
+
if (profile.settings.network === 'LIVE')
|
|
156
|
+
checks.push({ id: 'usd.terms', status: profile.termsAccepted ? 'pass' : 'warn', code: profile.termsAccepted ? 'USD_TERMS_ACCEPTED' : 'USD_TERMS_REQUIRED',
|
|
157
|
+
message: profile.termsAccepted ? 'The current USD payment terms are accepted.' : 'USD payments require the user to read and explicitly accept the current terms. Ordinary ATTO payments do not.',
|
|
158
|
+
...(!profile.termsAccepted ? { remediation: { steps: ['Show the terms to the user before requesting their acceptance.'], command: ['atto', '--data-dir', directory, 'terms', 'show'] } } : {}) });
|
|
159
|
+
return checks;
|
|
160
|
+
}
|
|
161
|
+
/** Full diagnostics, independent of application startup and without wallet mutations. */
|
|
162
|
+
export async function runDoctor(options = {}) {
|
|
163
|
+
const started = Date.now();
|
|
164
|
+
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
165
|
+
const controller = new AbortController();
|
|
166
|
+
const timer = setTimeout(() => controller.abort(), 60_000);
|
|
167
|
+
const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
168
|
+
const report = { status: 'pass', context: { interface: options.access ?? 'cli', executable: process.execPath,
|
|
169
|
+
nodeVersion: process.version, cliVersion: pkg.version, platform: `${process.platform}/${process.arch}` }, checks: [], durationMs: 0 };
|
|
170
|
+
try {
|
|
171
|
+
const supported = satisfies(process.version, pkg.engines.node);
|
|
172
|
+
report.checks.push({ id: 'runtime', status: supported ? 'pass' : 'fail', code: supported ? 'RUNTIME_SUPPORTED' : 'RUNTIME_UNSUPPORTED', message: `This package requires Node.js ${pkg.engines.node}.` });
|
|
173
|
+
let profile;
|
|
174
|
+
let snapshot;
|
|
175
|
+
try {
|
|
176
|
+
const directory = options.directory ?? (options.access === 'mcp' ? dedicatedMcpDirectory() : undefined);
|
|
177
|
+
if (directory)
|
|
178
|
+
report.context.directory = resolve(directory);
|
|
179
|
+
profile = inspectWalletProfile(directory);
|
|
180
|
+
Object.assign(report.context, profile);
|
|
181
|
+
snapshot = readDoctorProfile(profile.directory);
|
|
182
|
+
Object.assign(report.context, { network: snapshot.settings.network, nodeUrl: snapshot.settings.nodeUrl, workerUrl: snapshot.settings.workerUrl, settingsSource: snapshot.settingsSource });
|
|
183
|
+
report.checks.push({ id: 'profile', status: 'pass', code: 'PROFILE_READABLE', message: snapshot.settingsSource === 'defaults' ? 'No state database exists. Checks use labelled defaults without creating a profile.' : 'The public wallet snapshot is readable and its directory is accessible.' });
|
|
184
|
+
if (snapshot.permissions)
|
|
185
|
+
report.checks.push({ id: 'profile.permissions', status: snapshot.permissions.private ? 'pass' : 'warn',
|
|
186
|
+
code: snapshot.permissions.private ? 'PROFILE_PERMISSIONS_PRIVATE' : 'PROFILE_PERMISSIONS_BROAD',
|
|
187
|
+
message: snapshot.permissions.private ? 'The profile directory and database restrict access to their owner.' : 'The profile directory or database allows access by other OS users.',
|
|
188
|
+
evidence: snapshot.permissions,
|
|
189
|
+
...(!snapshot.permissions.private ? { remediation: { steps: ['Review ownership and permissions for this data directory and state.sqlite. Restrict the directory to mode 700 and the database to mode 600. Doctor has not changed them.'] } } : {}) });
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
report.checks.push(failure('profile', error, signal, ['Verify the data directory, file permissions, and supported wallet state version.', 'Preserve the existing profile and restore valid metadata or state from its backup; do not reset the wallet to fix access.']));
|
|
193
|
+
for (const id of ['keyring.credential', 'node.time', 'node.account', 'node.network', 'node.stream', 'worker.work', 'wallet.readiness', 'usd.price'])
|
|
194
|
+
report.checks.push(skipped(id, 'The selected profile could not be inspected safely. No alternate profile or endpoints were substituted.'));
|
|
195
|
+
return report;
|
|
196
|
+
}
|
|
197
|
+
report.checks.push(...readiness(snapshot, options, profile.directory));
|
|
198
|
+
const jobs = await Promise.allSettled([checkKeyring(profile, snapshot.identity, signal), checkNode(snapshot, signal), checkWorker(snapshot, signal), checkPrice(snapshot, signal), ...(options.globalDirectory ? [checkDirectory(snapshot, signal)] : [])]);
|
|
199
|
+
for (const [index, job] of jobs.entries()) {
|
|
200
|
+
if (job.status === 'fulfilled')
|
|
201
|
+
report.checks.push(...(Array.isArray(job.value) ? job.value : [job.value]));
|
|
202
|
+
else
|
|
203
|
+
report.checks.push(failure(['keyring.credential', 'node', 'worker.work', 'usd.price', 'labels.directory'][index], job.reason, signal, ['Rerun doctor from the same launch environment.']));
|
|
204
|
+
}
|
|
205
|
+
return report;
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
clearTimeout(timer);
|
|
209
|
+
controller.abort();
|
|
210
|
+
report.status = report.checks.some(check => check.status === 'fail') ? 'fail' : report.checks.some(check => check.status === 'warn') ? 'warn' : 'pass';
|
|
211
|
+
report.durationMs = Date.now() - started;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WalletProfile } from '../storage/profiles.js';
|
|
2
|
+
import type { WalletIdentity } from '../wallet/types.js';
|
|
3
|
+
import type { DoctorCheck } from './types.js';
|
|
4
|
+
import type { CredentialRequest, CredentialResult } from './credential-probe.js';
|
|
5
|
+
/** Bound native keyring calls too, and terminate any secret-tool subprocess with its parent. */
|
|
6
|
+
export declare function probeCredential(request: CredentialRequest, signal: AbortSignal, env?: NodeJS.ProcessEnv): Promise<CredentialResult>;
|
|
7
|
+
export declare function checkKeyring(profile: WalletProfile, identity: WalletIdentity | undefined, signal: AbortSignal): Promise<DoctorCheck[]>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { access, stat } from 'node:fs/promises';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import { delimiter, dirname, isAbsolute, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
/** Bound native keyring calls too, and terminate any secret-tool subprocess with its parent. */
|
|
7
|
+
export function probeCredential(request, signal, env = process.env) {
|
|
8
|
+
return new Promise(resolve => {
|
|
9
|
+
if (signal.aborted) {
|
|
10
|
+
resolve('timeout');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const child = spawn(process.execPath, [fileURLToPath(new URL('./credential-probe.js', import.meta.url))], {
|
|
14
|
+
env, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], detached: process.platform !== 'win32', windowsHide: true,
|
|
15
|
+
});
|
|
16
|
+
let result = 'unavailable';
|
|
17
|
+
let stopped = false;
|
|
18
|
+
const stop = () => {
|
|
19
|
+
stopped = true;
|
|
20
|
+
result = 'timeout';
|
|
21
|
+
if (child.pid) {
|
|
22
|
+
try {
|
|
23
|
+
if (process.platform === 'win32')
|
|
24
|
+
child.kill('SIGKILL');
|
|
25
|
+
else
|
|
26
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
27
|
+
}
|
|
28
|
+
catch { /* The child may already have exited. */ }
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const timer = setTimeout(stop, 30_000);
|
|
32
|
+
signal.addEventListener('abort', stop, { once: true });
|
|
33
|
+
child.on('message', value => {
|
|
34
|
+
if (!stopped && !signal.aborted && typeof value === 'string' && ['matched', 'readable', 'missing', 'invalid', 'mismatch', 'unavailable', 'backend_unavailable'].includes(value))
|
|
35
|
+
result = value;
|
|
36
|
+
});
|
|
37
|
+
child.on('error', () => { });
|
|
38
|
+
child.once('close', () => {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
signal.removeEventListener('abort', stop);
|
|
41
|
+
resolve(result);
|
|
42
|
+
});
|
|
43
|
+
child.once('spawn', () => {
|
|
44
|
+
if (signal.aborted)
|
|
45
|
+
stop();
|
|
46
|
+
else
|
|
47
|
+
child.send(request, error => { if (error)
|
|
48
|
+
child.kill(); });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function secretToolPath() {
|
|
53
|
+
for (const directory of (process.env.PATH ?? '').split(delimiter)) {
|
|
54
|
+
const candidate = join(directory || '.', 'secret-tool');
|
|
55
|
+
try {
|
|
56
|
+
await access(candidate, constants.X_OK);
|
|
57
|
+
if ((await stat(candidate)).isFile())
|
|
58
|
+
return candidate;
|
|
59
|
+
}
|
|
60
|
+
catch { /* Try the next PATH entry. */ }
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
function busPath(address) {
|
|
65
|
+
const match = address?.match(/^unix:path=([^,;]+)(?:,guid=[a-f\d]+)?$/i);
|
|
66
|
+
if (!match)
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
const path = decodeURIComponent(match[1]);
|
|
70
|
+
if (isAbsolute(path) && !/[\x00-\x1f\x7f]/.test(path))
|
|
71
|
+
return path;
|
|
72
|
+
}
|
|
73
|
+
catch { /* Other D-Bus address forms are left to the credential adapter. */ }
|
|
74
|
+
}
|
|
75
|
+
async function ownedSocket(path) {
|
|
76
|
+
try {
|
|
77
|
+
const [socket, directory] = await Promise.all([stat(path), stat(dirname(path))]);
|
|
78
|
+
return socket.isSocket() && socket.uid === process.getuid?.() && directory.isDirectory() && directory.uid === socket.uid;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function checkKeyring(profile, identity, signal) {
|
|
85
|
+
const checks = [];
|
|
86
|
+
if (process.platform === 'linux') {
|
|
87
|
+
const executable = await secretToolPath();
|
|
88
|
+
checks.push({ id: 'keyring.backend', status: executable ? 'pass' : 'fail', code: executable ? 'KEYRING_BACKEND_FOUND' : 'SECRET_TOOL_MISSING',
|
|
89
|
+
message: executable ? 'secret-tool is executable in this process PATH.' : 'secret-tool is not executable in this process PATH.',
|
|
90
|
+
...(executable ? { evidence: { executable } } : { remediation: { steps: ['Install secret-tool using your OS package manager, or expose its installed directory in the MCP launch PATH.', 'Rerun doctor from the failing environment.'] } }) });
|
|
91
|
+
const configuredBus = busPath(process.env.DBUS_SESSION_BUS_ADDRESS);
|
|
92
|
+
const socketFound = configuredBus ? await ownedSocket(configuredBus) : false;
|
|
93
|
+
checks.push({ id: 'keyring.environment', status: 'pass', code: 'KEYRING_ENVIRONMENT',
|
|
94
|
+
message: 'Environment observations only; the credential check below verifies actual access.',
|
|
95
|
+
evidence: { sessionBusAddressPresent: Boolean(process.env.DBUS_SESSION_BUS_ADDRESS), runtimeDirectoryPresent: Boolean(process.env.XDG_RUNTIME_DIR), configuredUserSocketFound: socketFound } });
|
|
96
|
+
if (!executable)
|
|
97
|
+
return [...checks, { id: 'keyring.credential', status: 'skipped', code: 'KEYRING_BACKEND_REQUIRED', message: 'Credential verification requires secret-tool.' }];
|
|
98
|
+
}
|
|
99
|
+
const request = { service: profile.credentialService, account: profile.credentialAccount,
|
|
100
|
+
...(identity ? { address: identity.address, fingerprint: identity.fingerprint } : {}) };
|
|
101
|
+
const result = await probeCredential(request, signal);
|
|
102
|
+
const outcomes = {
|
|
103
|
+
matched: { status: 'pass', code: 'KEYRING_CREDENTIAL_MATCHED', message: 'The stored credential is readable and matches this wallet.' },
|
|
104
|
+
readable: { status: 'warn', code: 'KEYRING_CREDENTIAL_WITHOUT_WALLET', message: 'A valid stored credential exists, but this profile has no initialized wallet.' },
|
|
105
|
+
missing: { status: identity ? 'fail' : 'warn', code: 'KEYRING_CREDENTIAL_MISSING', message: 'No credential was found for this profile.' },
|
|
106
|
+
invalid: { status: 'fail', code: 'KEYRING_CREDENTIAL_INVALID', message: 'The stored credential is not a valid Atto recovery phrase.' },
|
|
107
|
+
mismatch: { status: 'fail', code: 'KEYRING_CREDENTIAL_MISMATCH', message: 'The stored credential does not match the saved wallet identity.' },
|
|
108
|
+
unavailable: { status: 'fail', code: 'KEYRING_UNAVAILABLE', message: 'Credential access failed. The service may be unavailable, locked, or denying access.' },
|
|
109
|
+
backend_unavailable: { status: 'fail', code: 'KEYRING_BACKEND_UNAVAILABLE', message: 'The native password-store backend could not load.' },
|
|
110
|
+
timeout: { status: 'fail', code: 'KEYRING_TIMEOUT', message: 'The password-store check timed out or was cancelled.' },
|
|
111
|
+
};
|
|
112
|
+
const check = { id: 'keyring.credential', ...outcomes[result] };
|
|
113
|
+
if (['unavailable', 'timeout'].includes(result)) {
|
|
114
|
+
check.remediation = { steps: ['Unlock the OS password store and allow this application to access it.',
|
|
115
|
+
...(process.platform === 'linux' ? ['In a working desktop terminal, inspect DBUS_SESSION_BUS_ADDRESS and XDG_RUNTIME_DIR with printenv. Supply the actual session values in the MCP server env configuration.'] : []),
|
|
116
|
+
'Restart the MCP connection after changing its launch environment, then rerun its doctor tool.'] };
|
|
117
|
+
if (process.platform === 'linux' && !signal.aborted) {
|
|
118
|
+
const directories = [...new Set([process.env.XDG_RUNTIME_DIR, `/run/user/${process.getuid()}`])];
|
|
119
|
+
for (const directory of directories) {
|
|
120
|
+
if (!directory || !isAbsolute(directory) || /[\x00-\x1f\x7f]/.test(directory))
|
|
121
|
+
continue;
|
|
122
|
+
const path = join(directory, 'bus');
|
|
123
|
+
if (!await ownedSocket(path))
|
|
124
|
+
continue;
|
|
125
|
+
const suggestedEnv = { DBUS_SESSION_BUS_ADDRESS: `unix:path=${encodeURIComponent(path).replace(/%2F/g, '/')}`, XDG_RUNTIME_DIR: directory };
|
|
126
|
+
if (Object.entries(suggestedEnv).every(([key, value]) => process.env[key] === value))
|
|
127
|
+
continue;
|
|
128
|
+
const retried = await probeCredential(request, signal, { ...process.env, ...suggestedEnv });
|
|
129
|
+
// An absent credential does not prove the suggested environment fixes this wallet.
|
|
130
|
+
if (retried === 'matched' || (!identity && retried === 'readable')) {
|
|
131
|
+
check.remediation = { steps: ['Credential access succeeded only in an isolated probe with these environment overrides.',
|
|
132
|
+
'Add suggestedEnv to the MCP server env configuration, restart the connection, and rerun doctor. The current process was not changed.'], suggestedEnv, restartRequired: true };
|
|
133
|
+
check.code = 'KEYRING_ENVIRONMENT_FIX_VERIFIED';
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
// Try at most one alternative session, within the overall doctor deadline.
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (result !== 'matched') {
|
|
142
|
+
check.remediation = { steps: result === 'backend_unavailable'
|
|
143
|
+
? ['Reinstall the package for this OS and architecture, then rerun doctor.']
|
|
144
|
+
: !identity && result === 'missing'
|
|
145
|
+
? ['Create or import a wallet in your local terminal using this same data directory.']
|
|
146
|
+
: ['Verify the selected data directory and credential service/account.', 'Preserve the original public wallet profile and recover its matching credential from your private backup. Do not reset or replace the wallet to repair access.'] };
|
|
147
|
+
}
|
|
148
|
+
checks.push(check);
|
|
149
|
+
return checks;
|
|
150
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { WalletAddress, WalletIdentity, WalletSettings } from '../wallet/types.js';
|
|
2
|
+
export interface DoctorProfile {
|
|
3
|
+
settings: WalletSettings;
|
|
4
|
+
settingsSource: 'profile' | 'defaults';
|
|
5
|
+
identity?: WalletIdentity;
|
|
6
|
+
addresses: WalletAddress[];
|
|
7
|
+
access: 'read-only' | 'spend';
|
|
8
|
+
poolIndexes: number[];
|
|
9
|
+
pendingPayments: number;
|
|
10
|
+
resetPending: boolean;
|
|
11
|
+
termsAccepted: boolean;
|
|
12
|
+
permissions?: {
|
|
13
|
+
private: boolean;
|
|
14
|
+
directoryMode: string;
|
|
15
|
+
databaseMode?: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Read a consistent snapshot without StateStore's initialization, migration, or chmod. */
|
|
19
|
+
export declare function readDoctorProfile(directory: string): DoctorProfile;
|