@aryacode/dsh-aryacode-polish 0.3.7 → 0.4.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 +89 -0
- package/README.md +3 -2
- package/client/account.js +53 -60
- package/client/face.js +354 -75
- package/client/switches.json +6 -0
- package/docs/workspace-folder.md +67 -0
- package/entries/registry.js +6 -0
- package/lib/client.js +414 -136
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.4] - 2026-10-27
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **The row reported "this Host cannot open a folder" on a Host that can.** A Remote
|
|
8
|
+
method answers an envelope — `{ ok, value }` — not the bare return value, so
|
|
9
|
+
`canOpenWorkspacePath()` never looked like `true`. The row now reads
|
|
10
|
+
`ok === true && value === true`, tolerates a plain boolean, and checks the
|
|
11
|
+
`ok` flag of the open call itself instead of trusting that no throw means opened.
|
|
12
|
+
|
|
13
|
+
## [0.4.3] - 2026-10-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **The row still could not open anything.** 0.4.2 declared `remote.session` but the
|
|
18
|
+
guard checks each namespace on its own: reading `ctx.remote.session` needs both
|
|
19
|
+
`remote` and `remote.session` on the inject list, so the call failed one level
|
|
20
|
+
earlier. The registration now declares `['workspaces', 'remote', 'remote.session']`.
|
|
21
|
+
|
|
22
|
+
## [0.4.2] - 2026-10-27
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **The row did nothing when clicked.** The Shell refuses `ctx.get('remote.session')`
|
|
27
|
+
— a Remote namespace may only be read through a declared injection — so the open
|
|
28
|
+
call failed before it reached the Host. The registration now declares
|
|
29
|
+
`ctx.inject(['workspaces', 'remote.session'], …)` and calls the namespace the
|
|
30
|
+
injected context hands it. Found by the row reporting its own failure (0.4.1).
|
|
31
|
+
|
|
32
|
+
## [0.4.1] - 2026-10-27
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- The **Open workspace folder** row reports a failed open instead of doing nothing.
|
|
37
|
+
A menu row that silently ignores the click is impossible to diagnose in the
|
|
38
|
+
field; the bundle now writes one console.warn naming the failing step (no
|
|
39
|
+
Session Remote, a Host that cannot open a path, or the Remote error itself).
|
|
40
|
+
|
|
41
|
+
## [0.4.0] - 2026-10-27
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **Open workspace folder.** A Session's "…" menu gains an **Open workspace folder**
|
|
46
|
+
row that opens the Session's workspace directory in Explorer/Finder through the
|
|
47
|
+
Session Remote (`openWorkspacePath`), gated by `canOpenWorkspacePath` and resolved
|
|
48
|
+
from the Workspace snapshot. New switch `openWorkspaceFolder`, on by default, and
|
|
49
|
+
the row hides itself when the Session has no workspace or the Host cannot open a
|
|
50
|
+
path.
|
|
51
|
+
- `docs/workspace-folder.md` records why the requested surface — right-clicking the
|
|
52
|
+
workspace row itself — is not reachable from a plugin: that row has no context
|
|
53
|
+
menu, the workspace-scoped menu list slot does not exist, and native menus and
|
|
54
|
+
desktop IPC are closed to plugins. The bundle ships on the documented Session-row
|
|
55
|
+
slot rather than hijacking private markup, and the note says where the feature
|
|
56
|
+
should move if a workspace-scoped slot ever lands.
|
|
57
|
+
|
|
58
|
+
## [0.3.9] - 2026-10-27
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- **The cost pill now looks like it belongs in the composer.** It was a foreign
|
|
63
|
+
widget next to the harness pills: two lines of text ("≈$0.538 partially
|
|
64
|
+
priced") in the row's flow. It now wears the same skin as the statistics pills
|
|
65
|
+
— the same 13 px-minus-one scale, 20 px line height, `1px 8px` padding, tertiary
|
|
66
|
+
label colour, tabular digits, a 14 px glyph, and `white-space: nowrap`, so it
|
|
67
|
+
reads as one line at the same size as its neighbours.
|
|
68
|
+
- The "partially priced" caveat moved out of the pill and into the card, where it
|
|
69
|
+
can be read. The pill keeps a single `*` marker for the same fact.
|
|
70
|
+
- The breakdown card is now the harness's own stat-dialog: same panel width
|
|
71
|
+
clamp, 16 px padding, `--dsw-radius-lg` corners, menu backdrop, prominent
|
|
72
|
+
elevation, 12/18 type, a two-column definition list with tabular figures, and
|
|
73
|
+
a portaled `position: fixed` panel measured from the pill so it cannot be
|
|
74
|
+
clipped by the composer scroller. It closes on outside click and on Escape.
|
|
75
|
+
|
|
76
|
+
## [0.3.8] - 2026-10-27
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
- **The balance line never showed a number.** It waited for the host account
|
|
81
|
+
bundle's own access grant before reading, on the theory that the grant proves
|
|
82
|
+
the call is allowed. That grant belongs to the bundle that owns the account
|
|
83
|
+
stream, so for a plugin it can simply never arrive — the read hit its timeout,
|
|
84
|
+
resolved to nothing, and the sidebar looked empty with no error anywhere. The
|
|
85
|
+
line now reads the account Remote directly inside
|
|
86
|
+
`ctx.inject(['remote', 'remote.account'], …)`, and the Host still decides: an
|
|
87
|
+
unauthorised call comes back refused and the line stays hidden.
|
|
88
|
+
- The read retries on its own (1.5 s, 5 s, 15 s, 30 s, and again whenever the
|
|
89
|
+
window regains focus), so a Shell that is still signing in at first paint no
|
|
90
|
+
longer leaves a permanently blank line.
|
|
91
|
+
|
|
3
92
|
## [0.3.7] - 2026-10-27
|
|
4
93
|
|
|
5
94
|
### Fixed
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ installed into and patches nothing. It is one package, one Plugins row, and one
|
|
|
7
7
|
switch per feature, so a refinement that turns out to be wrong for a machine can
|
|
8
8
|
be turned off without uninstalling anything.
|
|
9
9
|
|
|
10
|
-
Status: **
|
|
10
|
+
Status: **three features shipped.** The version is the package's, and the card
|
|
11
11
|
header already shows it.
|
|
12
12
|
|
|
13
13
|
## What it is
|
|
@@ -32,6 +32,7 @@ Configure page automatically.
|
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| **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. |
|
|
34
34
|
| **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. |
|
|
35
|
+
| **Open workspace folder** (`openWorkspaceFolder`) | On | An **Open workspace folder** row in a Session's "…" menu that opens the Session's workspace in Explorer/Finder. Renders nothing when the Session has no workspace or the Host cannot open a path. See [Open a workspace folder](docs/workspace-folder.md) for why the request had to land here instead of on the workspace row itself. |
|
|
35
36
|
|
|
36
37
|
### How the cost is computed
|
|
37
38
|
|
|
@@ -130,7 +131,7 @@ client/switches.json the browser half of the seam — one entry per switch
|
|
|
130
131
|
scripts/build-client.mjs wraps client/face.js into lib/client.js
|
|
131
132
|
lib/client.js generated: the bundle the Shell loads
|
|
132
133
|
locale/ card title and description (en, zh)
|
|
133
|
-
docs/ parked investigations
|
|
134
|
+
docs/ feature notes and parked investigations
|
|
134
135
|
test/ node --test specs
|
|
135
136
|
```
|
|
136
137
|
|
package/client/account.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reading the account balance from a plugin face.
|
|
3
3
|
*
|
|
4
|
-
* The account Remote methods are
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The account Remote methods are the same ones the shipped account row uses. An
|
|
5
|
+
* earlier version waited for the host account bundle's own access grant before
|
|
6
|
+
* reading, on the theory that the grant proves the call is allowed. It does not:
|
|
7
|
+
* that grant belongs to the bundle that owns the account stream, and a plugin
|
|
8
|
+
* that waits for it can wait forever — the sidebar line then stays empty with no
|
|
9
|
+
* error anywhere, which is exactly what a user reports as "I don't see the
|
|
10
|
+
* balance".
|
|
11
|
+
*
|
|
12
|
+
* So this reads directly. The Host still decides: an unauthorised call comes back
|
|
13
|
+
* `{ ok: false }` (or throws) and this resolves to `undefined`. Every failure — no
|
|
14
|
+
* account namespace in this Shell, a refused call, a failed call — resolves to
|
|
15
|
+
* `undefined` instead of throwing, because a sidebar line is never worth breaking
|
|
16
|
+
* the shell over.
|
|
10
17
|
*
|
|
11
18
|
* This module is inlined into `lib/client.js` by `scripts/build-client.mjs` and
|
|
12
19
|
* imported directly by `test/account.spec.mjs`, so the tested source is the one
|
|
@@ -35,62 +42,48 @@ export function clientMetadata(version) {
|
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
/**
|
|
38
|
-
*
|
|
45
|
+
* The account Remote namespace, or undefined when this Shell has none.
|
|
39
46
|
*
|
|
40
|
-
* @param ctx - the browser plugin context;
|
|
41
|
-
*
|
|
42
|
-
* @returns
|
|
47
|
+
* @param ctx - the browser plugin context; the balance surface is registered
|
|
48
|
+
* inside `ctx.inject(['remote', 'remote.account'], …)` so this normally resolves.
|
|
49
|
+
* @returns the namespace, or undefined.
|
|
43
50
|
*/
|
|
44
|
-
export function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
export function accountService(ctx) {
|
|
52
|
+
try {
|
|
53
|
+
const remote = ctx.remote !== undefined ? ctx.remote : ctx.get('remote')
|
|
54
|
+
const account = remote !== undefined && remote !== null ? remote.account : undefined
|
|
55
|
+
return account !== undefined && account !== null && typeof account.getBalance === 'function' ? account : undefined
|
|
56
|
+
} catch {
|
|
57
|
+
return undefined
|
|
51
58
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Read the balance once.
|
|
63
|
+
*
|
|
64
|
+
* @param ctx - the browser plugin context.
|
|
65
|
+
* @param version - the version this build stamps into the account metadata.
|
|
66
|
+
* @param options - `timeoutMs` bounds a call that never settles.
|
|
67
|
+
* @returns the ready balance view, or undefined when there is no number to show.
|
|
68
|
+
*/
|
|
69
|
+
export async function readBalance(ctx, version, { timeoutMs = 15000 } = {}) {
|
|
70
|
+
const account = accountService(ctx)
|
|
71
|
+
if (account === undefined) return undefined
|
|
72
|
+
const call = (async () => {
|
|
73
|
+
const result = await account.getBalance(clientMetadata(version))
|
|
74
|
+
if (result === undefined || result === null || result.ok !== true) return undefined
|
|
75
|
+
const value = result.value
|
|
76
|
+
return value !== undefined && value !== null && value.status === 'ready' ? value : undefined
|
|
77
|
+
})().catch(() => undefined)
|
|
78
|
+
let timer
|
|
79
|
+
const timeout = new Promise(resolve => {
|
|
80
|
+
timer = setTimeout(() => resolve(undefined), timeoutMs)
|
|
81
|
+
// A pending balance read must not keep a Node process alive during tests.
|
|
82
|
+
if (typeof timer === 'object' && timer !== null && typeof timer.unref === 'function') timer.unref()
|
|
83
|
+
})
|
|
84
|
+
try {
|
|
85
|
+
return await Promise.race([call, timeout])
|
|
86
|
+
} finally {
|
|
87
|
+
clearTimeout(timer)
|
|
95
88
|
}
|
|
96
89
|
}
|