dsh-ntr 0.2.0 → 0.3.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/README.en.md +9 -6
- package/README.md +9 -6
- package/cordis.patch.yml +12 -0
- package/lib/bridge.js +654 -85
- package/lib/client/panel.js +224 -78
- package/lib/client/store.js +133 -1
- package/lib/client.js +702 -304
- package/lib/client.js.map +1 -1
- package/lib/impl.js +47 -0
- package/lib/index.js +15 -1
- package/lib/types/bridge.d.ts +208 -6
- package/lib/types/client/panel.d.ts +0 -1
- package/lib/types/client/store.d.ts +54 -1
- package/lib/types/impl.d.ts +26 -1
- package/lib/types/protocol.d.ts +73 -0
- package/lib/types/update.d.ts +183 -0
- package/lib/update.js +681 -0
- package/package.json +5 -1
- package/src/bridge.ts +700 -83
- package/src/client/panel.tsx +406 -205
- package/src/client/store.ts +146 -2
- package/src/impl.ts +66 -1
- package/src/index.ts +16 -1
- package/src/protocol.ts +76 -0
- package/src/update.ts +699 -0
package/lib/types/protocol.d.ts
CHANGED
|
@@ -229,6 +229,50 @@ export interface AcpPluginConfig {
|
|
|
229
229
|
* up and reports the `cancelled` stop as-is. Default 2.
|
|
230
230
|
*/
|
|
231
231
|
staleCancelMaxRetries?: number;
|
|
232
|
+
/**
|
|
233
|
+
* Inactivity watchdog for a turn the agent owes an answer: how long the turn
|
|
234
|
+
* may stay SILENT before the host finalizes it as stalled.
|
|
235
|
+
*
|
|
236
|
+
* The clock is `LiveSession.lastAgentActivityAt`, refreshed by ANY
|
|
237
|
+
* `session/update`, at every turn start, at each segment start (the host
|
|
238
|
+
* handing the turn back after a card), and when a permission ask arrives or
|
|
239
|
+
* is resolved. The watchdog only READS it while the turn is watched: an OPEN
|
|
240
|
+
* agent segment (always true on the `/acp`-RPC path, which has no driver
|
|
241
|
+
* stream) and no pending approval. The host-card execution phase and an
|
|
242
|
+
* unanswered ask are exempt by that gate, not because the clock is frozen —
|
|
243
|
+
* they legitimately move it too.
|
|
244
|
+
*
|
|
245
|
+
* Firing finalizes the TURN only (an `agent/notice` plus the usual turn
|
|
246
|
+
* end); it does NOT kill the agent process. The wedged `session/prompt` is
|
|
247
|
+
* abandoned, and the next message reclaims the session: parked with
|
|
248
|
+
* `releaseAll` and resumed with `session/load`, or reset outright when the
|
|
249
|
+
* agent declares no `loadSession` (the context is gone and the user is
|
|
250
|
+
* told).
|
|
251
|
+
*
|
|
252
|
+
* Default 600000 ms (10 minutes); `0` disables the WHOLE watchdog, the tool
|
|
253
|
+
* window included.
|
|
254
|
+
*/
|
|
255
|
+
turnStallMs?: number;
|
|
256
|
+
/**
|
|
257
|
+
* The longer silence window applied while ANY ACP tool call is still active
|
|
258
|
+
* by `toolCallId` — parallel calls are tracked as a set, so the window lasts
|
|
259
|
+
* until the LAST of them finishes. The agent is working on its own clock in
|
|
260
|
+
* there, so the plain turn window would call a healthy long-running tool
|
|
261
|
+
* stalled.
|
|
262
|
+
*
|
|
263
|
+
* Default 1800000 ms (30 minutes); `0` falls back to
|
|
264
|
+
* {@link AcpPluginConfig.turnStallMs}. It matches
|
|
265
|
+
* `TOOL_OUTPUT_WAIT_MS = 30 * 60 * 1000` in `presets/acp/acp-guard.mjs`, so
|
|
266
|
+
* the two halves of one turn agree on how long a tool may take.
|
|
267
|
+
*/
|
|
268
|
+
turnStallToolMs?: number;
|
|
269
|
+
/**
|
|
270
|
+
* How often the watchdog scans for silent turns, in milliseconds. A tuning /
|
|
271
|
+
* test knob only: NO settings-page control exists for it — and none for the
|
|
272
|
+
* two windows above either; the whole watchdog is configured in the patch
|
|
273
|
+
* file. Default 30000 ms.
|
|
274
|
+
*/
|
|
275
|
+
turnStallTickMs?: number;
|
|
232
276
|
}
|
|
233
277
|
export type AgentProcessState = 'stopped' | 'starting' | 'ready' | 'error';
|
|
234
278
|
/** Agent facts the browser may render (never includes env/argv secrets beyond argv itself, which is config-visible). */
|
|
@@ -669,6 +713,35 @@ export interface DefaultAgentSetPayload {
|
|
|
669
713
|
export type DefaultAgentResult = {
|
|
670
714
|
agentId?: string;
|
|
671
715
|
};
|
|
716
|
+
/** `plugin/version`: the installed host package's version, for the settings badge. */
|
|
717
|
+
export type PluginVersionResult = {
|
|
718
|
+
version?: string;
|
|
719
|
+
};
|
|
720
|
+
/**
|
|
721
|
+
* Where the plugin's own update check stands. The host half only ever reports
|
|
722
|
+
* facts it can read from the harness plugin manager and the npm registry — it
|
|
723
|
+
* never downloads, swaps files, or restarts itself.
|
|
724
|
+
*/
|
|
725
|
+
export type PluginUpdateStatus = 'unsupported' | 'not-installed' | 'source-install' | 'managed' | 'current' | 'available' | 'applying' | 'busy' | 'applied' | 'restart-required' | 'failed';
|
|
726
|
+
/**
|
|
727
|
+
* One update-check answer, shared by `plugin/update-check` and
|
|
728
|
+
* `plugin/update-apply`.
|
|
729
|
+
*/
|
|
730
|
+
export interface PluginUpdateState {
|
|
731
|
+
status: PluginUpdateStatus;
|
|
732
|
+
/** 运行中(内存里)的插件版本 */
|
|
733
|
+
current: string;
|
|
734
|
+
/** 磁盘上装着的版本(listBundles().version),可读时才有 */
|
|
735
|
+
installed?: string;
|
|
736
|
+
/** profile registry 链上最新的稳定版 */
|
|
737
|
+
latest?: string;
|
|
738
|
+
/** latest 的 npm 发布时间 ISO */
|
|
739
|
+
publishedAt?: string;
|
|
740
|
+
/** 实际按顺序问过的 registry */
|
|
741
|
+
registries?: string[];
|
|
742
|
+
/** 失败/额外说明(英文技术信息,UI 自己负责本地化) */
|
|
743
|
+
message?: string;
|
|
744
|
+
}
|
|
672
745
|
/**
|
|
673
746
|
* How the composer-edge agent tag shows liveness (the blue halo). `breath`
|
|
674
747
|
* pulses the halo at the speed below; `steady` shows a constant glow.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side built-in update for dsh-ntr.
|
|
3
|
+
*
|
|
4
|
+
* The plugin NEVER downloads a package, swaps files, or restarts itself. The
|
|
5
|
+
* harness plugin manager owns installation (profile bookkeeping, npm, the
|
|
6
|
+
* restart contract); this module only asks it what is installed, asks an npm
|
|
7
|
+
* registry what is published, and delegates the install back to the manager.
|
|
8
|
+
* Every method therefore reports a {@link PluginUpdateState} instead of
|
|
9
|
+
* throwing; version syntax is validated at the RPC boundary (`impl.ts`).
|
|
10
|
+
*/
|
|
11
|
+
import type { PluginUpdateState } from './protocol.ts';
|
|
12
|
+
/** The npm package this plugin is published as, and the bundle name to look for. */
|
|
13
|
+
export declare const UPDATE_PACKAGE = "dsh-ntr";
|
|
14
|
+
/** npm's public registry; the only base a private `resolved` may be widened to. */
|
|
15
|
+
export declare const NPM_PUBLIC_REGISTRY = "https://registry.npmjs.org";
|
|
16
|
+
/**
|
|
17
|
+
* Structural view of one row from the harness plugin manager's
|
|
18
|
+
* `listBundles()`. Deliberately local: importing the host package would tie
|
|
19
|
+
* this module to a runtime version it must merely probe structurally.
|
|
20
|
+
*/
|
|
21
|
+
export interface UpdateBundleInfo {
|
|
22
|
+
name: string;
|
|
23
|
+
installed: boolean;
|
|
24
|
+
removable?: boolean;
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
version?: string;
|
|
27
|
+
source?: string;
|
|
28
|
+
readOnlyReason?: string;
|
|
29
|
+
error?: {
|
|
30
|
+
code?: string;
|
|
31
|
+
message?: string;
|
|
32
|
+
diagnostic?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Structural view of `pluginManager.registries()`. */
|
|
36
|
+
export interface UpdateRegistryPlan {
|
|
37
|
+
registry?: string | null;
|
|
38
|
+
fallbackRegistries?: readonly string[];
|
|
39
|
+
resolved?: string | null;
|
|
40
|
+
}
|
|
41
|
+
/** Structural view of the manager's `installBundle()` / `removeBundle()` result (a ChangeResult). */
|
|
42
|
+
export interface UpdateInstallResult {
|
|
43
|
+
application?: string;
|
|
44
|
+
version?: string;
|
|
45
|
+
error?: {
|
|
46
|
+
code?: string;
|
|
47
|
+
message?: string;
|
|
48
|
+
diagnostic?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** The plugin-manager face this service needs. Every member is probed, never assumed. */
|
|
52
|
+
export interface UpdateManagerLike {
|
|
53
|
+
listBundles?(): Promise<readonly UpdateBundleInfo[]>;
|
|
54
|
+
registries?(): Promise<UpdateRegistryPlan>;
|
|
55
|
+
installBundle?(spec: string, options?: {
|
|
56
|
+
enabled?: boolean;
|
|
57
|
+
registry?: string;
|
|
58
|
+
}): Promise<UpdateInstallResult | undefined>;
|
|
59
|
+
/**
|
|
60
|
+
* Native-update half of the harness: `installBundle` refuses to replace an
|
|
61
|
+
* already-installed bundle while HMR holds it, so the host removes first.
|
|
62
|
+
* Optional — older hosts cannot install at all and answer `already-installed`.
|
|
63
|
+
*/
|
|
64
|
+
removeBundle?(name: string): Promise<UpdateInstallResult | undefined>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The shared eligibility verdict. `ok` carries the disk version (when the host
|
|
68
|
+
* reports one); the failure arm is already a full state, so check/apply can
|
|
69
|
+
* return it verbatim.
|
|
70
|
+
*/
|
|
71
|
+
export type BundleEligibility = {
|
|
72
|
+
ok: true;
|
|
73
|
+
installed?: string;
|
|
74
|
+
} | {
|
|
75
|
+
ok: false;
|
|
76
|
+
state: PluginUpdateState;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Whether a `listBundles().source` value names a registry-resolvable version
|
|
80
|
+
* spec — the only install shape this updater may replace. The test is positive
|
|
81
|
+
* on purpose: link/file/git/workspace/URL/tarball/relative forms all carry a
|
|
82
|
+
* `:` / `/` / `\` or start with `.`, so they fall out without an ever-growing
|
|
83
|
+
* blacklist (a new source scheme must not become silently updatable).
|
|
84
|
+
*/
|
|
85
|
+
export declare function isRegistrySpec(source: string): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Compare two semver strings (build metadata ignored, prerelease precedence per
|
|
88
|
+
* semver §11). Malformed segments count as `0`, so the function never throws on
|
|
89
|
+
* a value read off a registry.
|
|
90
|
+
*/
|
|
91
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
92
|
+
/**
|
|
93
|
+
* Turn the manager's registry plan into the ordered list this service will
|
|
94
|
+
* actually query, following the host's privacy rule: a *private* `resolved`
|
|
95
|
+
* registry must not be widened with the shared fallback chain.
|
|
96
|
+
*
|
|
97
|
+
* - No configured registry: query `resolved`; append fallbacks only when it is
|
|
98
|
+
* npm's public registry or one of the fallbacks themselves.
|
|
99
|
+
* - Configured registry: query it first, then `resolved` under the same
|
|
100
|
+
* condition, then the fallbacks.
|
|
101
|
+
*
|
|
102
|
+
* http(s) only (a private http registry is legitimate), trailing slashes
|
|
103
|
+
* normalized, duplicates dropped. A base that carries credentials in its URL is
|
|
104
|
+
* skipped: a bare GET cannot replay `.npmrc` auth, so it could only fail with a
|
|
105
|
+
* 401 that reads as "unreachable".
|
|
106
|
+
*/
|
|
107
|
+
export declare function registryPlan(plan: UpdateRegistryPlan): string[];
|
|
108
|
+
/**
|
|
109
|
+
* Pick the version an update should offer.
|
|
110
|
+
*
|
|
111
|
+
* `dist-tags.latest` leads when it is a stable release newer than `current`
|
|
112
|
+
* (that is the line npm itself blesses). Otherwise the highest stable,
|
|
113
|
+
* non-deprecated published version wins — a lagging `latest` tag must not hide
|
|
114
|
+
* a newer release. When nothing is newer, the stable `latest` tag is still
|
|
115
|
+
* returned so the UI can say "you are on the latest release".
|
|
116
|
+
*/
|
|
117
|
+
export declare function pickUpdateTarget(packument: unknown, current: string): {
|
|
118
|
+
version?: string;
|
|
119
|
+
publishedAt?: string;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Pure eligibility matrix, shared by check and apply.
|
|
123
|
+
*
|
|
124
|
+
* `undefined` means the manager (or one of its methods) is unavailable; an
|
|
125
|
+
* empty list means the manager answered but does not carry this plugin.
|
|
126
|
+
*/
|
|
127
|
+
export declare function bundleEligibility(bundles: readonly UpdateBundleInfo[] | undefined, currentVersion: string): BundleEligibility;
|
|
128
|
+
export interface UpdateServiceOptions {
|
|
129
|
+
resolveManager(): UpdateManagerLike | undefined;
|
|
130
|
+
currentVersion: string;
|
|
131
|
+
isBusy(): boolean;
|
|
132
|
+
fetchImpl?: typeof fetch;
|
|
133
|
+
now?: () => number;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Update checks and installs, memoized per registry base.
|
|
137
|
+
*
|
|
138
|
+
* Caching exists because the settings page polls: a 10-minute window keeps the
|
|
139
|
+
* check honest without turning every mount into a registry round trip. Apply
|
|
140
|
+
* clears the cache so the next check sees the freshly installed version.
|
|
141
|
+
*/
|
|
142
|
+
export declare class UpdateService {
|
|
143
|
+
private readonly resolveManager;
|
|
144
|
+
private readonly currentVersion;
|
|
145
|
+
private readonly isBusy;
|
|
146
|
+
private readonly fetchImpl;
|
|
147
|
+
private readonly now;
|
|
148
|
+
private readonly cache;
|
|
149
|
+
private applying;
|
|
150
|
+
/** Registry that answered the last `available` check, passed through to apply. */
|
|
151
|
+
private lastTarget?;
|
|
152
|
+
constructor(options: UpdateServiceOptions);
|
|
153
|
+
/** The manager surface plus the disk-version verdict, or a ready-to-return failure state. */
|
|
154
|
+
private eligibility;
|
|
155
|
+
private fetchTarget;
|
|
156
|
+
/** One registry's packument, via the 10-minute cache. Throws when unreachable. */
|
|
157
|
+
private targetFor;
|
|
158
|
+
/**
|
|
159
|
+
* Ask the manager's registry chain what is published; never installs.
|
|
160
|
+
*
|
|
161
|
+
* A mirror may lag behind the rest of the chain, so a base that only knows
|
|
162
|
+
* the running (or an older) release does not end the walk — the next base
|
|
163
|
+
* gets its turn. `busy` is deliberately not checked here: reading a registry
|
|
164
|
+
* costs no ACP work, and the settings card should still be able to say what
|
|
165
|
+
* is out there while a turn runs.
|
|
166
|
+
*/
|
|
167
|
+
check(): Promise<PluginUpdateState>;
|
|
168
|
+
/**
|
|
169
|
+
* Install one exact version through the harness plugin manager.
|
|
170
|
+
*
|
|
171
|
+
* Re-entry while an install is running answers `applying` instead of
|
|
172
|
+
* stacking a second npm operation; the install itself is the host's job.
|
|
173
|
+
*
|
|
174
|
+
* With HMR alive the manager refuses to replace a bundle already in the
|
|
175
|
+
* profile (`already-installed`), so the host's own update semantics are
|
|
176
|
+
* remove-then-install. That is NOT self-replacement: a human gesture started
|
|
177
|
+
* it, the manager still performs every file operation, and it unloads the
|
|
178
|
+
* live module first — exactly what the host does for any bundle update. If
|
|
179
|
+
* the reinstall fails, the previous version is put back through the same
|
|
180
|
+
* `installBundle` path.
|
|
181
|
+
*/
|
|
182
|
+
apply(version: string): Promise<PluginUpdateState>;
|
|
183
|
+
}
|