paseo-dsh-direct 0.2.14 → 0.2.15
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/client/settings-screen.tsx +18 -19
- package/client/settings-section.tsx +22 -0
- package/client/subagent-settings.tsx +98 -63
- package/package.json +2 -1
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { useSettings } from "@getpaseo/plugin/client";
|
|
1
|
+
import { useSettings, type PluginSurfaceProps } from "@getpaseo/plugin/client";
|
|
2
2
|
import { ScrollView } from "@getpaseo/plugin/client/react-native";
|
|
3
|
-
import { SettingsAction, SettingsInput, SettingsRow
|
|
3
|
+
import { SettingsAction, SettingsInput, SettingsRow } from "@getpaseo/plugin/client/ui";
|
|
4
|
+
import SettingsSection from "./settings-section.js";
|
|
4
5
|
import { useRef } from "react";
|
|
5
6
|
import { DSH_SETTINGS } from "../shared/settings.js";
|
|
6
7
|
import SubagentSettings from "./subagent-settings.js";
|
|
7
8
|
|
|
8
|
-
const EXECUTABLE_HINT =
|
|
9
|
-
|
|
10
|
-
const PROFILE_HINT =
|
|
11
|
-
'Name of the dsh profile to launch. PASEO_DSH_PROFILE overrides this; leave it empty to use the "paseo" profile.';
|
|
9
|
+
const EXECUTABLE_HINT = "DSH command or path; environment takes priority";
|
|
10
|
+
const PROFILE_HINT = "Default: paseo, unless set by environment";
|
|
12
11
|
|
|
13
12
|
/** Edits the dsh executable and profile that the server reads when it launches a bridge. */
|
|
14
|
-
export default function DshSettingsScreen() {
|
|
13
|
+
export default function DshSettingsScreen({ theme }: PluginSurfaceProps) {
|
|
15
14
|
const settings = useSettings(DSH_SETTINGS);
|
|
16
15
|
const draftState = useRef<{ revision: string | undefined; edits: { executable?: string; profile?: string } }>({
|
|
17
16
|
revision: undefined,
|
|
@@ -21,7 +20,7 @@ export default function DshSettingsScreen() {
|
|
|
21
20
|
if (settings.status === "loading") {
|
|
22
21
|
return (
|
|
23
22
|
<ScrollView>
|
|
24
|
-
<SettingsSection title="
|
|
23
|
+
<SettingsSection title="Connection" theme={theme}>
|
|
25
24
|
<SettingsRow label="Loading settings" />
|
|
26
25
|
</SettingsSection>
|
|
27
26
|
</ScrollView>
|
|
@@ -30,9 +29,9 @@ export default function DshSettingsScreen() {
|
|
|
30
29
|
if (settings.status === "error") {
|
|
31
30
|
return (
|
|
32
31
|
<ScrollView>
|
|
33
|
-
<SettingsSection title="
|
|
32
|
+
<SettingsSection title="Connection" theme={theme}>
|
|
34
33
|
<SettingsRow label="Settings unavailable" error={settings.error} />
|
|
35
|
-
<SettingsAction label="
|
|
34
|
+
<SettingsAction label="Load settings" actionLabel="Retry" onPress={() => void settings.reload()} />
|
|
36
35
|
</SettingsSection>
|
|
37
36
|
</ScrollView>
|
|
38
37
|
);
|
|
@@ -40,9 +39,9 @@ export default function DshSettingsScreen() {
|
|
|
40
39
|
if (settings.status === "invalid") {
|
|
41
40
|
return (
|
|
42
41
|
<ScrollView>
|
|
43
|
-
<SettingsSection title="
|
|
44
|
-
<SettingsRow label="
|
|
45
|
-
<SettingsAction label="
|
|
42
|
+
<SettingsSection title="Connection" theme={theme}>
|
|
43
|
+
<SettingsRow label="Saved settings are invalid" error={settings.error} hint="Reset to use default settings" />
|
|
44
|
+
<SettingsAction label="Restore defaults" actionLabel="Reset" onPress={() => void settings.reset()} disabled={settings.saving} />
|
|
46
45
|
</SettingsSection>
|
|
47
46
|
</ScrollView>
|
|
48
47
|
);
|
|
@@ -65,10 +64,10 @@ export default function DshSettingsScreen() {
|
|
|
65
64
|
|
|
66
65
|
return (
|
|
67
66
|
<ScrollView>
|
|
68
|
-
<SettingsSection title="
|
|
67
|
+
<SettingsSection title="Connection" theme={theme}>
|
|
69
68
|
<SettingsInput
|
|
70
69
|
key={`executable-${revision}`}
|
|
71
|
-
label="
|
|
70
|
+
label="Executable"
|
|
72
71
|
hint={EXECUTABLE_HINT}
|
|
73
72
|
placeholder="dsh"
|
|
74
73
|
initialValue={values.executable ?? ""}
|
|
@@ -79,7 +78,7 @@ export default function DshSettingsScreen() {
|
|
|
79
78
|
/>
|
|
80
79
|
<SettingsInput
|
|
81
80
|
key={`profile-${revision}`}
|
|
82
|
-
label="
|
|
81
|
+
label="Profile"
|
|
83
82
|
hint={PROFILE_HINT}
|
|
84
83
|
placeholder="paseo"
|
|
85
84
|
initialValue={values.profile ?? ""}
|
|
@@ -89,15 +88,15 @@ export default function DshSettingsScreen() {
|
|
|
89
88
|
disabled={settings.saving}
|
|
90
89
|
/>
|
|
91
90
|
<SettingsAction
|
|
92
|
-
label="
|
|
93
|
-
hint=
|
|
91
|
+
label="Save settings"
|
|
92
|
+
hint="Applies to new sessions"
|
|
94
93
|
error={settings.saveError}
|
|
95
94
|
actionLabel={settings.saving ? "Saving" : "Save"}
|
|
96
95
|
onPress={save}
|
|
97
96
|
disabled={settings.saving}
|
|
98
97
|
/>
|
|
99
98
|
</SettingsSection>
|
|
100
|
-
<SubagentSettings launchRevision={revision} launchSaving={settings.saving} />
|
|
99
|
+
<SubagentSettings theme={theme} launchRevision={revision} launchSaving={settings.saving} />
|
|
101
100
|
</ScrollView>
|
|
102
101
|
);
|
|
103
102
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PluginSurfaceProps } from "@getpaseo/plugin/client";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { Text, View } from "react-native";
|
|
4
|
+
|
|
5
|
+
/** Keep section titles more prominent than field labels in every host theme. */
|
|
6
|
+
export default function SettingsSection({ title, theme, children }: {
|
|
7
|
+
title: string;
|
|
8
|
+
theme: PluginSurfaceProps["theme"];
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}) {
|
|
11
|
+
return (
|
|
12
|
+
<View style={{ marginBottom: 24 }}>
|
|
13
|
+
<Text
|
|
14
|
+
accessibilityRole="header"
|
|
15
|
+
style={{ color: theme.colors.foreground, fontSize: 18, fontWeight: "600", marginHorizontal: 16, marginBottom: 8 }}
|
|
16
|
+
>
|
|
17
|
+
{title}
|
|
18
|
+
</Text>
|
|
19
|
+
<View>{children}</View>
|
|
20
|
+
</View>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useRpc } from "@getpaseo/plugin/client";
|
|
2
|
-
import { SettingsAction, SettingsInput, SettingsRow,
|
|
1
|
+
import { useRpc, type PluginSurfaceProps } from "@getpaseo/plugin/client";
|
|
2
|
+
import { Modal, SettingsAction, SettingsInput, SettingsRow, SettingsSwitch } from "@getpaseo/plugin/client/ui";
|
|
3
3
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { useState } from "react";
|
|
5
|
+
import SettingsSection from "./settings-section.js";
|
|
5
6
|
import {
|
|
6
7
|
readSubagentSettings,
|
|
7
8
|
saveSubagentSettings,
|
|
@@ -30,7 +31,8 @@ function errorMessage(error: unknown): string {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
/** Keeps edits tied to the exact profile and revision returned by the daemon. */
|
|
33
|
-
export default function SubagentSettings({ launchRevision, launchSaving }: {
|
|
34
|
+
export default function SubagentSettings({ theme, launchRevision, launchSaving }: {
|
|
35
|
+
theme: PluginSurfaceProps["theme"];
|
|
34
36
|
launchRevision: string;
|
|
35
37
|
launchSaving: boolean;
|
|
36
38
|
}) {
|
|
@@ -47,6 +49,7 @@ export default function SubagentSettings({ launchRevision, launchSaving }: {
|
|
|
47
49
|
});
|
|
48
50
|
const [draft, setDraft] = useState<Draft | null>(null);
|
|
49
51
|
const [search, setSearch] = useState("");
|
|
52
|
+
const [editorOpen, setEditorOpen] = useState(false);
|
|
50
53
|
const dirty = draft !== null && !sameSelection(draft.selection, draft.view.selection);
|
|
51
54
|
// A background read or a saved launch-setting change must never replace pending edits.
|
|
52
55
|
const activeDraft = dirty ? draft : null;
|
|
@@ -88,13 +91,12 @@ export default function SubagentSettings({ launchRevision, launchSaving }: {
|
|
|
88
91
|
|
|
89
92
|
if (!view || !selection) {
|
|
90
93
|
return (
|
|
91
|
-
<SettingsSection title="
|
|
94
|
+
<SettingsSection theme={theme} title="Subagents">
|
|
92
95
|
<SettingsRow
|
|
93
96
|
label={query.isError ? "Could not load subagent models" : "Loading subagent models"}
|
|
94
97
|
error={query.isError ? errorMessage(query.error) : undefined}
|
|
95
|
-
hint="Models and saved choices come from the active dsh profile."
|
|
96
98
|
/>
|
|
97
|
-
{query.isError && <SettingsAction label="Load models
|
|
99
|
+
{query.isError && <SettingsAction label="Load models" actionLabel="Retry" onPress={refresh} disabled={busy} />}
|
|
98
100
|
</SettingsSection>
|
|
99
101
|
);
|
|
100
102
|
}
|
|
@@ -134,70 +136,66 @@ export default function SubagentSettings({ launchRevision, launchSaving }: {
|
|
|
134
136
|
edit({ ...selection, allowedModels: [...routes.values()] });
|
|
135
137
|
};
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
// Older clients expose the same UI module without Modal; keep their inline editor.
|
|
140
|
+
const hasModal = typeof Modal !== "undefined" && Modal.Content != null;
|
|
141
|
+
const feedback = (
|
|
138
142
|
<>
|
|
139
|
-
|
|
143
|
+
{conflict && (
|
|
144
|
+
<SettingsRow label="Settings changed elsewhere" hint="Discard your edits and reload the saved settings" />
|
|
145
|
+
)}
|
|
146
|
+
{query.isError && (
|
|
147
|
+
<SettingsRow label="Could not refresh models" error={errorMessage(query.error)} hint={dirty ? "Your edits are kept; discard and refresh to retry" : "The previous list is shown; refresh to retry"} />
|
|
148
|
+
)}
|
|
149
|
+
{launchChanged && (
|
|
150
|
+
<SettingsRow label="Connection settings changed" error="These edits belong to the previous connection. Discard and refresh before saving." />
|
|
151
|
+
)}
|
|
152
|
+
{!view.supported && (
|
|
153
|
+
<SettingsRow
|
|
154
|
+
label="Model selection unavailable"
|
|
155
|
+
hint={view.reason ?? "Choose a profile that supports model selection, then refresh."}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
{view.failedProviders.length > 0 && (
|
|
140
159
|
<SettingsRow
|
|
141
|
-
label=
|
|
142
|
-
hint="
|
|
160
|
+
label="Some providers could not load models"
|
|
161
|
+
hint={`${view.failedProviders.join(", ")} · Saved choices kept; refresh to retry`}
|
|
143
162
|
/>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
)}
|
|
163
|
+
)}
|
|
164
|
+
{view.supported && !view.initialized && (
|
|
165
|
+
<SettingsRow label="No saved selection" hint="Choose models and save, or check your providers if the list is empty" />
|
|
166
|
+
)}
|
|
167
|
+
{missingCount > 0 && (
|
|
168
|
+
<SettingsRow label={`${missingCount} selected ${missingCount === 1 ? "model is" : "models are"} missing from this catalog`} hint="Saved choices are kept unless you remove them" />
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
172
|
+
const editor = (
|
|
173
|
+
<>
|
|
174
|
+
<SettingsSection theme={theme} title="Selection">
|
|
175
|
+
<SettingsRow
|
|
176
|
+
label={`${selection.allowedModels.length} selected · ${view.models.length} available · Profile: ${view.profile}`}
|
|
177
|
+
hint="Closing keeps your unsaved changes"
|
|
178
|
+
error={emptyEnabled ? "Select at least one model, or turn off Use selected models." : undefined}
|
|
179
|
+
/>
|
|
180
|
+
{feedback}
|
|
181
|
+
</SettingsSection>
|
|
182
|
+
<SettingsSection theme={theme} title="Model selection">
|
|
165
183
|
<SettingsSwitch
|
|
166
|
-
label="
|
|
167
|
-
hint="
|
|
184
|
+
label="Use selected models"
|
|
185
|
+
hint="Turn off to use the profile’s default behavior"
|
|
168
186
|
value={selection.enabled}
|
|
169
187
|
onValueChange={(enabled) => edit({ ...selection, enabled })}
|
|
170
188
|
disabled={busy || !view.supported}
|
|
171
189
|
/>
|
|
172
|
-
<
|
|
173
|
-
label={`${selection.allowedModels.length} selected · ${view.models.length} available`}
|
|
174
|
-
hint={missingCount > 0 ? `${missingCount} selected model(s) are not in the current catalog. You can keep or remove them below.` : "New models are never added to your saved selection automatically."}
|
|
175
|
-
error={emptyEnabled ? "Select at least one model, or turn off model selection before saving." : undefined}
|
|
176
|
-
/>
|
|
177
|
-
<SettingsInput label="Search models" placeholder="Name, model ID, or provider" initialValue={search} onChangeText={setSearch} />
|
|
178
|
-
<SettingsAction
|
|
179
|
-
label="Select all available models"
|
|
180
|
-
hint="Includes all providers, even models hidden by search. Keeps saved models that are currently unavailable."
|
|
181
|
-
actionLabel="Select all"
|
|
182
|
-
onPress={selectAvailable}
|
|
183
|
-
disabled={busy || !view.supported || view.models.length === 0 || view.models.every((model) => selectedKeys.has(subagentRouteKey(model)))}
|
|
184
|
-
/>
|
|
185
|
-
<SettingsAction label="Clear model selection" hint="Clears every selection, including models hidden by search." actionLabel="Clear" onPress={() => edit({ ...selection, allowedModels: [] })} disabled={busy || !view.supported || selectedKeys.size === 0} />
|
|
186
|
-
<SettingsAction
|
|
187
|
-
label={query.isFetching ? "Refreshing models" : "Refresh models"}
|
|
188
|
-
hint={dirty ? "Save or discard your changes before refreshing." : "Reload the catalog and saved selection for the active profile."}
|
|
189
|
-
actionLabel={query.isError ? "Retry" : "Refresh"}
|
|
190
|
-
onPress={refresh}
|
|
191
|
-
disabled={busy || dirty}
|
|
192
|
-
/>
|
|
190
|
+
<SettingsInput label="Search" placeholder="Name, ID or provider" initialValue={search} onChangeText={setSearch} />
|
|
193
191
|
</SettingsSection>
|
|
194
192
|
{[...groups].sort(([left], [right]) => left.localeCompare(right)).map(([provider, group]) => (
|
|
195
|
-
<SettingsSection key={provider} title={`${group.name} (${group.rows.filter((model) => selectedKeys.has(subagentRouteKey(model))).length}/${group.rows.length}${term ? " matching" : ""})`}>
|
|
193
|
+
<SettingsSection theme={theme} key={provider} title={`${group.name} (${group.rows.filter((model) => selectedKeys.has(subagentRouteKey(model))).length}/${group.rows.length}${term ? " matching" : ""})`}>
|
|
196
194
|
{group.rows.sort((left, right) => left.name.localeCompare(right.name) || left.model.localeCompare(right.model)).map((model) => (
|
|
197
195
|
<SettingsSwitch
|
|
198
196
|
key={subagentRouteKey(model)}
|
|
199
197
|
label={model.name}
|
|
200
|
-
hint={
|
|
198
|
+
hint={model.name === model.model ? (model.available ? undefined : "Not in this catalog") : `${model.model}${model.available ? "" : " · Not in this catalog"}`}
|
|
201
199
|
value={selectedKeys.has(subagentRouteKey(model))}
|
|
202
200
|
onValueChange={(checked) => toggle(model, checked)}
|
|
203
201
|
disabled={busy || !view.supported}
|
|
@@ -205,15 +203,32 @@ export default function SubagentSettings({ launchRevision, launchSaving }: {
|
|
|
205
203
|
))}
|
|
206
204
|
</SettingsSection>
|
|
207
205
|
))}
|
|
208
|
-
{groups.size === 0 && (
|
|
209
|
-
<SettingsSection title="Models">
|
|
210
|
-
<SettingsRow label={term ? "No models
|
|
206
|
+
{view.supported && groups.size === 0 && (
|
|
207
|
+
<SettingsSection theme={theme} title="Models">
|
|
208
|
+
<SettingsRow label={term ? "No matching models" : "No models available"} hint={term ? "Try a different name, ID or provider" : "Check your providers, then refresh"} />
|
|
211
209
|
</SettingsSection>
|
|
212
210
|
)}
|
|
213
|
-
<SettingsSection title="
|
|
211
|
+
<SettingsSection theme={theme} title="All models">
|
|
212
|
+
<SettingsAction
|
|
213
|
+
label="Available models"
|
|
214
|
+
hint="Includes models hidden by your search"
|
|
215
|
+
actionLabel="Select all"
|
|
216
|
+
onPress={selectAvailable}
|
|
217
|
+
disabled={busy || !view.supported || view.models.length === 0 || view.models.every((model) => selectedKeys.has(subagentRouteKey(model)))}
|
|
218
|
+
/>
|
|
219
|
+
<SettingsAction label="Selected models" hint="Clears every choice, including models not shown" actionLabel="Clear" onPress={() => edit({ ...selection, allowedModels: [] })} disabled={busy || !view.supported || selectedKeys.size === 0} />
|
|
220
|
+
<SettingsAction
|
|
221
|
+
label={query.isFetching ? "Refreshing models" : "Refresh models"}
|
|
222
|
+
hint={dirty ? "Save or discard edits first" : undefined}
|
|
223
|
+
actionLabel={query.isError ? "Retry" : "Refresh"}
|
|
224
|
+
onPress={refresh}
|
|
225
|
+
disabled={busy || dirty}
|
|
226
|
+
/>
|
|
227
|
+
</SettingsSection>
|
|
228
|
+
<SettingsSection theme={theme} title="Changes">
|
|
214
229
|
<SettingsAction
|
|
215
|
-
label={save.isSuccess ? "
|
|
216
|
-
hint=
|
|
230
|
+
label={save.isSuccess ? "Settings saved" : "Save selection"}
|
|
231
|
+
hint="Applies to new sessions"
|
|
217
232
|
error={saveError}
|
|
218
233
|
actionLabel={save.isPending ? "Saving" : "Save"}
|
|
219
234
|
onPress={() => {
|
|
@@ -222,8 +237,28 @@ export default function SubagentSettings({ launchRevision, launchSaving }: {
|
|
|
222
237
|
}}
|
|
223
238
|
disabled={!dirty || busy || emptyEnabled || conflict || launchChanged || !view.supported || query.isError}
|
|
224
239
|
/>
|
|
225
|
-
<SettingsAction label="
|
|
240
|
+
<SettingsAction label="Reload saved settings" hint="Discards your unsaved changes" actionLabel="Discard and refresh" onPress={discardAndRefresh} disabled={busy || (!dirty && !save.isError)} />
|
|
241
|
+
</SettingsSection>
|
|
242
|
+
</>
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<>
|
|
247
|
+
<SettingsSection theme={theme} title="Subagents">
|
|
248
|
+
<SettingsAction
|
|
249
|
+
label={`Model selection: ${selection.enabled ? "On" : "Off"}${save.isPending ? " · Saving…" : dirty ? " · Unsaved changes" : ""}`}
|
|
250
|
+
hint={`${selection.allowedModels.length} selected · ${view.models.length} available`}
|
|
251
|
+
actionLabel={!hasModal && editorOpen ? "Close editor" : "Edit models"}
|
|
252
|
+
onPress={() => setEditorOpen((value) => hasModal || !value)}
|
|
253
|
+
error={emptyEnabled ? "Select at least one model, or turn off Use selected models." : saveError}
|
|
254
|
+
/>
|
|
255
|
+
{feedback}
|
|
226
256
|
</SettingsSection>
|
|
257
|
+
{hasModal ? (
|
|
258
|
+
<Modal title="Subagent models" open={editorOpen} onOpenChange={setEditorOpen}>
|
|
259
|
+
<Modal.Content>{editor}</Modal.Content>
|
|
260
|
+
</Modal>
|
|
261
|
+
) : editorOpen ? editor : null}
|
|
227
262
|
</>
|
|
228
263
|
);
|
|
229
264
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paseo-dsh-direct",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "DeepSeek Harness Direct Provider Plugin for Paseo",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"mdast-util-from-markdown": "^2.0.3",
|
|
68
68
|
"mdast-util-gfm": "^3.1.0",
|
|
69
69
|
"micromark-extension-gfm": "^3.0.0",
|
|
70
|
+
"react-native": "0.81.5",
|
|
70
71
|
"tsx": "^4.22.4",
|
|
71
72
|
"typescript": "^5.9.3",
|
|
72
73
|
"zod": "^4.4.3"
|