@aryacode/dsh-aryacode-polish 0.0.0-stage → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +165 -0
- package/LICENSE +21 -0
- package/README.md +137 -2
- package/client/account.js +96 -0
- package/client/face.js +403 -0
- package/client/money.js +31 -0
- package/client/switches.json +17 -0
- package/cordis.patch.yml +17 -0
- package/docs/parked-spellcheck.md +105 -0
- package/entries/config.js +61 -0
- package/entries/cost.js +273 -0
- package/entries/polish.js +57 -0
- package/entries/registry.js +73 -0
- package/entries/usage.js +265 -0
- package/lib/client.js +561 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +10 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +73 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.4] - 2026-10-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **The bundle says which Harness it runs on.** `dsh.engines.dsh` is now declared
|
|
8
|
+
(`^0.2.1-rc.3`, the revision the two surfaces were verified against), so the
|
|
9
|
+
market can read the requirement instead of labelling the card "Host requirement
|
|
10
|
+
unknown", and an install onto an older Host can be refused rather than failing
|
|
11
|
+
at mount time. `test/manifests.spec.mjs` keeps the field present and semver.
|
|
12
|
+
|
|
13
|
+
## [0.3.3] - 2026-10-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **The balance read could wait forever.** When the Host withheld the
|
|
18
|
+
`account/watch` grant, the read's promise never settled: the callback never
|
|
19
|
+
ran, so nothing resolved and the line silently stayed empty for the life of the
|
|
20
|
+
page. The read now carries a timeout and also starts its call before the
|
|
21
|
+
effect's yielded disposer, so an effect runner that stops at the first yield
|
|
22
|
+
still performs it. `test/account.spec.mjs` pins all seven refusal paths.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The read itself moved into `client/account.js`, beside `client/money.js`, for
|
|
27
|
+
the same reason: the build inlines it into the browser bundle and the spec
|
|
28
|
+
imports the same source.
|
|
29
|
+
|
|
30
|
+
## [0.3.2] - 2026-10-27
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **The balance decisions are testable.** Which wallet a line shows, and how the
|
|
35
|
+
amount is rendered (currency symbol, two decimals, unparseable values shown
|
|
36
|
+
verbatim), moved into `client/money.js`. The build inlines that file into
|
|
37
|
+
`lib/client.js`, so `test/money.spec.mjs` covers the exact source the browser
|
|
38
|
+
runs instead of a copy.
|
|
39
|
+
|
|
40
|
+
## [0.3.1] - 2026-10-27
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- **The balance line had no observable mount point.** When the account exposed no
|
|
45
|
+
wallet it rendered `null`, which is correct for the user but indistinguishable
|
|
46
|
+
from "the surface was never registered" for anyone verifying the build. The
|
|
47
|
+
component now always renders its wrapper (`data-polish-balance="empty"`, hidden)
|
|
48
|
+
and fills it with the amount only when the Host returns one.
|
|
49
|
+
|
|
50
|
+
## [0.3.0] - 2026-10-27
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **Remaining account balance in the sidebar foot.** A line above the account row
|
|
55
|
+
shows the account's own wallet (`$17.33`), read once through a
|
|
56
|
+
`remoteAccess.whenAccess('account/watch', …)` grant so the call is allowed and
|
|
57
|
+
the grant is released as soon as the read settles. It renders nothing at all
|
|
58
|
+
when the switch is off, when the Host exposes no wallet, or when the read is
|
|
59
|
+
refused — an empty line beats a number the Host did not give us.
|
|
60
|
+
- **A second switch, `accountBalance`.** It is declared once in
|
|
61
|
+
`entries/registry.js` and once in `client/switches.json`, exactly like the cost
|
|
62
|
+
switch, so the Configure page grows it automatically.
|
|
63
|
+
|
|
64
|
+
### Notes
|
|
65
|
+
|
|
66
|
+
- The line sits in `sidebar.footer.action`, the one documented seat in the
|
|
67
|
+
sidebar foot. It renders *above* the account row, because
|
|
68
|
+
`SidebarRoot.tsx:405-411` puts `footerActions` before `settingsArea`; the only
|
|
69
|
+
seat below the login would be `settings.launcher`, which means shadowing the
|
|
70
|
+
account row and its whole menu.
|
|
71
|
+
|
|
72
|
+
## [0.2.4] - 2026-10-27
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- **The card leads with the number it is about.** It now opens with the
|
|
77
|
+
"Estimated session cost" heading and the live `≈$…` amount, then a stacked bar
|
|
78
|
+
showing how the session's tokens split between cached input, uncached input,
|
|
79
|
+
cache writes and output, then the rows: cache hit, tokens, model, provider,
|
|
80
|
+
requests, and the estimate again above the footnote. Model and provider are
|
|
81
|
+
always named (they were only listed when a session had more than one), so a
|
|
82
|
+
first look says which route the money was spent on.
|
|
83
|
+
|
|
84
|
+
## [0.2.3] - 2026-10-27
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
|
|
88
|
+
- **A sub-cent turn no longer reads `≈$0`.** `formatMoney` rounded to three
|
|
89
|
+
decimals, so a real 22.1K-token turn whose actual cost was about $0.000095
|
|
90
|
+
showed as zero. Amounts under a cent now keep two significant digits
|
|
91
|
+
(`≈$0.000095`), cents keep three decimals, and dollars keep two.
|
|
92
|
+
|
|
93
|
+
## [0.2.2] - 2026-10-27
|
|
94
|
+
|
|
95
|
+
### Fixed
|
|
96
|
+
|
|
97
|
+
- The pill's `≈` was stored as cp1252 mojibake (`≈`), so a real turn rendered
|
|
98
|
+
`≈$0.003` instead of `≈$0.003`. Same cause as the two strings repaired in
|
|
99
|
+
0.2.1 — a PowerShell round-trip over the face source — and the last of that
|
|
100
|
+
damage. Verified against a real turn in Dev AryaCode.
|
|
101
|
+
|
|
102
|
+
## [0.2.1] - 2026-10-27
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- **The Configure page found no form.** The page body asked
|
|
107
|
+
`configForms.get('<package>#<row>')`, but the Plugins page builds a row's form
|
|
108
|
+
from the **row id** alone (`PluginManagerPage.tsx:1735` passes
|
|
109
|
+
`form={formFor(openRow.rowId)}`, and `formFor` matches `view.ns === id`). The
|
|
110
|
+
lookup now asks for `polish`, so the row's page reads the live form instead of
|
|
111
|
+
saying the row has no configuration to change. The `plugins.row.config`
|
|
112
|
+
registration key is unchanged: that one is still `<package>#<row>`.
|
|
113
|
+
- Two strings that a PowerShell round-trip had written as cp1252 mojibake
|
|
114
|
+
(`Loading configuration…`, `Saving…`, and an em dash) are back to UTF-8.
|
|
115
|
+
|
|
116
|
+
## [0.2.0] - 2026-10-27
|
|
117
|
+
|
|
118
|
+
### Added
|
|
119
|
+
|
|
120
|
+
- **Estimated session cost.** A composer-dock pill (`≈$0.39`) that opens a
|
|
121
|
+
breakdown card: cache hit, uncached input, cached input, output, requests, and
|
|
122
|
+
the estimate itself, with the same "your provider's bill is authoritative"
|
|
123
|
+
footnote. The money is computed **host-side**: `entries/usage.js` folds
|
|
124
|
+
model-attributed usage out of the durable session log, and `entries/cost.js`
|
|
125
|
+
registers a `polishCost` projection whose wire view is already priced at the
|
|
126
|
+
published DeepSeek rates (off-peak table, peak at 2×, cache writes billed as
|
|
127
|
+
misses). The browser half only formats numbers, so no rate table ships to the
|
|
128
|
+
client and an unknown model or provider leaves the estimate marked incomplete
|
|
129
|
+
instead of quietly reporting a wrong number.
|
|
130
|
+
- **`test/cost.spec.mjs`.** Fold and pricing coverage: peak/off-peak, cache
|
|
131
|
+
rates, cache writes as misses, unknown route handling, retry accounting, and
|
|
132
|
+
the empty-session wire view.
|
|
133
|
+
|
|
134
|
+
## [0.1.1] - 2026-10-27
|
|
135
|
+
|
|
136
|
+
### Added
|
|
137
|
+
|
|
138
|
+
- **The row's Configure page.** `lib/client.js` is the bundle's browser face: it
|
|
139
|
+
registers the `polish` row's configuration page, so the row now has a page of
|
|
140
|
+
its own that reads the row's live config form and writes it back with the
|
|
141
|
+
Host's revision. With no feature landed yet the page states that plainly
|
|
142
|
+
instead of showing a switch that toggles nothing.
|
|
143
|
+
- **`client/switches.json`.** The browser half of the feature seam: one entry per
|
|
144
|
+
switch, and `test/client-face.spec.mjs` fails when it and `entries/registry.js`
|
|
145
|
+
disagree in either direction.
|
|
146
|
+
|
|
147
|
+
## [0.1.0] - 2026-10-27
|
|
148
|
+
|
|
149
|
+
### Added
|
|
150
|
+
|
|
151
|
+
- **AryaCode Polish bundle.** One package, one row (`polish`), one switch per
|
|
152
|
+
quality-of-life feature, so each refinement can be turned off from its
|
|
153
|
+
Configure page without uninstalling the bundle.
|
|
154
|
+
- **Feature seam.** `entries/registry.js` is the single list of features, and the
|
|
155
|
+
row's config schema is derived from it: a new entry gains its volatile switch
|
|
156
|
+
with no second registration, and switching a feature off disposes its host
|
|
157
|
+
half instead of merely hiding it.
|
|
158
|
+
- **`docs/parked-spellcheck.md`.** Records why right-click spelling corrections
|
|
159
|
+
cannot be a plugin, and the exact shell change that would restore them.
|
|
160
|
+
|
|
161
|
+
### Not shipped
|
|
162
|
+
|
|
163
|
+
- Right-click spelling corrections. The candidates came from Chromium's
|
|
164
|
+
spellchecker through the Electron main process; the shell no longer reads
|
|
165
|
+
them, and no plugin contract can reach that menu. Parked with evidence.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KonnectSuite
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,138 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AryaCode Polish
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
One install for the small things that make AryaCode feel finished.
|
|
4
|
+
|
|
5
|
+
AryaCode Polish is a plugin **bundle**: it adds surfaces to the app it is
|
|
6
|
+
installed into and patches nothing. It is one package, one Plugins row, and one
|
|
7
|
+
switch per feature, so a refinement that turns out to be wrong for a machine can
|
|
8
|
+
be turned off without uninstalling anything.
|
|
9
|
+
|
|
10
|
+
Status: **0.2.0.** One feature shipped: the estimated session cost pill.
|
|
11
|
+
|
|
12
|
+
## What it is
|
|
13
|
+
|
|
14
|
+
- **One row.** The bundle inserts a single row, id `polish`, into the profile's
|
|
15
|
+
composition (`cordis.patch.yml`). That row is the install surface: it mounts
|
|
16
|
+
the host half of every enabled feature and names the bare package specifier so
|
|
17
|
+
the Shell can find the browser face.
|
|
18
|
+
- **One switch per feature.** Every feature is gated by its own boolean in the
|
|
19
|
+
row's Configure page, on by default. Turning it off disposes that feature only.
|
|
20
|
+
- **Additive.** No fork of the shell, no edits to `app.asar`, no patched
|
|
21
|
+
installed app, no DOM hacks against private class names.
|
|
22
|
+
|
|
23
|
+
## Switches
|
|
24
|
+
|
|
25
|
+
Row: **AryaCode Polish** (`polish`).
|
|
26
|
+
|
|
27
|
+
Each entry in `entries/registry.js` becomes one volatile boolean in this
|
|
28
|
+
Configure page automatically.
|
|
29
|
+
|
|
30
|
+
| Switch | Default | What it does |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| **Estimated session cost** (`sessionCost`) | On | A `≈$0.39` pill in the composer dock that opens a breakdown card — cache hit, uncached input, cached input, output, requests, and the estimate — priced from the session's recorded usage at the published DeepSeek rates. |
|
|
33
|
+
| **Account balance** (`accountBalance`) | On | The account's remaining wallet (`$17.33`) in the sidebar foot, above the account row. Renders nothing when the Host exposes no wallet, so it never shows a number nobody gave it. |
|
|
34
|
+
|
|
35
|
+
### How the cost is computed
|
|
36
|
+
|
|
37
|
+
The numbers come from the Host, never from the browser:
|
|
38
|
+
|
|
39
|
+
- `entries/usage.js` folds model-attributed provider usage out of the durable
|
|
40
|
+
session log, one row per provider, model, and hour, and tags each band as peak
|
|
41
|
+
or off-peak (Beijing weekdays 09–12 and 14–18 are billed at 2×).
|
|
42
|
+
- `entries/cost.js` registers the `polishCost` projection and prices that fold:
|
|
43
|
+
cache reads at the cache rate, uncached input and cache writes at the miss
|
|
44
|
+
rate, output at the output rate. The wire view is already money, so no rate
|
|
45
|
+
table reaches the client.
|
|
46
|
+
- An unknown provider, model, or rate period keeps its tokens and marks the
|
|
47
|
+
estimate **incomplete** — the pill says so rather than reporting a number it
|
|
48
|
+
cannot stand behind.
|
|
49
|
+
- The rate table is dated and lives in one place. When DeepSeek changes prices,
|
|
50
|
+
append a period; do not edit history.
|
|
51
|
+
|
|
52
|
+
## Declaring a feature
|
|
53
|
+
|
|
54
|
+
`entries/registry.js` is the single list of features. One entry is one feature:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
{
|
|
58
|
+
id: 'spellcheck', // becomes the config field and the switch
|
|
59
|
+
label: 'Spelling corrections',
|
|
60
|
+
description: 'Right-click a misspelled word to pick a correction.',
|
|
61
|
+
defaultOn: true,
|
|
62
|
+
mount: spellcheck, // optional host half: a Cordis plugin
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The row's `Config` schema is derived from that list, so a new entry gains its
|
|
67
|
+
volatile switch with no second registration. `mount` is plugged in when the
|
|
68
|
+
switch is on and disposed when it is off, so a disabled feature stops costing the
|
|
69
|
+
app rather than merely hiding. A feature that only draws UI omits `mount` and
|
|
70
|
+
contributes a client face instead.
|
|
71
|
+
|
|
72
|
+
## Parked
|
|
73
|
+
|
|
74
|
+
**Right-click spelling corrections** — investigated and not shipped. The
|
|
75
|
+
candidates you saw in AryaAI came from Chromium's spellchecker through the
|
|
76
|
+
Electron main process, which the AryaCode shell no longer reads; a plugin cannot
|
|
77
|
+
reach it. The finding, the exact 10-line shell change that would restore it, and
|
|
78
|
+
why the plugin alternatives were rejected are in
|
|
79
|
+
[`docs/parked-spellcheck.md`](docs/parked-spellcheck.md).
|
|
80
|
+
|
|
81
|
+
## Install
|
|
82
|
+
|
|
83
|
+
From the AryaCode Plugins market: open the market, find **AryaCode Polish**,
|
|
84
|
+
install, then quit and reopen AryaCode — the market registry is cached in the
|
|
85
|
+
running process.
|
|
86
|
+
|
|
87
|
+
Manual, for development only:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
node --import tsx/esm apps/cli/src/bin.ts plugin --profile web add <path-to-tgz>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
- AryaCode `^0.2.1-rc.3` (declared as `dsh.engines.dsh`; the revision both
|
|
96
|
+
surfaces were verified against).
|
|
97
|
+
- AryaCode with the Cordis plugin API (`@deepseek-ai/cordis ~4.0.4`).
|
|
98
|
+
- Node `^22.19.0 || >=24.0.0` to build from source.
|
|
99
|
+
|
|
100
|
+
## Development
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pnpm install
|
|
104
|
+
pnpm test # patch shape, config accessors, feature seam, docs parity
|
|
105
|
+
pnpm pack # produces aryacode-dsh-aryacode-polish-<version>.tgz
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Test on **Dev AryaCode** (`%LOCALAPPDATA%\AryaCode-dev\home`), never on the
|
|
109
|
+
installed app.
|
|
110
|
+
|
|
111
|
+
## Layout
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
cordis.patch.yml one insert, one row
|
|
115
|
+
entries/polish.js the row: Config derived from the registry, apply()
|
|
116
|
+
entries/registry.js the feature list — the one place a feature is declared
|
|
117
|
+
entries/usage.js the fold: model-attributed usage out of the session log
|
|
118
|
+
entries/cost.js the polishCost projection: rates, pricing, host mount
|
|
119
|
+
entries/features/ one module per feature
|
|
120
|
+
entries/config.js volatile-reference accessors (resolve/flag/list/text)
|
|
121
|
+
lib/index.js mount point re-exporting the row
|
|
122
|
+
client/face.js browser face source: the Configure page and every surface
|
|
123
|
+
client/switches.json the browser half of the seam — one entry per switch
|
|
124
|
+
scripts/build-client.mjs wraps client/face.js into lib/client.js
|
|
125
|
+
lib/client.js generated: the bundle the Shell loads
|
|
126
|
+
locale/ card title and description (en, zh)
|
|
127
|
+
docs/ parked investigations
|
|
128
|
+
test/ node --test specs
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The Configure page is generated, not hand-maintained: `lib/client.js` is built
|
|
132
|
+
from `client/face.js` and `client/switches.json` by `pnpm build:client` (which
|
|
133
|
+
`pnpm pack` runs), and `test/client-face.spec.mjs` fails when `switches.json` and
|
|
134
|
+
`entries/registry.js` disagree in either direction.
|
|
135
|
+
|
|
136
|
+
## Licence
|
|
137
|
+
|
|
138
|
+
MIT © KonnectSuite
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the account balance from a plugin face.
|
|
3
|
+
*
|
|
4
|
+
* The account Remote methods are gated, so the read runs inside a
|
|
5
|
+
* `remoteAccess.whenAccess('account/watch', …)` grant and the grant is released
|
|
6
|
+
* as soon as the read settles. Every failure — no service in this Shell, a
|
|
7
|
+
* refused grant, a failed call — resolves to `undefined` instead of throwing: a
|
|
8
|
+
* sidebar line is never worth breaking the shell over, and a line that stays
|
|
9
|
+
* empty is honest about a number it never received.
|
|
10
|
+
*
|
|
11
|
+
* This module is inlined into `lib/client.js` by `scripts/build-client.mjs` and
|
|
12
|
+
* imported directly by `test/account.spec.mjs`, so the tested source is the one
|
|
13
|
+
* the browser runs.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build the account request identity for one balance read.
|
|
18
|
+
*
|
|
19
|
+
* The shipped account bundle inlines `DSH_CLIENT_VERSION` at build time; a plugin
|
|
20
|
+
* bundle has no such define, so the build stamps this package's own version and
|
|
21
|
+
* prefers the shell's value when one is inlined.
|
|
22
|
+
*
|
|
23
|
+
* @param version - the version this build stamps.
|
|
24
|
+
* @returns the metadata the account Remote methods carry.
|
|
25
|
+
*/
|
|
26
|
+
export function clientMetadata(version) {
|
|
27
|
+
const stamped = typeof process !== 'undefined' && process.env !== undefined ? process.env.DSH_CLIENT_VERSION : undefined
|
|
28
|
+
return {
|
|
29
|
+
version: typeof stamped === 'string' && stamped !== '' ? stamped : version,
|
|
30
|
+
locale: typeof navigator !== 'undefined' && typeof navigator.language === 'string' && navigator.language !== ''
|
|
31
|
+
? navigator.language
|
|
32
|
+
: 'en',
|
|
33
|
+
timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build the one-shot balance reader.
|
|
39
|
+
*
|
|
40
|
+
* @param ctx - the browser plugin context; `ctx.get` resolves an optional service.
|
|
41
|
+
* @param version - the version this build stamps into the account metadata.
|
|
42
|
+
* @returns a reader that resolves to the balance view, or undefined when unavailable.
|
|
43
|
+
*/
|
|
44
|
+
export function makeBalanceReader(ctx, version, { timeoutMs = 10000 } = {}) {
|
|
45
|
+
const service = name => {
|
|
46
|
+
try {
|
|
47
|
+
return ctx.get(name)
|
|
48
|
+
} catch {
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return async () => {
|
|
53
|
+
const access = service('remoteAccess')
|
|
54
|
+
const remote = service('remote')
|
|
55
|
+
if (access === undefined || remote === undefined) return undefined
|
|
56
|
+
const metadata = clientMetadata(version)
|
|
57
|
+
return await new Promise(resolve => {
|
|
58
|
+
let settled = false
|
|
59
|
+
let stop
|
|
60
|
+
const timer = setTimeout(() => finish(undefined), timeoutMs)
|
|
61
|
+
const finish = value => {
|
|
62
|
+
if (settled) return
|
|
63
|
+
settled = true
|
|
64
|
+
clearTimeout(timer)
|
|
65
|
+
resolve(value)
|
|
66
|
+
if (typeof stop === 'function') {
|
|
67
|
+
try {
|
|
68
|
+
stop()
|
|
69
|
+
} catch {
|
|
70
|
+
/* the grant may already have been released */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// The read starts before the yield so an effect runner that stops at the
|
|
75
|
+
// first yielded disposer still performs the call.
|
|
76
|
+
stop = access.whenAccess('account/watch', function* () {
|
|
77
|
+
void (async () => {
|
|
78
|
+
try {
|
|
79
|
+
const result = await remote.account.getBalance(metadata)
|
|
80
|
+
const value = result !== undefined && result.ok === true ? result.value : undefined
|
|
81
|
+
finish(value !== undefined && value.status === 'ready' ? value : undefined)
|
|
82
|
+
} catch {
|
|
83
|
+
finish(undefined)
|
|
84
|
+
}
|
|
85
|
+
})()
|
|
86
|
+
yield () => {
|
|
87
|
+
if (!settled) {
|
|
88
|
+
settled = true
|
|
89
|
+
clearTimeout(timer)
|
|
90
|
+
resolve(undefined)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|