dsh-ssh-tui 0.6.4 → 0.7.0-rc.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.en.md +14 -0
- package/README.md +14 -0
- package/docs/remote-ops.md +133 -0
- package/docs/screenshots/compare.png +0 -0
- package/docs/screenshots/headless.png +0 -0
- package/docs/screenshots/workspace.png +0 -0
- package/lib/approval-cache.js +158 -0
- package/lib/approval-cache.js.map +1 -0
- package/lib/attach.js +66 -7
- package/lib/attach.js.map +1 -1
- package/lib/auto-approval.js +13 -0
- package/lib/auto-approval.js.map +1 -1
- package/lib/color-depth.js +192 -0
- package/lib/color-depth.js.map +1 -0
- package/lib/commands.js +5 -0
- package/lib/commands.js.map +1 -1
- package/lib/copy-text.js +1 -0
- package/lib/copy-text.js.map +1 -1
- package/lib/dialogs.js +81 -8
- package/lib/dialogs.js.map +1 -1
- package/lib/display-sock.js +24 -8
- package/lib/display-sock.js.map +1 -1
- package/lib/doctor.js +380 -0
- package/lib/doctor.js.map +1 -0
- package/lib/footer.js +76 -6
- package/lib/footer.js.map +1 -1
- package/lib/i18n/en.js +136 -3
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/zh.js +137 -4
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +8 -1
- package/lib/index.js.map +1 -1
- package/lib/keymap.js +172 -0
- package/lib/keymap.js.map +1 -0
- package/lib/line-diff.js +224 -0
- package/lib/line-diff.js.map +1 -0
- package/lib/line-mode.js +86 -0
- package/lib/line-mode.js.map +1 -0
- package/lib/paint.js +158 -26
- package/lib/paint.js.map +1 -1
- package/lib/plan.js +51 -8
- package/lib/plan.js.map +1 -1
- package/lib/preset-authoring.js +110 -0
- package/lib/preset-authoring.js.map +1 -0
- package/lib/preset-picker.js +105 -0
- package/lib/preset-picker.js.map +1 -0
- package/lib/preset-rows.js +288 -34
- package/lib/preset-rows.js.map +1 -1
- package/lib/selection.js +154 -0
- package/lib/selection.js.map +1 -0
- package/lib/term-text.js +106 -1
- package/lib/term-text.js.map +1 -1
- package/lib/tool-present.js +157 -21
- package/lib/tool-present.js.map +1 -1
- package/lib/tui.js +1428 -97
- package/lib/tui.js.map +1 -1
- package/lib/types/approval-cache.d.ts +125 -0
- package/lib/types/attach.d.ts +29 -0
- package/lib/types/auto-approval.d.ts +11 -0
- package/lib/types/color-depth.d.ts +50 -0
- package/lib/types/commands.d.ts +9 -0
- package/lib/types/dialogs.d.ts +36 -5
- package/lib/types/display-sock.d.ts +6 -0
- package/lib/types/doctor.d.ts +118 -0
- package/lib/types/footer.d.ts +35 -0
- package/lib/types/keymap.d.ts +68 -0
- package/lib/types/line-diff.d.ts +107 -0
- package/lib/types/line-mode.d.ts +36 -0
- package/lib/types/paint.d.ts +89 -4
- package/lib/types/plan.d.ts +7 -0
- package/lib/types/preset-authoring.d.ts +135 -0
- package/lib/types/preset-picker.d.ts +64 -0
- package/lib/types/preset-rows.d.ts +113 -9
- package/lib/types/selection.d.ts +81 -0
- package/lib/types/term-text.d.ts +29 -0
- package/lib/types/tool-present.d.ts +53 -3
- package/lib/types/transcript-types.d.ts +12 -2
- package/lib/types/tui.d.ts +231 -2
- package/package.json +33 -32
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/preset`: the terminal's authoring surface for agent presets.
|
|
3
|
+
*
|
|
4
|
+
* A preset is a directory — an id, a composition file, and optional display
|
|
5
|
+
* metadata — and the web client reaches the same four operations the plugin
|
|
6
|
+
* exposes to it: list, read, copy, delete. This module mirrors that surface and
|
|
7
|
+
* adds display-name/description edits, which are the one authoring write the
|
|
8
|
+
* upstream service does not offer; it stays pure so every refusal is testable
|
|
9
|
+
* without a host, and `tui.ts` performs the two calls that touch the disk.
|
|
10
|
+
*
|
|
11
|
+
* The rules are the upstream ones, re-stated here so a command refuses before
|
|
12
|
+
* it calls out:
|
|
13
|
+
*
|
|
14
|
+
* - only `user` presets are writable; a shipped preset is refused by the
|
|
15
|
+
* service as well (`agent-preset/read-only`);
|
|
16
|
+
* - a copy never overwrites: the destination id must be free;
|
|
17
|
+
* - ids match `/^[a-z0-9][a-z0-9-]*$/`;
|
|
18
|
+
* - deleting the preset the session is running is refused here, even though the
|
|
19
|
+
* service allows it — the running session would keep its composition while
|
|
20
|
+
* every later launch falls back to the default.
|
|
21
|
+
* @module dsh-ssh-tui/preset-authoring
|
|
22
|
+
*/
|
|
23
|
+
import { dirname } from 'node:path';
|
|
24
|
+
import { renderPresetMetadata, } from '@deepseek-ai/dsh-agent-presets';
|
|
25
|
+
/** The id shape discovery accepts; a directory that fails it is skipped. */
|
|
26
|
+
export const PRESET_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
27
|
+
/** Whether an id would be discovered at all. */
|
|
28
|
+
export function validatePresetId(id) {
|
|
29
|
+
return PRESET_ID_PATTERN.test(id);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The directory a preset owns.
|
|
33
|
+
*
|
|
34
|
+
* `AgentPreset.path` is the composition file the preset publishes, not the
|
|
35
|
+
* directory, so anything that writes beside it (display metadata) starts here.
|
|
36
|
+
* @param preset - a discovered preset.
|
|
37
|
+
* @returns the absolute preset directory.
|
|
38
|
+
*/
|
|
39
|
+
export function presetDirectory(preset) {
|
|
40
|
+
return dirname(preset.path);
|
|
41
|
+
}
|
|
42
|
+
export function isRefusal(value) {
|
|
43
|
+
return 'error' in value;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Plan a copy of an existing preset under a new id.
|
|
47
|
+
*
|
|
48
|
+
* Copy is the only way to create one (upstream has no create or rename), and it
|
|
49
|
+
* carries the whole directory — composition, display metadata, and any scripts
|
|
50
|
+
* a preset ships beside them.
|
|
51
|
+
* @param input - the roster, whether authoring is possible, and the request.
|
|
52
|
+
* @returns the call to make, or why it must be refused.
|
|
53
|
+
*/
|
|
54
|
+
export function planCopy(input) {
|
|
55
|
+
if (!input.authorable)
|
|
56
|
+
return { error: 'read-only' };
|
|
57
|
+
if (!validatePresetId(input.id))
|
|
58
|
+
return { error: 'invalid-id', id: input.id };
|
|
59
|
+
if (!input.presets.some(preset => preset.id === input.from))
|
|
60
|
+
return { error: 'not-found', id: input.from };
|
|
61
|
+
const taken = input.presets.find(preset => preset.id === input.id);
|
|
62
|
+
if (taken !== undefined)
|
|
63
|
+
return { error: 'exists', id: input.id, name: taken.name ?? taken.id };
|
|
64
|
+
return {
|
|
65
|
+
kind: 'copy',
|
|
66
|
+
from: input.from,
|
|
67
|
+
id: input.id,
|
|
68
|
+
...(input.name === undefined || input.name.trim() === '' ? {} : { name: input.name.trim() }),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Plan a display-metadata edit (name, description, or both).
|
|
73
|
+
*
|
|
74
|
+
* The current document is merged, so editing one field never drops the other,
|
|
75
|
+
* and `order` survives. A shipped preset cannot be edited: its directory is
|
|
76
|
+
* read-only by definition.
|
|
77
|
+
* @param input - the preset, its current metadata, and the fields to change.
|
|
78
|
+
* @returns the file to write, or why it must be refused.
|
|
79
|
+
*/
|
|
80
|
+
export function planMetadata(input) {
|
|
81
|
+
if (input.preset.trust !== 'user')
|
|
82
|
+
return { error: 'system', id: input.preset.id };
|
|
83
|
+
const merged = {
|
|
84
|
+
...input.current,
|
|
85
|
+
...(input.patch.name === undefined ? {} : { name: input.patch.name.trim() }),
|
|
86
|
+
...(input.patch.description === undefined ? {} : { description: input.patch.description.trim() }),
|
|
87
|
+
};
|
|
88
|
+
const text = renderPresetMetadata(merged);
|
|
89
|
+
// Upstream renders nothing when every field is empty: there is no document to
|
|
90
|
+
// publish, and silently deleting the file is not this command's business.
|
|
91
|
+
if (text === undefined)
|
|
92
|
+
return { error: 'empty-metadata', id: input.preset.id };
|
|
93
|
+
return { kind: 'metadata', id: input.preset.id, directory: presetDirectory(input.preset), text };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Plan a deletion.
|
|
97
|
+
* @param input - the preset, whether authoring is possible, and which preset
|
|
98
|
+
* this session is running.
|
|
99
|
+
* @returns the call to make, or why it must be refused.
|
|
100
|
+
*/
|
|
101
|
+
export function planDelete(input) {
|
|
102
|
+
if (!input.authorable)
|
|
103
|
+
return { error: 'read-only' };
|
|
104
|
+
if (input.preset.trust !== 'user')
|
|
105
|
+
return { error: 'system', id: input.preset.id };
|
|
106
|
+
if (input.current)
|
|
107
|
+
return { error: 'running', id: input.preset.id };
|
|
108
|
+
return { kind: 'delete', id: input.preset.id };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=preset-authoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-authoring.js","sourceRoot":"","sources":["../src/preset-authoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/mode` picker's list: which presets to show, in what order, and what to
|
|
3
|
+
* say about each one.
|
|
4
|
+
*
|
|
5
|
+
* The dialog is a flat list of options, so the grouping a browser surface can
|
|
6
|
+
* draw is carried in three cheap ways: the groups are ordered (shipped first,
|
|
7
|
+
* then authored locally), every description names its group, and a preset that
|
|
8
|
+
* declares a position shows it. Nothing here touches the filesystem or the
|
|
9
|
+
* service — the caller passes what `list()` returned.
|
|
10
|
+
* @module dsh-ssh-tui/preset-picker
|
|
11
|
+
*/
|
|
12
|
+
import { t } from './i18n/index.js';
|
|
13
|
+
import { presetLabel } from './preset-label.js';
|
|
14
|
+
/**
|
|
15
|
+
* Within a group: a declared position first, in ascending order, then the rest
|
|
16
|
+
* by id. `order` is the preset's own statement about where it belongs, so a
|
|
17
|
+
* deployment's curated sequence survives discovery.
|
|
18
|
+
*/
|
|
19
|
+
export function comparePresets(left, right) {
|
|
20
|
+
const leftOrder = left.order;
|
|
21
|
+
const rightOrder = right.order;
|
|
22
|
+
if (leftOrder !== undefined && rightOrder !== undefined && leftOrder !== rightOrder) {
|
|
23
|
+
return leftOrder - rightOrder;
|
|
24
|
+
}
|
|
25
|
+
if (leftOrder !== undefined && rightOrder === undefined)
|
|
26
|
+
return -1;
|
|
27
|
+
if (leftOrder === undefined && rightOrder !== undefined)
|
|
28
|
+
return 1;
|
|
29
|
+
return left.id.localeCompare(right.id);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* What one preset's line says underneath its name: where it sits, what it is
|
|
33
|
+
* for, and why it cannot run — in that order, because a broken preset's reason
|
|
34
|
+
* is the only thing the reader needs.
|
|
35
|
+
*/
|
|
36
|
+
export function presetOptionDescription(preset, currentId) {
|
|
37
|
+
const parts = [preset.trust === 'user' ? t('mode.groupUser') : t('mode.groupSystem')];
|
|
38
|
+
if (preset.id === currentId)
|
|
39
|
+
parts.push(t('mode.currentTag'));
|
|
40
|
+
if (preset.broken !== undefined && preset.broken !== '') {
|
|
41
|
+
parts.push(t('mode.brokenSuffix', { reason: preset.broken }));
|
|
42
|
+
}
|
|
43
|
+
else if (preset.description !== undefined && preset.description.trim() !== '') {
|
|
44
|
+
parts.push(preset.description.trim());
|
|
45
|
+
}
|
|
46
|
+
if (preset.order !== undefined)
|
|
47
|
+
parts.push(t('mode.orderTag', { order: preset.order }));
|
|
48
|
+
return parts.join(' · ');
|
|
49
|
+
}
|
|
50
|
+
/** One option's label: the name the roster publishes, never empty. */
|
|
51
|
+
export function presetOptionLabel(preset) {
|
|
52
|
+
return presetLabel(preset.id, preset.name, preset.trust);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Group and order the roster for the picker.
|
|
56
|
+
* @param presets - what the service listed.
|
|
57
|
+
* @param currentId - the preset in effect, marked in each description.
|
|
58
|
+
* @returns shipped presets first, then authored ones; empty groups are omitted.
|
|
59
|
+
*/
|
|
60
|
+
export function groupPresets(presets, currentId) {
|
|
61
|
+
const groups = [];
|
|
62
|
+
for (const trust of ['system', 'user']) {
|
|
63
|
+
const members = presets.filter(preset => preset.trust === trust).sort(comparePresets);
|
|
64
|
+
if (members.length === 0)
|
|
65
|
+
continue;
|
|
66
|
+
groups.push({
|
|
67
|
+
trust,
|
|
68
|
+
title: trust === 'user' ? t('mode.groupUser') : t('mode.groupSystem'),
|
|
69
|
+
options: members.map(preset => ({
|
|
70
|
+
id: preset.id,
|
|
71
|
+
label: presetOptionLabel(preset),
|
|
72
|
+
description: presetOptionDescription(preset, currentId),
|
|
73
|
+
...(preset.broken === undefined ? {} : { broken: preset.broken }),
|
|
74
|
+
aliases: [...new Set([
|
|
75
|
+
preset.id,
|
|
76
|
+
...(preset.name === undefined || preset.name.trim() === '' ? [] : [preset.name.trim()]),
|
|
77
|
+
presetOptionLabel(preset),
|
|
78
|
+
])],
|
|
79
|
+
})),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return groups;
|
|
83
|
+
}
|
|
84
|
+
/** The same list, flattened the way the dialog takes it. */
|
|
85
|
+
export function flattenGroups(groups) {
|
|
86
|
+
return groups.flatMap(group => group.options);
|
|
87
|
+
}
|
|
88
|
+
/** Whether a preset answers to the name a caller typed. */
|
|
89
|
+
export function optionMatches(option, query) {
|
|
90
|
+
const needle = query.trim().toLowerCase();
|
|
91
|
+
return needle !== '' && option.aliases.some(alias => alias.toLowerCase() === needle);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Narrow the roster to what a typed query matches: the id, the published name,
|
|
95
|
+
* and the description, case-insensitively. An empty query keeps everything, so
|
|
96
|
+
* the picker opens on the full roster.
|
|
97
|
+
*/
|
|
98
|
+
export function filterPresets(presets, query) {
|
|
99
|
+
const needle = query.trim().toLowerCase();
|
|
100
|
+
if (needle === '')
|
|
101
|
+
return [...presets];
|
|
102
|
+
return presets.filter(preset => [preset.id, preset.name ?? '', preset.description ?? '']
|
|
103
|
+
.some(field => field.toLowerCase().includes(needle)));
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=preset-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-picker.js","sourceRoot":"","sources":["../src/preset-picker.ts"],"names":[],"mappings":"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"}
|
package/lib/preset-rows.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The profile patch that mounts the agent-preset roster `/mode` needs
|
|
2
|
+
* The profile patch that mounts the agent-preset roster `/mode` needs, plus the
|
|
3
|
+
* repair planner `/doctor` uses to fix a profile patch.
|
|
3
4
|
*
|
|
4
5
|
* A terminal profile built on `dsh-base` composes no preset roster (only the
|
|
5
6
|
* Web bundle does), and this plugin's own bundle patch may not mount one: DSH
|
|
@@ -12,52 +13,158 @@
|
|
|
12
13
|
* The roster is not cosmetic: without it `/mode` cannot switch, and the tools
|
|
13
14
|
* the shipped presets own (`ask_user_question`, `present`, PTC's presentation)
|
|
14
15
|
* are absent from the agent's catalog.
|
|
16
|
+
*
|
|
17
|
+
* Everything in this module is either a pure text function or a file write that
|
|
18
|
+
* takes a backup first: `/doctor` may repair a user's profile patch, and that
|
|
19
|
+
* must never lose the previous content or touch a row the user wrote.
|
|
15
20
|
* @module dsh-ssh-tui/preset-rows
|
|
16
21
|
*/
|
|
17
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
22
|
+
import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
18
23
|
import { dirname, join } from 'node:path';
|
|
24
|
+
import yaml from 'js-yaml';
|
|
19
25
|
/**
|
|
20
|
-
* The
|
|
21
|
-
*
|
|
22
|
-
*
|
|
26
|
+
* The rows the terminal profile has to mount itself: the roster `/mode` lists,
|
|
27
|
+
* the TypeScript runtime the PTC preset needs, and the host-owned subagent
|
|
28
|
+
* delegation setting.
|
|
23
29
|
*/
|
|
24
|
-
export const
|
|
30
|
+
export const ROSTER_ROWS = [
|
|
31
|
+
{ id: 'agent-presets', name: '@deepseek-ai/dsh-agent-presets', config: ['default: standard'] },
|
|
32
|
+
{ id: 'code-runtime', name: '@deepseek-ai/dsh-code-runtime-worker-thread' },
|
|
33
|
+
{ id: 'subagent-model-selection-settings', name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings' },
|
|
34
|
+
];
|
|
35
|
+
/** The comment header the block introduces itself with, in both writers. */
|
|
36
|
+
export const ROSTER_PATCH_HEADER = `# dsh-ssh-tui /mode: the agent-preset roster (standard / minimal / PTC /
|
|
25
37
|
# cordis, plus every preset under $DSH_HOME/.agent-presets) and the two host
|
|
26
38
|
# services the shipped presets need. dsh-base composes no roster in a terminal
|
|
27
39
|
# profile, and a third-party bundle patch may not mount an @deepseek-ai row, so
|
|
28
40
|
# the profile's user layer owns them.
|
|
29
|
-
- insert:
|
|
30
|
-
- id: agent-presets
|
|
31
|
-
name: '@deepseek-ai/dsh-agent-presets'
|
|
32
|
-
config:
|
|
33
|
-
default: standard
|
|
34
|
-
|
|
35
|
-
- id: code-runtime
|
|
36
|
-
name: '@deepseek-ai/dsh-code-runtime-worker-thread'
|
|
37
|
-
|
|
38
|
-
- id: subagent-model-selection-settings
|
|
39
|
-
name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
|
|
40
41
|
`;
|
|
42
|
+
/** One item of an `insert:` list, indented as the block writes it. */
|
|
43
|
+
function rosterItemText(row) {
|
|
44
|
+
const lines = [` - id: ${row.id}`, ` name: '${row.name}'`];
|
|
45
|
+
if (row.config !== undefined && row.config.length > 0) {
|
|
46
|
+
lines.push(' config:');
|
|
47
|
+
for (const line of row.config)
|
|
48
|
+
lines.push(` ${line}`);
|
|
49
|
+
}
|
|
50
|
+
return `${lines.join('\n')}\n`;
|
|
51
|
+
}
|
|
52
|
+
/** One top-level `- insert:` entry mounting exactly these rows. */
|
|
53
|
+
export function rosterInsertEntry(rows) {
|
|
54
|
+
return `- insert:\n${rows.map(rosterItemText).join('\n')}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The exact profile patch block that mounts the roster and the two host
|
|
58
|
+
* services the shipped presets need. `scripts/ensure-profile-rows.sh` carries
|
|
59
|
+
* the same text; a test compares the two so they cannot drift.
|
|
60
|
+
*/
|
|
61
|
+
export const ROSTER_PATCH_BLOCK = `${ROSTER_PATCH_HEADER}${rosterInsertEntry(ROSTER_ROWS)}`;
|
|
41
62
|
/** The profile patch file the roster block belongs in. */
|
|
42
63
|
export function rosterPatchPath(home, profile) {
|
|
43
64
|
return join(home, 'profiles', profile, 'cordis.patch.yml');
|
|
44
65
|
}
|
|
66
|
+
/** Every `id:` the raw text writes, with the 1-based line it sits on, in order. */
|
|
67
|
+
function idLines(text) {
|
|
68
|
+
const out = [];
|
|
69
|
+
text.split('\n').forEach((line, index) => {
|
|
70
|
+
const match = /^\s*(?:-\s*)?id:\s*['"]?([^'"\s#]+)['"]?/u.exec(line);
|
|
71
|
+
if (match?.[1] !== undefined)
|
|
72
|
+
out.push({ id: match[1], line: index + 1 });
|
|
73
|
+
});
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
45
76
|
/**
|
|
46
|
-
*
|
|
47
|
-
* already names the roster row.
|
|
77
|
+
* Read one patch file into the rows it declares.
|
|
48
78
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* @
|
|
53
|
-
* @returns the new text, or `undefined` when nothing has to change.
|
|
79
|
+
* Unparseable YAML is reported instead of thrown: `/doctor` has to *describe*
|
|
80
|
+
* a broken patch, which is exactly when the loader would refuse to boot.
|
|
81
|
+
* @param text - the patch file's content.
|
|
82
|
+
* @returns the rows, and whether the text parsed at all.
|
|
54
83
|
*/
|
|
55
|
-
export function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
84
|
+
export function analyzePatch(text) {
|
|
85
|
+
let loaded;
|
|
86
|
+
try {
|
|
87
|
+
loaded = yaml.load(text);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return { parseable: false, error: error instanceof Error ? error.message : String(error), rows: [] };
|
|
91
|
+
}
|
|
92
|
+
const entries = Array.isArray(loaded) ? loaded : loaded === null || loaded === undefined ? [] : [loaded];
|
|
93
|
+
const lines = idLines(text);
|
|
94
|
+
const used = new Map();
|
|
95
|
+
const rows = [];
|
|
96
|
+
const lineFor = (id) => {
|
|
97
|
+
const seen = used.get(id) ?? 0;
|
|
98
|
+
used.set(id, seen + 1);
|
|
99
|
+
const matches = lines.filter(entry => entry.id === id);
|
|
100
|
+
return matches[seen]?.line ?? matches[matches.length - 1]?.line ?? 0;
|
|
101
|
+
};
|
|
102
|
+
entries.forEach((entry, entryIndex) => {
|
|
103
|
+
if (typeof entry !== 'object' || entry === null)
|
|
104
|
+
return;
|
|
105
|
+
const record = entry;
|
|
106
|
+
const insert = record.insert;
|
|
107
|
+
if (Array.isArray(insert)) {
|
|
108
|
+
insert.forEach((item, itemIndex) => {
|
|
109
|
+
if (typeof item !== 'object' || item === null)
|
|
110
|
+
return;
|
|
111
|
+
const row = item;
|
|
112
|
+
if (typeof row.id !== 'string' || row.id === '')
|
|
113
|
+
return;
|
|
114
|
+
rows.push({
|
|
115
|
+
id: row.id,
|
|
116
|
+
...(typeof row.name === 'string' && row.name !== '' ? { name: row.name } : {}),
|
|
117
|
+
kind: 'insert',
|
|
118
|
+
entry: entryIndex,
|
|
119
|
+
item: itemIndex,
|
|
120
|
+
line: lineFor(row.id),
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (typeof record.id !== 'string' || record.id === '')
|
|
126
|
+
return;
|
|
127
|
+
const kind = record.disable === true
|
|
128
|
+
? 'disable'
|
|
129
|
+
: 'override' in record || 'config' in record ? 'override' : 'other';
|
|
130
|
+
rows.push({ id: record.id, kind, entry: entryIndex, line: lineFor(record.id) });
|
|
131
|
+
});
|
|
132
|
+
return { parseable: true, rows };
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Insert rows that appear more than once, keeping the first occurrence.
|
|
136
|
+
*
|
|
137
|
+
* A row is duplicated when an earlier insert declares the same id, or mounts
|
|
138
|
+
* the same module name — the two ways a second mount reaches the loader and
|
|
139
|
+
* fails with "service … has been registered". Overrides and disables are never
|
|
140
|
+
* duplicates of an insert: they target a row instead of mounting it.
|
|
141
|
+
* @param rows - every row the patch declares.
|
|
142
|
+
* @returns the later occurrences, in file order.
|
|
143
|
+
*/
|
|
144
|
+
export function duplicatePatchRows(rows) {
|
|
145
|
+
const seenIds = new Set();
|
|
146
|
+
const seenNames = new Set();
|
|
147
|
+
const duplicates = [];
|
|
148
|
+
for (const row of rows) {
|
|
149
|
+
if (row.kind !== 'insert')
|
|
150
|
+
continue;
|
|
151
|
+
const duplicate = seenIds.has(row.id) || (row.name !== undefined && seenNames.has(row.name));
|
|
152
|
+
seenIds.add(row.id);
|
|
153
|
+
if (row.name !== undefined)
|
|
154
|
+
seenNames.add(row.name);
|
|
155
|
+
if (duplicate)
|
|
156
|
+
duplicates.push(row);
|
|
157
|
+
}
|
|
158
|
+
return duplicates;
|
|
159
|
+
}
|
|
160
|
+
/** Whether a patch already declares one roster row, by id or by module name. */
|
|
161
|
+
export function patchNamesRow(rows, row) {
|
|
162
|
+
return rows.some(candidate => (candidate.kind === 'insert' && candidate.id === row.id)
|
|
163
|
+
|| candidate.name === row.name);
|
|
164
|
+
}
|
|
165
|
+
/** The roster rows a patch does not declare yet. */
|
|
166
|
+
export function missingRosterRows(rows) {
|
|
167
|
+
return ROSTER_ROWS.filter(row => !patchNamesRow(rows, row));
|
|
61
168
|
}
|
|
62
169
|
/** The template a missing profile patch starts from. */
|
|
63
170
|
const PATCH_TEMPLATE = `# Your patch layer for this dsh profile, applied after every bundle layer:
|
|
@@ -66,6 +173,125 @@ const PATCH_TEMPLATE = `# Your patch layer for this dsh profile, applied after e
|
|
|
66
173
|
[]
|
|
67
174
|
|
|
68
175
|
`;
|
|
176
|
+
/** Text ending in exactly one newline. */
|
|
177
|
+
function normalizeEnd(text) {
|
|
178
|
+
return `${text.replace(/\n+$/u, '')}\n`;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The patch text with the given roster rows mounted, or `undefined` when the
|
|
182
|
+
* file already declares every one of them.
|
|
183
|
+
*
|
|
184
|
+
* A row is matched by id or by module name, so a profile that composes the
|
|
185
|
+
* roster through its own bundle is left alone. A file that is exactly `[]`
|
|
186
|
+
* (the profile template) is replaced; anything else keeps its content and gains
|
|
187
|
+
* a new `- insert:` entry after a blank line.
|
|
188
|
+
* @param existing - the patch file's current text.
|
|
189
|
+
* @param missing - the roster rows to mount; defaults to all of them.
|
|
190
|
+
* @returns the new text, or `undefined` when nothing has to change.
|
|
191
|
+
*/
|
|
192
|
+
export function planRosterRepair(existing, missing = ROSTER_ROWS) {
|
|
193
|
+
const analysis = analyzePatch(existing);
|
|
194
|
+
const toAdd = analysis.parseable
|
|
195
|
+
? missing.filter(row => !patchNamesRow(analysis.rows, row))
|
|
196
|
+
: [...missing];
|
|
197
|
+
if (toAdd.length === 0)
|
|
198
|
+
return undefined;
|
|
199
|
+
const entry = `${ROSTER_PATCH_HEADER}${rosterInsertEntry(toAdd)}`;
|
|
200
|
+
if (/^\s*\[\s*\]\s*$/m.test(existing)) {
|
|
201
|
+
return { text: existing.replace(/^\s*\[\s*\]\s*$/m, entry.trimEnd() + '\n'), added: toAdd.map(row => row.id), removed: [] };
|
|
202
|
+
}
|
|
203
|
+
return { text: `${normalizeEnd(existing)}\n${entry}`, added: toAdd.map(row => row.id), removed: [] };
|
|
204
|
+
}
|
|
205
|
+
/** Top-level entry ranges, each extended over directly preceding comments. */
|
|
206
|
+
function entryRanges(lines) {
|
|
207
|
+
const starts = [];
|
|
208
|
+
lines.forEach((line, index) => {
|
|
209
|
+
if (/^-\s/u.test(line) || /^-$/u.test(line))
|
|
210
|
+
starts.push(index);
|
|
211
|
+
});
|
|
212
|
+
return starts.map((start, at) => {
|
|
213
|
+
const end = starts[at + 1] ?? lines.length;
|
|
214
|
+
let head = start;
|
|
215
|
+
while (head > 0 && /^\s*#/u.test(lines[head - 1] ?? '') && !/^\s*$/u.test(lines[head - 1] ?? ''))
|
|
216
|
+
head -= 1;
|
|
217
|
+
// A comment block that also precedes an earlier entry belongs to that one.
|
|
218
|
+
if (at > 0 && head <= (starts[at - 1] ?? 0))
|
|
219
|
+
head = start;
|
|
220
|
+
return { start: head, end };
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/** Item ranges inside one entry, each extended over directly preceding comments. */
|
|
224
|
+
function itemRanges(lines, entry) {
|
|
225
|
+
const starts = [];
|
|
226
|
+
for (let index = entry.start; index < entry.end; index += 1) {
|
|
227
|
+
if (/^\s+-\s/u.test(lines[index] ?? ''))
|
|
228
|
+
starts.push(index);
|
|
229
|
+
}
|
|
230
|
+
return starts.map((start, at) => ({ start, end: starts[at + 1] ?? entry.end }));
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The patch text with every repeated insert removed, or `undefined` when the
|
|
234
|
+
* file has no duplicate.
|
|
235
|
+
*
|
|
236
|
+
* Only the later occurrence is cut, and only its own item when the enclosing
|
|
237
|
+
* `- insert:` entry mounts other rows too. Comments directly above a removed
|
|
238
|
+
* entry go with it; anything the user wrote around it stays.
|
|
239
|
+
* @param existing - the patch file's current text.
|
|
240
|
+
* @returns the new text and what was removed, or `undefined` when nothing is.
|
|
241
|
+
*/
|
|
242
|
+
export function planDuplicateRepair(existing) {
|
|
243
|
+
const analysis = analyzePatch(existing);
|
|
244
|
+
const duplicates = duplicatePatchRows(analysis.rows);
|
|
245
|
+
if (duplicates.length === 0)
|
|
246
|
+
return undefined;
|
|
247
|
+
const lines = existing.split('\n');
|
|
248
|
+
const trailing = lines[lines.length - 1] === '' ? lines.pop() === '' : false;
|
|
249
|
+
const ranges = entryRanges(lines);
|
|
250
|
+
const cuts = [];
|
|
251
|
+
const duplicateKeys = new Set(duplicates.map(row => `${row.entry}:${row.item}`));
|
|
252
|
+
for (const duplicate of duplicates) {
|
|
253
|
+
const entry = ranges[duplicate.entry];
|
|
254
|
+
if (entry === undefined || duplicate.item === undefined)
|
|
255
|
+
continue;
|
|
256
|
+
// An entry whose every item repeats an earlier one goes as a whole; one that
|
|
257
|
+
// also mounts rows the user still needs loses only the repeated items.
|
|
258
|
+
const survivors = analysis.rows.filter(row => row.kind === 'insert' && row.entry === duplicate.entry && !duplicateKeys.has(`${row.entry}:${row.item}`));
|
|
259
|
+
if (survivors.length === 0) {
|
|
260
|
+
cuts.push(entry);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const item = itemRanges(lines, entry)[duplicate.item];
|
|
264
|
+
if (item !== undefined)
|
|
265
|
+
cuts.push(item);
|
|
266
|
+
}
|
|
267
|
+
if (cuts.length === 0)
|
|
268
|
+
return undefined;
|
|
269
|
+
const sorted = [...cuts].sort((left, right) => left.start - right.start);
|
|
270
|
+
const kept = [];
|
|
271
|
+
/** Indices in `kept` where a cut ended: the only places formatting may change. */
|
|
272
|
+
const seams = [];
|
|
273
|
+
let cursor = 0;
|
|
274
|
+
for (const cut of sorted) {
|
|
275
|
+
if (cut.start < cursor)
|
|
276
|
+
continue;
|
|
277
|
+
kept.push(...lines.slice(cursor, cut.start));
|
|
278
|
+
seams.push(kept.length);
|
|
279
|
+
cursor = cut.end;
|
|
280
|
+
}
|
|
281
|
+
kept.push(...lines.slice(cursor));
|
|
282
|
+
// A cut can leave two blank runs facing each other; fold them to one, and
|
|
283
|
+
// leave every other line of the user's file exactly as it was.
|
|
284
|
+
for (const seam of seams.reverse()) {
|
|
285
|
+
while (/^\s*$/u.test(kept[seam] ?? 'x') && /^\s*$/u.test(kept[seam - 1] ?? 'x'))
|
|
286
|
+
kept.splice(seam, 1);
|
|
287
|
+
}
|
|
288
|
+
const text = `${kept.join('\n').replace(/\n+$/u, '')}\n`;
|
|
289
|
+
return text === existing ? undefined : {
|
|
290
|
+
text: trailing ? text : text.replace(/\n$/u, ''),
|
|
291
|
+
added: [],
|
|
292
|
+
removed: duplicates.map(row => ({ id: row.id, line: row.line })),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
69
295
|
/**
|
|
70
296
|
* Mount the roster in one profile, unless it is already composed.
|
|
71
297
|
*
|
|
@@ -75,9 +301,10 @@ const PATCH_TEMPLATE = `# Your patch layer for this dsh profile, applied after e
|
|
|
75
301
|
* half-written patch would only be seen by the next launch.
|
|
76
302
|
* @param home - the harness home carrying `profiles/`.
|
|
77
303
|
* @param profile - the profile to patch.
|
|
304
|
+
* @param missing - the rows to mount; defaults to the whole roster.
|
|
78
305
|
* @returns `present` when the row already exists, else `written`.
|
|
79
306
|
*/
|
|
80
|
-
export async function ensureRosterRows(home, profile) {
|
|
307
|
+
export async function ensureRosterRows(home, profile, missing = ROSTER_ROWS) {
|
|
81
308
|
const path = rosterPatchPath(home, profile);
|
|
82
309
|
let existing = PATCH_TEMPLATE;
|
|
83
310
|
try {
|
|
@@ -86,11 +313,38 @@ export async function ensureRosterRows(home, profile) {
|
|
|
86
313
|
catch {
|
|
87
314
|
// Missing file: start from the template the launcher would have written.
|
|
88
315
|
}
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
316
|
+
const repair = planRosterRepair(existing, missing);
|
|
317
|
+
if (repair === undefined)
|
|
91
318
|
return 'present';
|
|
92
319
|
await mkdir(dirname(path), { recursive: true });
|
|
93
|
-
await writeFile(path,
|
|
320
|
+
await writeFile(path, repair.text, 'utf8');
|
|
94
321
|
return 'written';
|
|
95
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Write a repaired patch next to a copy of the previous content.
|
|
325
|
+
*
|
|
326
|
+
* `/doctor` changes a file the user owns, so the old text is kept beside it
|
|
327
|
+
* before anything is replaced. A file that does not exist yet needs no backup.
|
|
328
|
+
* @param path - the patch file to replace.
|
|
329
|
+
* @param text - the new content.
|
|
330
|
+
* @param stamp - filename-safe timestamp for the backup, injectable for tests.
|
|
331
|
+
* @returns the backup path, or `undefined` when there was nothing to back up.
|
|
332
|
+
*/
|
|
333
|
+
export async function writePatchWithBackup(path, text, stamp = new Date().toISOString().replace(/[:.]/gu, '-')) {
|
|
334
|
+
let backup;
|
|
335
|
+
try {
|
|
336
|
+
backup = `${path}.bak-${stamp}`;
|
|
337
|
+
await copyFile(path, backup);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
backup = undefined;
|
|
341
|
+
}
|
|
342
|
+
await mkdir(dirname(path), { recursive: true });
|
|
343
|
+
await writeFile(path, text, 'utf8');
|
|
344
|
+
return backup;
|
|
345
|
+
}
|
|
346
|
+
/** The patch text the roster block would produce on its own, for callers that report it. */
|
|
347
|
+
export function rosterPatchText(existing) {
|
|
348
|
+
return planRosterRepair(existing)?.text;
|
|
349
|
+
}
|
|
96
350
|
//# sourceMappingURL=preset-rows.js.map
|
package/lib/preset-rows.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-rows.js","sourceRoot":"","sources":["../src/preset-rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;CAgBjC,CAAA;AAED,0DAA0D;AAC1D,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC3D,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,IAAI,2CAA2C,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAA;IAChF,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAA;IACvH,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,kBAAkB,EAAE,CAAA;AACzF,CAAC;AAED,wDAAwD;AACxD,MAAM,cAAc,GAAG;;;;;CAKtB,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAe;IAClE,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3C,IAAI,QAAQ,GAAG,cAAc,CAAA;IAC7B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IACtC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACxC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACnC,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
1
|
+
{"version":3,"file":"preset-rows.js","sourceRoot":"","sources":["../src/preset-rows.ts"],"names":[],"mappings":"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"}
|