@ran-sh/dsh-crew 1.5.0 → 1.6.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/.claude-plugin/plugin.json +1 -1
- package/lib/client.js +2 -905
- package/official-web-bridge/lib/client.js +1 -71
- package/package.json +1 -1
- package/src/client/collapsible-sections.mjs +5 -7
- package/src/client/index.tsx +3 -357
- package/src/client/quick-entry.tsx +1 -1
- package/src/client/quick-panel.tsx +3 -38
- package/src/client/surface-detection.mjs +1 -1
- package/src/hub/entry.mjs +6 -0
- package/src/hub/index.mjs +1 -76
- package/src/i18n.mjs +1 -1
- package/src/install/install-legacy.mjs +35 -43
- package/src/install/npx-lifecycle.mjs +16 -2
- package/src/policy-legacy.mjs +1 -51
- package/src/provider-inventory.mjs +1 -3
- package/src/release-in-use.mjs +110 -0
- package/src/runtime-controls.mjs +1 -7
- package/src/runtime-identity.mjs +1 -1
- package/src/server.mjs +1 -2
- package/src/image-output.mjs +0 -71
- package/src/multimodal.mjs +0 -0
- package/src/vision-route.mjs +0 -138
package/lib/client.js
CHANGED
|
@@ -37,9 +37,7 @@ const SETTINGS_SECTION_IDS = Object.freeze([
|
|
|
37
37
|
"dispatch",
|
|
38
38
|
"adaptive",
|
|
39
39
|
"runtime",
|
|
40
|
-
"multimodal",
|
|
41
40
|
"harnessProviders",
|
|
42
|
-
"providers",
|
|
43
41
|
"jobs"
|
|
44
42
|
]);
|
|
45
43
|
function createDefaultSectionState() {
|
|
@@ -415,7 +413,7 @@ function classifyCrewSurface({ bridgeStatus, runtime } = {}) {
|
|
|
415
413
|
/**
|
|
416
414
|
* Surface capability model. The NATIVE 3210 Crew harness is the single full
|
|
417
415
|
* control plane; the OFFICIAL 3080 surface is a narrow quick-controls panel
|
|
418
|
-
* (total switch
|
|
416
|
+
* (total switch and the flash/pro model priority lists) plus a
|
|
419
417
|
* deep link back to 3210. Unknown surfaces get diagnostics only — never write
|
|
420
418
|
* authority. This replaces the old binary full-vs-minimal assumption.
|
|
421
419
|
*/
|
|
@@ -1180,9 +1178,7 @@ const COPY = {
|
|
|
1180
1178
|
schedule: "波峰波谷调度",
|
|
1181
1179
|
adaptive: "自适应路由",
|
|
1182
1180
|
runtime: "运行 / 生效边界",
|
|
1183
|
-
multimodal: "视觉与生图",
|
|
1184
1181
|
harnessProviders: "Harness Providers",
|
|
1185
|
-
providers: "多模态适配器",
|
|
1186
1182
|
jobs: "任务状态"
|
|
1187
1183
|
},
|
|
1188
1184
|
harnessHint: "DeepSeek 官方 Harness 的 dsh-crew profile:Provider、Harness Models 与运行时配置",
|
|
@@ -1275,12 +1271,6 @@ const COPY = {
|
|
|
1275
1271
|
proReviewsFlash: "Pro 复审 Flash 改动",
|
|
1276
1272
|
proReviewsFlashHint: "Flash 成功后自动追加一次 Pro 复审(仅 blocking 派发)",
|
|
1277
1273
|
proReviewsLocked: "Review Pipeline 模式固定开启",
|
|
1278
|
-
enableCrewVision: "启用 Crew 视觉",
|
|
1279
|
-
enableCrewVisionHint: "关闭后不注册 Crew 的 describe_image 与视觉 route(可与你的 dsh-vision 等共存)。",
|
|
1280
|
-
enableImagegen: "启用生图",
|
|
1281
|
-
enableImagegenHint: "关闭后不注册 Crew 的 generate_image。",
|
|
1282
|
-
capRestartHint: "该开关改变工具注册,需重启 DSH 生效",
|
|
1283
|
-
capDisabledNote: "Crew 工具/route 已禁用(重启 DSH 后生效)",
|
|
1284
1274
|
tier: "默认档位",
|
|
1285
1275
|
effort: "默认推理",
|
|
1286
1276
|
mode: "执行模式",
|
|
@@ -1296,10 +1286,6 @@ const COPY = {
|
|
|
1296
1286
|
escalateHint: "flash 失败自动用 pro 重试一次",
|
|
1297
1287
|
presetFlash: "flash 模式",
|
|
1298
1288
|
presetPro: "pro 模式",
|
|
1299
|
-
multimodal: "多模态",
|
|
1300
|
-
visionProvider: "视觉 provider",
|
|
1301
|
-
visionModel: "视觉模型",
|
|
1302
|
-
imagegenProvider: "生图 provider",
|
|
1303
1289
|
harnessProviders: "Harness Providers",
|
|
1304
1290
|
harnessProviderHint: "DeepSeek 官方 Harness 的 3210 dsh-crew profile 中的真实 Provider 注册与生命周期;不是视觉 / 生图适配器。",
|
|
1305
1291
|
providerState: (state) => `状态:${state}`,
|
|
@@ -1340,48 +1326,11 @@ const COPY = {
|
|
|
1340
1326
|
credentialPurging: "清理中…",
|
|
1341
1327
|
credentialPurgeUnavailable: "凭据清理不可用",
|
|
1342
1328
|
credentialPurgeUnverified: "已执行清理,但验证失败;引用仍保留用于恢复检查",
|
|
1343
|
-
customProviders: "多模态适配器",
|
|
1344
|
-
addProvider: "+ 添加多模态适配器",
|
|
1345
|
-
noCustomProviders: "暂无。添加后会出现在上方的视觉 / 生图 provider 选择里。",
|
|
1346
|
-
providerName: "名称",
|
|
1347
|
-
modelsField: "模型列表(逗号分隔)",
|
|
1348
|
-
providerType: "接入方式",
|
|
1349
|
-
typeApi: "API · 兼容 OpenAI 接口",
|
|
1350
|
-
typeCli: "CLI · 本地命令",
|
|
1351
|
-
baseUrl: "Base URL",
|
|
1352
|
-
apiKey: "API Key",
|
|
1353
|
-
imagegenModel: "生图模型(留空=不提供生图)",
|
|
1354
|
-
visionCmd: "视觉命令(可选)",
|
|
1355
|
-
imagegenCmd: "生图命令(可选)",
|
|
1356
|
-
apiFormHint: "走 OpenAI 兼容接口:视觉调 {base}/chat/completions(图片以 base64 内联),生图调 {base}/images/generations。Key 保存在本机 ~/.config/dsh-crew/config.json,不会外发到第三方。",
|
|
1357
|
-
cliFormHint: "命令经 bash 执行,占位符会被安全引用后代入。视觉: {image} {question} {model},stdout 即答案;生图: {prompt} {output} {size},命令须把图片写到 {output}。两条命令至少填一条。",
|
|
1358
|
-
saveProvider: "保存",
|
|
1359
|
-
cancel: "取消",
|
|
1360
|
-
edit: "编辑",
|
|
1361
|
-
del: "删除",
|
|
1362
|
-
testProvider: "连通测试",
|
|
1363
|
-
testing: "测试中…",
|
|
1364
|
-
testTip: "按当前填写的内容实测:API 会检查可达性与鉴权并真发一次视觉请求;CLI 会检查可执行文件并真跑一次视觉命令。生图只校验配置,不实际出图。",
|
|
1365
|
-
testPass: "连通正常",
|
|
1366
|
-
testFail: "连通失败",
|
|
1367
1329
|
staleHub: "本 DSH 实例还在跑旧版插件,缺少该接口 —— 重启 DSH 后再试",
|
|
1368
1330
|
emptyResponse: (path, status) => `${path} → HTTP ${status},响应为空`,
|
|
1369
1331
|
badJson: (path, body) => `${path} → 非 JSON 响应: ${body}`,
|
|
1370
|
-
refreshModels: "重新从 CLI 获取模型列表",
|
|
1371
1332
|
presetDefault: (id) => `default · 跟随 DSH${id ? ` (${id})` : ""}`,
|
|
1372
1333
|
progressTip: (t, turn, step, calls) => `耗时 ${t} · 第${turn}轮第${step}步 · ${calls} 次工具调用`,
|
|
1373
|
-
confirmDeleteProvider: (n) => `删除自定义 provider「${n}」?`,
|
|
1374
|
-
needName: "⚠ 名称必填",
|
|
1375
|
-
needCmd: "⚠ 视觉/生图命令至少填一条",
|
|
1376
|
-
needBaseUrl: "⚠ Base URL 必填",
|
|
1377
|
-
needModels: "⚠ 模型列表至少填一个",
|
|
1378
|
-
keySet: "已配置",
|
|
1379
|
-
keyPlaceholder: "sk-…(留空则不发送 Authorization)",
|
|
1380
|
-
addModelTip: "添加模型到列表",
|
|
1381
|
-
removeModelTip: "从列表移除当前模型",
|
|
1382
|
-
modelPlaceholder: "模型 id,回车确认",
|
|
1383
|
-
capVision: "视觉",
|
|
1384
|
-
capImagegen: "生图",
|
|
1385
1334
|
groupDispatch: "派发",
|
|
1386
1335
|
groupRuntime: "运行",
|
|
1387
1336
|
groupMultimodal: "多模态",
|
|
@@ -1427,14 +1376,6 @@ const COPY = {
|
|
|
1427
1376
|
t: "执行与连接",
|
|
1428
1377
|
d: "worker 会话跑在本实例内还是独立进程,单个任务的超时上限,以及 CC / Codex 探测本实例的地址。"
|
|
1429
1378
|
},
|
|
1430
|
-
cardMM: {
|
|
1431
|
-
t: "视觉与生图",
|
|
1432
|
-
d: "给纯文本的 DSH 模型借来眼睛和画笔:describe_image、会话贴图转写与 generate_image 都用这里的设置。"
|
|
1433
|
-
},
|
|
1434
|
-
cardCustomProv: {
|
|
1435
|
-
t: "自定义 Provider",
|
|
1436
|
-
d: "接入自己的 API 或本地命令;保存后会出现在上面的视觉 / 生图 provider 选择里。"
|
|
1437
|
-
},
|
|
1438
1379
|
modeDesc: {
|
|
1439
1380
|
auto: "auto · 优先 3210",
|
|
1440
1381
|
hub: "hub · 必须 3210"
|
|
@@ -1541,9 +1482,7 @@ const COPY = {
|
|
|
1541
1482
|
schedule: "Peak / off-peak scheduling",
|
|
1542
1483
|
adaptive: "Adaptive routing",
|
|
1543
1484
|
runtime: "Runtime / activation boundaries",
|
|
1544
|
-
multimodal: "Vision & image generation",
|
|
1545
1485
|
harnessProviders: "Harness Providers",
|
|
1546
|
-
providers: "Multimodal adapters",
|
|
1547
1486
|
jobs: "Task status"
|
|
1548
1487
|
},
|
|
1549
1488
|
harnessHint: "The official DeepSeek Harness dsh-crew profile: providers, Harness Models, and runtime configuration",
|
|
@@ -1636,12 +1575,6 @@ const COPY = {
|
|
|
1636
1575
|
proReviewsFlash: "Pro reviews Flash changes",
|
|
1637
1576
|
proReviewsFlashHint: "Automatically follow a successful Flash run with one Pro review (blocking dispatch only)",
|
|
1638
1577
|
proReviewsLocked: "always on in Review Pipeline mode",
|
|
1639
|
-
enableCrewVision: "Enable Crew Vision",
|
|
1640
|
-
enableCrewVisionHint: "When off, the Crew describe_image tool and vision route are not registered (coexist with your own dsh-vision or other plugins).",
|
|
1641
|
-
enableImagegen: "Enable Image Generation",
|
|
1642
|
-
enableImagegenHint: "When off, the Crew generate_image tool is not registered.",
|
|
1643
|
-
capRestartHint: "This switch changes tool registration and takes effect after a DSH restart",
|
|
1644
|
-
capDisabledNote: "Crew tool/route disabled (effective after DSH restart)",
|
|
1645
1578
|
tier: "Default tier",
|
|
1646
1579
|
effort: "Default effort",
|
|
1647
1580
|
mode: "Mode",
|
|
@@ -1657,12 +1590,8 @@ const COPY = {
|
|
|
1657
1590
|
escalateHint: "retry a failed flash run once on pro",
|
|
1658
1591
|
presetFlash: "flash preset",
|
|
1659
1592
|
presetPro: "pro preset",
|
|
1660
|
-
multimodal: "Multimodal",
|
|
1661
|
-
visionProvider: "Vision provider",
|
|
1662
|
-
visionModel: "Vision model",
|
|
1663
|
-
imagegenProvider: "Image-gen provider",
|
|
1664
1593
|
harnessProviders: "Harness Providers",
|
|
1665
|
-
harnessProviderHint: "Live Provider registration and lifecycle in the official DeepSeek Harness dsh-crew profile on 3210
|
|
1594
|
+
harnessProviderHint: "Live Provider registration and lifecycle in the official DeepSeek Harness dsh-crew profile on 3210.",
|
|
1666
1595
|
providerState: (state) => `State: ${state}`,
|
|
1667
1596
|
providerModels: (count) => `${count} models`,
|
|
1668
1597
|
providerJobs: (count) => `${count} running jobs`,
|
|
@@ -1701,48 +1630,11 @@ const COPY = {
|
|
|
1701
1630
|
credentialPurging: "Purging…",
|
|
1702
1631
|
credentialPurgeUnavailable: "Credential purge unavailable",
|
|
1703
1632
|
credentialPurgeUnverified: "Purge ran but verification failed; the reference remains visible for recovery",
|
|
1704
|
-
customProviders: "Multimodal adapters",
|
|
1705
|
-
addProvider: "+ Add multimodal adapter",
|
|
1706
|
-
noCustomProviders: "None yet. Added providers appear in the vision / image-gen selects above.",
|
|
1707
|
-
providerName: "Name",
|
|
1708
|
-
modelsField: "Models (comma-separated)",
|
|
1709
|
-
providerType: "Connection",
|
|
1710
|
-
typeApi: "API · OpenAI-compatible",
|
|
1711
|
-
typeCli: "CLI · local command",
|
|
1712
|
-
baseUrl: "Base URL",
|
|
1713
|
-
apiKey: "API key",
|
|
1714
|
-
imagegenModel: "Image-gen model (empty = no image generation)",
|
|
1715
|
-
visionCmd: "Vision command (optional)",
|
|
1716
|
-
imagegenCmd: "Image-gen command (optional)",
|
|
1717
|
-
apiFormHint: "OpenAI-compatible: vision calls {base}/chat/completions (image inlined as base64), image-gen calls {base}/images/generations. The key is stored locally in ~/.config/dsh-crew/config.json and never sent to third parties.",
|
|
1718
|
-
cliFormHint: "Commands run via bash with shell-quoted placeholders. Vision: {image} {question} {model}, stdout is the answer; image-gen: {prompt} {output} {size}, the command must write the image to {output}. Fill in at least one command.",
|
|
1719
|
-
saveProvider: "Save",
|
|
1720
|
-
cancel: "Cancel",
|
|
1721
|
-
edit: "Edit",
|
|
1722
|
-
del: "Delete",
|
|
1723
|
-
testProvider: "Test",
|
|
1724
|
-
testing: "Testing…",
|
|
1725
|
-
testTip: "Probes what is currently filled in: API checks reachability and auth then makes one real vision call; CLI checks the executable then runs the vision command once. Image generation is only validated, never actually run.",
|
|
1726
|
-
testPass: "Connection OK",
|
|
1727
|
-
testFail: "Connection failed",
|
|
1728
1633
|
staleHub: "This DSH instance is still running an older build of the plugin and lacks this route — restart DSH and retry",
|
|
1729
1634
|
emptyResponse: (path, status) => `${path} → HTTP ${status}, empty response`,
|
|
1730
1635
|
badJson: (path, body) => `${path} → non-JSON response: ${body}`,
|
|
1731
|
-
refreshModels: "Re-fetch the model list from the CLI",
|
|
1732
1636
|
presetDefault: (id) => `default · follow DSH${id ? ` (${id})` : ""}`,
|
|
1733
1637
|
progressTip: (t, turn, step, calls) => `${t} elapsed · turn ${turn}, step ${step} · ${calls} tool calls`,
|
|
1734
|
-
confirmDeleteProvider: (n) => `Delete custom provider "${n}"?`,
|
|
1735
|
-
needName: "⚠ Name is required",
|
|
1736
|
-
needCmd: "⚠ Fill in at least one command",
|
|
1737
|
-
needBaseUrl: "⚠ Base URL is required",
|
|
1738
|
-
needModels: "⚠ Add at least one model",
|
|
1739
|
-
keySet: "set",
|
|
1740
|
-
keyPlaceholder: "sk-… (leave empty to send no Authorization header)",
|
|
1741
|
-
addModelTip: "Add a model to the list",
|
|
1742
|
-
removeModelTip: "Remove the current model from the list",
|
|
1743
|
-
modelPlaceholder: "model id, Enter to confirm",
|
|
1744
|
-
capVision: "vision",
|
|
1745
|
-
capImagegen: "image-gen",
|
|
1746
1638
|
groupDispatch: "Dispatch",
|
|
1747
1639
|
groupRuntime: "Runtime",
|
|
1748
1640
|
groupMultimodal: "Multimodal",
|
|
@@ -1788,14 +1680,6 @@ const COPY = {
|
|
|
1788
1680
|
t: "Execution & connection",
|
|
1789
1681
|
d: "Whether worker sessions run inside this instance or a separate process, the per-job timeout, and the address CC / Codex probes."
|
|
1790
1682
|
},
|
|
1791
|
-
cardMM: {
|
|
1792
|
-
t: "Vision & image generation",
|
|
1793
|
-
d: "Lends the harness's text-only models eyes and a brush: describe_image, pasted-image transcription and generate_image all use these settings."
|
|
1794
|
-
},
|
|
1795
|
-
cardCustomProv: {
|
|
1796
|
-
t: "Custom providers",
|
|
1797
|
-
d: "Bring your own API or local command; saved providers appear in the vision / image-gen selects above."
|
|
1798
|
-
},
|
|
1799
1683
|
modeDesc: {
|
|
1800
1684
|
auto: "auto · prefer 3210",
|
|
1801
1685
|
hub: "hub · require 3210"
|
|
@@ -2002,20 +1886,6 @@ const S = {
|
|
|
2002
1886
|
fontVariantNumeric: "tabular-nums"
|
|
2003
1887
|
}
|
|
2004
1888
|
};
|
|
2005
|
-
const VISION_MODELS = {
|
|
2006
|
-
"claude-code": [
|
|
2007
|
-
"haiku",
|
|
2008
|
-
"sonnet",
|
|
2009
|
-
"opus"
|
|
2010
|
-
],
|
|
2011
|
-
codex: [
|
|
2012
|
-
"default",
|
|
2013
|
-
"gpt-5.4-mini",
|
|
2014
|
-
"gpt-5.6"
|
|
2015
|
-
],
|
|
2016
|
-
grok: ["default"],
|
|
2017
|
-
agy: ["default"]
|
|
2018
|
-
};
|
|
2019
1889
|
function CustomSelect({ value, options, onChange, minWidth }) {
|
|
2020
1890
|
const [open, setOpen] = (0, react.useState)(null);
|
|
2021
1891
|
(0, react.useEffect)(() => {
|
|
@@ -2284,7 +2154,6 @@ function WorkersPanel({ ctx }) {
|
|
|
2284
2154
|
const [presetOptions, setPresetOptions] = (0, react.useState)([{ value: "default" }]);
|
|
2285
2155
|
const [notice, setNotice] = (0, react.useState)("");
|
|
2286
2156
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
2287
|
-
const [provForm, setProvForm] = (0, react.useState)(null);
|
|
2288
2157
|
const [modelAdd, setModelAdd] = (0, react.useState)(null);
|
|
2289
2158
|
const [modelCatalog, setModelCatalog] = (0, react.useState)(null);
|
|
2290
2159
|
const [modelCatalogError, setModelCatalogError] = (0, react.useState)("");
|
|
@@ -2298,7 +2167,6 @@ function WorkersPanel({ ctx }) {
|
|
|
2298
2167
|
const [providerLifecycleBusy, setProviderLifecycleBusy] = (0, react.useState)(null);
|
|
2299
2168
|
const [modelPickerTier, setModelPickerTier] = (0, react.useState)(null);
|
|
2300
2169
|
const [modelQuery, setModelQuery] = (0, react.useState)("");
|
|
2301
|
-
const [testResult, setTestResult] = (0, react.useState)(null);
|
|
2302
2170
|
const [expandedSections, setExpandedSections] = (0, react.useState)(() => readSectionState(sectionStorage()));
|
|
2303
2171
|
const [surface, setSurface] = (0, react.useState)("detecting");
|
|
2304
2172
|
const [readinessEnvelope, setReadinessEnvelope] = (0, react.useState)({});
|
|
@@ -2332,9 +2200,6 @@ function WorkersPanel({ ctx }) {
|
|
|
2332
2200
|
(0, react.useEffect)(() => {
|
|
2333
2201
|
if (providerInventoryError) setExpandedSections((current) => openSections(current, ["harnessProviders"]));
|
|
2334
2202
|
}, [providerInventoryError]);
|
|
2335
|
-
(0, react.useEffect)(() => {
|
|
2336
|
-
if (testResult && !testResult.busy && testResult.ok === false) setExpandedSections((current) => openSections(current, ["providers"]));
|
|
2337
|
-
}, [testResult]);
|
|
2338
2203
|
(0, react.useEffect)(() => {
|
|
2339
2204
|
if (jobs.some((job) => job.status === "running")) setExpandedSections((current) => openSections(current, ["jobs"]));
|
|
2340
2205
|
}, [jobs]);
|
|
@@ -2651,230 +2516,6 @@ function WorkersPanel({ ctx }) {
|
|
|
2651
2516
|
}));
|
|
2652
2517
|
applyPatch({ review: patch });
|
|
2653
2518
|
};
|
|
2654
|
-
const customProviders = config?.custom_providers ?? [];
|
|
2655
|
-
const extraModels = config?.extra_models ?? {};
|
|
2656
|
-
const RESERVED_IDS = [
|
|
2657
|
-
"claude-code",
|
|
2658
|
-
"codex",
|
|
2659
|
-
"grok",
|
|
2660
|
-
"agy",
|
|
2661
|
-
"off",
|
|
2662
|
-
"custom",
|
|
2663
|
-
"default"
|
|
2664
|
-
];
|
|
2665
|
-
const provType = (p) => p.type ?? (p.vision_command || p.imagegen_command ? "cli" : "api");
|
|
2666
|
-
const canSee = (p) => provType(p) === "cli" ? !!p.vision_command : !!p.base_url && (p.models?.length ?? 0) > 0;
|
|
2667
|
-
const canDraw = (p) => provType(p) === "cli" ? !!p.imagegen_command : !!p.base_url && !!String(p.imagegen_model ?? "").trim();
|
|
2668
|
-
const visionProviderOptions = [
|
|
2669
|
-
{ value: "claude-code" },
|
|
2670
|
-
{ value: "codex" },
|
|
2671
|
-
{ value: "grok" },
|
|
2672
|
-
{ value: "agy" },
|
|
2673
|
-
...customProviders.filter(canSee).map((p) => ({
|
|
2674
|
-
value: p.id,
|
|
2675
|
-
label: p.name || p.id
|
|
2676
|
-
})),
|
|
2677
|
-
{ value: "off" }
|
|
2678
|
-
];
|
|
2679
|
-
const imagegenProviderOptions = [
|
|
2680
|
-
{ value: "codex" },
|
|
2681
|
-
{
|
|
2682
|
-
value: "agy",
|
|
2683
|
-
label: "agy (nano banana)"
|
|
2684
|
-
},
|
|
2685
|
-
{
|
|
2686
|
-
value: "grok",
|
|
2687
|
-
label: "grok (imagine)"
|
|
2688
|
-
},
|
|
2689
|
-
...customProviders.filter(canDraw).map((p) => ({
|
|
2690
|
-
value: p.id,
|
|
2691
|
-
label: p.name || p.id
|
|
2692
|
-
})),
|
|
2693
|
-
{ value: "off" }
|
|
2694
|
-
];
|
|
2695
|
-
const visionModelOptions = (() => {
|
|
2696
|
-
if (!config) return [];
|
|
2697
|
-
const p = config.vision_provider;
|
|
2698
|
-
const custom = customProviders.find((c) => c.id === p);
|
|
2699
|
-
const base = custom ? [{ value: "default" }, ...(custom.models ?? []).map((m) => ({ value: m }))] : dynModels[p] ?? (VISION_MODELS[p] ?? ["default"]).map((m) => ({ value: m }));
|
|
2700
|
-
const extras = (extraModels[p] ?? []).filter((m) => !base.some((o) => o.value === m)).map((m) => ({ value: m }));
|
|
2701
|
-
return [...base, ...extras];
|
|
2702
|
-
})();
|
|
2703
|
-
const commitModelAdd = () => {
|
|
2704
|
-
const m = (modelAdd ?? "").trim();
|
|
2705
|
-
setModelAdd(null);
|
|
2706
|
-
if (m === "" || !config) return;
|
|
2707
|
-
const p = config.vision_provider;
|
|
2708
|
-
const cur = extraModels[p] ?? [];
|
|
2709
|
-
const nextExtra = {
|
|
2710
|
-
...extraModels,
|
|
2711
|
-
[p]: cur.includes(m) ? cur : [...cur, m]
|
|
2712
|
-
};
|
|
2713
|
-
setConfig((c) => ({
|
|
2714
|
-
...c,
|
|
2715
|
-
extra_models: nextExtra,
|
|
2716
|
-
vision_model: m
|
|
2717
|
-
}));
|
|
2718
|
-
applyPatch({
|
|
2719
|
-
extra_models: nextExtra,
|
|
2720
|
-
vision_model: m
|
|
2721
|
-
});
|
|
2722
|
-
};
|
|
2723
|
-
const removeCurrentModel = () => {
|
|
2724
|
-
if (!config) return;
|
|
2725
|
-
const p = config.vision_provider;
|
|
2726
|
-
const m = config.vision_model;
|
|
2727
|
-
const nextExtra = {
|
|
2728
|
-
...extraModels,
|
|
2729
|
-
[p]: (extraModels[p] ?? []).filter((x) => x !== m)
|
|
2730
|
-
};
|
|
2731
|
-
const fallback = visionModelOptions.find((o) => o.value !== m)?.value ?? "default";
|
|
2732
|
-
setConfig((c) => ({
|
|
2733
|
-
...c,
|
|
2734
|
-
extra_models: nextExtra,
|
|
2735
|
-
vision_model: fallback
|
|
2736
|
-
}));
|
|
2737
|
-
applyPatch({
|
|
2738
|
-
extra_models: nextExtra,
|
|
2739
|
-
vision_model: fallback
|
|
2740
|
-
});
|
|
2741
|
-
};
|
|
2742
|
-
const saveProviderForm = () => {
|
|
2743
|
-
if (!provForm || !config) return;
|
|
2744
|
-
const name = provForm.name.trim();
|
|
2745
|
-
if (name === "") {
|
|
2746
|
-
setNotice(copy.needName);
|
|
2747
|
-
return;
|
|
2748
|
-
}
|
|
2749
|
-
const models = provForm.models.split(/[,,\n]+/).map((s) => s.trim()).filter(Boolean);
|
|
2750
|
-
const vision_command = provForm.vision_command.trim();
|
|
2751
|
-
const imagegen_command = provForm.imagegen_command.trim();
|
|
2752
|
-
if (provForm.type === "cli") {
|
|
2753
|
-
if (vision_command === "" && imagegen_command === "") {
|
|
2754
|
-
setNotice(copy.needCmd);
|
|
2755
|
-
return;
|
|
2756
|
-
}
|
|
2757
|
-
} else {
|
|
2758
|
-
if (provForm.base_url.trim() === "") {
|
|
2759
|
-
setNotice(copy.needBaseUrl);
|
|
2760
|
-
return;
|
|
2761
|
-
}
|
|
2762
|
-
if (models.length === 0) {
|
|
2763
|
-
setNotice(copy.needModels);
|
|
2764
|
-
return;
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
let id = provForm.originalId;
|
|
2768
|
-
if (!id) {
|
|
2769
|
-
const base = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "provider";
|
|
2770
|
-
id = base;
|
|
2771
|
-
let n = 2;
|
|
2772
|
-
while (RESERVED_IDS.includes(id) || customProviders.some((p) => p.id === id)) id = `${base}-${n++}`;
|
|
2773
|
-
}
|
|
2774
|
-
const entry = provForm.type === "api" ? {
|
|
2775
|
-
id,
|
|
2776
|
-
name,
|
|
2777
|
-
type: "api",
|
|
2778
|
-
models,
|
|
2779
|
-
base_url: provForm.base_url.trim(),
|
|
2780
|
-
api_key: provForm.api_key,
|
|
2781
|
-
imagegen_model: provForm.imagegen_model.trim()
|
|
2782
|
-
} : {
|
|
2783
|
-
id,
|
|
2784
|
-
name,
|
|
2785
|
-
type: "cli",
|
|
2786
|
-
models,
|
|
2787
|
-
vision_command,
|
|
2788
|
-
imagegen_command
|
|
2789
|
-
};
|
|
2790
|
-
const next = provForm.originalId ? customProviders.map((p) => p.id === provForm.originalId ? entry : p) : [...customProviders, entry];
|
|
2791
|
-
field("custom_providers", next);
|
|
2792
|
-
setProvForm(null);
|
|
2793
|
-
};
|
|
2794
|
-
const runProviderTest = (key, entry) => {
|
|
2795
|
-
setTestResult({
|
|
2796
|
-
key,
|
|
2797
|
-
busy: true
|
|
2798
|
-
});
|
|
2799
|
-
post("/provider-test", entry).then((r) => setTestResult(r.ok ? {
|
|
2800
|
-
key,
|
|
2801
|
-
busy: false,
|
|
2802
|
-
ok: r.result.ok,
|
|
2803
|
-
steps: r.result.steps
|
|
2804
|
-
} : {
|
|
2805
|
-
key,
|
|
2806
|
-
busy: false,
|
|
2807
|
-
ok: false,
|
|
2808
|
-
error: r.error
|
|
2809
|
-
})).catch((e) => setTestResult({
|
|
2810
|
-
key,
|
|
2811
|
-
busy: false,
|
|
2812
|
-
ok: false,
|
|
2813
|
-
error: String(e?.message ?? e)
|
|
2814
|
-
}));
|
|
2815
|
-
};
|
|
2816
|
-
/** Shared renderer for a test outcome under the button that triggered it. */
|
|
2817
|
-
const testReport = (key) => {
|
|
2818
|
-
if (!testResult || testResult.key !== key) return null;
|
|
2819
|
-
if (testResult.busy) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2820
|
-
style: {
|
|
2821
|
-
fontSize: 11.5,
|
|
2822
|
-
opacity: .6
|
|
2823
|
-
},
|
|
2824
|
-
children: copy.testing
|
|
2825
|
-
});
|
|
2826
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2827
|
-
style: {
|
|
2828
|
-
fontSize: 11.5,
|
|
2829
|
-
display: "flex",
|
|
2830
|
-
flexDirection: "column",
|
|
2831
|
-
gap: 2
|
|
2832
|
-
},
|
|
2833
|
-
children: [
|
|
2834
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2835
|
-
style: { color: testResult.ok ? "#3fb950" : "#f85149" },
|
|
2836
|
-
children: testResult.ok ? `✓ ${copy.testPass}` : `✗ ${copy.testFail}`
|
|
2837
|
-
}),
|
|
2838
|
-
testResult.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2839
|
-
style: {
|
|
2840
|
-
opacity: .7,
|
|
2841
|
-
wordBreak: "break-all"
|
|
2842
|
-
},
|
|
2843
|
-
children: testResult.error
|
|
2844
|
-
}),
|
|
2845
|
-
(testResult.steps ?? []).map((st, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2846
|
-
style: {
|
|
2847
|
-
opacity: .7,
|
|
2848
|
-
wordBreak: "break-all"
|
|
2849
|
-
},
|
|
2850
|
-
children: [
|
|
2851
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2852
|
-
style: { color: st.ok ? "#3fb950" : "#f85149" },
|
|
2853
|
-
children: st.ok ? "✓" : "✗"
|
|
2854
|
-
}),
|
|
2855
|
-
" ",
|
|
2856
|
-
st.name,
|
|
2857
|
-
": ",
|
|
2858
|
-
st.detail
|
|
2859
|
-
]
|
|
2860
|
-
}, i))
|
|
2861
|
-
]
|
|
2862
|
-
});
|
|
2863
|
-
};
|
|
2864
|
-
const deleteProvider = (id) => {
|
|
2865
|
-
if (!config) return;
|
|
2866
|
-
const patch = { custom_providers: customProviders.filter((p) => p.id !== id) };
|
|
2867
|
-
if (config.vision_provider === id) {
|
|
2868
|
-
patch.vision_provider = "claude-code";
|
|
2869
|
-
patch.vision_model = "haiku";
|
|
2870
|
-
}
|
|
2871
|
-
if (config.imagegen_provider === id) patch.imagegen_provider = "codex";
|
|
2872
|
-
setConfig((c) => ({
|
|
2873
|
-
...c,
|
|
2874
|
-
...patch
|
|
2875
|
-
}));
|
|
2876
|
-
applyPatch(patch);
|
|
2877
|
-
};
|
|
2878
2519
|
const deleteHarnessProvider = async (record) => {
|
|
2879
2520
|
if (!record || record.delete_capability !== "supported" || record.desired_state === "absent" || Number(record.references?.active_jobs ?? 0) > 0) {
|
|
2880
2521
|
setNotice(copy.providerDeleteBlocked);
|
|
@@ -4636,191 +4277,6 @@ function WorkersPanel({ ctx }) {
|
|
|
4636
4277
|
locale
|
|
4637
4278
|
})]
|
|
4638
4279
|
}),
|
|
4639
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
4640
|
-
sectionId: "multimodal",
|
|
4641
|
-
title: copy.sectionNames.multimodal,
|
|
4642
|
-
summary: sectionSummary(config.vision_enabled ? `${copy.capVision}: ${config.vision_provider}` : `${copy.capVision}: off`, config.imagegen_enabled ? `${copy.capImagegen}: ${config.imagegen_provider}` : `${copy.capImagegen}: off`),
|
|
4643
|
-
expanded: !!expandedSections.multimodal,
|
|
4644
|
-
onToggle: () => toggleSection("multimodal"),
|
|
4645
|
-
children: block(copy.cardMM, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4646
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4647
|
-
style: {
|
|
4648
|
-
...S.field,
|
|
4649
|
-
flexDirection: "row",
|
|
4650
|
-
alignItems: "center",
|
|
4651
|
-
gap: 6,
|
|
4652
|
-
gridColumn: "1 / -1"
|
|
4653
|
-
},
|
|
4654
|
-
title: copy.capRestartHint,
|
|
4655
|
-
children: [
|
|
4656
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4657
|
-
type: "checkbox",
|
|
4658
|
-
checked: !!config.vision_enabled,
|
|
4659
|
-
onChange: (e) => field("vision_enabled", e.target.checked)
|
|
4660
|
-
}),
|
|
4661
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4662
|
-
style: { fontSize: 12.5 },
|
|
4663
|
-
children: copy.enableCrewVision
|
|
4664
|
-
}),
|
|
4665
|
-
config.vision_enabled === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4666
|
-
style: {
|
|
4667
|
-
fontSize: 11,
|
|
4668
|
-
opacity: .55
|
|
4669
|
-
},
|
|
4670
|
-
children: copy.capDisabledNote
|
|
4671
|
-
})
|
|
4672
|
-
]
|
|
4673
|
-
}),
|
|
4674
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4675
|
-
style: S.field,
|
|
4676
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4677
|
-
style: S.fieldLabel,
|
|
4678
|
-
children: copy.visionProvider
|
|
4679
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomSelect, {
|
|
4680
|
-
value: config.vision_provider,
|
|
4681
|
-
onChange: (p) => {
|
|
4682
|
-
const custom = customProviders.find((c) => c.id === p);
|
|
4683
|
-
const m = custom ? custom.models?.[0] ?? "default" : (VISION_MODELS[p] ?? ["default"])[0];
|
|
4684
|
-
setModelAdd(null);
|
|
4685
|
-
setConfig((c) => ({
|
|
4686
|
-
...c,
|
|
4687
|
-
vision_provider: p,
|
|
4688
|
-
vision_model: m
|
|
4689
|
-
}));
|
|
4690
|
-
applyPatch({
|
|
4691
|
-
vision_provider: p,
|
|
4692
|
-
vision_model: m
|
|
4693
|
-
});
|
|
4694
|
-
if ((p === "grok" || p === "agy") && !dynModels[p]) get(`/vision-models?provider=${p}`).then((r) => {
|
|
4695
|
-
if (r.ok) setDynModels((d) => ({
|
|
4696
|
-
...d,
|
|
4697
|
-
[p]: r.models
|
|
4698
|
-
}));
|
|
4699
|
-
}).catch(() => {});
|
|
4700
|
-
},
|
|
4701
|
-
options: visionProviderOptions
|
|
4702
|
-
})]
|
|
4703
|
-
}),
|
|
4704
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4705
|
-
style: S.field,
|
|
4706
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4707
|
-
style: S.fieldLabel,
|
|
4708
|
-
children: copy.visionModel
|
|
4709
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4710
|
-
style: {
|
|
4711
|
-
display: "flex",
|
|
4712
|
-
gap: 6,
|
|
4713
|
-
alignItems: "center"
|
|
4714
|
-
},
|
|
4715
|
-
children: [
|
|
4716
|
-
modelAdd === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomSelect, {
|
|
4717
|
-
value: config.vision_model,
|
|
4718
|
-
onChange: (v) => field("vision_model", v),
|
|
4719
|
-
options: visionModelOptions
|
|
4720
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4721
|
-
autoFocus: true,
|
|
4722
|
-
style: {
|
|
4723
|
-
...S.input,
|
|
4724
|
-
width: "100%",
|
|
4725
|
-
boxSizing: "border-box",
|
|
4726
|
-
...S.mono
|
|
4727
|
-
},
|
|
4728
|
-
value: modelAdd,
|
|
4729
|
-
placeholder: copy.modelPlaceholder,
|
|
4730
|
-
onChange: (e) => setModelAdd(e.target.value),
|
|
4731
|
-
onBlur: commitModelAdd,
|
|
4732
|
-
onKeyDown: (e) => {
|
|
4733
|
-
if (e.key === "Enter") e.currentTarget.blur();
|
|
4734
|
-
if (e.key === "Escape") setModelAdd(null);
|
|
4735
|
-
}
|
|
4736
|
-
}),
|
|
4737
|
-
modelAdd === null && (config.vision_provider === "grok" || config.vision_provider === "agy") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4738
|
-
type: "button",
|
|
4739
|
-
title: copy.refreshModels,
|
|
4740
|
-
disabled: busy,
|
|
4741
|
-
style: {
|
|
4742
|
-
...S.btn,
|
|
4743
|
-
padding: "3px 8px",
|
|
4744
|
-
flexShrink: 0
|
|
4745
|
-
},
|
|
4746
|
-
onClick: () => {
|
|
4747
|
-
const p = config.vision_provider;
|
|
4748
|
-
setBusy(true);
|
|
4749
|
-
get(`/vision-models?provider=${p}&refresh=1`).then((r) => {
|
|
4750
|
-
if (r.ok) setDynModels((d) => ({
|
|
4751
|
-
...d,
|
|
4752
|
-
[p]: r.models
|
|
4753
|
-
}));
|
|
4754
|
-
}).catch(() => {}).finally(() => setBusy(false));
|
|
4755
|
-
},
|
|
4756
|
-
children: "↻"
|
|
4757
|
-
}),
|
|
4758
|
-
modelAdd === null && config.vision_provider !== "off" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4759
|
-
type: "button",
|
|
4760
|
-
title: copy.addModelTip,
|
|
4761
|
-
style: {
|
|
4762
|
-
...S.btn,
|
|
4763
|
-
padding: "3px 8px",
|
|
4764
|
-
flexShrink: 0
|
|
4765
|
-
},
|
|
4766
|
-
onClick: () => setModelAdd(""),
|
|
4767
|
-
children: "+"
|
|
4768
|
-
}),
|
|
4769
|
-
modelAdd === null && (extraModels[config.vision_provider] ?? []).includes(config.vision_model) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4770
|
-
type: "button",
|
|
4771
|
-
title: copy.removeModelTip,
|
|
4772
|
-
style: {
|
|
4773
|
-
...S.btn,
|
|
4774
|
-
padding: "3px 8px",
|
|
4775
|
-
flexShrink: 0
|
|
4776
|
-
},
|
|
4777
|
-
onClick: removeCurrentModel,
|
|
4778
|
-
children: "−"
|
|
4779
|
-
})
|
|
4780
|
-
]
|
|
4781
|
-
})]
|
|
4782
|
-
}),
|
|
4783
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4784
|
-
style: {
|
|
4785
|
-
...S.field,
|
|
4786
|
-
flexDirection: "row",
|
|
4787
|
-
alignItems: "center",
|
|
4788
|
-
gap: 6,
|
|
4789
|
-
gridColumn: "1 / -1"
|
|
4790
|
-
},
|
|
4791
|
-
title: copy.capRestartHint,
|
|
4792
|
-
children: [
|
|
4793
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4794
|
-
type: "checkbox",
|
|
4795
|
-
checked: !!config.imagegen_enabled,
|
|
4796
|
-
onChange: (e) => field("imagegen_enabled", e.target.checked)
|
|
4797
|
-
}),
|
|
4798
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4799
|
-
style: { fontSize: 12.5 },
|
|
4800
|
-
children: copy.enableImagegen
|
|
4801
|
-
}),
|
|
4802
|
-
config.imagegen_enabled === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4803
|
-
style: {
|
|
4804
|
-
fontSize: 11,
|
|
4805
|
-
opacity: .55
|
|
4806
|
-
},
|
|
4807
|
-
children: copy.capDisabledNote
|
|
4808
|
-
})
|
|
4809
|
-
]
|
|
4810
|
-
}),
|
|
4811
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4812
|
-
style: S.field,
|
|
4813
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4814
|
-
style: S.fieldLabel,
|
|
4815
|
-
children: copy.imagegenProvider
|
|
4816
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomSelect, {
|
|
4817
|
-
value: config.imagegen_provider,
|
|
4818
|
-
onChange: (v) => field("imagegen_provider", v),
|
|
4819
|
-
options: imagegenProviderOptions
|
|
4820
|
-
})]
|
|
4821
|
-
})
|
|
4822
|
-
] }))
|
|
4823
|
-
}),
|
|
4824
4280
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
4825
4281
|
sectionId: "harnessProviders",
|
|
4826
4282
|
title: copy.sectionNames.harnessProviders,
|
|
@@ -5149,365 +4605,6 @@ function WorkersPanel({ ctx }) {
|
|
|
5149
4605
|
]
|
|
5150
4606
|
})
|
|
5151
4607
|
]
|
|
5152
|
-
}),
|
|
5153
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
5154
|
-
sectionId: "providers",
|
|
5155
|
-
title: copy.sectionNames.providers,
|
|
5156
|
-
summary: sectionSummary(copy.providerCount(customProviders.length), testResult?.ok === false && copy.testFail),
|
|
5157
|
-
expanded: !!expandedSections.providers,
|
|
5158
|
-
onToggle: () => toggleSection("providers"),
|
|
5159
|
-
children: block(copy.cardCustomProv, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5160
|
-
customProviders.length === 0 && provForm === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5161
|
-
style: {
|
|
5162
|
-
fontSize: 12,
|
|
5163
|
-
opacity: .5
|
|
5164
|
-
},
|
|
5165
|
-
children: copy.noCustomProviders
|
|
5166
|
-
}),
|
|
5167
|
-
customProviders.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5168
|
-
style: {
|
|
5169
|
-
display: "flex",
|
|
5170
|
-
alignItems: "center",
|
|
5171
|
-
gap: 8,
|
|
5172
|
-
fontSize: 12.5
|
|
5173
|
-
},
|
|
5174
|
-
children: [
|
|
5175
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5176
|
-
style: { fontWeight: 500 },
|
|
5177
|
-
children: p.name || p.id
|
|
5178
|
-
}),
|
|
5179
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5180
|
-
style: {
|
|
5181
|
-
...S.chip(false),
|
|
5182
|
-
opacity: .7
|
|
5183
|
-
},
|
|
5184
|
-
children: provType(p) === "api" ? "API" : "CLI"
|
|
5185
|
-
}),
|
|
5186
|
-
canSee(p) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5187
|
-
style: S.chip(true),
|
|
5188
|
-
children: copy.capVision
|
|
5189
|
-
}),
|
|
5190
|
-
canDraw(p) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5191
|
-
style: S.chip(true),
|
|
5192
|
-
children: copy.capImagegen
|
|
5193
|
-
}),
|
|
5194
|
-
provType(p) === "api" && !!p.api_key && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5195
|
-
style: {
|
|
5196
|
-
fontSize: 11.5,
|
|
5197
|
-
opacity: .5
|
|
5198
|
-
},
|
|
5199
|
-
children: ["key ", copy.keySet]
|
|
5200
|
-
}),
|
|
5201
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
5202
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5203
|
-
style: {
|
|
5204
|
-
...S.btn,
|
|
5205
|
-
padding: "2px 8px"
|
|
5206
|
-
},
|
|
5207
|
-
title: copy.testTip,
|
|
5208
|
-
disabled: !!testResult?.busy,
|
|
5209
|
-
onClick: () => runProviderTest(`row:${p.id}`, p),
|
|
5210
|
-
children: testResult?.key === `row:${p.id}` && testResult.busy ? copy.testing : copy.testProvider
|
|
5211
|
-
}),
|
|
5212
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5213
|
-
style: {
|
|
5214
|
-
...S.btn,
|
|
5215
|
-
padding: "2px 8px"
|
|
5216
|
-
},
|
|
5217
|
-
onClick: () => setProvForm({
|
|
5218
|
-
originalId: p.id,
|
|
5219
|
-
name: p.name ?? p.id,
|
|
5220
|
-
type: provType(p),
|
|
5221
|
-
models: (p.models ?? []).join(", "),
|
|
5222
|
-
base_url: p.base_url ?? "",
|
|
5223
|
-
api_key: p.api_key ?? "",
|
|
5224
|
-
imagegen_model: p.imagegen_model ?? "",
|
|
5225
|
-
vision_command: p.vision_command ?? "",
|
|
5226
|
-
imagegen_command: p.imagegen_command ?? ""
|
|
5227
|
-
}),
|
|
5228
|
-
children: copy.edit
|
|
5229
|
-
}),
|
|
5230
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5231
|
-
style: {
|
|
5232
|
-
...S.btn,
|
|
5233
|
-
padding: "2px 8px",
|
|
5234
|
-
opacity: .75
|
|
5235
|
-
},
|
|
5236
|
-
onClick: () => {
|
|
5237
|
-
if (confirm(copy.confirmDeleteProvider(p.name || p.id))) deleteProvider(p.id);
|
|
5238
|
-
},
|
|
5239
|
-
children: copy.del
|
|
5240
|
-
})
|
|
5241
|
-
]
|
|
5242
|
-
}, p.id)),
|
|
5243
|
-
customProviders.some((p) => testResult?.key === `row:${p.id}`) && testReport(testResult.key),
|
|
5244
|
-
provForm === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5245
|
-
style: S.btn,
|
|
5246
|
-
onClick: () => setProvForm({
|
|
5247
|
-
name: "",
|
|
5248
|
-
type: "api",
|
|
5249
|
-
models: "",
|
|
5250
|
-
base_url: "",
|
|
5251
|
-
api_key: "",
|
|
5252
|
-
imagegen_model: "",
|
|
5253
|
-
vision_command: "",
|
|
5254
|
-
imagegen_command: ""
|
|
5255
|
-
}),
|
|
5256
|
-
children: copy.addProvider
|
|
5257
|
-
}) }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5258
|
-
style: {
|
|
5259
|
-
borderTop: "1px solid rgba(128,128,128,0.16)",
|
|
5260
|
-
paddingTop: 10,
|
|
5261
|
-
display: "flex",
|
|
5262
|
-
flexDirection: "column",
|
|
5263
|
-
gap: 10
|
|
5264
|
-
},
|
|
5265
|
-
children: [
|
|
5266
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5267
|
-
style: S.blockGrid,
|
|
5268
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5269
|
-
style: S.field,
|
|
5270
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5271
|
-
style: S.fieldLabel,
|
|
5272
|
-
children: copy.providerName
|
|
5273
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5274
|
-
autoFocus: true,
|
|
5275
|
-
style: {
|
|
5276
|
-
...S.input,
|
|
5277
|
-
width: "100%",
|
|
5278
|
-
boxSizing: "border-box"
|
|
5279
|
-
},
|
|
5280
|
-
value: provForm.name,
|
|
5281
|
-
onChange: (e) => setProvForm({
|
|
5282
|
-
...provForm,
|
|
5283
|
-
name: e.target.value
|
|
5284
|
-
})
|
|
5285
|
-
})]
|
|
5286
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5287
|
-
style: S.field,
|
|
5288
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5289
|
-
style: S.fieldLabel,
|
|
5290
|
-
children: copy.providerType
|
|
5291
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomSelect, {
|
|
5292
|
-
value: provForm.type,
|
|
5293
|
-
onChange: (v) => setProvForm({
|
|
5294
|
-
...provForm,
|
|
5295
|
-
type: v
|
|
5296
|
-
}),
|
|
5297
|
-
options: [{
|
|
5298
|
-
value: "api",
|
|
5299
|
-
label: copy.typeApi
|
|
5300
|
-
}, {
|
|
5301
|
-
value: "cli",
|
|
5302
|
-
label: copy.typeCli
|
|
5303
|
-
}]
|
|
5304
|
-
})]
|
|
5305
|
-
})]
|
|
5306
|
-
}),
|
|
5307
|
-
provForm.type === "api" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5308
|
-
style: S.blockGrid,
|
|
5309
|
-
children: [
|
|
5310
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5311
|
-
style: S.field,
|
|
5312
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5313
|
-
style: S.fieldLabel,
|
|
5314
|
-
children: copy.baseUrl
|
|
5315
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5316
|
-
style: {
|
|
5317
|
-
...S.input,
|
|
5318
|
-
width: "100%",
|
|
5319
|
-
boxSizing: "border-box",
|
|
5320
|
-
...S.mono
|
|
5321
|
-
},
|
|
5322
|
-
value: provForm.base_url,
|
|
5323
|
-
placeholder: "https://api.example.com/v1",
|
|
5324
|
-
onChange: (e) => setProvForm({
|
|
5325
|
-
...provForm,
|
|
5326
|
-
base_url: e.target.value
|
|
5327
|
-
})
|
|
5328
|
-
})]
|
|
5329
|
-
}),
|
|
5330
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5331
|
-
style: S.field,
|
|
5332
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5333
|
-
style: S.fieldLabel,
|
|
5334
|
-
children: copy.apiKey
|
|
5335
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5336
|
-
type: "password",
|
|
5337
|
-
autoComplete: "off",
|
|
5338
|
-
style: {
|
|
5339
|
-
...S.input,
|
|
5340
|
-
width: "100%",
|
|
5341
|
-
boxSizing: "border-box",
|
|
5342
|
-
...S.mono
|
|
5343
|
-
},
|
|
5344
|
-
value: provForm.api_key,
|
|
5345
|
-
placeholder: copy.keyPlaceholder,
|
|
5346
|
-
onChange: (e) => setProvForm({
|
|
5347
|
-
...provForm,
|
|
5348
|
-
api_key: e.target.value
|
|
5349
|
-
})
|
|
5350
|
-
})]
|
|
5351
|
-
}),
|
|
5352
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5353
|
-
style: S.field,
|
|
5354
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5355
|
-
style: S.fieldLabel,
|
|
5356
|
-
children: copy.modelsField
|
|
5357
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5358
|
-
style: {
|
|
5359
|
-
...S.input,
|
|
5360
|
-
width: "100%",
|
|
5361
|
-
boxSizing: "border-box",
|
|
5362
|
-
...S.mono
|
|
5363
|
-
},
|
|
5364
|
-
value: provForm.models,
|
|
5365
|
-
placeholder: "gpt-4o-mini, qwen-vl-max",
|
|
5366
|
-
onChange: (e) => setProvForm({
|
|
5367
|
-
...provForm,
|
|
5368
|
-
models: e.target.value
|
|
5369
|
-
})
|
|
5370
|
-
})]
|
|
5371
|
-
}),
|
|
5372
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5373
|
-
style: S.field,
|
|
5374
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5375
|
-
style: S.fieldLabel,
|
|
5376
|
-
children: copy.imagegenModel
|
|
5377
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5378
|
-
style: {
|
|
5379
|
-
...S.input,
|
|
5380
|
-
width: "100%",
|
|
5381
|
-
boxSizing: "border-box",
|
|
5382
|
-
...S.mono
|
|
5383
|
-
},
|
|
5384
|
-
value: provForm.imagegen_model,
|
|
5385
|
-
placeholder: "dall-e-3",
|
|
5386
|
-
onChange: (e) => setProvForm({
|
|
5387
|
-
...provForm,
|
|
5388
|
-
imagegen_model: e.target.value
|
|
5389
|
-
})
|
|
5390
|
-
})]
|
|
5391
|
-
})
|
|
5392
|
-
]
|
|
5393
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5394
|
-
style: {
|
|
5395
|
-
fontSize: 11.5,
|
|
5396
|
-
opacity: .55
|
|
5397
|
-
},
|
|
5398
|
-
children: copy.apiFormHint
|
|
5399
|
-
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5400
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5401
|
-
style: S.field,
|
|
5402
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5403
|
-
style: S.fieldLabel,
|
|
5404
|
-
children: copy.visionCmd
|
|
5405
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5406
|
-
style: {
|
|
5407
|
-
...S.input,
|
|
5408
|
-
width: "100%",
|
|
5409
|
-
boxSizing: "border-box",
|
|
5410
|
-
...S.mono
|
|
5411
|
-
},
|
|
5412
|
-
value: provForm.vision_command,
|
|
5413
|
-
placeholder: "mycli see {image} --ask {question} --model {model}",
|
|
5414
|
-
onChange: (e) => setProvForm({
|
|
5415
|
-
...provForm,
|
|
5416
|
-
vision_command: e.target.value
|
|
5417
|
-
})
|
|
5418
|
-
})]
|
|
5419
|
-
}),
|
|
5420
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5421
|
-
style: S.field,
|
|
5422
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5423
|
-
style: S.fieldLabel,
|
|
5424
|
-
children: copy.imagegenCmd
|
|
5425
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5426
|
-
style: {
|
|
5427
|
-
...S.input,
|
|
5428
|
-
width: "100%",
|
|
5429
|
-
boxSizing: "border-box",
|
|
5430
|
-
...S.mono
|
|
5431
|
-
},
|
|
5432
|
-
value: provForm.imagegen_command,
|
|
5433
|
-
placeholder: "mycli gen {prompt} -o {output}",
|
|
5434
|
-
onChange: (e) => setProvForm({
|
|
5435
|
-
...provForm,
|
|
5436
|
-
imagegen_command: e.target.value
|
|
5437
|
-
})
|
|
5438
|
-
})]
|
|
5439
|
-
}),
|
|
5440
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5441
|
-
style: S.field,
|
|
5442
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5443
|
-
style: S.fieldLabel,
|
|
5444
|
-
children: copy.modelsField
|
|
5445
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5446
|
-
style: {
|
|
5447
|
-
...S.input,
|
|
5448
|
-
width: "100%",
|
|
5449
|
-
boxSizing: "border-box",
|
|
5450
|
-
...S.mono
|
|
5451
|
-
},
|
|
5452
|
-
value: provForm.models,
|
|
5453
|
-
placeholder: "model-a, model-b",
|
|
5454
|
-
onChange: (e) => setProvForm({
|
|
5455
|
-
...provForm,
|
|
5456
|
-
models: e.target.value
|
|
5457
|
-
})
|
|
5458
|
-
})]
|
|
5459
|
-
}),
|
|
5460
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5461
|
-
style: {
|
|
5462
|
-
fontSize: 11.5,
|
|
5463
|
-
opacity: .55
|
|
5464
|
-
},
|
|
5465
|
-
children: copy.cliFormHint
|
|
5466
|
-
})
|
|
5467
|
-
] }),
|
|
5468
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5469
|
-
style: {
|
|
5470
|
-
display: "flex",
|
|
5471
|
-
gap: 8,
|
|
5472
|
-
alignItems: "center"
|
|
5473
|
-
},
|
|
5474
|
-
children: [
|
|
5475
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5476
|
-
style: S.btn,
|
|
5477
|
-
onClick: saveProviderForm,
|
|
5478
|
-
children: copy.saveProvider
|
|
5479
|
-
}),
|
|
5480
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5481
|
-
style: S.btn,
|
|
5482
|
-
title: copy.testTip,
|
|
5483
|
-
disabled: !!testResult?.busy,
|
|
5484
|
-
onClick: () => runProviderTest("form", {
|
|
5485
|
-
id: provForm.originalId ?? "draft",
|
|
5486
|
-
name: provForm.name || "draft",
|
|
5487
|
-
type: provForm.type,
|
|
5488
|
-
models: provForm.models.split(/[,,\n]+/).map((x) => x.trim()).filter(Boolean),
|
|
5489
|
-
base_url: provForm.base_url.trim(),
|
|
5490
|
-
api_key: provForm.api_key,
|
|
5491
|
-
imagegen_model: provForm.imagegen_model.trim(),
|
|
5492
|
-
vision_command: provForm.vision_command.trim(),
|
|
5493
|
-
imagegen_command: provForm.imagegen_command.trim()
|
|
5494
|
-
}),
|
|
5495
|
-
children: testResult?.key === "form" && testResult.busy ? copy.testing : copy.testProvider
|
|
5496
|
-
}),
|
|
5497
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5498
|
-
style: {
|
|
5499
|
-
...S.btn,
|
|
5500
|
-
opacity: .75
|
|
5501
|
-
},
|
|
5502
|
-
onClick: () => setProvForm(null),
|
|
5503
|
-
children: copy.cancel
|
|
5504
|
-
})
|
|
5505
|
-
]
|
|
5506
|
-
}),
|
|
5507
|
-
testReport("form")
|
|
5508
|
-
]
|
|
5509
|
-
})
|
|
5510
|
-
] }), false)
|
|
5511
4608
|
})
|
|
5512
4609
|
] }),
|
|
5513
4610
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|