dsh-ssh-tui 0.3.3 → 0.3.5
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 +58 -11
- package/README.md +52 -14
- package/lib/index.js +33 -4
- package/lib/index.js.map +1 -1
- package/lib/session-lock.js +113 -0
- package/lib/session-lock.js.map +1 -0
- package/lib/tui.js +498 -52
- package/lib/tui.js.map +1 -1
- package/lib/types/session-lock.d.ts +26 -0
- package/lib/types/tui.d.ts +63 -1
- package/lib/types/update-check.d.ts +4 -0
- package/lib/update-check.js +49 -0
- package/lib/update-check.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SessionLockInfo {
|
|
2
|
+
pid: number;
|
|
3
|
+
sessionId: string;
|
|
4
|
+
startedAt: string;
|
|
5
|
+
tty?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class SessionLockHeldError extends Error {
|
|
8
|
+
readonly lock: SessionLockInfo;
|
|
9
|
+
readonly path: string;
|
|
10
|
+
constructor(lock: SessionLockInfo, path: string);
|
|
11
|
+
}
|
|
12
|
+
export declare function sessionLockPath(sessionId: string, dshHome?: string): string;
|
|
13
|
+
export declare function parseSessionLock(raw: string): SessionLockInfo | undefined;
|
|
14
|
+
/** True when `pid` still exists on this machine (best-effort). */
|
|
15
|
+
export declare function processIsAlive(pid: number): boolean;
|
|
16
|
+
export declare function formatLockHeldMessage(lock: SessionLockInfo): string;
|
|
17
|
+
export declare function sessionLockDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
18
|
+
export declare function acquireSessionLock(sessionId: string, options?: {
|
|
19
|
+
pid?: number;
|
|
20
|
+
tty?: string | null;
|
|
21
|
+
dshHome?: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
path: string;
|
|
24
|
+
info: SessionLockInfo;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function releaseSessionLock(path: string, pid?: number): Promise<void>;
|
package/lib/types/tui.d.ts
CHANGED
|
@@ -143,6 +143,17 @@ type Row = {
|
|
|
143
143
|
phase: 'active' | 'paused' | 'blocked' | 'complete' | 'cleared';
|
|
144
144
|
blockedReason?: string;
|
|
145
145
|
expanded: boolean;
|
|
146
|
+
} | {
|
|
147
|
+
kind: 'compaction';
|
|
148
|
+
compactionId: string;
|
|
149
|
+
status: 'running' | 'ok' | 'error';
|
|
150
|
+
startedAt: number;
|
|
151
|
+
endedAt?: number;
|
|
152
|
+
pruneCount: number;
|
|
153
|
+
prunedTokens: number;
|
|
154
|
+
summary?: string;
|
|
155
|
+
error?: string;
|
|
156
|
+
expanded: boolean;
|
|
146
157
|
} | {
|
|
147
158
|
kind: 'system';
|
|
148
159
|
text: string;
|
|
@@ -261,6 +272,35 @@ export interface OpenCodeSource {
|
|
|
261
272
|
* routes are recognized by their `opencode.ai` base URL.
|
|
262
273
|
*/
|
|
263
274
|
export declare function openCodeSourceFor(provider: string, llmPiAiSection: unknown): OpenCodeSource | null;
|
|
275
|
+
export type QuotaPeriod = 'hourly' | 'weekly' | 'monthly' | 'unknown';
|
|
276
|
+
export interface QuotaWindow {
|
|
277
|
+
label: string;
|
|
278
|
+
period: QuotaPeriod;
|
|
279
|
+
/** Remaining percent of the window (100 = unused). */
|
|
280
|
+
remainingPercent: number;
|
|
281
|
+
resetsAt?: string;
|
|
282
|
+
}
|
|
283
|
+
export interface QuotaSnapshot {
|
|
284
|
+
provider: string;
|
|
285
|
+
plan: string;
|
|
286
|
+
windows: QuotaWindow[];
|
|
287
|
+
}
|
|
288
|
+
export declare function remainingPercentFromUsed(usedPercent: number): number;
|
|
289
|
+
/** Cross a remaining-percent threshold from above (50 / 25 / 10 / 5). */
|
|
290
|
+
export declare function crossedQuotaThresholds(previousRemaining: number | undefined, remaining: number): number[];
|
|
291
|
+
export declare function quotaAlertText(snapshot: QuotaSnapshot, window: QuotaWindow): string;
|
|
292
|
+
/**
|
|
293
|
+
* How often to re-fetch quota, based on the tightest window.
|
|
294
|
+
* Hourly/5h: every 10 turns, every 4 when near a threshold.
|
|
295
|
+
* Weekly: every 50 turns, every 10 when near.
|
|
296
|
+
* Monthly: every 80 turns, every 20 when near.
|
|
297
|
+
*/
|
|
298
|
+
export declare function quotaRefreshEveryTurns(window: QuotaWindow | undefined): number;
|
|
299
|
+
export declare function parseSuperGrokBilling(payload: unknown): QuotaSnapshot;
|
|
300
|
+
export declare function parseOpenCodeGoQuota(payload: unknown, provider: string): QuotaSnapshot;
|
|
301
|
+
export declare function formatQuotaSnapshot(snapshot: QuotaSnapshot): string;
|
|
302
|
+
/** Tightest remaining window — used for threshold alerts. */
|
|
303
|
+
export declare function tightestQuotaWindow(snapshot: QuotaSnapshot): QuotaWindow | undefined;
|
|
264
304
|
/** Render the OpenCode Go quota payload as a transcript block. */
|
|
265
305
|
export declare function formatOpenCodeGoUsage(payload: unknown, source: OpenCodeSource): string;
|
|
266
306
|
/** Whether `text` could still grow into a recognized escape sequence. */
|
|
@@ -307,6 +347,12 @@ export declare function parseFindQuery(raw: string): {
|
|
|
307
347
|
category?: CardCategory;
|
|
308
348
|
query: string;
|
|
309
349
|
};
|
|
350
|
+
export declare function compactionHeaderText(row: {
|
|
351
|
+
status: 'running' | 'ok' | 'error';
|
|
352
|
+
pruneCount: number;
|
|
353
|
+
prunedTokens: number;
|
|
354
|
+
error?: string;
|
|
355
|
+
}): string;
|
|
310
356
|
/** Transcript rows matching a `/find` query, newest last. */
|
|
311
357
|
export declare function matchTranscriptRows(rows: readonly Row[], raw: string): Row[];
|
|
312
358
|
/** One-line note under an expanded plan strip. */
|
|
@@ -448,6 +494,12 @@ export declare class SshTui {
|
|
|
448
494
|
private paintIntervalMs;
|
|
449
495
|
private paintLink;
|
|
450
496
|
private paintProbed;
|
|
497
|
+
private sessionTitle;
|
|
498
|
+
private llmRetry;
|
|
499
|
+
private quotaSnapshot;
|
|
500
|
+
private quotaAlerted;
|
|
501
|
+
private quotaTurnsSinceRefresh;
|
|
502
|
+
private quotaRefreshInFlight;
|
|
451
503
|
private searchHits;
|
|
452
504
|
private searchIndex;
|
|
453
505
|
private searchQuery;
|
|
@@ -456,6 +508,7 @@ export declare class SshTui {
|
|
|
456
508
|
constructor(ctx: Context, agent: Agent, config: TuiConfig);
|
|
457
509
|
/** Enter raw mode, switch to the alternate screen, and start listening. */
|
|
458
510
|
start(): void;
|
|
511
|
+
private notifyPluginUpdate;
|
|
459
512
|
private startRenderTimer;
|
|
460
513
|
private calibratePaintInterval;
|
|
461
514
|
/** Replay the durable session log so a resumed session renders its history. */
|
|
@@ -538,6 +591,10 @@ export declare class SshTui {
|
|
|
538
591
|
private readonly handleDisposed;
|
|
539
592
|
/** Plan-mode / command / team events that plugins merge into SessionEventMap. */
|
|
540
593
|
private handleExtensionEvent;
|
|
594
|
+
private findCompactionRow;
|
|
595
|
+
private handleCompactionEvent;
|
|
596
|
+
private handleCommandRun;
|
|
597
|
+
private handleCommandDone;
|
|
541
598
|
private handleGoalChange;
|
|
542
599
|
private handleSubagentExtensionEvent;
|
|
543
600
|
/** Fold a live subagent's own session events into that child's card. */
|
|
@@ -616,8 +673,13 @@ export declare class SshTui {
|
|
|
616
673
|
private fetchOpenCodeGoUsage;
|
|
617
674
|
/** Explain Zen metered billing instead of pretending it has a quota. */
|
|
618
675
|
private zenUsageText;
|
|
619
|
-
/** /usage and /quota:
|
|
676
|
+
/** /usage and /quota: remaining quota for the current subscription provider. */
|
|
620
677
|
private runUsageCommand;
|
|
678
|
+
private applyQuotaSnapshot;
|
|
679
|
+
private refreshQuota;
|
|
680
|
+
private fetchQuotaSnapshot;
|
|
681
|
+
private resolveSuperGrokToken;
|
|
682
|
+
private fetchJson;
|
|
621
683
|
private readonly handleData;
|
|
622
684
|
/** Handle one data chunk that may contain bracketed-paste markers. */
|
|
623
685
|
private processPasteChunk;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function compareSemver(a: string, b: string): number;
|
|
2
|
+
export declare function formatUpdateNotice(current: string, latest: string): string;
|
|
3
|
+
export declare function fetchLatestNpmVersion(fetchImpl?: typeof fetch): Promise<string | undefined>;
|
|
4
|
+
export declare function checkForPluginUpdate(current: string): Promise<string | undefined>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort npm latest check. Never auto-upgrades; failures stay quiet.
|
|
3
|
+
*/
|
|
4
|
+
const NPM_REGISTRY = 'https://registry.npmjs.org/dsh-ssh-tui/latest';
|
|
5
|
+
const CHECK_TIMEOUT_MS = 4000;
|
|
6
|
+
export function compareSemver(a, b) {
|
|
7
|
+
const parse = (value) => {
|
|
8
|
+
const core = value.trim().replace(/^v/u, '').split('-')[0] ?? '0';
|
|
9
|
+
const parts = core.split('.').map(part => Number.parseInt(part, 10));
|
|
10
|
+
return [0, 1, 2].map(i => Number.isFinite(parts[i]) ? parts[i] : 0);
|
|
11
|
+
};
|
|
12
|
+
const left = parse(a);
|
|
13
|
+
const right = parse(b);
|
|
14
|
+
for (let i = 0; i < 3; i++) {
|
|
15
|
+
if (left[i] !== right[i])
|
|
16
|
+
return left[i] - right[i];
|
|
17
|
+
}
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
export function formatUpdateNotice(current, latest) {
|
|
21
|
+
return `发现新版本 dsh-ssh-tui ${latest}(当前 ${current})。更新:dsh plugin --profile tui add dsh-ssh-tui`;
|
|
22
|
+
}
|
|
23
|
+
export async function fetchLatestNpmVersion(fetchImpl = fetch) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetchImpl(NPM_REGISTRY, {
|
|
26
|
+
headers: { accept: 'application/json' },
|
|
27
|
+
signal: AbortSignal.timeout(CHECK_TIMEOUT_MS),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok)
|
|
30
|
+
return undefined;
|
|
31
|
+
const body = await response.json();
|
|
32
|
+
return typeof body.version === 'string' && body.version.trim() !== '' ? body.version.trim() : undefined;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function checkForPluginUpdate(current) {
|
|
39
|
+
if (process.env.DSH_TUI_NO_UPDATE_CHECK === '1' || process.env.DSH_TUI_NO_UPDATE_CHECK === 'true') {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const latest = await fetchLatestNpmVersion();
|
|
43
|
+
if (latest === undefined)
|
|
44
|
+
return undefined;
|
|
45
|
+
if (compareSemver(latest, current) <= 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
return formatUpdateNotice(current, latest);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=update-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,YAAY,GAAG,+CAA+C,CAAA;AACpE,MAAM,gBAAgB,GAAG,IAAI,CAAA;AAE7B,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS;IAChD,MAAM,KAAK,GAAG,CAAC,KAAa,EAAY,EAAE;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;QACjE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;QACpE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtE,CAAC,CAAA;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACrB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,CAAC,CAAE,KAAK,KAAK,CAAC,CAAC,CAAE;YAAE,OAAO,IAAI,CAAC,CAAC,CAAE,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;IACzD,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,MAAc;IAChE,OAAO,qBAAqB,MAAM,OAAO,OAAO,+CAA+C,CAAA;AACjG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,YAA0B,KAAK;IAE/B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,YAAY,EAAE;YAC7C,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC;SAC9C,CAAC,CAAA;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,SAAS,CAAA;QAClC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA2B,CAAA;QAC3D,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACzG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe;IACxD,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,EAAE,CAAC;QAClG,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAA;IAC5C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC1C,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACzD,OAAO,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAC5C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ssh-tui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"dsh",
|
|
8
|
+
"dsh-plugin",
|
|
8
9
|
"plugin",
|
|
9
10
|
"tui",
|
|
10
11
|
"terminal",
|
|
11
|
-
"ssh"
|
|
12
|
+
"ssh",
|
|
13
|
+
"cordis"
|
|
12
14
|
],
|
|
13
15
|
"author": "cyjyyd",
|
|
14
16
|
"repository": {
|