myshell-tools 2.8.0 → 2.13.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/CHANGELOG.md +38 -0
- package/README.md +34 -3
- package/dist/cli.js +35 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts +36 -1
- package/dist/commands/doctor.js +130 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.js +16 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/login.d.ts +8 -5
- package/dist/commands/login.js +143 -44
- package/dist/commands/login.js.map +1 -1
- package/dist/core/classify.d.ts +20 -4
- package/dist/core/classify.js +241 -43
- package/dist/core/classify.js.map +1 -1
- package/dist/core/orchestrate.js +115 -85
- package/dist/core/orchestrate.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/infra/config.d.ts +6 -0
- package/dist/infra/config.js.map +1 -1
- package/dist/infra/credentials.d.ts +28 -0
- package/dist/infra/credentials.js +52 -1
- package/dist/infra/credentials.js.map +1 -1
- package/dist/infra/update-check.d.ts +67 -0
- package/dist/infra/update-check.js +181 -0
- package/dist/infra/update-check.js.map +1 -0
- package/dist/interface/menu.d.ts +69 -0
- package/dist/interface/menu.js +334 -30
- package/dist/interface/menu.js.map +1 -1
- package/dist/interface/render.js +8 -1
- package/dist/interface/render.js.map +1 -1
- package/dist/interface/run.d.ts +12 -3
- package/dist/interface/run.js +3 -3
- package/dist/interface/run.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/infra/update-check.ts — Self-update detection for myshell-tools.
|
|
3
|
+
*
|
|
4
|
+
* Checks the npm registry for the latest published version, caches the result
|
|
5
|
+
* so the check costs nothing on subsequent runs within the TTL, and reports
|
|
6
|
+
* whether an update is available.
|
|
7
|
+
*
|
|
8
|
+
* Architecture rules:
|
|
9
|
+
* - NEVER throws — all errors are caught and return a safe default.
|
|
10
|
+
* - NEVER hits the network in tests — `fetchLatest` is injected so tests stay hermetic.
|
|
11
|
+
* - Cache is stored atomically at ~/.myshell-tools/update-check.json.
|
|
12
|
+
* - Date.now() is allowed here (infra layer, same as config.ts / atomic.ts).
|
|
13
|
+
*/
|
|
14
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { atomicWrite } from './atomic.js';
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Constants
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const TTL_MS_DEFAULT = 24 * 60 * 60 * 1000; // 24 hours
|
|
22
|
+
const FETCH_TIMEOUT_MS = 1_500;
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Path helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
function getCacheDir(homeDir) {
|
|
27
|
+
return join(homeDir, '.myshell-tools');
|
|
28
|
+
}
|
|
29
|
+
function getCachePath(homeDir) {
|
|
30
|
+
return join(getCacheDir(homeDir), 'update-check.json');
|
|
31
|
+
}
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Cache helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Load the update cache from disk. Returns null on missing/corrupt file.
|
|
37
|
+
* Never throws.
|
|
38
|
+
*/
|
|
39
|
+
export async function loadUpdateCache(homeDir) {
|
|
40
|
+
const home = homeDir ?? homedir();
|
|
41
|
+
try {
|
|
42
|
+
const raw = await readFile(getCachePath(home), 'utf8');
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
if (parsed !== null &&
|
|
45
|
+
typeof parsed === 'object' &&
|
|
46
|
+
'checkedAt' in parsed &&
|
|
47
|
+
'latest' in parsed &&
|
|
48
|
+
typeof parsed['checkedAt'] === 'number' &&
|
|
49
|
+
typeof parsed['latest'] === 'string') {
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Persist the update cache atomically. Creates the .myshell-tools directory
|
|
60
|
+
* if it does not exist. Never throws.
|
|
61
|
+
*/
|
|
62
|
+
export async function saveUpdateCache(latest, now, homeDir) {
|
|
63
|
+
const home = homeDir ?? homedir();
|
|
64
|
+
try {
|
|
65
|
+
await mkdir(getCacheDir(home), { recursive: true });
|
|
66
|
+
const cache = { checkedAt: now, latest };
|
|
67
|
+
await atomicWrite(getCachePath(home), JSON.stringify(cache, null, 2));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Silently ignore — failing to cache is not a fatal error.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Version comparison
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Compare two semver-ish version strings.
|
|
78
|
+
*
|
|
79
|
+
* Splits on `.`, compares numeric major/minor/patch segments in order.
|
|
80
|
+
* Any `-prerelease` suffix is stripped before comparison.
|
|
81
|
+
* Non-numeric segments are treated as 0.
|
|
82
|
+
* Returns true iff latest > current.
|
|
83
|
+
* Never throws.
|
|
84
|
+
*/
|
|
85
|
+
export function isNewerVersion(latest, current) {
|
|
86
|
+
try {
|
|
87
|
+
const parse = (v) => {
|
|
88
|
+
// Strip any prerelease suffix (e.g. "1.2.3-beta.1" → "1.2.3")
|
|
89
|
+
const base = v.split('-')[0] ?? '';
|
|
90
|
+
return base.split('.').map((part) => {
|
|
91
|
+
const n = parseInt(part, 10);
|
|
92
|
+
return Number.isFinite(n) ? n : 0;
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
const lParts = parse(latest);
|
|
96
|
+
const cParts = parse(current);
|
|
97
|
+
const len = Math.max(lParts.length, cParts.length);
|
|
98
|
+
for (let i = 0; i < len; i++) {
|
|
99
|
+
const l = lParts[i] ?? 0;
|
|
100
|
+
const c = cParts[i] ?? 0;
|
|
101
|
+
if (l > c)
|
|
102
|
+
return true;
|
|
103
|
+
if (l < c)
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return false; // equal
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// npm registry fetch (real network — injected in tests)
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
/**
|
|
116
|
+
* Fetch the latest published version of myshell-tools from the npm registry.
|
|
117
|
+
*
|
|
118
|
+
* Uses global fetch with a 1500ms AbortSignal timeout.
|
|
119
|
+
* Returns the version string, or null on any error/timeout.
|
|
120
|
+
* Never throws.
|
|
121
|
+
*/
|
|
122
|
+
export async function fetchLatestFromNpm() {
|
|
123
|
+
const ac = new AbortController();
|
|
124
|
+
const timer = setTimeout(() => { ac.abort(); }, FETCH_TIMEOUT_MS);
|
|
125
|
+
try {
|
|
126
|
+
const res = await fetch('https://registry.npmjs.org/myshell-tools/latest', {
|
|
127
|
+
signal: ac.signal,
|
|
128
|
+
});
|
|
129
|
+
if (!res.ok)
|
|
130
|
+
return null;
|
|
131
|
+
const data = await res.json();
|
|
132
|
+
if (data !== null &&
|
|
133
|
+
typeof data === 'object' &&
|
|
134
|
+
'version' in data &&
|
|
135
|
+
typeof data['version'] === 'string') {
|
|
136
|
+
return data['version'] ?? null;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Check whether a newer version of myshell-tools is available.
|
|
149
|
+
*
|
|
150
|
+
* Uses a cache file so the npm registry is only contacted once per TTL (default 24h).
|
|
151
|
+
* If the cache is fresh, the registry is NOT contacted at all.
|
|
152
|
+
* On any error returns { current, latest: null, updateAvailable: false }.
|
|
153
|
+
* Never throws.
|
|
154
|
+
*/
|
|
155
|
+
export async function checkForUpdate(opts) {
|
|
156
|
+
const { currentVersion, now, homeDir, ttlMs = TTL_MS_DEFAULT } = opts;
|
|
157
|
+
const fetchFn = opts.fetchLatest ?? fetchLatestFromNpm;
|
|
158
|
+
try {
|
|
159
|
+
// Check if we have a fresh cache
|
|
160
|
+
const cache = await loadUpdateCache(homeDir);
|
|
161
|
+
if (cache !== null && now - cache.checkedAt < ttlMs) {
|
|
162
|
+
// Cache is fresh — use it without hitting the network
|
|
163
|
+
const updateAvailable = isNewerVersion(cache.latest, currentVersion);
|
|
164
|
+
return { current: currentVersion, latest: cache.latest, updateAvailable };
|
|
165
|
+
}
|
|
166
|
+
// Cache is stale or missing — fetch from the registry
|
|
167
|
+
const latest = await fetchFn();
|
|
168
|
+
if (latest !== null) {
|
|
169
|
+
await saveUpdateCache(latest, now, homeDir);
|
|
170
|
+
}
|
|
171
|
+
if (latest === null) {
|
|
172
|
+
return { current: currentVersion, latest: null, updateAvailable: false };
|
|
173
|
+
}
|
|
174
|
+
const updateAvailable = isNewerVersion(latest, currentVersion);
|
|
175
|
+
return { current: currentVersion, latest, updateAvailable };
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return { current: currentVersion, latest: null, updateAvailable: false };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=update-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.js","sourceRoot":"","sources":["../../src/infra/update-check.ts"],"names":[],"mappings":"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"}
|
package/dist/interface/menu.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type { EnvironmentStatus } from '../providers/detect.js';
|
|
|
23
23
|
import type { Provider, ProviderId, SandboxLevel } from '../providers/port.js';
|
|
24
24
|
import type { OutputSink } from './render.js';
|
|
25
25
|
import type { LoginMethod } from '../commands/login.js';
|
|
26
|
+
import type { UpdateCheckResult } from '../infra/update-check.js';
|
|
26
27
|
export interface MenuContext {
|
|
27
28
|
readonly version: string;
|
|
28
29
|
readonly clock: Clock;
|
|
@@ -67,6 +68,30 @@ export interface MenuContext {
|
|
|
67
68
|
* spawning during tests (e.g. after first-run onboarding or [j]/[k]/[o] login).
|
|
68
69
|
*/
|
|
69
70
|
readonly detectEnvironment?: () => Promise<EnvironmentStatus>;
|
|
71
|
+
/**
|
|
72
|
+
* Optional injected update-check for testing. When provided, `startMenu` uses
|
|
73
|
+
* this instead of the real `checkForUpdate` from infra/update-check.ts,
|
|
74
|
+
* preventing real npm registry requests from being made during tests.
|
|
75
|
+
*
|
|
76
|
+
* Returns the update check result (current, latest, updateAvailable).
|
|
77
|
+
*/
|
|
78
|
+
readonly checkForUpdate?: () => Promise<UpdateCheckResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Optional injected self-update function for testing. When provided, `startMenu`
|
|
81
|
+
* uses this instead of the real `npm install -g myshell-tools@latest` subprocess.
|
|
82
|
+
*
|
|
83
|
+
* Returns true when the update succeeded (exit code 0), false otherwise.
|
|
84
|
+
* Never throws.
|
|
85
|
+
*/
|
|
86
|
+
readonly updateSelf?: (out: OutputSink) => Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Optional injected relaunch function for testing. When provided, `startMenu`
|
|
89
|
+
* uses this instead of the real `execa('myshell-tools', …)` re-exec.
|
|
90
|
+
*
|
|
91
|
+
* Returns the exit code of the relaunched process (or 1 on spawn failure).
|
|
92
|
+
* Used only for the opt-in auto-update path.
|
|
93
|
+
*/
|
|
94
|
+
readonly relaunch?: () => Promise<number>;
|
|
70
95
|
}
|
|
71
96
|
/**
|
|
72
97
|
* Parse a yes/no answer from a raw input line, with a configurable default.
|
|
@@ -136,6 +161,50 @@ export declare function renderBudgetLine(spend: SpendSummary, _color: boolean):
|
|
|
136
161
|
* Returns string[] (no ANSI — pure string building, safe for tests).
|
|
137
162
|
*/
|
|
138
163
|
export declare function renderConversationList(metas: ConversationMeta[], nowMs: number): string[];
|
|
164
|
+
/**
|
|
165
|
+
* Count how many timestamps in `times` fall within the half-open window
|
|
166
|
+
* `[now - windowMs, now]` (both endpoints inclusive).
|
|
167
|
+
*
|
|
168
|
+
* Pure — no I/O, no side effects, never throws.
|
|
169
|
+
*
|
|
170
|
+
* @param times - Immutable array of epoch-ms timestamps (e.g. from ctx.clock.now()).
|
|
171
|
+
* @param now - The current epoch-ms (e.g. from ctx.clock.now()).
|
|
172
|
+
* @param windowMs - Width of the sliding window in milliseconds (e.g. 1500).
|
|
173
|
+
* @returns Number of entries within the window.
|
|
174
|
+
*/
|
|
175
|
+
export declare function countRecentInterrupts(times: readonly number[], now: number, windowMs: number): number;
|
|
176
|
+
/**
|
|
177
|
+
* Decide what action to take based on the number of recent Ctrl+C presses and
|
|
178
|
+
* whether a task is currently running.
|
|
179
|
+
*
|
|
180
|
+
* Rules:
|
|
181
|
+
* count >= 3 → `'exit-app'`
|
|
182
|
+
* count === 2 → `'to-menu'`
|
|
183
|
+
* count === 1 && taskRunning → `'cancel-task'`
|
|
184
|
+
* count === 1 && !taskRunning → `'hint'`
|
|
185
|
+
* count <= 0 → `'hint'` (defensive)
|
|
186
|
+
*
|
|
187
|
+
* Pure — never throws, no I/O, no side effects.
|
|
188
|
+
*
|
|
189
|
+
* @param count - Number of recent interrupt presses (from countRecentInterrupts).
|
|
190
|
+
* @param taskRunning - Whether a task is currently in-flight.
|
|
191
|
+
* @returns The action to take.
|
|
192
|
+
*/
|
|
193
|
+
export declare function interpretInterrupt(count: number, taskRunning: boolean): 'cancel-task' | 'to-menu' | 'exit-app' | 'hint';
|
|
194
|
+
/**
|
|
195
|
+
* Decide whether a raw-session SIGINT count warrants escaping back to the menu.
|
|
196
|
+
*
|
|
197
|
+
* Returns true when count >= 2 (rapid double Ctrl+C), false otherwise.
|
|
198
|
+
* A single press (count === 1) is left entirely to the child process — the
|
|
199
|
+
* terminal already delivers SIGINT to the whole foreground process group, so
|
|
200
|
+
* claude/codex/opencode handles its own cancel without interference from us.
|
|
201
|
+
*
|
|
202
|
+
* Pure — no I/O, no side effects, never throws.
|
|
203
|
+
*
|
|
204
|
+
* @param count - Number of recent Ctrl+C presses (from countRecentInterrupts).
|
|
205
|
+
* @returns True when the user should be returned to the myshell-tools menu.
|
|
206
|
+
*/
|
|
207
|
+
export declare function shouldEscapeRawSession(count: number): boolean;
|
|
139
208
|
/**
|
|
140
209
|
* Start the sessions-first interactive menu.
|
|
141
210
|
*
|