herdr-remote 0.2.18 → 0.2.19
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/bin/herdr-remote.js +3 -225
- package/dist/agent-status.js +130 -0
- package/dist/binary-plist.js +157 -0
- package/dist/cli.js +255 -0
- package/dist/config.js +270 -0
- package/dist/connector/agent-watch.js +110 -0
- package/dist/connector/font-server.js +165 -0
- package/dist/connector/host-connector.js +430 -0
- package/dist/connector/lock.js +66 -0
- package/dist/connector/main.js +18 -0
- package/dist/connector/sessions.js +366 -0
- package/dist/connector/update-report.js +67 -0
- package/dist/connector/wire.js +42 -0
- package/{src → dist}/exit-codes.js +1 -4
- package/dist/font-subset.js +110 -0
- package/dist/herdr-api.js +246 -0
- package/dist/herdr-command.js +223 -0
- package/dist/herdr-plugin.js +116 -0
- package/dist/herdr-server.js +189 -0
- package/dist/i18n/en.js +223 -0
- package/dist/i18n/index.js +48 -0
- package/dist/i18n/zh.js +222 -0
- package/dist/keepalive/detached.js +78 -0
- package/dist/keepalive/environment.js +44 -0
- package/dist/keepalive/index.js +110 -0
- package/dist/keepalive/launchd.js +117 -0
- package/dist/keepalive/systemd.js +108 -0
- package/dist/keepalive/types.js +1 -0
- package/dist/lib/process.js +12 -0
- package/dist/lifecycle.js +59 -0
- package/dist/net-interfaces.js +73 -0
- package/dist/pasted-files.js +115 -0
- package/dist/paths.js +62 -0
- package/dist/pty-session.js +96 -0
- package/dist/relay-client.js +106 -0
- package/dist/relay-urls.js +99 -0
- package/dist/runtime.js +106 -0
- package/dist/service.js +260 -0
- package/dist/settings-model.js +298 -0
- package/dist/socket-discovery.js +48 -0
- package/dist/supervisor.js +319 -0
- package/dist/terminal-font/deps.js +73 -0
- package/dist/terminal-font/files.js +354 -0
- package/dist/terminal-font/formats.js +335 -0
- package/dist/terminal-font/index.js +187 -0
- package/dist/terminal-font/readers.js +316 -0
- package/dist/terminal-font/terminals.js +128 -0
- package/dist/terminal-palette.js +339 -0
- package/dist/tui/App.js +195 -0
- package/dist/tui/api.js +79 -0
- package/dist/tui/components/TextField.js +50 -0
- package/dist/tui/components/common.js +80 -0
- package/dist/tui/index.js +36 -0
- package/dist/tui/mouse/index.js +164 -0
- package/dist/tui/mouse/protocol.js +96 -0
- package/dist/tui/mouse/source.js +74 -0
- package/dist/tui/screens/About.js +166 -0
- package/dist/tui/screens/Herdr.js +74 -0
- package/dist/tui/screens/Keepalive.js +84 -0
- package/dist/tui/screens/Overview.js +36 -0
- package/dist/tui/screens/Pair.js +74 -0
- package/dist/tui/screens/Relay.js +242 -0
- package/dist/tui/screens/Services.js +63 -0
- package/dist/tui/screens/Wizard.js +235 -0
- package/dist/tui/terminal.js +39 -0
- package/dist/tui/theme.js +33 -0
- package/dist/updater.js +382 -0
- package/herdr-plugin.toml +1 -1
- package/package.json +8 -8
- package/dist/tui.mjs +0 -5775
- package/src/agent-status.js +0 -139
- package/src/binary-plist.js +0 -146
- package/src/config.js +0 -454
- package/src/font-subset.js +0 -110
- package/src/herdr-api.js +0 -244
- package/src/herdr-command.js +0 -231
- package/src/herdr-plugin.js +0 -130
- package/src/herdr-server.js +0 -201
- package/src/host-connector.js +0 -1069
- package/src/i18n/en.js +0 -240
- package/src/i18n/index.js +0 -56
- package/src/i18n/zh.js +0 -239
- package/src/keepalive.js +0 -460
- package/src/lifecycle.js +0 -62
- package/src/net-interfaces.js +0 -76
- package/src/pasted-files.js +0 -176
- package/src/pty-session.js +0 -92
- package/src/service.js +0 -530
- package/src/settings-model.js +0 -323
- package/src/socket-discovery.js +0 -60
- package/src/state.js +0 -47
- package/src/supervisor.js +0 -250
- package/src/terminal-font.js +0 -1204
- package/src/terminal-palette.js +0 -325
- package/src/updater.js +0 -394
package/dist/updater.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// Self-update for the npm-installed CLI.
|
|
2
|
+
//
|
|
3
|
+
// The update path is only meaningful for a package installed from npm. A source
|
|
4
|
+
// checkout is managed by git and a linked development copy by the developer, so
|
|
5
|
+
// this refuses to touch either: silently running `npm install -g` over a
|
|
6
|
+
// checkout would replace the tree someone is working in.
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { PACKAGE_ROOT } from './paths.js';
|
|
12
|
+
const PACKAGE_NAME = 'herdr-remote';
|
|
13
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
14
|
+
/**
|
|
15
|
+
* A public mirror, tried last.
|
|
16
|
+
*
|
|
17
|
+
* `registry.npmjs.org` is not reachable from every network this runs on — in
|
|
18
|
+
* mainland China it usually is not — and "could not reach npm registry" on a
|
|
19
|
+
* machine that installs packages perfectly well is a bug report, not a
|
|
20
|
+
* diagnosis. The mirror is read-only and only ever asked for a version number.
|
|
21
|
+
*/
|
|
22
|
+
const MIRROR_REGISTRY = 'https://registry.npmmirror.com';
|
|
23
|
+
function currentVersion() {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8')).version;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return '0.0.0';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* How this copy got here.
|
|
33
|
+
*
|
|
34
|
+
* - `npm` installed from the registry; updating is `npm install -g`.
|
|
35
|
+
* - `linked` `npm link`ed into a global tree from a working copy.
|
|
36
|
+
* - `source` run straight out of a checkout.
|
|
37
|
+
*
|
|
38
|
+
* The distinction is drawn from the path rather than from npm, which would
|
|
39
|
+
* mean shelling out on every render.
|
|
40
|
+
*/
|
|
41
|
+
function installKind() {
|
|
42
|
+
const root = PACKAGE_ROOT;
|
|
43
|
+
// A real install always lives inside a node_modules tree.
|
|
44
|
+
if (!root.split(path.sep).includes('node_modules'))
|
|
45
|
+
return 'source';
|
|
46
|
+
try {
|
|
47
|
+
// `npm link` leaves a symlink where a published install has a directory.
|
|
48
|
+
if (fs.lstatSync(root).isSymbolicLink())
|
|
49
|
+
return 'linked';
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Unreadable: treat as a normal install and let npm report the problem.
|
|
53
|
+
}
|
|
54
|
+
return 'npm';
|
|
55
|
+
}
|
|
56
|
+
/** `HERDR_REMOTE_UPDATE_CHECK=0` turns the automatic checks off (offline machines, tests). */
|
|
57
|
+
function updateChecksEnabled(env = process.env) {
|
|
58
|
+
return env.HERDR_REMOTE_UPDATE_CHECK !== '0';
|
|
59
|
+
}
|
|
60
|
+
function canSelfUpdate() {
|
|
61
|
+
return installKind() === 'npm';
|
|
62
|
+
}
|
|
63
|
+
function normalizeRegistry(value) {
|
|
64
|
+
if (typeof value !== 'string')
|
|
65
|
+
return null;
|
|
66
|
+
const trimmed = value.trim().replace(/\/+$/, '');
|
|
67
|
+
if (!/^https?:\/\//i.test(trimmed))
|
|
68
|
+
return null;
|
|
69
|
+
return trimmed;
|
|
70
|
+
}
|
|
71
|
+
/** The `registry=` line from an npmrc file, if it has one. */
|
|
72
|
+
function registryFromNpmrc(filePath) {
|
|
73
|
+
let contents;
|
|
74
|
+
try {
|
|
75
|
+
contents = fs.readFileSync(filePath, 'utf8');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
let found = null;
|
|
81
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
82
|
+
const line = rawLine.trim();
|
|
83
|
+
if (!line || line.startsWith('#') || line.startsWith(';'))
|
|
84
|
+
continue;
|
|
85
|
+
const match = /^registry\s*=\s*(.+)$/i.exec(line);
|
|
86
|
+
// The last assignment wins, as it does for npm itself.
|
|
87
|
+
if (match)
|
|
88
|
+
found = normalizeRegistry(match[1].replace(/^["']|["']$/g, ''));
|
|
89
|
+
}
|
|
90
|
+
return found;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Where to ask, in the order to ask.
|
|
94
|
+
*
|
|
95
|
+
* Whatever npm itself is configured to use comes first: a machine behind a
|
|
96
|
+
* corporate proxy or on a mirror has already answered this question, and
|
|
97
|
+
* ignoring that answer is what made the check fail on a machine where
|
|
98
|
+
* `npm install` works. The public registry and then a public mirror follow, so
|
|
99
|
+
* a private registry that does not carry this package is not the end of it.
|
|
100
|
+
*/
|
|
101
|
+
function registryCandidates({ env = process.env, home = os.homedir(), cwd = process.cwd(), } = {}) {
|
|
102
|
+
const candidates = [];
|
|
103
|
+
const add = (value) => {
|
|
104
|
+
const normalized = normalizeRegistry(value);
|
|
105
|
+
if (normalized && !candidates.includes(normalized))
|
|
106
|
+
candidates.push(normalized);
|
|
107
|
+
};
|
|
108
|
+
// npm exports its whole config into the environment of anything it runs.
|
|
109
|
+
add(env.npm_config_registry);
|
|
110
|
+
add(env.NPM_CONFIG_REGISTRY);
|
|
111
|
+
add(env.HERDR_REMOTE_REGISTRY);
|
|
112
|
+
add(registryFromNpmrc(path.join(cwd, '.npmrc')));
|
|
113
|
+
add(registryFromNpmrc(path.join(home, '.npmrc')));
|
|
114
|
+
add(DEFAULT_REGISTRY);
|
|
115
|
+
add(MIRROR_REGISTRY);
|
|
116
|
+
return candidates;
|
|
117
|
+
}
|
|
118
|
+
/** Compare two `MAJOR.MINOR.PATCH` strings. Returns 1, -1 or 0. */
|
|
119
|
+
function compareVersions(a, b) {
|
|
120
|
+
const parse = (value) => String(value)
|
|
121
|
+
.split('-')[0]
|
|
122
|
+
.split('.')
|
|
123
|
+
.map((part) => Number.parseInt(part, 10) || 0);
|
|
124
|
+
const left = parse(a);
|
|
125
|
+
const right = parse(b);
|
|
126
|
+
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
127
|
+
const diff = (left[index] || 0) - (right[index] || 0);
|
|
128
|
+
if (diff > 0)
|
|
129
|
+
return 1;
|
|
130
|
+
if (diff < 0)
|
|
131
|
+
return -1;
|
|
132
|
+
}
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
/** One JSON request with a deadline. Never throws. */
|
|
136
|
+
async function fetchJson(url, { timeoutMs, fetchImpl }) {
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
139
|
+
try {
|
|
140
|
+
const response = await fetchImpl(url, {
|
|
141
|
+
signal: controller.signal,
|
|
142
|
+
headers: { Accept: 'application/json', 'Cache-Control': 'no-cache' },
|
|
143
|
+
});
|
|
144
|
+
if (!response.ok)
|
|
145
|
+
return { ok: false, message: `HTTP ${response.status ?? '?'}` };
|
|
146
|
+
return { ok: true, body: await response.json() };
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
const failure = error;
|
|
150
|
+
return {
|
|
151
|
+
ok: false,
|
|
152
|
+
message: failure?.name === 'AbortError' ? 'timed out' : String(failure?.message || error),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* One registry, one question: what is the latest published version?
|
|
161
|
+
*
|
|
162
|
+
* The dist-tags endpoint first: a few bytes, answered fresh by npmjs and
|
|
163
|
+
* npmmirror alike. `/<package>/latest` is the fallback for registries without
|
|
164
|
+
* it, asked for plain JSON. It used to be asked for npm's abbreviated install
|
|
165
|
+
* format, which npmjs refuses on that path with a 406, so every check quietly
|
|
166
|
+
* fell through to a mirror that had not synced the release yet.
|
|
167
|
+
*/
|
|
168
|
+
async function askRegistry(registry, { timeoutMs, fetchImpl }) {
|
|
169
|
+
const endpoints = [
|
|
170
|
+
{
|
|
171
|
+
url: `${registry}/-/package/${PACKAGE_NAME}/dist-tags`,
|
|
172
|
+
read: (body) => body?.latest,
|
|
173
|
+
},
|
|
174
|
+
{ url: `${registry}/${PACKAGE_NAME}/latest`, read: (body) => body?.version },
|
|
175
|
+
];
|
|
176
|
+
let message = null;
|
|
177
|
+
for (const endpoint of endpoints) {
|
|
178
|
+
const attempt = await fetchJson(endpoint.url, { timeoutMs, fetchImpl });
|
|
179
|
+
if (!attempt.ok) {
|
|
180
|
+
message = attempt.message;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const latest = endpoint.read(attempt.body);
|
|
184
|
+
if (typeof latest === 'string' && latest)
|
|
185
|
+
return { ok: true, latest };
|
|
186
|
+
message = 'registry answered without a version';
|
|
187
|
+
}
|
|
188
|
+
return { ok: false, message };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Ask what the current release is.
|
|
192
|
+
*
|
|
193
|
+
* Never throws: an update check is a convenience, and a machine that is offline
|
|
194
|
+
* or behind a proxy should still get a working settings screen.
|
|
195
|
+
*
|
|
196
|
+
* Every registry is asked at once and the newest answer wins. Taking the first
|
|
197
|
+
* answer let a mirror a few minutes (or hours) behind report the release before
|
|
198
|
+
* last as current. `sources` lists the registries that already carry the
|
|
199
|
+
* newest version, preferred order first, which is where an install should come
|
|
200
|
+
* from; `behind` lists the ones that do not, so the screen can say why.
|
|
201
|
+
*/
|
|
202
|
+
async function checkForUpdate({ timeoutMs = 6000, fetchImpl = globalThis.fetch, registries = registryCandidates(), } = {}) {
|
|
203
|
+
const current = currentVersion();
|
|
204
|
+
const attempts = await Promise.all(registries.map(async (registry) => ({
|
|
205
|
+
registry,
|
|
206
|
+
...(await askRegistry(registry, { timeoutMs, fetchImpl })),
|
|
207
|
+
})));
|
|
208
|
+
const answers = attempts.filter((attempt) => attempt.ok);
|
|
209
|
+
if (answers.length === 0) {
|
|
210
|
+
// The reason is carried out with the failure. "Could not reach npm
|
|
211
|
+
// registry" with nothing after it leaves the user guessing between DNS, a
|
|
212
|
+
// proxy, a firewall and a mirror that does not carry the package.
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
current,
|
|
216
|
+
errorKey: 'update.errorNetwork',
|
|
217
|
+
message: attempts
|
|
218
|
+
.map((attempt) => `${attempt.registry}: ${'message' in attempt ? attempt.message : ''}`)
|
|
219
|
+
.join('; '),
|
|
220
|
+
triedRegistries: registries,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const latest = answers
|
|
224
|
+
.map((answer) => answer.latest)
|
|
225
|
+
.reduce((best, version) => (compareVersions(version, best) > 0 ? version : best));
|
|
226
|
+
const sources = answers
|
|
227
|
+
.filter((answer) => compareVersions(answer.latest, latest) === 0)
|
|
228
|
+
.map((answer) => answer.registry);
|
|
229
|
+
const behind = answers
|
|
230
|
+
.filter((answer) => compareVersions(answer.latest, latest) < 0)
|
|
231
|
+
.map((answer) => ({ registry: answer.registry, version: answer.latest }));
|
|
232
|
+
return {
|
|
233
|
+
ok: true,
|
|
234
|
+
current,
|
|
235
|
+
latest,
|
|
236
|
+
registry: sources[0],
|
|
237
|
+
sources,
|
|
238
|
+
behind,
|
|
239
|
+
updateAvailable: compareVersions(latest, current) > 0,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/** Just the lines npm meant for a person: its `npm error` / `npm ERR!` summary. */
|
|
243
|
+
function npmErrorSummary(output) {
|
|
244
|
+
const lines = String(output || '')
|
|
245
|
+
.split(/\r?\n/)
|
|
246
|
+
.map((line) => line.trim())
|
|
247
|
+
.filter((line) => /^npm (error|ERR!)/.test(line) && !/complete log of this run/i.test(line))
|
|
248
|
+
.map((line) => line.replace(/^npm (error|ERR!)\s*/, ''))
|
|
249
|
+
.filter(Boolean);
|
|
250
|
+
return (lines.length > 0
|
|
251
|
+
? lines.slice(0, 3)
|
|
252
|
+
: String(output || '')
|
|
253
|
+
.trim()
|
|
254
|
+
.split(/\r?\n/)
|
|
255
|
+
.slice(-3)).join(' ');
|
|
256
|
+
}
|
|
257
|
+
/** "No matching version": the registry has not caught up with its own dist-tag. */
|
|
258
|
+
function isNotYetPublished(output) {
|
|
259
|
+
return /\bETARGET\b|notarget|No matching version found/i.test(String(output || ''));
|
|
260
|
+
}
|
|
261
|
+
/** What is actually installed now, read from disk rather than the require cache. */
|
|
262
|
+
function installedVersionOnDisk() {
|
|
263
|
+
try {
|
|
264
|
+
return JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8')).version;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function runNpm(spawnImpl, args, timeoutMs) {
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
let child;
|
|
273
|
+
try {
|
|
274
|
+
child = spawnImpl('npm', args, { timeout: timeoutMs });
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
resolve({
|
|
278
|
+
ok: false,
|
|
279
|
+
spawnFailed: true,
|
|
280
|
+
output: String(error?.message || error),
|
|
281
|
+
});
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
let output = '';
|
|
285
|
+
let settled = false;
|
|
286
|
+
const finish = (result) => {
|
|
287
|
+
if (!settled) {
|
|
288
|
+
settled = true;
|
|
289
|
+
resolve(result);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const collect = (chunk) => {
|
|
293
|
+
output += String(chunk);
|
|
294
|
+
};
|
|
295
|
+
child.stdout?.on('data', collect);
|
|
296
|
+
child.stderr?.on('data', collect);
|
|
297
|
+
child.on('error', (error) => finish({ ok: false, spawnFailed: true, output: error.message }));
|
|
298
|
+
child.on('close', (code) => finish({ ok: code === 0, output: output.trim() }));
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
const VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
302
|
+
/**
|
|
303
|
+
* Install a release over this one.
|
|
304
|
+
*
|
|
305
|
+
* Resolves with `{ ok, output }` rather than rejecting, so the caller can show
|
|
306
|
+
* npm's own message; npm's diagnostics are more useful than anything that could
|
|
307
|
+
* be invented here.
|
|
308
|
+
*
|
|
309
|
+
* - The exact version the check found is installed, not `@latest`, from a
|
|
310
|
+
* registry that reported it, with `--prefer-online` so npm revalidates the
|
|
311
|
+
* package metadata it cached before the release existed.
|
|
312
|
+
* - Right after a release, npm's view of the package can still lack the new
|
|
313
|
+
* version although its dist-tag already points at it: npm answers ETARGET.
|
|
314
|
+
* That is a wait, not a failure, so it is retried a few times, then the next
|
|
315
|
+
* registry that carries the version is tried.
|
|
316
|
+
* - Success is what is on disk afterwards, not npm's exit code.
|
|
317
|
+
*/
|
|
318
|
+
async function performUpdate({ spawnImpl = spawn, timeoutMs = 180_000,
|
|
319
|
+
// Seam for tests: the suite runs from a checkout, where the guard below is
|
|
320
|
+
// correctly the only reachable outcome.
|
|
321
|
+
installKindImpl = installKind,
|
|
322
|
+
// The registries that reported `version`, preferred first. Empty means
|
|
323
|
+
// "whatever npm is configured with", which is right when no check has run.
|
|
324
|
+
registry = '', sources = [], version = null, attempts = 3, retryDelayMs = 15_000, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), readInstalledVersion = installedVersionOnDisk, onAttempt = () => { }, } = {}) {
|
|
325
|
+
const kind = installKindImpl();
|
|
326
|
+
if (kind !== 'npm')
|
|
327
|
+
return { ok: false, errorKey: `update.cannot.${kind}` };
|
|
328
|
+
const target = typeof version === 'string' && VERSION_PATTERN.test(version) ? version : null;
|
|
329
|
+
const spec = `${PACKAGE_NAME}@${target || 'latest'}`;
|
|
330
|
+
const registries = [];
|
|
331
|
+
for (const candidate of [registry, ...sources]) {
|
|
332
|
+
const normalized = normalizeRegistry(candidate);
|
|
333
|
+
if (normalized && !registries.includes(normalized))
|
|
334
|
+
registries.push(normalized);
|
|
335
|
+
}
|
|
336
|
+
if (registries.length === 0)
|
|
337
|
+
registries.push('');
|
|
338
|
+
let last = { ok: false, output: '' };
|
|
339
|
+
let notYetPublished = false;
|
|
340
|
+
for (const source of registries) {
|
|
341
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
342
|
+
onAttempt({ registry: source, attempt });
|
|
343
|
+
const args = ['install', '-g', spec, '--prefer-online'];
|
|
344
|
+
if (source)
|
|
345
|
+
args.push('--registry', source);
|
|
346
|
+
const result = await runNpm(spawnImpl, args, timeoutMs);
|
|
347
|
+
if (result.ok) {
|
|
348
|
+
const installed = readInstalledVersion();
|
|
349
|
+
if (target && installed && compareVersions(installed, target) < 0) {
|
|
350
|
+
return {
|
|
351
|
+
ok: false,
|
|
352
|
+
errorKey: 'update.errorNotApplied',
|
|
353
|
+
installed,
|
|
354
|
+
output: result.output.slice(-2000),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
return { ok: true, installed, output: result.output.slice(-2000) };
|
|
358
|
+
}
|
|
359
|
+
last = result;
|
|
360
|
+
if (result.spawnFailed) {
|
|
361
|
+
return {
|
|
362
|
+
ok: false,
|
|
363
|
+
errorKey: 'update.errorFailed',
|
|
364
|
+
output: result.output,
|
|
365
|
+
summary: result.output,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
notYetPublished = isNotYetPublished(result.output);
|
|
369
|
+
if (!notYetPublished)
|
|
370
|
+
break; // Not a wait: this registry will not do better.
|
|
371
|
+
if (attempt < attempts)
|
|
372
|
+
await sleep(retryDelayMs);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
ok: false,
|
|
377
|
+
errorKey: notYetPublished ? 'update.errorNotYetPublished' : 'update.errorFailed',
|
|
378
|
+
output: String(last.output || '').slice(-2000),
|
|
379
|
+
summary: npmErrorSummary(last.output),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
export { DEFAULT_REGISTRY, MIRROR_REGISTRY, canSelfUpdate, checkForUpdate, compareVersions, currentVersion, installKind, installedVersionOnDisk, performUpdate, registryCandidates, updateChecksEnabled, };
|
package/herdr-plugin.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "herdr-remote",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.19",
|
|
4
4
|
"description": "Browser access to Herdr workspaces: plugin, host connector, and configuration TUI",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/dibin666/herdr-remote.git",
|
|
@@ -26,20 +27,19 @@
|
|
|
26
27
|
"bin": {
|
|
27
28
|
"herdr-remote": "./bin/herdr-remote.js"
|
|
28
29
|
},
|
|
29
|
-
"main": "./
|
|
30
|
+
"main": "./dist/service.js",
|
|
30
31
|
"files": [
|
|
31
32
|
"bin",
|
|
32
33
|
"dist",
|
|
33
|
-
"src",
|
|
34
34
|
"herdr-plugin.toml",
|
|
35
35
|
"config.example.json",
|
|
36
36
|
"README.zh-CN.md"
|
|
37
37
|
],
|
|
38
38
|
"scripts": {
|
|
39
|
-
"build": "node
|
|
39
|
+
"build": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
40
40
|
"prepack": "npm run build",
|
|
41
|
-
"typecheck": "tsc --noEmit",
|
|
42
|
-
"test": "
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
43
|
"start": "node bin/herdr-remote.js"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"@types/node": "^22.13.5",
|
|
56
56
|
"@types/qrcode": "^1.5.6",
|
|
57
57
|
"@types/react": "^19.2.0",
|
|
58
|
-
"esbuild": "^0.25.0",
|
|
59
58
|
"ink-testing-library": "^4.0.0",
|
|
60
|
-
"typescript": "~5.7.3"
|
|
59
|
+
"typescript": "~5.7.3",
|
|
60
|
+
"vitest": "^3.2.7"
|
|
61
61
|
}
|
|
62
62
|
}
|