@deepseek-harness-tui/dsh-tui 0.7.0 → 0.7.2
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 +9 -6
- package/bin/dsh-tui.js +67 -14
- package/cordis.patch.yml +10 -0
- package/lib/types/commands.js +1 -1
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/design-system/ThemedText.d.ts +2 -2
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +1 -3
- package/lib/types/components/sessions/SessionListRow.d.ts +26 -0
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionListRow.js +35 -0
- package/lib/types/components/sessions/SessionPreview.d.ts +25 -0
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionPreview.js +60 -0
- package/lib/types/dsh-adapter/channel.d.ts +23 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +190 -75
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +8 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +1 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +10 -1
- package/lib/types/dsh-adapter/packaged-presets.d.ts +22 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-presets.js +90 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +60 -4
- package/lib/types/dsh-adapter/presets.d.ts +16 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +23 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts +29 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/digest.js +224 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts +100 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/frames.js +246 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts +50 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/header.js +62 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/index.js +15 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +41 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/list.js +213 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +48 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/store.js +164 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts +142 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/types.js +14 -0
- package/lib/types/i18n.d.ts +122 -14
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +37 -6
- package/lib/types/screens/Chat.d.ts +3 -2
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +28 -144
- package/lib/types/screens/SessionBrowser.d.ts +37 -0
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -0
- package/lib/types/screens/SessionBrowser.js +437 -0
- package/lib/types/sessionHistory.d.ts +0 -8
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessions/format.d.ts +117 -0
- package/lib/types/sessions/format.d.ts.map +1 -0
- package/lib/types/sessions/format.js +244 -0
- package/lib/types/sessions/view.d.ts +125 -0
- package/lib/types/sessions/view.d.ts.map +1 -0
- package/lib/types/sessions/view.js +222 -0
- package/package.json +12 -5
- package/presets/liangshen/.dsh-tui-managed.json +5 -0
- package/presets/liangshen/agent.cordis.yml +389 -0
- package/presets/liangshen/compaction-epoch.mjs +81 -0
- package/presets/liangshen/custom-bash.mjs +126 -0
- package/presets/liangshen/instruction-hint.mjs +181 -0
- package/presets/liangshen/preset.yml +3 -0
- package/presets/liangshen/skill-search.mjs +142 -0
- package/presets/liangshen/tool-bootstrap.mjs +300 -0
- package/lib/invariant.js +0 -28
- package/lib/types/components/ResumePicker.d.ts +0 -26
- package/lib/types/components/ResumePicker.d.ts.map +0 -1
- package/lib/types/components/ResumePicker.js +0 -50
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { assembleContextFor, installModelSelection } from '@deepseek-ai/dsh-agent';
|
|
3
|
-
import { isUserInvocable } from '@deepseek-ai/dsh-skill';
|
|
3
|
+
import { isUserInvocable, renderSkillContent } from '@deepseek-ai/dsh-skill';
|
|
4
4
|
import { createUserMessage, isTokenDelta, MessageId, ReasoningEffortId, } from '@deepseek-ai/dsh-llm';
|
|
5
5
|
import { runSideQuestion, wrapSideQuestion } from './sideQuestion.js';
|
|
6
6
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
7
7
|
import { renderContextSections, renderPrompt } from '@deepseek-ai/dsh-system-prompt';
|
|
8
8
|
import { loadBaselineInstructions } from '@deepseek-ai/dsh-agent-instructions';
|
|
9
9
|
import { extname, isAbsolute, join } from 'node:path';
|
|
10
|
-
import { completeCommands, LOCAL_COMMANDS } from '../commands.js';
|
|
11
|
-
import { clearResumeTarget, forgetSession,
|
|
12
|
-
import { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes,
|
|
10
|
+
import { completeCommands, isLocalCommandName, LOCAL_COMMANDS, parseCommandName } from '../commands.js';
|
|
11
|
+
import { clearResumeTarget, forgetSession, readResumeTarget, touchSession, writeResumeTarget } from '../sessionHistory.js';
|
|
12
|
+
import { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, sessionsRoots } from './compat/index.js';
|
|
13
|
+
import { listSummaries, locateSession, noteBranch, previewSession, } from './sessions/index.js';
|
|
13
14
|
import { writeActivityFrames } from '../activityPrefs.js';
|
|
14
15
|
import { readEffortPref, writeEffortPref } from '../effortPrefs.js';
|
|
15
16
|
import { readModelPref, writeModelPref } from '../modelPrefs.js';
|
|
@@ -26,6 +27,11 @@ import { modeDisplayName, resolveSessionModes } from '../sessionModes.js';
|
|
|
26
27
|
import { ActivityTracker } from 'dsh-working-activity/status';
|
|
27
28
|
import { attachSessionToWorkspace } from './workspace.js';
|
|
28
29
|
import { createLocalWorkspaceRuntime } from './workspaces.js';
|
|
30
|
+
/**
|
|
31
|
+
* Delay before re-reading a skill catalog that reported an incomplete
|
|
32
|
+
* observation (a provider whose directory watcher is still warming).
|
|
33
|
+
*/
|
|
34
|
+
const SKILL_COMMAND_RETRY_MS = 800;
|
|
29
35
|
const ARGS_PREVIEW_LIMIT = 160;
|
|
30
36
|
const RESULT_PREVIEW_LIMIT = 240;
|
|
31
37
|
/** Local `!`-command output cap (mirrors the result preview limit). */
|
|
@@ -251,18 +257,8 @@ function coalesceReplayEvents(events) {
|
|
|
251
257
|
}
|
|
252
258
|
/** Buffer below the context window at which CC warns (autoCompact.ts). */
|
|
253
259
|
const CONTEXT_WARNING_BUFFER_TOKENS = 20_000;
|
|
254
|
-
/** How many
|
|
255
|
-
|
|
256
|
-
const SESSION_TITLE_DEPTH = 20;
|
|
257
|
-
/** Picker title cap, in characters. */
|
|
258
|
-
const SESSION_TITLE_LIMIT = 40;
|
|
259
|
-
/** One-line session title: whitespace folded, capped with an ellipsis. */
|
|
260
|
-
function shortenTitle(text) {
|
|
261
|
-
const flat = text.replace(/\s+/g, ' ').trim();
|
|
262
|
-
return flat.length <= SESSION_TITLE_LIMIT
|
|
263
|
-
? flat
|
|
264
|
-
: `${flat.slice(0, SESSION_TITLE_LIMIT - 1)}…`;
|
|
265
|
-
}
|
|
260
|
+
/** How many trailing exchanges the browser's preview pane asks for. */
|
|
261
|
+
const PREVIEW_ENTRIES = 8;
|
|
266
262
|
/** Resolve once a `turn/end` event newer than `fromSeq` lands in the session
|
|
267
263
|
* log (Agent.cancel closes the turn asynchronously), or when the timeout
|
|
268
264
|
* expires. Polling the session log is race-free here: fork reads the same
|
|
@@ -298,7 +294,11 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
298
294
|
// command/run + command/done records). Absent the service, only the
|
|
299
295
|
// built-in local commands exist.
|
|
300
296
|
const commandService = ctx.get('commands');
|
|
301
|
-
|
|
297
|
+
// Workspace registry runtime (optional service, issue #183): mounted by
|
|
298
|
+
// the bundle patch's dsh-tui-workspaces row; absent the row (stale patch
|
|
299
|
+
// or a bare embedder), degrade to the local-only runtime. plugin.ts owns
|
|
300
|
+
// the degraded-boot warning for profile launches.
|
|
301
|
+
const workspaceService = ctx.get('tuiWorkspaces') ?? createLocalWorkspaceRuntime();
|
|
302
302
|
const commandTrees = ctx.get('tuiCommandTrees');
|
|
303
303
|
// Shift+Tab session-mode cycle: cordis.yml `modes` wins; absent/empty/
|
|
304
304
|
// atom-less → the built-in default/plan/full cycle (sessionModes.ts).
|
|
@@ -977,6 +977,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
977
977
|
bindAgent();
|
|
978
978
|
refreshCommandList();
|
|
979
979
|
void refreshLoadedContext();
|
|
980
|
+
void refreshSkillCommands();
|
|
980
981
|
// The forked session (rewind) becomes the most recently used.
|
|
981
982
|
touchSession(childId);
|
|
982
983
|
state.emit();
|
|
@@ -1103,6 +1104,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1103
1104
|
bindAgent();
|
|
1104
1105
|
refreshCommandList();
|
|
1105
1106
|
void refreshLoadedContext();
|
|
1107
|
+
void refreshSkillCommands();
|
|
1106
1108
|
// Keep the `--resume` launcher contract pointing at the same session.
|
|
1107
1109
|
writeResumeTarget(sessionId);
|
|
1108
1110
|
// The resumed session is now the most recently used.
|
|
@@ -1225,6 +1227,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1225
1227
|
bindAgent();
|
|
1226
1228
|
refreshCommandList();
|
|
1227
1229
|
void refreshLoadedContext();
|
|
1230
|
+
void refreshSkillCommands();
|
|
1228
1231
|
clearResumeTarget();
|
|
1229
1232
|
// The brand-new session becomes the most recently used.
|
|
1230
1233
|
touchSession(handle.agent.id);
|
|
@@ -1398,6 +1401,7 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1398
1401
|
bindAgent();
|
|
1399
1402
|
refreshCommandList();
|
|
1400
1403
|
void refreshLoadedContext();
|
|
1404
|
+
void refreshSkillCommands();
|
|
1401
1405
|
// The model-switched fork becomes the most recently used.
|
|
1402
1406
|
touchSession(childId);
|
|
1403
1407
|
state.emit();
|
|
@@ -1661,61 +1665,21 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1661
1665
|
return listFilesDeep(fs, state.cwd);
|
|
1662
1666
|
},
|
|
1663
1667
|
async listSessions() {
|
|
1664
|
-
//
|
|
1665
|
-
//
|
|
1668
|
+
// Every stored session, classified and unfiltered. Which of them a
|
|
1669
|
+
// surface shows — this project only, conversations only, sub-agent runs
|
|
1670
|
+
// folded away — is a view decision, and keeping it out of here is what
|
|
1671
|
+
// lets the browser toggle those views without re-reading a single log.
|
|
1666
1672
|
const persistence = ctx.get('sessionPersistence');
|
|
1667
1673
|
if (!persistence)
|
|
1668
1674
|
return [];
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
// MRU ordering: DSH headers carry only createdAt, so dsh-tui keeps its
|
|
1675
|
-
// own last-used timestamps (touchSession on resume/submit/new) and
|
|
1676
|
-
// falls back to createdAt for sessions never touched in this install.
|
|
1677
|
-
const lastUsed = readLastUsed();
|
|
1678
|
-
const records = local
|
|
1679
|
-
.map(header => ({
|
|
1680
|
-
id: header.id,
|
|
1681
|
-
// Titles load lazily below (first user message); until then the
|
|
1682
|
-
// cwd basename stands in (matching the status line), with a
|
|
1683
|
-
// short id when absent.
|
|
1684
|
-
title: basename(header.cwd ?? '') || `session ${String(header.id).slice(0, 8)}`,
|
|
1685
|
-
cwd: header.cwd ?? '',
|
|
1686
|
-
createdAt: header.createdAt,
|
|
1687
|
-
updatedAt: lastUsed[header.id] ?? header.createdAt,
|
|
1688
|
-
}))
|
|
1689
|
-
.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
1690
|
-
// Title = the session's session/title event (auto: first prompt;
|
|
1691
|
-
// manual: /rename), else its FIRST user message — the picker's most
|
|
1692
|
-
// useful label. Read via the tolerant compat log reader instead of
|
|
1693
|
-
// persistence.load: the backend validates every event against
|
|
1694
|
-
// KNOWN_SESSION_EVENT_TYPES and throws the whole load on an unmarked
|
|
1695
|
-
// an unregistered third-party type, which would otherwise leave the
|
|
1696
|
-
// affected session titled with the cwd
|
|
1697
|
-
// basename. An unreadable log keeps the basename fallback.
|
|
1698
|
-
const empty = new Set();
|
|
1699
|
-
for (const record of records.slice(0, SESSION_TITLE_DEPTH)) {
|
|
1700
|
-
const info = readSessionTitleFromLog(String(record.id));
|
|
1701
|
-
if (info === undefined)
|
|
1702
|
-
continue; // keep the basename fallback
|
|
1703
|
-
if (!info.hasUserMessage) {
|
|
1704
|
-
// Launch artifact — a session with no user message holds no
|
|
1705
|
-
// conversation to resume, so drop it from the picker (its
|
|
1706
|
-
// createdAt-only updatedAt would otherwise pin it near the
|
|
1707
|
-
// top forever, one per dsh-tui launch).
|
|
1708
|
-
empty.add(record.id);
|
|
1709
|
-
continue;
|
|
1710
|
-
}
|
|
1711
|
-
if (info.title !== undefined)
|
|
1712
|
-
record.title = shortenTitle(info.title);
|
|
1713
|
-
}
|
|
1714
|
-
return records.filter(record => !empty.has(record.id));
|
|
1715
|
-
}
|
|
1716
|
-
catch {
|
|
1675
|
+
return listSummaries(persistence);
|
|
1676
|
+
},
|
|
1677
|
+
async previewSession(sessionId) {
|
|
1678
|
+
const persistence = ctx.get('sessionPersistence');
|
|
1679
|
+
if (!persistence)
|
|
1717
1680
|
return [];
|
|
1718
|
-
|
|
1681
|
+
const path = await locateSession(persistence, sessionId);
|
|
1682
|
+
return path === undefined ? [] : previewSession(path, PREVIEW_ENTRIES);
|
|
1719
1683
|
},
|
|
1720
1684
|
setResumeTarget(sessionId) {
|
|
1721
1685
|
writeResumeTarget(sessionId);
|
|
@@ -1755,12 +1719,10 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1755
1719
|
}
|
|
1756
1720
|
if (appendSessionTitle(sessionId, title) !== 'appended')
|
|
1757
1721
|
return false;
|
|
1758
|
-
//
|
|
1759
|
-
//
|
|
1760
|
-
//
|
|
1761
|
-
//
|
|
1762
|
-
// title event is durable. A rename IS user interaction with the
|
|
1763
|
-
// session — touching it pulls it into the title window.
|
|
1722
|
+
// The append changed the log, so the next listing sees a new revision,
|
|
1723
|
+
// re-derives, and reads back the very title event just written — no
|
|
1724
|
+
// second path to the same answer. Touching it is about ordering, not
|
|
1725
|
+
// titles: a rename is user interaction, so the row belongs at the top.
|
|
1764
1726
|
touchSession(sessionId);
|
|
1765
1727
|
return true;
|
|
1766
1728
|
},
|
|
@@ -1973,6 +1935,9 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1973
1935
|
return [t('subagent-query-failed', { err: error instanceof Error ? error.message : String(error) })];
|
|
1974
1936
|
}
|
|
1975
1937
|
},
|
|
1938
|
+
releaseContributions() {
|
|
1939
|
+
releaseSkillCommands();
|
|
1940
|
+
},
|
|
1976
1941
|
traceEvents() {
|
|
1977
1942
|
// Immutable per-append snapshot (dsh-session caches the frozen array);
|
|
1978
1943
|
// reads follow agent swaps (/resume /rewind /new) automatically.
|
|
@@ -2093,6 +2058,11 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
2093
2058
|
...(descriptions === undefined ? {} : { descriptions }),
|
|
2094
2059
|
tag: descriptor.input?.hint,
|
|
2095
2060
|
external: true,
|
|
2061
|
+
// Skills reach the registry as ordinary commands, so the menu would
|
|
2062
|
+
// lose the marker HelpMenu uses to keep them out of the chrome list.
|
|
2063
|
+
// This channel registered them and is the authority on which names
|
|
2064
|
+
// are skills.
|
|
2065
|
+
...(skillCommands.has(descriptor.name) ? { skill: true } : {}),
|
|
2096
2066
|
});
|
|
2097
2067
|
}
|
|
2098
2068
|
}
|
|
@@ -2165,8 +2135,148 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
2165
2135
|
};
|
|
2166
2136
|
ctx.on('commands/change', refreshCommandList);
|
|
2167
2137
|
ctx.on('skills/change', refreshCommandList);
|
|
2138
|
+
/**
|
|
2139
|
+
* The view a skill-catalog read must be taken through, as ONE value.
|
|
2140
|
+
*
|
|
2141
|
+
* The registry is host-plane but scope-LAYERED: a provider mounted by an
|
|
2142
|
+
* agent preset's standing composition files into that preset's layer, and a
|
|
2143
|
+
* read taken without the scope sees only the host layer. Passing the pair
|
|
2144
|
+
* together keeps a read from being taken half-scoped.
|
|
2145
|
+
*
|
|
2146
|
+
* @param target - the agent whose view is wanted.
|
|
2147
|
+
*/
|
|
2148
|
+
const skillViewOptions = (target) => ({
|
|
2149
|
+
scope: target,
|
|
2150
|
+
cwd: state.cwd,
|
|
2151
|
+
});
|
|
2152
|
+
/** The skill registry as the given agent sees it, or undefined when a boot
|
|
2153
|
+
* mounts none. `serviceForAgent` resolves through the agent's mount and
|
|
2154
|
+
* falls back to the host context. */
|
|
2155
|
+
const skillRegistryFor = (target) => serviceForAgent(ctx, target, 'skills');
|
|
2156
|
+
/**
|
|
2157
|
+
* Skill commands this channel owns, by skill name. The value keeps the
|
|
2158
|
+
* description the command was registered with so an edited SKILL.md
|
|
2159
|
+
* re-registers instead of leaving a stale menu entry.
|
|
2160
|
+
*/
|
|
2161
|
+
const skillCommands = new Map();
|
|
2162
|
+
/** Skill names the registry refused (name taken, or invalid) — warn once. */
|
|
2163
|
+
const skillCommandsRefused = new Set();
|
|
2164
|
+
/** Pending re-read after an incomplete catalog observation. */
|
|
2165
|
+
let skillCommandsRetry;
|
|
2166
|
+
/**
|
|
2167
|
+
* Publish every user-invocable skill as a slash command (issue #86).
|
|
2168
|
+
*
|
|
2169
|
+
* The completion menu already lists these skills, but a menu entry is not a
|
|
2170
|
+
* command: nothing dispatches it, so typing the name and pressing Enter does
|
|
2171
|
+
* nothing. Registering through the host command registry is what makes them
|
|
2172
|
+
* runnable, and buys three things the TUI would otherwise reimplement:
|
|
2173
|
+
* `register` emits `commands/change`, so the menu merge folds the entry in
|
|
2174
|
+
* on its own; Enter dispatches through the normal command path, so the
|
|
2175
|
+
* invocation is logged as a paired `command/run`/`command/done` like every
|
|
2176
|
+
* other command; and the handler runs host-side, so invoking a skill is
|
|
2177
|
+
* DETERMINISTIC — the body is injected here, instead of sending `/name` to
|
|
2178
|
+
* the model and depending on it to recognize the text and reach for its
|
|
2179
|
+
* skill loader.
|
|
2180
|
+
*
|
|
2181
|
+
* `userInvocable` covers "human-facing command catalogs AND loaders", so
|
|
2182
|
+
* discovery alone would honor half the flag.
|
|
2183
|
+
*/
|
|
2184
|
+
const refreshSkillCommands = async () => {
|
|
2185
|
+
if (commandService === undefined)
|
|
2186
|
+
return;
|
|
2187
|
+
const target = agent;
|
|
2188
|
+
const registry = skillRegistryFor(target);
|
|
2189
|
+
if (registry === undefined)
|
|
2190
|
+
return;
|
|
2191
|
+
let observation;
|
|
2192
|
+
try {
|
|
2193
|
+
observation = await registry.snapshot(skillViewOptions(target));
|
|
2194
|
+
}
|
|
2195
|
+
catch (error) {
|
|
2196
|
+
ctx.logger.warn('skill commands: catalog read failed: %o', error);
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2199
|
+
if (target !== agent)
|
|
2200
|
+
return;
|
|
2201
|
+
// A provider still warming its watcher reports an incomplete observation;
|
|
2202
|
+
// re-read once so a cold start cannot leave the menu permanently short.
|
|
2203
|
+
if (!observation.complete && skillCommandsRetry === undefined) {
|
|
2204
|
+
skillCommandsRetry = setTimeout(() => {
|
|
2205
|
+
skillCommandsRetry = undefined;
|
|
2206
|
+
void refreshSkillCommands();
|
|
2207
|
+
}, SKILL_COMMAND_RETRY_MS);
|
|
2208
|
+
}
|
|
2209
|
+
const wanted = new Map(observation.skills
|
|
2210
|
+
.filter(skill => isUserInvocable(skill))
|
|
2211
|
+
// A name the TUI's own command grammar cannot parse would show in the
|
|
2212
|
+
// menu and then fail to dispatch when typed; ask the real parser
|
|
2213
|
+
// instead of restating its pattern here.
|
|
2214
|
+
.filter(skill => parseCommandName(`/${skill.name}`)?.name === skill.name)
|
|
2215
|
+
// Built-in locals win a name collision, exactly as they do over
|
|
2216
|
+
// plugin-registered commands in refreshCommandList.
|
|
2217
|
+
.filter(skill => !isLocalCommandName(skill.name))
|
|
2218
|
+
.map(skill => [skill.name, skill.description]));
|
|
2219
|
+
for (const [name, entry] of skillCommands) {
|
|
2220
|
+
if (wanted.get(name) === entry.description)
|
|
2221
|
+
continue;
|
|
2222
|
+
entry.dispose();
|
|
2223
|
+
skillCommands.delete(name);
|
|
2224
|
+
}
|
|
2225
|
+
for (const [name, description] of wanted) {
|
|
2226
|
+
if (skillCommands.has(name) || skillCommandsRefused.has(name))
|
|
2227
|
+
continue;
|
|
2228
|
+
// Another plugin already owns this name (plan/goal/…): leave it alone.
|
|
2229
|
+
if (commandService.find(target, name) !== undefined)
|
|
2230
|
+
continue;
|
|
2231
|
+
try {
|
|
2232
|
+
const dispose = commandService.register({
|
|
2233
|
+
name,
|
|
2234
|
+
description,
|
|
2235
|
+
// The injected body is the payload; recording the (empty) raw input
|
|
2236
|
+
// would only duplicate the command name into the session log.
|
|
2237
|
+
recordInput: false,
|
|
2238
|
+
handler: async ({ agent: invoker, signal }) => {
|
|
2239
|
+
const view = { ...skillViewOptions(invoker), signal };
|
|
2240
|
+
const skill = await skillRegistryFor(invoker)?.get(name, view);
|
|
2241
|
+
if (skill === undefined || !isUserInvocable(skill)) {
|
|
2242
|
+
return { kind: 'error', text: t('skill-unavailable', { name }) };
|
|
2243
|
+
}
|
|
2244
|
+
// The official user-explicit invocation shape (dsh-skill's
|
|
2245
|
+
// SkillInvocationSource): the rendered body rides as instructions
|
|
2246
|
+
// the model follows, and transcript consumers present it from the
|
|
2247
|
+
// source metadata instead of re-parsing model-facing text.
|
|
2248
|
+
invoker.followup(createUserMessage({
|
|
2249
|
+
content: [{ type: 'text', text: renderSkillContent(skill) }],
|
|
2250
|
+
source: { kind: 'skill-invocation', name, form: 'instructions' },
|
|
2251
|
+
}));
|
|
2252
|
+
// Silent success: the agent visibly starts working on the skill,
|
|
2253
|
+
// which is the feedback (CC shows no banner either).
|
|
2254
|
+
return { kind: 'success' };
|
|
2255
|
+
},
|
|
2256
|
+
});
|
|
2257
|
+
skillCommands.set(name, { dispose, description });
|
|
2258
|
+
}
|
|
2259
|
+
catch (error) {
|
|
2260
|
+
skillCommandsRefused.add(name);
|
|
2261
|
+
ctx.logger.warn(`skill commands: "${name}" not registrable: %o`, error);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
2265
|
+
ctx.on('skills/change', () => {
|
|
2266
|
+
void refreshSkillCommands();
|
|
2267
|
+
});
|
|
2268
|
+
/** See {@link Channel.releaseContributions}. */
|
|
2269
|
+
const releaseSkillCommands = () => {
|
|
2270
|
+
if (skillCommandsRetry !== undefined)
|
|
2271
|
+
clearTimeout(skillCommandsRetry);
|
|
2272
|
+
skillCommandsRetry = undefined;
|
|
2273
|
+
for (const entry of skillCommands.values())
|
|
2274
|
+
entry.dispose();
|
|
2275
|
+
skillCommands.clear();
|
|
2276
|
+
};
|
|
2168
2277
|
refreshCommandList();
|
|
2169
2278
|
void refreshLoadedContext();
|
|
2279
|
+
void refreshSkillCommands();
|
|
2170
2280
|
let nextRowId = 0;
|
|
2171
2281
|
/** The leaf's bash executor (dsh-bash-local in the example leaf) — the DSH
|
|
2172
2282
|
* execution seam for local `!` commands and the git status breadcrumb. The
|
|
@@ -2915,6 +3025,11 @@ ${output}
|
|
|
2915
3025
|
const branch = result.stdout.text.trim();
|
|
2916
3026
|
if (branch !== '') {
|
|
2917
3027
|
state.gitBranch = branch;
|
|
3028
|
+
// Note it against the session too. A session log records no branch,
|
|
3029
|
+
// and nothing can reconstruct one after the fact, so the browser can
|
|
3030
|
+
// only show a branch for sessions this install actually used — which
|
|
3031
|
+
// is exactly what the column claims.
|
|
3032
|
+
noteBranch(agent.session.id, branch);
|
|
2918
3033
|
state.emit();
|
|
2919
3034
|
}
|
|
2920
3035
|
})
|
|
@@ -2927,7 +3042,7 @@ ${output}
|
|
|
2927
3042
|
refreshGitBranch();
|
|
2928
3043
|
return state;
|
|
2929
3044
|
}
|
|
2930
|
-
/**
|
|
3045
|
+
/** Trailing path segment (`C:/a/b` → `b`). */
|
|
2931
3046
|
function basename(path) {
|
|
2932
3047
|
const parts = path.split(/[\\/]/);
|
|
2933
3048
|
return parts[parts.length - 1] ?? path;
|
|
@@ -18,5 +18,5 @@
|
|
|
18
18
|
* registration API (issue #153).
|
|
19
19
|
* @module @deepseek-harness-tui/dsh-tui/compat
|
|
20
20
|
*/
|
|
21
|
-
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
21
|
+
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, findSessionLogFile, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
22
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,6BAA6B,EAC7B,0BAA0B,EAC1B,uBAAuB,EACvB,aAAa,GACd,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,6BAA6B,EAC7B,kBAAkB,EAClB,0BAA0B,EAC1B,uBAAuB,EACvB,aAAa,GACd,MAAM,iBAAiB,CAAA"}
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
* registration API (issue #153).
|
|
19
19
|
* @module @deepseek-harness-tui/dsh-tui/compat
|
|
20
20
|
*/
|
|
21
|
-
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
21
|
+
export { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, findSessionLogFile, LEGACY_SESSION_EVENT_TYPES, readSessionTitleFromLog, sessionsRoots, } from './sessionLog.js';
|
|
@@ -14,6 +14,14 @@ export declare const LEGACY_SESSION_EVENT_TYPES: readonly string[];
|
|
|
14
14
|
* explicit DSH_TUI_SESSION_ROOT always outranks the defaults.
|
|
15
15
|
*/
|
|
16
16
|
export declare function sessionsRoots(): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Locate a session's log by scanning workspace directories for the session
|
|
19
|
+
* id — deliberately NOT replicating the persistence plugin's workspace-key
|
|
20
|
+
* sanitization, so the helpers survive upstream key-scheme changes.
|
|
21
|
+
* @param sessionId - Session id (directory name under each workspace dir).
|
|
22
|
+
* @returns Absolute path of session.jsonl.zstd, or undefined when absent.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findSessionLogFile(sessionId: string): string | undefined;
|
|
17
25
|
/**
|
|
18
26
|
* Register every {@link LEGACY_SESSION_EVENT_TYPES} type as known in EVERY
|
|
19
27
|
* reachable KNOWN_SESSION_EVENT_TYPES copy, ahead of the strict read path
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionLog.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/sessionLog.ts"],"names":[],"mappings":"AAqDA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAwB,CAAA;AAKhF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CASxC;
|
|
1
|
+
{"version":3,"file":"sessionLog.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/compat/sessionLog.ts"],"names":[],"mappings":"AAqDA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAwB,CAAA;AAKhF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CASxC;AAaD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAexE;AA+BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAkCpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAyBzD;AAyBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,aAAa,CAwB/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAwB7E"}
|
|
@@ -88,7 +88,7 @@ function isSafeSessionId(sessionId) {
|
|
|
88
88
|
* @param sessionId - Session id (directory name under each workspace dir).
|
|
89
89
|
* @returns Absolute path of session.jsonl.zstd, or undefined when absent.
|
|
90
90
|
*/
|
|
91
|
-
function findSessionLogFile(sessionId) {
|
|
91
|
+
export function findSessionLogFile(sessionId) {
|
|
92
92
|
if (!isSafeSessionId(sessionId))
|
|
93
93
|
return undefined;
|
|
94
94
|
for (const root of sessionsRoots()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEzD,eAAO,MAAM,IAAI,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEzD,eAAO,MAAM,IAAI,YAAY,CAAA;AAU7B,eAAO,MAAM,MAAM,UAAa,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;yEAKqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;gDAC4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;gFAE4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;sBAIkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;4BAGwB;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;kFAC8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;gCAG4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA0BhC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import Schema from '@deepseek-ai/schemastery';
|
|
2
2
|
export const name = 'dsh-tui';
|
|
3
|
-
|
|
3
|
+
// `tuiWorkspaces` must stay OUT of this code-level inject (issue #183): the
|
|
4
|
+
// dsh CLI resolves the bundle's cordis.patch.yml from the FIRST copy of this
|
|
5
|
+
// package found from its own install anchor (typically the global launcher),
|
|
6
|
+
// while the Loader imports the plugin module from the profile's copy. When
|
|
7
|
+
// the two copies skew, the patch may predate the dsh-tui-workspaces row — a
|
|
8
|
+
// hard inject here then deadlocks the whole tree at boot ("pending (waiting
|
|
9
|
+
// for service: tuiWorkspaces)"). The bundle patch keeps tuiWorkspaces in the
|
|
10
|
+
// row-level inject purely as an ordering guarantee when the row exists; when
|
|
11
|
+
// it does not, plugin.ts/channel.ts fall back to a local workspace runtime.
|
|
12
|
+
export const inject = ['agents'];
|
|
4
13
|
export const Config = Schema.object({
|
|
5
14
|
sessionId: Schema.string().required(false),
|
|
6
15
|
// No schema defaults on the route: a `.default()` here would make an
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PackagedPresetResult {
|
|
2
|
+
id: string;
|
|
3
|
+
status: 'installed' | 'updated' | 'current' | 'conflict';
|
|
4
|
+
}
|
|
5
|
+
export interface PackagedPresetOptions {
|
|
6
|
+
dshHome?: string;
|
|
7
|
+
sourceRoot?: string;
|
|
8
|
+
moduleUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Resolve the package asset in both src/tsx and compiled npm layouts. */
|
|
11
|
+
export declare function packagedPresetRoot(moduleUrl?: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Materialize presets shipped by dsh-tui into the Harness user preset root.
|
|
14
|
+
*
|
|
15
|
+
* The official launcher replaces the roster's configured roots with its own
|
|
16
|
+
* shipped root at the end of profile composition, so a bundle patch cannot
|
|
17
|
+
* add a second system root. The user root is the roster's supported extension
|
|
18
|
+
* seam and is discovered on every list/resolve call. Existing unmarked
|
|
19
|
+
* directories are never overwritten.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ensurePackagedPresets(options?: PackagedPresetOptions): PackagedPresetResult[];
|
|
22
|
+
//# sourceMappingURL=packaged-presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packaged-presets.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/packaged-presets.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAA;CACzD;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAmBD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,MAAwB,GAAG,MAAM,CAQ9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,GAAG,oBAAoB,EAAE,CAkDjG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const OWNER = '@deepseek-harness-tui/dsh-tui';
|
|
7
|
+
const MARKER = '.dsh-tui-managed.json';
|
|
8
|
+
function readMarker(directory) {
|
|
9
|
+
try {
|
|
10
|
+
const value = JSON.parse(readFileSync(join(directory, MARKER), 'utf8'));
|
|
11
|
+
if (value.owner === OWNER
|
|
12
|
+
&& typeof value.preset === 'string'
|
|
13
|
+
&& typeof value.revision === 'string'
|
|
14
|
+
&& value.revision.length > 0) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// An absent or user-authored marker means this directory is not ours.
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Resolve the package asset in both src/tsx and compiled npm layouts. */
|
|
24
|
+
export function packagedPresetRoot(moduleUrl = import.meta.url) {
|
|
25
|
+
const directory = dirname(fileURLToPath(moduleUrl));
|
|
26
|
+
const candidates = [join(directory, '../../presets'), join(directory, '../../../presets')];
|
|
27
|
+
const found = candidates.find(candidate => existsSync(candidate));
|
|
28
|
+
if (found === undefined) {
|
|
29
|
+
throw new Error(`dsh-tui: packaged preset root is missing (checked ${candidates.join(', ')})`);
|
|
30
|
+
}
|
|
31
|
+
return found;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Materialize presets shipped by dsh-tui into the Harness user preset root.
|
|
35
|
+
*
|
|
36
|
+
* The official launcher replaces the roster's configured roots with its own
|
|
37
|
+
* shipped root at the end of profile composition, so a bundle patch cannot
|
|
38
|
+
* add a second system root. The user root is the roster's supported extension
|
|
39
|
+
* seam and is discovered on every list/resolve call. Existing unmarked
|
|
40
|
+
* directories are never overwritten.
|
|
41
|
+
*/
|
|
42
|
+
export function ensurePackagedPresets(options = {}) {
|
|
43
|
+
const sourceRoot = options.sourceRoot ?? packagedPresetRoot(options.moduleUrl);
|
|
44
|
+
const dshHome = options.dshHome ?? process.env.DSH_HOME ?? join(homedir(), '.dsh');
|
|
45
|
+
const targetRoot = join(dshHome, '.agent-presets');
|
|
46
|
+
const results = [];
|
|
47
|
+
for (const entry of readdirSync(sourceRoot, { withFileTypes: true })) {
|
|
48
|
+
if (!entry.isDirectory())
|
|
49
|
+
continue;
|
|
50
|
+
const id = entry.name;
|
|
51
|
+
const source = join(sourceRoot, id);
|
|
52
|
+
const sourceMarker = readMarker(source);
|
|
53
|
+
if (sourceMarker === undefined || sourceMarker.preset !== id) {
|
|
54
|
+
throw new Error(`dsh-tui: packaged preset ${id} has no valid ${MARKER}`);
|
|
55
|
+
}
|
|
56
|
+
const target = join(targetRoot, id);
|
|
57
|
+
if (!existsSync(target)) {
|
|
58
|
+
mkdirSync(targetRoot, { recursive: true });
|
|
59
|
+
cpSync(source, target, { recursive: true, force: false, errorOnExist: true });
|
|
60
|
+
results.push({ id, status: 'installed' });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const targetMarker = readMarker(target);
|
|
64
|
+
if (targetMarker === undefined || targetMarker.preset !== id) {
|
|
65
|
+
results.push({ id, status: 'conflict' });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (targetMarker.revision === sourceMarker.revision) {
|
|
69
|
+
results.push({ id, status: 'current' });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const suffix = `${process.pid}-${randomUUID()}`;
|
|
73
|
+
const staged = join(targetRoot, `.${id}.staged-${suffix}`);
|
|
74
|
+
const backup = join(targetRoot, `.${id}.backup-${suffix}`);
|
|
75
|
+
cpSync(source, staged, { recursive: true, force: false, errorOnExist: true });
|
|
76
|
+
try {
|
|
77
|
+
renameSync(target, backup);
|
|
78
|
+
renameSync(staged, target);
|
|
79
|
+
rmSync(backup, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (!existsSync(target) && existsSync(backup))
|
|
83
|
+
renameSync(backup, target);
|
|
84
|
+
rmSync(staged, { recursive: true, force: true });
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
results.push({ id, status: 'updated' });
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AA6BnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2bvE;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV"}
|