@khorsheed/dsh-local-agent-kimi 0.2.0-rc.1 → 0.2.0
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/CHANGELOG.md +26 -0
- package/README.en.md +24 -112
- package/README.i18n.yaml +2 -2
- package/README.md +24 -112
- package/lib/client.js +816 -324
- package/lib/index.js +2121 -1311
- package/lib/types/client/SettingsCard.d.ts +75 -24
- package/lib/types/client/SettingsCard.js +110 -137
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/client/index.d.ts +6 -25
- package/lib/types/client/index.js +134 -51
- package/lib/types/client/locales.d.ts +34 -24
- package/lib/types/client/locales.js +38 -28
- package/lib/types/endpoint-auth.d.ts +202 -0
- package/lib/types/endpoint-auth.js +287 -0
- package/lib/types/index.d.ts +19 -2
- package/lib/types/index.js +146 -19
- package/lib/types/kimi-cli-provider.d.ts +31 -4
- package/lib/types/kimi-cli-provider.js +110 -60
- package/lib/types/live-driver.d.ts +17 -5
- package/lib/types/live-driver.js +345 -129
- package/lib/types/live-switch.d.ts +16 -1
- package/lib/types/live-switch.js +11 -1
- package/lib/types/model-broker.d.ts +12 -2
- package/lib/types/model-broker.js +51 -6
- package/lib/types/model-catalog.d.ts +44 -1
- package/lib/types/model-catalog.js +110 -17
- package/lib/types/model-configuration.d.ts +26 -0
- package/lib/types/model-configuration.js +43 -0
- package/lib/types/provision.d.ts +102 -31
- package/lib/types/provision.js +257 -45
- package/lib/types/records.d.ts +0 -20
- package/lib/types/records.js +1 -40
- package/lib/types/session-mirror.d.ts +115 -52
- package/lib/types/session-mirror.js +286 -210
- package/lib/types/session-view.d.ts +23 -2
- package/lib/types/session-view.js +37 -7
- package/locale/en.json +2 -2
- package/locale/zh.json +2 -2
- package/package.json +21 -22
- package/lib/invariant.js +0 -31
- package/lib/types/credential-guard.d.ts +0 -29
- package/lib/types/credential-guard.js +0 -107
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -33
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type { HarnessModelPickerInput } from '@khorsheed/dsh-local-agent/client';
|
|
2
|
-
import type { ReactNode } from 'react';
|
|
3
1
|
import type { SettingsScope } from '@khorsheed/dsh-local-agent/src/client/settings-scope.ts';
|
|
4
2
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
5
|
-
import type {
|
|
3
|
+
import type { LocalAgentEndpointAuthFields, LocalAgentEndpointAuthState, LocalAgentModelDirectory } from '@khorsheed/dsh-local-agent/types';
|
|
6
4
|
import { type ProviderAuthBlockProps, type ProviderAuthInjected } from '@khorsheed/dsh-local-agent/src/client/ProviderAuthBlock.tsx';
|
|
7
5
|
import { NS } from './locales.ts';
|
|
8
6
|
/** The `local-agent-kimi` settings namespace section the card edits. */
|
|
@@ -17,12 +15,11 @@ export interface KimiLiveSettings {
|
|
|
17
15
|
* exactly as before the key existed.
|
|
18
16
|
*/
|
|
19
17
|
model?: string;
|
|
20
|
-
/** Model identifiers saved before; the
|
|
18
|
+
/** Model identifiers saved before; the member picker's suggestion vocabulary, capped at five. */
|
|
21
19
|
recentModels?: readonly string[];
|
|
22
20
|
}
|
|
23
21
|
/** Injected face of the kimi settings card. */
|
|
24
22
|
export interface KimiSettingsCardInjected {
|
|
25
|
-
renderModelPicker?: ((props: HarnessModelPickerInput) => ReactNode) | undefined;
|
|
26
23
|
/** Bound settings scope for the local-agent-kimi namespace. */
|
|
27
24
|
scope: SettingsScope<KimiLiveSettings>;
|
|
28
25
|
/** The auth block's query/command faces, backed by the family core Remote. */
|
|
@@ -30,33 +27,77 @@ export interface KimiSettingsCardInjected {
|
|
|
30
27
|
/** Translate bound to the family core's `local-agent` namespace (the auth block's copy lives there). */
|
|
31
28
|
authT: ProviderAuthBlockProps['t'];
|
|
32
29
|
/**
|
|
33
|
-
* The harness's
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
30
|
+
* The harness's curated model list (the family gateway's
|
|
31
|
+
* `modelList`/`configureModelList` Remotes): the editor's ordered
|
|
32
|
+
* shortlist — its first entry is the default model. Absent on a core that
|
|
33
|
+
* predates the Remotes — the card then hides the editor.
|
|
37
34
|
*/
|
|
38
|
-
|
|
35
|
+
modelList?: {
|
|
36
|
+
read(): Promise<readonly string[] | null | undefined>;
|
|
37
|
+
write(models: readonly string[]): Promise<readonly string[] | null | undefined>;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The endpoint's served model catalog (the family gateway's
|
|
41
|
+
* `modelDirectory` Remote): the add dialog's candidate vocabulary. Absent
|
|
42
|
+
* on a core that predates the Remote — the dialog then shows its reason.
|
|
43
|
+
*/
|
|
44
|
+
modelDirectory?: (refresh?: boolean) => Promise<LocalAgentModelDirectory | null | undefined>;
|
|
45
|
+
/**
|
|
46
|
+
* The harness's endpoint verification (the family gateway's `checkEndpoint`
|
|
47
|
+
* Remote): a token-free probe of the SAVED endpoint, answered as verdict
|
|
48
|
+
* text. Absent on a core that predates the Remote — the card then hides
|
|
49
|
+
* the verify button.
|
|
50
|
+
*/
|
|
51
|
+
checkEndpoint?: () => Promise<string | null | undefined>;
|
|
52
|
+
/**
|
|
53
|
+
* The harness's endpoint credential face (the family gateway's
|
|
54
|
+
* `endpointAuth`/`configureEndpoint` Remotes): the card's credential form
|
|
55
|
+
* reads and writes the harness's OWN credential store through it — never
|
|
56
|
+
* the settings scope, so no key ever lands in the profile patch document.
|
|
57
|
+
* Absent on a core that predates the Remotes — the card then hides the
|
|
58
|
+
* form. `write` answers the post-write resolved state.
|
|
59
|
+
*/
|
|
60
|
+
endpointAuth?: {
|
|
61
|
+
read(): Promise<LocalAgentEndpointAuthState | null | undefined>;
|
|
62
|
+
write(fields: LocalAgentEndpointAuthFields): Promise<LocalAgentEndpointAuthState | null | undefined>;
|
|
63
|
+
};
|
|
39
64
|
hooks: {
|
|
40
65
|
/** The same scope as a hooks source, so the card reacts to external writes. */
|
|
41
66
|
settings: SettingsScope<KimiLiveSettings>;
|
|
42
67
|
};
|
|
43
68
|
}
|
|
44
69
|
/**
|
|
45
|
-
* Full props of the 0.1.5 settings.plugin.item card
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
70
|
+
* Full props of the retired 0.1.5 settings.plugin.item card. The host removed
|
|
71
|
+
* that seat and client/index.ts no longer registers the component — the live
|
|
72
|
+
* settings surface is the plugins.item entry built on the BundleConfig
|
|
73
|
+
* component below; this export remains for the component spec and direct
|
|
74
|
+
* composers. The removed slot's owner share was intentionally empty and its
|
|
75
|
+
* root scope delivered the global seats; the structural type below is exact
|
|
76
|
+
* about the one seat the auth block consumes.
|
|
50
77
|
*/
|
|
51
78
|
export type KimiSettingsCardProps = InjectFace<KimiSettingsCardInjected> & PropsLocale<typeof NS> & {
|
|
52
79
|
/** The standing root-scope global seat (ui-session's merge), delivered to the 0.1.5 card by the renderer. */
|
|
53
80
|
useSessions: ProviderAuthBlockProps['useSessions'];
|
|
54
81
|
};
|
|
55
|
-
/** Full props of the
|
|
56
|
-
export type KimiBundleConfigProps = PropsRuntime<'plugins.
|
|
82
|
+
/** Full props of the plugins.item entry (owner prop `view`). */
|
|
83
|
+
export type KimiBundleConfigProps = PropsRuntime<'plugins.item'> & InjectFace<KimiSettingsCardInjected> & PropsLocale<typeof NS>;
|
|
84
|
+
/**
|
|
85
|
+
* Full props of the harness's section inside the core's「本机 Agent 委派」
|
|
86
|
+
* family card: the same inject face minus the slot-owner share, plus the one
|
|
87
|
+
* global seat the body consumes, forwarded by the family card (the section
|
|
88
|
+
* render bypasses the slot runtime, so its remaining standard seats are not
|
|
89
|
+
* fabricable here and are deliberately absent).
|
|
90
|
+
*/
|
|
91
|
+
export type KimiCardSectionProps = InjectFace<KimiSettingsCardInjected> & PropsLocale<typeof NS> & {
|
|
92
|
+
/** Whether the section is currently expanded (the family card mounts the body only while it is). */
|
|
93
|
+
readonly active: boolean;
|
|
94
|
+
/** The standing root-scope global seat (ui-session's merge), forwarded by the family card. */
|
|
95
|
+
readonly useSessions: ProviderAuthBlockProps['useSessions'];
|
|
96
|
+
};
|
|
57
97
|
/**
|
|
58
|
-
* The kimi harness settings card
|
|
59
|
-
*
|
|
98
|
+
* The kimi harness settings card of the retired 0.1.5 plugin configuration
|
|
99
|
+
* tab (no longer registered; the live surface is the plugins.item entry
|
|
100
|
+
* below): the same collapsible chrome the official plugin cards use, re-implemented
|
|
60
101
|
* locally (the bundle-purity gate forbids value-importing the official chrome
|
|
61
102
|
* — the context-guard pattern), with the at-a-glance credential dot in the
|
|
62
103
|
* header. Explanation copy lives in hover/focus bubbles behind ⓘ anchors —
|
|
@@ -66,12 +107,22 @@ export type KimiBundleConfigProps = PropsRuntime<'plugins.bundle.config'> & Inje
|
|
|
66
107
|
*/
|
|
67
108
|
export declare function KimiSettingsCard(props: KimiSettingsCardProps): import("react").JSX.Element;
|
|
68
109
|
/**
|
|
69
|
-
* The
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
110
|
+
* The provider's card on the Plugins page (the `plugins.item` entry
|
|
111
|
+
* client/index.ts registers while the Host serves the namespace AND the core
|
|
112
|
+
* predates the family card): the page
|
|
113
|
+
* draws the title, the icon, and the crumb itself, so the entry renders the
|
|
114
|
+
* one-liner for the `summary` view and the bare form — with its own save
|
|
115
|
+
* control — for `page`.
|
|
73
116
|
* @param props - the owner view, the injected scope/auth faces, and copy.
|
|
74
117
|
* @returns the entry.
|
|
75
118
|
*/
|
|
76
|
-
export declare function KimiBundleConfig({ view, useSettings, scope, auth, authT,
|
|
119
|
+
export declare function KimiBundleConfig({ view, useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t }: KimiBundleConfigProps): import("react").JSX.Element;
|
|
120
|
+
/**
|
|
121
|
+
* The harness's section inside the core's「本机 Agent 委派」family card: the
|
|
122
|
+
* same useCardState + CardBody the plugins.item page view renders — the
|
|
123
|
+
* collapsible chrome belongs to the family card now.
|
|
124
|
+
* @param props - the injected scope/auth faces, the forwarded session seat, and copy.
|
|
125
|
+
* @returns the section body.
|
|
126
|
+
*/
|
|
127
|
+
export declare function KimiCardSection({ active, useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t }: KimiCardSectionProps): import("react").JSX.Element;
|
|
77
128
|
//# sourceMappingURL=SettingsCard.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect,
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { IconChevronDownOutlineMedium } from './icons.js';
|
|
5
5
|
// The shared auth block is bundled from the family core's source — the
|
|
@@ -7,157 +7,119 @@ import { IconChevronDownOutlineMedium } from './icons.js';
|
|
|
7
7
|
// the block carries no runtime identity to share.
|
|
8
8
|
import { ProviderAuthBlock, } from '@khorsheed/dsh-local-agent/src/client/ProviderAuthBlock.tsx';
|
|
9
9
|
import { AuthStatusDot } from '@khorsheed/dsh-local-agent/src/client/AuthStatusDot.tsx';
|
|
10
|
-
import { useHarnessAuthStatus } from '@khorsheed/dsh-local-agent/src/client/auth-status.ts';
|
|
10
|
+
import { publishAuthStatus, useHarnessAuthStatus } from '@khorsheed/dsh-local-agent/src/client/auth-status.ts';
|
|
11
|
+
import { ModelListSection } from '@khorsheed/dsh-local-agent/src/client/ModelListSection.tsx';
|
|
11
12
|
import css from './SettingsCard.module.css';
|
|
12
|
-
/** How many previously-saved model identifiers the input suggests. */
|
|
13
|
-
const RECENT_MODEL_LIMIT = 5;
|
|
14
13
|
/**
|
|
15
|
-
* The staged card state both settings surfaces share: the resident-mode
|
|
16
|
-
*
|
|
14
|
+
* The staged card state both settings surfaces share: the resident-mode
|
|
15
|
+
* switch writes through the bound settingsScope immediately
|
|
17
16
|
* (revision-fenced), so a flip takes effect on the next delegation round
|
|
18
|
-
* without a reload; the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @param active - whether the surface
|
|
17
|
+
* without a reload; the endpoint credential form talks to the harness's own
|
|
18
|
+
* store through the endpointAuth face. The「可用模型」section is the family
|
|
19
|
+
* core's shared ModelListSection, which owns its own state.
|
|
20
|
+
* @param active - whether the surface currently shows the body (gates the credential-form fetch).
|
|
22
21
|
* @param useSettings - the bound settings hook from the inject hooks compartment.
|
|
23
22
|
* @param scope - the bound settings scope the writes go through.
|
|
24
|
-
* @param
|
|
25
|
-
* @param t - copy.
|
|
23
|
+
* @param endpointAuth - the harness's endpoint credential face; absent hides the credential form.
|
|
26
24
|
*/
|
|
27
|
-
function useCardState(active, useSettings, scope,
|
|
25
|
+
function useCardState(active, useSettings, scope, endpointAuth) {
|
|
28
26
|
const snapshot = useSettings(value => value);
|
|
29
27
|
const [saved, setSaved] = useState(false);
|
|
30
28
|
const [error, setError] = useState(false);
|
|
31
29
|
const ready = snapshot.status === 'ready' && snapshot.writable;
|
|
32
30
|
const live = snapshot.value?.live ?? false;
|
|
33
|
-
// The
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const [
|
|
40
|
-
const [
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
31
|
+
// The endpoint credential form talks to the harness's OWN credential store
|
|
32
|
+
// through the endpointAuth face — never the settings scope, so no key ever
|
|
33
|
+
// lands in the profile patch document. baseUrl echoes (it is not a
|
|
34
|
+
// secret); the key never does — a set key shows as a "configured"
|
|
35
|
+
// placeholder and the input holds only the draft of a REPLACEMENT. A blank
|
|
36
|
+
// write clears the field (the config layer then answers for it again).
|
|
37
|
+
const [endpointState, setEndpointState] = useState(null);
|
|
38
|
+
const [baseUrlDraft, setBaseUrlDraft] = useState(null);
|
|
39
|
+
const [apiKeyDraft, setApiKeyDraft] = useState(null);
|
|
40
|
+
const [endpointSaved, setEndpointSaved] = useState(false);
|
|
41
|
+
const [endpointError, setEndpointError] = useState(false);
|
|
42
|
+
const endpointDirty = baseUrlDraft !== null || apiKeyDraft !== null;
|
|
43
|
+
const endpointUnavailable = endpointAuth === undefined || endpointState === 'unavailable';
|
|
44
|
+
const endpointLoading = !endpointUnavailable && endpointState === null;
|
|
45
|
+
const apiKeyConfigured = endpointState !== null && endpointState !== 'unavailable' && endpointState.apiKeySet;
|
|
46
46
|
useEffect(() => {
|
|
47
|
-
if (!active)
|
|
47
|
+
if (!active || endpointAuth === undefined)
|
|
48
48
|
return;
|
|
49
49
|
let stale = false;
|
|
50
|
-
void
|
|
51
|
-
|
|
50
|
+
void endpointAuth.read().then((state) => { if (!stale)
|
|
51
|
+
setEndpointState(state ?? 'unavailable'); }, () => { if (!stale)
|
|
52
|
+
setEndpointState('unavailable'); });
|
|
52
53
|
return () => { stale = true; };
|
|
53
|
-
}, [active,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
}, [active, endpointAuth, endpointSaved]);
|
|
55
|
+
// The credential save: one `configureEndpoint` call carrying only the
|
|
56
|
+
// dirty fields (a blank draft clears its field); the answer is the
|
|
57
|
+
// post-write resolved state, so the chips and the auth probe flip without
|
|
58
|
+
// a re-read.
|
|
59
|
+
const saveEndpoint = () => {
|
|
60
|
+
if (endpointAuth === undefined)
|
|
61
|
+
return;
|
|
62
|
+
setEndpointSaved(false);
|
|
63
|
+
setEndpointError(false);
|
|
64
|
+
const fields = {
|
|
65
|
+
...baseUrlDraft !== null ? { baseUrl: baseUrlDraft.trim() === '' ? null : baseUrlDraft.trim() } : {},
|
|
66
|
+
...apiKeyDraft !== null ? { apiKey: apiKeyDraft.trim() === '' ? null : apiKeyDraft.trim() } : {},
|
|
67
|
+
};
|
|
68
|
+
void endpointAuth.write(fields).then((state) => {
|
|
69
|
+
if (state !== undefined && state !== null) {
|
|
70
|
+
setEndpointState(state);
|
|
71
|
+
// Flip the status dot with the write's own answer — only in the
|
|
72
|
+
// authenticated direction.
|
|
73
|
+
if (state.apiKeySet)
|
|
74
|
+
publishAuthStatus('kimi', 'authenticated');
|
|
69
75
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
setBaseUrlDraft(null);
|
|
77
|
+
setApiKeyDraft(null);
|
|
78
|
+
setEndpointSaved(true);
|
|
79
|
+
}, () => { setEndpointError(true); });
|
|
73
80
|
};
|
|
74
81
|
const write = (field, value) => {
|
|
75
82
|
setSaved(false);
|
|
76
83
|
setError(false);
|
|
77
84
|
void scope.set(field, value).then(() => { setSaved(true); }, () => { setError(true); });
|
|
78
85
|
};
|
|
79
|
-
// The suggestion vocabulary: the broker's deduped union when the surface
|
|
80
|
-
// answered, the card's own recent-models memory when it did not. Never a
|
|
81
|
-
// hardcoded catalog. A non-empty list turns the field into a select-like
|
|
82
|
-
// control: the always-visible chevron opens the same vocabulary as a menu.
|
|
83
|
-
const choices = modelInfo?.choices ?? recentModels;
|
|
84
|
-
const [modelMenuOpen, setModelMenuOpen] = useState(false);
|
|
85
|
-
const [modelMenuUp, setModelMenuUp] = useState(false);
|
|
86
|
-
const modelFieldRef = useRef(null);
|
|
87
|
-
// Click-outside and Esc close the model menu (the member composer's picker
|
|
88
|
-
// pattern); the upward flip is measured when the menu opens.
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
if (!modelMenuOpen)
|
|
91
|
-
return;
|
|
92
|
-
const onPointerDown = (event) => {
|
|
93
|
-
if (modelFieldRef.current?.contains(event.target) === true)
|
|
94
|
-
return;
|
|
95
|
-
setModelMenuOpen(false);
|
|
96
|
-
};
|
|
97
|
-
const onKeyDown = (event) => {
|
|
98
|
-
if (event.key === 'Escape')
|
|
99
|
-
setModelMenuOpen(false);
|
|
100
|
-
};
|
|
101
|
-
document.addEventListener('mousedown', onPointerDown);
|
|
102
|
-
document.addEventListener('keydown', onKeyDown);
|
|
103
|
-
return () => {
|
|
104
|
-
document.removeEventListener('mousedown', onPointerDown);
|
|
105
|
-
document.removeEventListener('keydown', onKeyDown);
|
|
106
|
-
};
|
|
107
|
-
}, [modelMenuOpen]);
|
|
108
|
-
const toggleModelMenu = () => {
|
|
109
|
-
if (!modelMenuOpen) {
|
|
110
|
-
const rect = modelFieldRef.current?.getBoundingClientRect();
|
|
111
|
-
if (rect !== undefined) {
|
|
112
|
-
const spaceBelow = window.innerHeight - rect.bottom;
|
|
113
|
-
setModelMenuUp(spaceBelow < 260 && rect.top > spaceBelow);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
setModelMenuOpen(open => !open);
|
|
117
|
-
};
|
|
118
|
-
// An unset field DISPLAYS the default it follows (dimmed, as the input's
|
|
119
|
-
// placeholder — inherited, never pinned), down the chain the broker
|
|
120
|
-
// reports: the scoped config's model, then a cli-builtin source that still
|
|
121
|
-
// names its default (a harness catalog's isDefault entry), then the last
|
|
122
|
-
// observed model the records know, and last the generic copy.
|
|
123
|
-
const inheritedModel = modelInfo?.cliDefault !== undefined && modelInfo.cliDefault !== ''
|
|
124
|
-
? modelInfo.cliDefault
|
|
125
|
-
: modelInfo?.source === 'cli-builtin' && modelInfo.effective !== undefined && modelInfo.effective !== ''
|
|
126
|
-
? modelInfo.effective
|
|
127
|
-
: modelInfo?.lastObserved !== undefined && modelInfo.lastObserved !== ''
|
|
128
|
-
? modelInfo.lastObserved
|
|
129
|
-
: undefined;
|
|
130
|
-
const modelPlaceholder = inheritedModel ?? t('model.placeholder');
|
|
131
|
-
// The menu's leading item: the same chain, spelled as the follow-default
|
|
132
|
-
// choice. It reads checked while the field is unset, and picking it clears
|
|
133
|
-
// the draft back to follow-default (a save then unsets the key).
|
|
134
|
-
const modelDefaultItem = modelInfo?.cliDefault !== undefined && modelInfo.cliDefault !== ''
|
|
135
|
-
? t('model.menuDefault.cliConfig', { model: modelInfo.cliDefault })
|
|
136
|
-
: modelInfo?.source === 'cli-builtin' && modelInfo.effective !== undefined && modelInfo.effective !== ''
|
|
137
|
-
? t('model.menuDefault.cliBuiltin', { model: modelInfo.effective })
|
|
138
|
-
: modelInfo?.lastObserved !== undefined && modelInfo.lastObserved !== ''
|
|
139
|
-
? t('model.menuDefault.lastObserved', { model: modelInfo.lastObserved })
|
|
140
|
-
: t('model.menuDefault');
|
|
141
86
|
return {
|
|
142
|
-
snapshot, saved, error, ready, live,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
87
|
+
snapshot, saved, error, ready, live, write,
|
|
88
|
+
endpointUnavailable, endpointLoading,
|
|
89
|
+
baseUrlValue: baseUrlDraft ?? (endpointState !== null && endpointState !== 'unavailable' ? endpointState.baseUrl ?? '' : ''),
|
|
90
|
+
apiKeyConfigured, apiKeyDraft,
|
|
91
|
+
endpointSaved, endpointError, endpointDirty,
|
|
92
|
+
saveEndpoint, setBaseUrlDraft, setApiKeyDraft,
|
|
146
93
|
};
|
|
147
94
|
}
|
|
148
|
-
/** The card body both settings surfaces share: the auth block, the
|
|
149
|
-
function CardBody({ state, auth, authT,
|
|
150
|
-
const { snapshot, saved, error, ready, live,
|
|
151
|
-
// The
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
const
|
|
156
|
-
|
|
95
|
+
/** The card body both settings surfaces share: the auth block, the model-list section, and the resident-mode block. */
|
|
96
|
+
function CardBody({ state, active, auth, authT, modelList, modelDirectory, checkEndpoint, useSessions, t }) {
|
|
97
|
+
const { snapshot, saved, error, ready, live, write, endpointUnavailable, endpointLoading, baseUrlValue, apiKeyConfigured, apiKeyDraft, endpointSaved, endpointError, endpointDirty, saveEndpoint, setBaseUrlDraft, setApiKeyDraft, } = state;
|
|
98
|
+
// The verify button: runs the gateway's `checkEndpoint` Remote against the
|
|
99
|
+
// SAVED endpoint (unsaved drafts deliberately disable it — what it verifies
|
|
100
|
+
// is the saved configuration) and shows the verdict inline. Session-less:
|
|
101
|
+
// a settings card may have no main agent session to run a slash command.
|
|
102
|
+
const [checking, setChecking] = useState(false);
|
|
103
|
+
const [checkText, setCheckText] = useState(null);
|
|
104
|
+
const runCheck = () => {
|
|
105
|
+
if (checkEndpoint === undefined)
|
|
106
|
+
return;
|
|
107
|
+
setChecking(true);
|
|
108
|
+
setCheckText(null);
|
|
109
|
+
void checkEndpoint().then((text) => {
|
|
110
|
+
setCheckText(text ?? t('endpoint.checkUnavailable'));
|
|
111
|
+
setChecking(false);
|
|
112
|
+
}, () => {
|
|
113
|
+
setCheckText(t('endpoint.checkFailed'));
|
|
114
|
+
setChecking(false);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
return (_jsxs("div", { className: css.body, children: [_jsxs("section", { className: css.block, children: [_jsxs("h3", { className: css.blockTitle, children: [t('auth.title'), _jsx(Tooltip, { label: t('endpoint.info'), side: "bottom", maxWidth: 360, children: _jsx("button", { type: "button", className: css.info, "aria-label": t('endpoint.info.aria'), children: "\u24D8" }) })] }), _jsx(ProviderAuthBlock, { harness: { id: 'kimi', label: 'Kimi Code' }, useSessions: useSessions, status: auth.status, runCommand: auth.runCommand, t: authT }), endpointUnavailable && (_jsx("span", { className: css.hint, children: t('endpoint.unavailable') })), !endpointUnavailable && (_jsxs("div", { className: css.endpointFields, children: [_jsxs("label", { className: css.endpointField, children: [_jsx("span", { className: css.endpointLabel, children: t('endpoint.baseUrl') }), _jsx("input", { type: "text", className: css.modelInput, value: baseUrlValue, placeholder: t('endpoint.baseUrl.placeholder'), "aria-label": t('endpoint.baseUrl'), disabled: endpointLoading, onChange: (event) => { setBaseUrlDraft(event.currentTarget.value); } })] }), _jsxs("label", { className: css.endpointField, children: [_jsxs("span", { className: css.endpointLabel, children: [t('endpoint.apiKey'), apiKeyConfigured && _jsx("span", { className: css.configuredChip, children: t('endpoint.configured') })] }), _jsx("input", { type: "password", className: css.modelInput, value: apiKeyDraft ?? '', placeholder: apiKeyConfigured ? t('endpoint.secret.configured') : t('endpoint.secret.placeholder'), "aria-label": t('endpoint.apiKey'), autoComplete: "off", disabled: endpointLoading, onChange: (event) => { setApiKeyDraft(event.currentTarget.value); } })] }), _jsxs("div", { className: css.row, children: [_jsx("button", { type: "button", className: css.modelSave, disabled: endpointLoading || !endpointDirty, onClick: () => { saveEndpoint(); }, children: t('endpoint.save') }), checkEndpoint !== undefined && (_jsx("button", { type: "button", className: css.modelSave, disabled: endpointLoading || endpointDirty || checking, title: endpointDirty ? t('endpoint.checkDirtyHint') : undefined, onClick: () => { runCheck(); }, children: checking ? t('endpoint.checking') : t('endpoint.check') })), endpointSaved && _jsx("span", { className: css.saved, children: t('endpoint.applied') }), endpointError && _jsx("span", { className: css.errorText, children: t('endpoint.error') })] }), checkText !== null && _jsx("span", { className: css.hint, children: checkText })] }))] }), _jsx(ModelListSection, { active: active, modelList: modelList, modelDirectory: modelDirectory, directoryRefresh: endpointSaved, t: authT }), _jsxs("section", { className: css.block, children: [_jsxs("div", { className: css.row, children: [_jsxs("span", { className: css.rowLabel, children: [t('live.title'), _jsx(Tooltip, { label: t('live.info'), side: "bottom", maxWidth: 360, children: _jsx("button", { type: "button", className: css.info, "aria-label": t('live.info.aria'), children: "\u24D8" }) })] }), _jsx("button", { type: "button", role: "switch", "aria-checked": live, "aria-label": t('live.title'), className: live ? `${css.switch} ${css.switchOn}` : css.switch, disabled: !ready, onClick: () => { write('live', !live); }, children: _jsx("span", { className: css.knob }) })] }), saved && _jsx("span", { className: css.saved, children: t('live.applied') }), error && _jsx("span", { className: css.errorText, children: t('live.error') }), snapshot.status === 'unavailable' && (_jsx("span", { className: css.hint, children: t('live.unavailable') }))] })] }));
|
|
157
118
|
}
|
|
158
119
|
/**
|
|
159
|
-
* The kimi harness settings card
|
|
160
|
-
*
|
|
120
|
+
* The kimi harness settings card of the retired 0.1.5 plugin configuration
|
|
121
|
+
* tab (no longer registered; the live surface is the plugins.item entry
|
|
122
|
+
* below): the same collapsible chrome the official plugin cards use, re-implemented
|
|
161
123
|
* locally (the bundle-purity gate forbids value-importing the official chrome
|
|
162
124
|
* — the context-guard pattern), with the at-a-glance credential dot in the
|
|
163
125
|
* header. Explanation copy lives in hover/focus bubbles behind ⓘ anchors —
|
|
@@ -166,11 +128,9 @@ function CardBody({ state, auth, authT, renderModelPicker, useSessions, t }) {
|
|
|
166
128
|
* @returns the card.
|
|
167
129
|
*/
|
|
168
130
|
export function KimiSettingsCard(props) {
|
|
169
|
-
const { useSettings, scope, auth, authT,
|
|
131
|
+
const { useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t } = props;
|
|
170
132
|
const [open, setOpen] = useState(false);
|
|
171
|
-
|
|
172
|
-
// vocabulary while the card is collapsed), so the 0.1.5 card always reads.
|
|
173
|
-
const state = useCardState(true, useSettings, scope, harnessModel, t);
|
|
133
|
+
const state = useCardState(open, useSettings, scope, endpointAuth);
|
|
174
134
|
// The at-a-glance credential dot in the collapsed header: every mount
|
|
175
135
|
// re-probes (a credential expiring mid-session must flip the dot at the
|
|
176
136
|
// next view); the bus dedupes identical results, so no re-render storm.
|
|
@@ -179,19 +139,32 @@ export function KimiSettingsCard(props) {
|
|
|
179
139
|
: authStatus === 'anonymous' ? 'settings.notAuthenticated'
|
|
180
140
|
: authStatus === 'checking' ? 'loading' : 'error');
|
|
181
141
|
const title = t('card.title');
|
|
182
|
-
return (_jsxs("li", { className: open ? `${css.card} ${css.cardOpen}` : css.card, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${t(open ? 'card.collapse' : 'card.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsxs("span", { className: css.nameRow, children: [_jsx("span", { className: css.name, children: title }), _jsx(AuthStatusDot, { status: authStatus, label: dotLabel })] }), _jsx("span", { className: css.description, children: t('card.description') })] }), _jsx(IconChevronDownOutlineMedium, { className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron })] }), open && _jsx(CardBody, { state: state, auth: auth, authT: authT,
|
|
142
|
+
return (_jsxs("li", { className: open ? `${css.card} ${css.cardOpen}` : css.card, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${t(open ? 'card.collapse' : 'card.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsxs("span", { className: css.nameRow, children: [_jsx("span", { className: css.name, children: title }), _jsx(AuthStatusDot, { status: authStatus, label: dotLabel })] }), _jsx("span", { className: css.description, children: t('card.description') })] }), _jsx(IconChevronDownOutlineMedium, { className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron })] }), open && _jsx(CardBody, { state: state, active: open, auth: auth, authT: authT, modelList: modelList, modelDirectory: modelDirectory, checkEndpoint: checkEndpoint, useSessions: useSessions, t: t })] }));
|
|
183
143
|
}
|
|
184
144
|
/**
|
|
185
|
-
* The
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
145
|
+
* The provider's card on the Plugins page (the `plugins.item` entry
|
|
146
|
+
* client/index.ts registers while the Host serves the namespace AND the core
|
|
147
|
+
* predates the family card): the page
|
|
148
|
+
* draws the title, the icon, and the crumb itself, so the entry renders the
|
|
149
|
+
* one-liner for the `summary` view and the bare form — with its own save
|
|
150
|
+
* control — for `page`.
|
|
189
151
|
* @param props - the owner view, the injected scope/auth faces, and copy.
|
|
190
152
|
* @returns the entry.
|
|
191
153
|
*/
|
|
192
|
-
export function KimiBundleConfig({ view, useSettings, scope, auth, authT,
|
|
193
|
-
const state = useCardState(view === 'page', useSettings, scope,
|
|
154
|
+
export function KimiBundleConfig({ view, useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t }) {
|
|
155
|
+
const state = useCardState(view === 'page', useSettings, scope, endpointAuth);
|
|
194
156
|
if (view === 'summary')
|
|
195
157
|
return _jsx("span", { className: css.description, children: t('card.description') });
|
|
196
|
-
return _jsx(CardBody, { state: state, auth: auth, authT: authT,
|
|
158
|
+
return _jsx(CardBody, { state: state, active: view === 'page', auth: auth, authT: authT, modelList: modelList, modelDirectory: modelDirectory, checkEndpoint: checkEndpoint, useSessions: useSessions, t: t });
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The harness's section inside the core's「本机 Agent 委派」family card: the
|
|
162
|
+
* same useCardState + CardBody the plugins.item page view renders — the
|
|
163
|
+
* collapsible chrome belongs to the family card now.
|
|
164
|
+
* @param props - the injected scope/auth faces, the forwarded session seat, and copy.
|
|
165
|
+
* @returns the section body.
|
|
166
|
+
*/
|
|
167
|
+
export function KimiCardSection({ active, useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t }) {
|
|
168
|
+
const state = useCardState(active, useSettings, scope, endpointAuth);
|
|
169
|
+
return _jsx(CardBody, { state: state, active: active, auth: auth, authT: authT, modelList: modelList, modelDirectory: modelDirectory, checkEndpoint: checkEndpoint, useSessions: useSessions, t: t });
|
|
197
170
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generated by scripts/sync-icon-artwork.mts from @deepseek-ai/dsh-client-ui-primitives
|
|
3
|
-
* (v0.1
|
|
3
|
+
* (v0.2.1-rc.1, harness 1600d8a1e1) — do not edit by hand. The 0.1.5 and rc.1 host lines
|
|
4
4
|
* share no icon export name, so the rc.1 artwork is inlined here: pure SVG,
|
|
5
5
|
* no host runtime identity, identical on every line. Re-run the generator
|
|
6
6
|
* after adding an icon import or bumping the harness checkout.
|
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local-agent-kimi plugin, browser half: the kimi settings surface, one face
|
|
3
|
-
* per install shape and host line — a standalone install renders the
|
|
4
|
-
* bundle's own configuration on its Plugins-page detail view
|
|
5
|
-
* (`plugins.bundle.config`, keyed by package name), a family-bundle install
|
|
6
|
-
* renders the same card through the row-level configure entry on the
|
|
7
|
-
* BUNDLE's detail view (`plugins.row.config`, keyed
|
|
8
|
-
* `@khorsheed/dsh-bundle-local-agent#local-agent-kimi`), and 0.1.5 renders
|
|
9
|
-
* the `settings.plugin.item` card (keyed to the `local-agent-kimi`
|
|
10
|
-
* settings namespace the host half registers) in the official
|
|
11
|
-
* Plugins → 可配置插件 tab. The card carries the family core's shared
|
|
12
|
-
* ProviderAuthBlock (login/logout/device-code, backed by the core's
|
|
13
|
-
* localAgentGateway Remote and the commands channel), the default-model block
|
|
14
|
-
* (free-text field, the broker-fed effective-model line and choices), and the
|
|
15
|
-
* resident-mode switch, whose writes ride the bound settingsScope — the host
|
|
16
|
-
* watcher hot-applies them, no reload. Every read degrades: an absent gateway
|
|
17
|
-
* renders the auth block's 'unavailable' state and drops the effective-model
|
|
18
|
-
* line, an unregistered namespace disables the live controls.
|
|
19
|
-
* @module @khorsheed/dsh-local-agent-kimi/client
|
|
20
|
-
*/
|
|
21
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
22
2
|
import { en, NS, zh, type LocalAgentKimiKey } from './locales.ts';
|
|
23
|
-
export { KimiBundleConfig, KimiSettingsCard } from './SettingsCard.tsx';
|
|
24
|
-
export type { KimiLiveSettings, KimiBundleConfigProps, KimiSettingsCardInjected, KimiSettingsCardProps, } from './SettingsCard.tsx';
|
|
3
|
+
export { KimiBundleConfig, KimiCardSection, KimiSettingsCard } from './SettingsCard.tsx';
|
|
4
|
+
export type { KimiLiveSettings, KimiBundleConfigProps, KimiCardSectionProps, KimiSettingsCardInjected, KimiSettingsCardProps, } from './SettingsCard.tsx';
|
|
25
5
|
export { en, NS, zh };
|
|
26
6
|
export type { LocalAgentKimiKey };
|
|
27
7
|
/**
|
|
@@ -30,12 +10,13 @@ export type { LocalAgentKimiKey };
|
|
|
30
10
|
* line (`settingsScope` on 0.1.5, `configForms` on rc.1), so a static entry
|
|
31
11
|
* would pend the whole client plugin on the other line; bindSettingsScope
|
|
32
12
|
* probes the serving one instead (the settings client package is a declared
|
|
33
|
-
* client inject, so one of them is always already up).
|
|
13
|
+
* client inject, so one of them is always already up). The card track probes
|
|
14
|
+
* `configForms` through the same kind of deferred inject (see apply).
|
|
34
15
|
*/
|
|
35
16
|
export declare const inject: string[];
|
|
36
17
|
/**
|
|
37
|
-
* Client plugin body: register the dictionaries and the kimi settings
|
|
38
|
-
*
|
|
18
|
+
* Client plugin body: register the dictionaries and the kimi settings card
|
|
19
|
+
* bound to the local-agent-kimi namespace.
|
|
39
20
|
* @param ctx - client root context.
|
|
40
21
|
*/
|
|
41
22
|
export declare function apply(ctx: Context): void;
|