@deepseek-harness-tui/dsh-tui 0.7.2 → 0.7.3
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.md +1 -1
- package/bin/dsh-tui.js +5 -0
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +6 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +5 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +5 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +52 -74
- package/lib/types/components/SearchBox.d.ts +4 -0
- package/lib/types/components/SearchBox.d.ts.map +1 -1
- package/lib/types/components/SearchBox.js +101 -2
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +4 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts.map +1 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.js +10 -5
- package/lib/types/components/WorkingSpinner.d.ts +3 -1
- package/lib/types/components/WorkingSpinner.d.ts.map +1 -1
- package/lib/types/components/WorkingSpinner.js +2 -2
- package/lib/types/components/design-system/ThemedText.d.ts +7 -1
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +2 -2
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +52 -8
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +12 -3
- package/lib/types/dsh-adapter/channel.d.ts +10 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +45 -9
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/packaged-presets.js +5 -2
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +7 -3
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/providerWizard.js +3 -1
- package/lib/types/i18n.d.ts +32 -8
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +9 -3
- package/lib/types/ink/components/Text.d.ts +9 -1
- package/lib/types/ink/components/Text.d.ts.map +1 -1
- package/lib/types/ink/components/Text.js +7 -6
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +4 -2
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +76 -9
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +4 -1
- package/lib/types/ink/terminal.d.ts +40 -4
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +57 -5
- package/lib/types/ink/termio/tokenize.d.ts +6 -0
- package/lib/types/ink/termio/tokenize.d.ts.map +1 -1
- package/lib/types/ink/termio/tokenize.js +18 -4
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +76 -16
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +30 -15
- package/lib/types/sessions/view.d.ts.map +1 -1
- package/lib/types/sessions/view.js +13 -1
- package/lib/types/update.d.ts +21 -7
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +66 -16
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +4 -1
- package/package.json +33 -9
|
@@ -243,6 +243,14 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
243
243
|
}, []);
|
|
244
244
|
/** Startup context panel: expanded by header click or Ctrl+T. */
|
|
245
245
|
const [loadedContextOpen, setLoadedContextOpen] = React.useState(false);
|
|
246
|
+
/**
|
|
247
|
+
* Whether the startup context panel is on screen. Derived once and read by
|
|
248
|
+
* BOTH the Ctrl+T handler and the render below: the key's meaning depends on
|
|
249
|
+
* the panel being visible, so a second copy of this condition is a place for
|
|
250
|
+
* the two to disagree — which is how the key came to advertise one thing and
|
|
251
|
+
* do another in the first place.
|
|
252
|
+
*/
|
|
253
|
+
const loadedContextVisible = channel.rows.length === 0 && channel.loadedContext !== undefined;
|
|
246
254
|
/** `/` transcript search (less-style incsearch, ported from CC's REPL). */
|
|
247
255
|
const [searchOpen, setSearchOpen] = React.useState(false);
|
|
248
256
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
@@ -306,10 +314,17 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
306
314
|
// Spinner timing refs, fed from channel state each render (the spinner
|
|
307
315
|
// only mounts while working, so values are stable for the mount).
|
|
308
316
|
const responseLengthRef = React.useRef(0);
|
|
317
|
+
const uploadTokensRef = React.useRef(0);
|
|
309
318
|
const loadingStartTimeRef = React.useRef(0);
|
|
310
319
|
const totalPausedMsRef = React.useRef(0);
|
|
311
320
|
const pauseStartTimeRef = React.useRef(null);
|
|
312
321
|
responseLengthRef.current = channel.responseChars;
|
|
322
|
+
// Most recent request's real upload (input + cache read/write occupy the
|
|
323
|
+
// wire exactly like the context window); 0 until the first usage event.
|
|
324
|
+
const lastUploadTokens = channel.lastUsage === undefined
|
|
325
|
+
? 0
|
|
326
|
+
: channel.lastUsage.input + channel.lastUsage.cacheRead + channel.lastUsage.cacheWrite;
|
|
327
|
+
uploadTokensRef.current = lastUploadTokens;
|
|
313
328
|
loadingStartTimeRef.current = channel.turnStart;
|
|
314
329
|
const thinkingStatus = useThinkingStatus(channel.spinnerMode === 'thinking');
|
|
315
330
|
// Terminal tab title (ported from CC's AnimatedTerminalTitle): the session
|
|
@@ -814,14 +829,28 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
814
829
|
});
|
|
815
830
|
return true;
|
|
816
831
|
case 'login': {
|
|
817
|
-
const key = process.env.DEEPSEEK_API_KEY;
|
|
818
|
-
const lines = [
|
|
819
|
-
`${t('login-api-key', { key: key ? key.slice(0, 6) + '…' + key.slice(-4) : t('login-key-missing') })}`,
|
|
820
|
-
`${t('login-base-url', { url: process.env.DEEPSEEK_BASE_URL ?? t('login-official-endpoint') })}`,
|
|
821
|
-
t('login-source-hint'),
|
|
822
|
-
];
|
|
823
832
|
setHelpOpen(false);
|
|
824
|
-
channel.
|
|
833
|
+
void channel.describeCredential('DEEPSEEK_API_KEY')
|
|
834
|
+
.catch(() => undefined)
|
|
835
|
+
.then(status => {
|
|
836
|
+
const keyStatus = status === undefined
|
|
837
|
+
? t('login-credentials-unavailable')
|
|
838
|
+
: status.configured
|
|
839
|
+
? t('login-key-configured', { ref: 'DEEPSEEK_API_KEY' })
|
|
840
|
+
: t('login-key-missing');
|
|
841
|
+
channel.pushLocal('/login', [
|
|
842
|
+
t('login-api-key', { status: keyStatus }),
|
|
843
|
+
...(status === undefined
|
|
844
|
+
? []
|
|
845
|
+
: [
|
|
846
|
+
t('login-credential-source', { source: status.source ?? t('login-source-none') }),
|
|
847
|
+
t('login-credential-storage', {
|
|
848
|
+
mode: t(status.writable ? 'login-storage-writable' : 'login-storage-read-only'),
|
|
849
|
+
}),
|
|
850
|
+
]),
|
|
851
|
+
t('login-base-url', { url: process.env.DEEPSEEK_BASE_URL ?? t('login-official-endpoint') }),
|
|
852
|
+
]);
|
|
853
|
+
});
|
|
825
854
|
return true;
|
|
826
855
|
}
|
|
827
856
|
case 'logout':
|
|
@@ -1538,10 +1567,22 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1538
1567
|
}
|
|
1539
1568
|
}
|
|
1540
1569
|
else if (key.leftArrow) {
|
|
1541
|
-
|
|
1570
|
+
// Step by code point, not UTF-16 unit: an emoji is two units, and
|
|
1571
|
+
// a mid-pair caret offset would split it in the SearchBox render.
|
|
1572
|
+
setHistoryCursor(cursor => {
|
|
1573
|
+
if (cursor <= 0)
|
|
1574
|
+
return 0;
|
|
1575
|
+
const ch = [...historyQuery.slice(0, cursor)].pop();
|
|
1576
|
+
return cursor - ch.length;
|
|
1577
|
+
});
|
|
1542
1578
|
}
|
|
1543
1579
|
else if (key.rightArrow) {
|
|
1544
|
-
setHistoryCursor(cursor =>
|
|
1580
|
+
setHistoryCursor(cursor => {
|
|
1581
|
+
if (cursor >= historyQuery.length)
|
|
1582
|
+
return historyQuery.length;
|
|
1583
|
+
const ch = [...historyQuery.slice(cursor)][0];
|
|
1584
|
+
return cursor + ch.length;
|
|
1585
|
+
});
|
|
1545
1586
|
}
|
|
1546
1587
|
else if (key.home) {
|
|
1547
1588
|
setHistoryCursor(0);
|
|
@@ -1589,11 +1630,25 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1589
1630
|
return;
|
|
1590
1631
|
}
|
|
1591
1632
|
if (isMod(key) && input === 't') {
|
|
1592
|
-
// Ctrl+T
|
|
1593
|
-
//
|
|
1594
|
-
//
|
|
1595
|
-
//
|
|
1596
|
-
//
|
|
1633
|
+
// Ctrl+T means "expand what is in front of you", and the two things it
|
|
1634
|
+
// can expand never share the screen.
|
|
1635
|
+
//
|
|
1636
|
+
// The startup loaded-context panel renders only while the transcript is
|
|
1637
|
+
// empty (see the render below) — and that is exactly the window where
|
|
1638
|
+
// the trajectory has nothing in it yet, since the trajectory is folded
|
|
1639
|
+
// from the session's own events. So the panel wins while it is up, and
|
|
1640
|
+
// the scene takes over for the rest of the session.
|
|
1641
|
+
//
|
|
1642
|
+
// Claiming the key outright for the scene was wrong even though the
|
|
1643
|
+
// panel binding looked dead: the panel prints its own `(Ctrl+T 展开)`
|
|
1644
|
+
// hint, so the key still had a visible promise attached to it, and the
|
|
1645
|
+
// scene it opened instead was necessarily empty at that moment. That
|
|
1646
|
+
// left the panel reachable only by clicking its header, which is not a
|
|
1647
|
+
// keyboard path at all.
|
|
1648
|
+
if (loadedContextVisible) {
|
|
1649
|
+
setLoadedContextOpen(previous => !previous);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1597
1652
|
openScene();
|
|
1598
1653
|
return;
|
|
1599
1654
|
}
|
|
@@ -1732,11 +1787,16 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1732
1787
|
seekRow(lastUser.id);
|
|
1733
1788
|
else
|
|
1734
1789
|
handle?.scrollToBottom();
|
|
1735
|
-
} })), _jsxs(ScrollBox, { ref: setHandle, flexDirection: "column", flexGrow: 1, flexShrink: 1, stickyScroll: true, children: [_jsx(LogoHeader, { model: channel.model, effort: channel.reasoningEffort, cwd: channel.displayCwd }),
|
|
1790
|
+
} })), _jsxs(ScrollBox, { ref: setHandle, flexDirection: "column", flexGrow: 1, flexShrink: 1, stickyScroll: true, children: [_jsx(LogoHeader, { model: channel.model, effort: channel.reasoningEffort, cwd: channel.displayCwd }), loadedContextVisible && (_jsx(LoadedContextPanel, { context: channel.loadedContext, open: loadedContextOpen, onToggle: () => { setLoadedContextOpen(previous => !previous); } })), _jsx(MessageList, { rows: channel.rows, failureHintRowId: failureHintRowId, failureHint: t('traj-hint-failure', { key: `${modLabel}t` }), expanded: expanded, expandedRows: expandedRows, selectedId: selectionActive ? selectedId : null, onToggleRow: toggleRowExpanded, model: channel.model, showAll: showAllMessages, thinkingVisible: thinkingVisible, onToggleAll: () => { setShowAllMessages(previous => !previous); }, onLoadOlder: () => channel.loadOlder(), registerRowRef: registerRowRef, scrollHandle: handle, forceMountRowId: forceMountRowId, newSinceRowId: isSticky ? null : lastSeenRowIdRef.current, onUnseenCount: setUnseenCount })] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [showPill && (_jsx(NewMessagesPill, { count: unseenCount, onClick: () => handle?.scrollToBottom() })), channel.working &&
|
|
1736
1791
|
(channel.activityEnabled &&
|
|
1737
1792
|
channel.workingActivity !== undefined &&
|
|
1738
1793
|
channel.workingActivity.line !== '' &&
|
|
1739
|
-
channel.workingActivity.phase !== 'idle' ? (_jsx(Box, { marginTop: 1, children: _jsx(ActivityLine, { activity: channel.workingActivity, activityFrames: channel.activityFrames, warnPct: activityWarnPct, warnDanger: activityWarnPct !== undefined && activityWarnPct >= 95,
|
|
1794
|
+
channel.workingActivity.phase !== 'idle' ? (_jsx(Box, { marginTop: 1, children: _jsx(ActivityLine, { activity: channel.workingActivity, activityFrames: channel.activityFrames, warnPct: activityWarnPct, warnDanger: activityWarnPct !== undefined && activityWarnPct >= 95,
|
|
1795
|
+
// Upload = real tokens of the last request; download =
|
|
1796
|
+
// the animated chars/4 estimate, matching the classic
|
|
1797
|
+
// spinner's counter (the suffix used raw chars before,
|
|
1798
|
+
// inflating the reading next to a real upload number).
|
|
1799
|
+
suffix: `${lastUploadTokens > 0 ? ` · ↑ ${formatTokens(lastUploadTokens)}` : ''} · ↓ ${formatTokens(Math.round(channel.responseChars / 4))} tokens` }) })) : (_jsx(WorkingSpinner, { mode: channel.spinnerMode, hasActiveTools: channel.activeToolCount > 0, responseLengthRef: responseLengthRef, uploadTokensRef: uploadTokensRef, loadingStartTimeRef: loadingStartTimeRef, totalPausedMsRef: totalPausedMsRef, pauseStartTimeRef: pauseStartTimeRef, thinkingStatus: thinkingStatus }))), _jsx(GoalTodoPanel, { channel: channel }), approvalSnapshot !== null ? (_jsx(ApprovalPanel, { approval: approvalSnapshot, onDecide: outcome => approvals.decide(outcome) }, approvalSnapshot.key)) : btw !== null ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(BtwPanel, { question: btw.question, answer: btw.answer, error: btw.error, streaming: !btw.done, onClose: closeBtw, onCopy: () => {
|
|
1740
1800
|
void setClipboard(btw.answer ?? '').then(raw => { if (raw)
|
|
1741
1801
|
process.stdout.write(raw); });
|
|
1742
1802
|
channel.notify(t('copied-chars', { n: (btw.answer ?? '').length }), { timeoutMs: 1500 });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionBrowser.d.ts","sourceRoot":"","sources":["../../../src/screens/SessionBrowser.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAsBzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAoDxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,WAAW,EACX,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9C,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"SessionBrowser.d.ts","sourceRoot":"","sources":["../../../src/screens/SessionBrowser.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAsBzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAoDxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,WAAW,EACX,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9C,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,CAselB"}
|
|
@@ -112,6 +112,10 @@ export function SessionBrowser({ channel, home, sameProject, onClose, }) {
|
|
|
112
112
|
const [previewOpen, setPreviewOpen] = React.useState(false);
|
|
113
113
|
const [preview, setPreview] = React.useState([]);
|
|
114
114
|
const [previewLoading, setPreviewLoading] = React.useState(false);
|
|
115
|
+
// React batches every parsed key from one stdin chunk. Lock actions in a
|
|
116
|
+
// ref so a repeated Enter cannot start the same async operation twice
|
|
117
|
+
// before the mode change is rendered.
|
|
118
|
+
const actionPendingRef = React.useRef(false);
|
|
115
119
|
// One clock per render pass: every relative time on screen must agree, and
|
|
116
120
|
// re-reading it per row would let two rows a millisecond apart round to
|
|
117
121
|
// different minutes.
|
|
@@ -237,9 +241,17 @@ export function SessionBrowser({ channel, home, sameProject, onClose, }) {
|
|
|
237
241
|
setFilters(current => ({ ...current, ...update(current) }));
|
|
238
242
|
topRef.current = 0;
|
|
239
243
|
};
|
|
244
|
+
const runAction = (action) => {
|
|
245
|
+
if (actionPendingRef.current)
|
|
246
|
+
return;
|
|
247
|
+
actionPendingRef.current = true;
|
|
248
|
+
void action().finally(() => {
|
|
249
|
+
actionPendingRef.current = false;
|
|
250
|
+
});
|
|
251
|
+
};
|
|
240
252
|
/** Run one mutation, report it, and re-list — reporting a failure either way. */
|
|
241
253
|
const mutate = (action, done, failed) => {
|
|
242
|
-
|
|
254
|
+
runAction(async () => {
|
|
243
255
|
let ok = false;
|
|
244
256
|
let reason;
|
|
245
257
|
try {
|
|
@@ -250,7 +262,7 @@ export function SessionBrowser({ channel, home, sameProject, onClose, }) {
|
|
|
250
262
|
}
|
|
251
263
|
report(ok ? done : reason === undefined ? failed : `${failed} · ${reason}`, ok ? 'info' : 'error');
|
|
252
264
|
await reload();
|
|
253
|
-
})
|
|
265
|
+
});
|
|
254
266
|
};
|
|
255
267
|
const runDelete = (target) => mutate(() => channel.deleteSession(target.id), t('resume-deleted', { name: target.title.text }), t('resume-delete-failed', { name: target.title.text }));
|
|
256
268
|
const runRename = (target, title) => mutate(() => channel.renameSessionTo(target.id, title), t('rename-done', { title }), t('resume-rename-failed', { name: target.title.text }));
|
|
@@ -259,7 +271,7 @@ export function SessionBrowser({ channel, home, sameProject, onClose, }) {
|
|
|
259
271
|
// below, and deleting from a list that moved under us would be a
|
|
260
272
|
// destructive action aimed at whatever happens to be there now.
|
|
261
273
|
const ids = [...view.emptyIds];
|
|
262
|
-
|
|
274
|
+
runAction(async () => {
|
|
263
275
|
let removed = 0;
|
|
264
276
|
for (const id of ids) {
|
|
265
277
|
try {
|
|
@@ -272,9 +284,11 @@ export function SessionBrowser({ channel, home, sameProject, onClose, }) {
|
|
|
272
284
|
}
|
|
273
285
|
report(t('session-cleaned', { n: removed }), 'info');
|
|
274
286
|
await reload();
|
|
275
|
-
})
|
|
287
|
+
});
|
|
276
288
|
};
|
|
277
289
|
useInput((input, key) => {
|
|
290
|
+
if (actionPendingRef.current)
|
|
291
|
+
return;
|
|
278
292
|
// A notice describes what the LAST action did; the next keystroke makes it
|
|
279
293
|
// stale, so it goes as soon as the user acts again.
|
|
280
294
|
if (notice !== undefined)
|
|
@@ -338,19 +352,20 @@ export function SessionBrowser({ channel, home, sameProject, onClose, }) {
|
|
|
338
352
|
// that explanation to the conversation the user is not looking at, and
|
|
339
353
|
// leave them staring at an unchanged transcript wondering what Enter
|
|
340
354
|
// did. `resumeTo` reports its own reasons; this only has to stay put.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
355
|
+
runAction(async () => {
|
|
356
|
+
try {
|
|
357
|
+
const ok = await channel.resumeTo(target.id);
|
|
358
|
+
if (ok) {
|
|
359
|
+
channel.notify(t('resume-resumed'));
|
|
360
|
+
onClose();
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
setNotice({ text: t('session-resume-refused'), tone: 'error' });
|
|
364
|
+
}
|
|
347
365
|
}
|
|
348
|
-
|
|
349
|
-
setNotice({ text: t('session-resume-
|
|
366
|
+
catch (error) {
|
|
367
|
+
setNotice({ text: t('session-resume-failed', { err: message(error) }), tone: 'error' });
|
|
350
368
|
}
|
|
351
|
-
})
|
|
352
|
-
.catch((error) => {
|
|
353
|
-
setNotice({ text: t('session-resume-failed', { err: message(error) }), tone: 'error' });
|
|
354
369
|
});
|
|
355
370
|
}
|
|
356
371
|
else if (key.escape) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/sessions/view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAChC;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,EAAE,cAK7B,CAAA;AAED,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1F,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAA;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACtH,WAAW,
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/sessions/view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAChC;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,EAAE,cAK7B,CAAA;AAED,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1F,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAA;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACtH,WAAW,CAmGb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAK9F;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAKhG;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAGhG;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,CAoBR;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAU1F"}
|
|
@@ -85,7 +85,19 @@ export function buildView(sessions, filters, context) {
|
|
|
85
85
|
const rows = [];
|
|
86
86
|
let shown = 0;
|
|
87
87
|
let lastProject;
|
|
88
|
-
|
|
88
|
+
const ordered = visible.sort((left, right) => right.updatedAt - left.updatedAt);
|
|
89
|
+
if (filters.allProjects) {
|
|
90
|
+
// Order project groups by their newest visible session, then keep every
|
|
91
|
+
// session from that project together in MRU order. A global MRU sort alone
|
|
92
|
+
// can interleave A/B/A and emit the same project header more than once.
|
|
93
|
+
const projectOrder = new Map();
|
|
94
|
+
for (const session of ordered) {
|
|
95
|
+
if (!projectOrder.has(session.cwd))
|
|
96
|
+
projectOrder.set(session.cwd, projectOrder.size);
|
|
97
|
+
}
|
|
98
|
+
ordered.sort((left, right) => projectOrder.get(left.cwd) - projectOrder.get(right.cwd) || right.updatedAt - left.updatedAt);
|
|
99
|
+
}
|
|
100
|
+
for (const session of ordered) {
|
|
89
101
|
// Group headers only earn their line when more than one project is in
|
|
90
102
|
// play; inside a single project they would repeat the same path forever.
|
|
91
103
|
if (filters.allProjects && session.cwd !== lastProject) {
|
package/lib/types/update.d.ts
CHANGED
|
@@ -55,22 +55,36 @@ export declare function resolveTuiUpdateTarget(): Promise<TuiUpdateTarget>;
|
|
|
55
55
|
* or blocks the interactive TUI.
|
|
56
56
|
*/
|
|
57
57
|
export declare function checkForTuiUpdate(): Promise<TuiUpdateInfo | undefined>;
|
|
58
|
+
/** Build the profile-manager command, preferring a preflight-pinned version. */
|
|
59
|
+
export declare function tuiUpdatePluginArgs(profile: string, targetVersion?: string): string[];
|
|
60
|
+
/**
|
|
61
|
+
* The pnpm Windows tmp-rename race signature (issue #225): pnpm swaps a
|
|
62
|
+
* package directory via a `<name>_tmp_<pid>` staging dir, and a file lock or
|
|
63
|
+
* AV scan makes the scandir/rename fail with ENOENT/EPERM/EBUSY. The failure
|
|
64
|
+
* is transient — the identical command succeeds on retry — but the crashed
|
|
65
|
+
* run leaves a half-updated profile (manifest pins the old version while the
|
|
66
|
+
* lockfile already carries the new snapshot), which presents as "update did
|
|
67
|
+
* nothing" (#209). Genuine resolution errors never carry the `_tmp_<pid>`
|
|
68
|
+
* token, so matching both keeps the retry from masking real failures.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isTransientUpdateFailure(stderr: string): boolean;
|
|
58
71
|
/**
|
|
59
72
|
* Update the installed dsh-tui package and restart the same launcher while
|
|
60
73
|
* preserving the active session. The TUI must already be unmounted before
|
|
61
74
|
* this is called so pnpm output cannot corrupt the rendered terminal frame.
|
|
62
75
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* registry still serving the old `latest`).
|
|
76
|
+
* When the preflight registry check resolved an exact target, pass that
|
|
77
|
+
* version to pnpm instead of resolving `latest` a second time. This avoids a
|
|
78
|
+
* stale mirror/dist-tag response between the check and install. If preflight
|
|
79
|
+
* failed, retain the `--latest` fallback: a plain `pnpm update` stays inside
|
|
80
|
+
* the manifest range and can restart unchanged across minor releases.
|
|
69
81
|
*
|
|
70
82
|
* @param sessionId - Session to resume in the replacement process.
|
|
71
83
|
* @param profile - The dsh profile this TUI was launched with; updating any
|
|
72
84
|
* other profile would leave the running install untouched.
|
|
85
|
+
* @param targetVersion - Exact version returned by the preflight registry
|
|
86
|
+
* check, or undefined when that check failed and pnpm should resolve latest.
|
|
73
87
|
* @returns Exit codes for the update run and the replacement process.
|
|
74
88
|
*/
|
|
75
|
-
export declare function updateTuiAndRestart(sessionId: string, profile: string): Promise<TuiUpdateResult>;
|
|
89
|
+
export declare function updateTuiAndRestart(sessionId: string, profile: string, targetVersion?: string): Promise<TuiUpdateResult>;
|
|
76
90
|
//# sourceMappingURL=update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAIlD,OAAO,EAAE,UAAU,EAAE,CAAA;AAQrB,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAiBxD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,MAAM,GAAG,SAAS,CAahG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIzE;AA4BD;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CASvE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAG5E;
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAIlD,OAAO,EAAE,UAAU,EAAE,CAAA;AAQrB,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAiBxD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,MAAM,GAAG,SAAS,CAahG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIzE;AA4BD;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CASvE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAG5E;AA6DD,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAIrF;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,eAAe,CAAC,CAgD1B"}
|
package/lib/types/update.js
CHANGED
|
@@ -155,9 +155,17 @@ function runProcess(command, args, options = {}) {
|
|
|
155
155
|
: [command, args];
|
|
156
156
|
const child = spawn(runCommand, runArgs, {
|
|
157
157
|
env: options.env,
|
|
158
|
-
stdio: 'inherit',
|
|
158
|
+
stdio: options.onStderr === undefined ? 'inherit' : ['inherit', 'inherit', 'pipe'],
|
|
159
159
|
shell: useShell,
|
|
160
160
|
});
|
|
161
|
+
if (options.onStderr !== undefined && child.stderr !== null) {
|
|
162
|
+
const onStderr = options.onStderr;
|
|
163
|
+
child.stderr.on('data', (chunk) => {
|
|
164
|
+
const text = chunk.toString('utf8');
|
|
165
|
+
process.stderr.write(text);
|
|
166
|
+
onStderr(text);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
161
169
|
const finish = (code) => {
|
|
162
170
|
if (settled)
|
|
163
171
|
return;
|
|
@@ -171,35 +179,77 @@ function runProcess(command, args, options = {}) {
|
|
|
171
179
|
child.once('close', code => finish(code ?? 1));
|
|
172
180
|
});
|
|
173
181
|
}
|
|
182
|
+
/** Build the profile-manager command, preferring a preflight-pinned version. */
|
|
183
|
+
export function tuiUpdatePluginArgs(profile, targetVersion) {
|
|
184
|
+
return targetVersion === undefined
|
|
185
|
+
? ['plugin', '--profile', profile, 'update', '--latest', PACKAGE_NAME]
|
|
186
|
+
: ['plugin', '--profile', profile, 'update', `${PACKAGE_NAME}@${targetVersion}`];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The pnpm Windows tmp-rename race signature (issue #225): pnpm swaps a
|
|
190
|
+
* package directory via a `<name>_tmp_<pid>` staging dir, and a file lock or
|
|
191
|
+
* AV scan makes the scandir/rename fail with ENOENT/EPERM/EBUSY. The failure
|
|
192
|
+
* is transient — the identical command succeeds on retry — but the crashed
|
|
193
|
+
* run leaves a half-updated profile (manifest pins the old version while the
|
|
194
|
+
* lockfile already carries the new snapshot), which presents as "update did
|
|
195
|
+
* nothing" (#209). Genuine resolution errors never carry the `_tmp_<pid>`
|
|
196
|
+
* token, so matching both keeps the retry from masking real failures.
|
|
197
|
+
*/
|
|
198
|
+
export function isTransientUpdateFailure(stderr) {
|
|
199
|
+
return /ENOENT|EPERM|EBUSY/i.test(stderr) && /_tmp_\d+/i.test(stderr);
|
|
200
|
+
}
|
|
174
201
|
/**
|
|
175
202
|
* Update the installed dsh-tui package and restart the same launcher while
|
|
176
203
|
* preserving the active session. The TUI must already be unmounted before
|
|
177
204
|
* this is called so pnpm output cannot corrupt the rendered terminal frame.
|
|
178
205
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* registry still serving the old `latest`).
|
|
206
|
+
* When the preflight registry check resolved an exact target, pass that
|
|
207
|
+
* version to pnpm instead of resolving `latest` a second time. This avoids a
|
|
208
|
+
* stale mirror/dist-tag response between the check and install. If preflight
|
|
209
|
+
* failed, retain the `--latest` fallback: a plain `pnpm update` stays inside
|
|
210
|
+
* the manifest range and can restart unchanged across minor releases.
|
|
185
211
|
*
|
|
186
212
|
* @param sessionId - Session to resume in the replacement process.
|
|
187
213
|
* @param profile - The dsh profile this TUI was launched with; updating any
|
|
188
214
|
* other profile would leave the running install untouched.
|
|
215
|
+
* @param targetVersion - Exact version returned by the preflight registry
|
|
216
|
+
* check, or undefined when that check failed and pnpm should resolve latest.
|
|
189
217
|
* @returns Exit codes for the update run and the replacement process.
|
|
190
218
|
*/
|
|
191
|
-
export async function updateTuiAndRestart(sessionId, profile) {
|
|
219
|
+
export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
|
|
192
220
|
const dsh = process.platform === 'win32' ? 'dsh.cmd' : 'dsh';
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
221
|
+
const updateArgs = tuiUpdatePluginArgs(profile, targetVersion);
|
|
222
|
+
let updateStderr = '';
|
|
223
|
+
const capture = (chunk) => { updateStderr += chunk; };
|
|
224
|
+
let updateCode = await runProcess(dsh, updateArgs, { shell: true, onStderr: capture });
|
|
225
|
+
// Transient Windows tmp-rename race (issue #225): retry the identical
|
|
226
|
+
// command once — it succeeds on a clean second run, and only the
|
|
227
|
+
// `_tmp_<pid>` race signature qualifies, never a real resolution error.
|
|
228
|
+
if (updateCode !== 0 && isTransientUpdateFailure(updateStderr)) {
|
|
229
|
+
process.stderr.write('dsh-tui: transient pnpm failure (Windows tmp-rename race) — retrying once…\n');
|
|
230
|
+
updateStderr = '';
|
|
231
|
+
updateCode = await runProcess(dsh, updateArgs, { shell: true, onStderr: capture });
|
|
232
|
+
}
|
|
201
233
|
if (updateCode !== 0)
|
|
202
234
|
return { updateCode, restartCode: updateCode };
|
|
235
|
+
// Post-update verification (issue #225): pnpm can report success yet leave
|
|
236
|
+
// the profile half-updated (manifest old / lockfile new). Verify against
|
|
237
|
+
// the preflight target; a full `install` reconciles lockfile →
|
|
238
|
+
// node_modules, and if the mismatch survives, stop before restarting into
|
|
239
|
+
// a mixed state and hand the user the exact repair command instead.
|
|
240
|
+
if (targetVersion !== undefined) {
|
|
241
|
+
let installed = installedTuiVersion();
|
|
242
|
+
if (installed !== targetVersion) {
|
|
243
|
+
await runProcess(dsh, ['plugin', '--profile', profile, 'install'], { shell: true });
|
|
244
|
+
installed = installedTuiVersion();
|
|
245
|
+
}
|
|
246
|
+
if (installed !== targetVersion) {
|
|
247
|
+
process.stderr.write(`dsh-tui: update completed but the profile still runs ${installed ?? 'an unreadable version'} ` +
|
|
248
|
+
`(expected ${targetVersion}) — the profile is half-updated. Repair manually with:\n` +
|
|
249
|
+
` dsh plugin --profile ${profile} add ${PACKAGE_NAME}@${targetVersion}\n`);
|
|
250
|
+
return { updateCode: 1, restartCode: 1 };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
203
253
|
const restartCode = await runProcess(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
|
|
204
254
|
env: {
|
|
205
255
|
...process.env,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,qEAAqE;AACrE,eAAO,MAAM,QAAQ,QAA8B,CAAA;AAEnD,wEAAwE;AACxE,eAAO,MAAM,eAAe,QAA6B,CAAA;AAEzD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,MAAwB,EAChC,MAAM,GAAE,MAAiB,GACxB,OAAO,
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,qEAAqE;AACrE,eAAO,MAAM,QAAQ,QAA8B,CAAA;AAEnD,wEAAwE;AACxE,eAAO,MAAM,eAAe,QAA6B,CAAA;AAEzD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,MAAwB,EAChC,MAAM,GAAE,MAAiB,GACxB,OAAO,CAOT;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxD,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAEV"}
|
package/lib/types/utils/paths.js
CHANGED
|
@@ -35,7 +35,10 @@ export const LEGACY_DATA_DIR = join(homeDir(), '.dsh-cc');
|
|
|
35
35
|
export function migrateLegacyDataDir(legacy = LEGACY_DATA_DIR, target = DATA_DIR) {
|
|
36
36
|
if (!existsSync(legacy) || existsSync(target))
|
|
37
37
|
return false;
|
|
38
|
-
|
|
38
|
+
// `filter` forces the JS copy path: the native cpSync fast path fails with
|
|
39
|
+
// EIO (or crashes) on Windows when the destination lives under a home
|
|
40
|
+
// directory whose name contains non-ASCII characters (e.g. `C:\Users\米`).
|
|
41
|
+
cpSync(legacy, target, { recursive: true, filter: () => true });
|
|
39
42
|
return true;
|
|
40
43
|
}
|
|
41
44
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-harness-tui/dsh-tui",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Claude Code style interactive TUI front door for DeepSeek Harness agents, built on the ported Ink core.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,16 +57,12 @@
|
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
60
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6"
|
|
61
|
-
},
|
|
62
|
-
"dependencies": {
|
|
63
|
-
"@alcalzone/ansi-tokenize": "^0.3.0",
|
|
64
60
|
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
|
|
65
61
|
"@deepseek-ai/dsh-agent-instructions": "^0.1.0-rc.6",
|
|
66
62
|
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6",
|
|
67
|
-
"@deepseek-ai/dsh-agent-tool-presentation": "^0.1.0-rc.6",
|
|
68
63
|
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
69
64
|
"@deepseek-ai/dsh-cordis-host-runner": "^0.1.0-rc.6",
|
|
65
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
70
66
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
71
67
|
"@deepseek-ai/dsh-persona": "^0.1.0-rc.6",
|
|
72
68
|
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
@@ -83,14 +79,17 @@
|
|
|
83
79
|
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6",
|
|
84
80
|
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.6",
|
|
85
81
|
"@deepseek-ai/dsh-workspace": "^0.1.0-rc.6",
|
|
86
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
82
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"@alcalzone/ansi-tokenize": "^0.3.0",
|
|
87
86
|
"auto-bind": "^4.0.0 || ^5.0.0",
|
|
88
87
|
"bidi-js": "^1.0.0",
|
|
89
88
|
"chalk": "^5.0.0",
|
|
90
89
|
"cli-boxes": "^3.0.0 || ^4.0.0",
|
|
91
90
|
"cli-highlight": "^2.1.11",
|
|
92
91
|
"code-excerpt": "^3.0.0 || ^4.0.0",
|
|
93
|
-
"dsh-working-activity": "^0.2.
|
|
92
|
+
"dsh-working-activity": "^0.2.6",
|
|
94
93
|
"emoji-regex": "^10.0.0",
|
|
95
94
|
"figures": "^6.1.0",
|
|
96
95
|
"get-east-asian-width": "^1.0.0",
|
|
@@ -113,26 +112,47 @@
|
|
|
113
112
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
114
113
|
"@deepseek-ai/cordis-plugin-include": "^1.0.6",
|
|
115
114
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
115
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
|
|
116
|
+
"@deepseek-ai/dsh-agent-instructions": "^0.1.0-rc.6",
|
|
117
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6",
|
|
116
118
|
"@deepseek-ai/dsh-atomic-write": "^0.1.0-rc.6",
|
|
117
119
|
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6",
|
|
118
120
|
"@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
|
|
119
121
|
"@deepseek-ai/dsh-code-runtime": "^0.1.0-rc.6",
|
|
122
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.6",
|
|
123
|
+
"@deepseek-ai/dsh-cordis-host-runner": "^0.1.0-rc.6",
|
|
120
124
|
"@deepseek-ai/dsh-fs": "^0.1.0-rc.6",
|
|
121
125
|
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6",
|
|
122
126
|
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
127
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
128
|
+
"@deepseek-ai/dsh-persona": "^0.1.0-rc.6",
|
|
123
129
|
"@deepseek-ai/dsh-sandbox": "^0.1.0-rc.6",
|
|
124
130
|
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6",
|
|
125
131
|
"@deepseek-ai/dsh-scope": "^0.1.0-rc.6",
|
|
132
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
126
133
|
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.6",
|
|
127
134
|
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.0-rc.6",
|
|
128
135
|
"@deepseek-ai/dsh-session-persistence-sqlite": "^0.1.0-rc.6",
|
|
129
136
|
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
|
|
137
|
+
"@deepseek-ai/dsh-skill": "^0.1.0-rc.6",
|
|
138
|
+
"@deepseek-ai/dsh-storage": "^0.1.0-rc.6",
|
|
139
|
+
"@deepseek-ai/dsh-storage-domain": "^0.1.0-rc.6",
|
|
140
|
+
"@deepseek-ai/dsh-storage-json": "^0.1.0-rc.6",
|
|
130
141
|
"@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6",
|
|
142
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
|
|
143
|
+
"@deepseek-ai/dsh-terminal": "^0.1.0-rc.6",
|
|
144
|
+
"@deepseek-ai/dsh-terminal-bash": "^0.1.0-rc.6",
|
|
131
145
|
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.6",
|
|
146
|
+
"@deepseek-ai/dsh-tool-ask-user": "^0.1.0-rc.6",
|
|
147
|
+
"@deepseek-ai/dsh-tool-bash-persistent": "^0.1.0-rc.6",
|
|
148
|
+
"@deepseek-ai/dsh-tool-cordis": "^0.1.0-rc.6",
|
|
132
149
|
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
|
|
133
150
|
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6",
|
|
134
151
|
"@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6",
|
|
152
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.6",
|
|
135
153
|
"@deepseek-ai/dsh-web-app": "0.1.0-rc.6",
|
|
154
|
+
"@deepseek-ai/dsh-workspace": "^0.1.0-rc.6",
|
|
155
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
136
156
|
"@types/lodash-es": "^4.17.0",
|
|
137
157
|
"@types/node": "^22.0.0",
|
|
138
158
|
"@types/react": "^19.0.0",
|
|
@@ -152,11 +172,15 @@
|
|
|
152
172
|
"clean": "node scripts/clean-lib.mjs",
|
|
153
173
|
"compile": "npm run clean && tsc -p tsconfig.json",
|
|
154
174
|
"build": "npm run compile && npm run verify:build",
|
|
175
|
+
"dev": "node scripts/dev-test.mjs",
|
|
176
|
+
"dev:copy-config": "node scripts/dev-copy-config.mjs",
|
|
177
|
+
"dev:test": "node scripts/verify-dev-command.mjs && node scripts/dev-test.mjs --no-launch",
|
|
155
178
|
"tui": "node --import tsx/esm scripts/run.ts",
|
|
156
179
|
"smoke": "node --import tsx/esm scripts/smoke.tsx",
|
|
157
|
-
"verify:build": "npm run verify:boundary && npm run verify:contract && npm run verify:patch-surface && npm run verify:packaged-presets && npm run verify:minimal-preset-tools && npm run verify:liangshen-bootstrap",
|
|
180
|
+
"verify:build": "npm run verify:boundary && npm run verify:contract && npm run verify:manifest-deps && npm run verify:patch-surface && npm run verify:packaged-presets && npm run verify:minimal-preset-tools && npm run verify:liangshen-bootstrap",
|
|
158
181
|
"verify:boundary": "node --import tsx/esm scripts/verify-adapter-boundary.ts",
|
|
159
182
|
"verify:contract": "node --import tsx/esm scripts/verify-upstream-contract.ts",
|
|
183
|
+
"verify:manifest-deps": "node --import tsx/esm scripts/verify-manifest-deps.ts",
|
|
160
184
|
"verify:patch-surface": "node --import tsx/esm scripts/verify-patch-surface.ts",
|
|
161
185
|
"verify:packaged-presets": "node scripts/verify-packaged-presets.mjs",
|
|
162
186
|
"verify:minimal-preset-tools": "node scripts/verify-minimal-preset-tools.mjs",
|