cli-jaw 2.2.10 → 2.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +15 -0
- package/README.ko.md +15 -0
- package/README.md +15 -0
- package/dist/bin/cli-jaw.js +5 -1
- package/dist/bin/cli-jaw.js.map +1 -1
- package/dist/bin/commands/doctor.js +22 -2
- package/dist/bin/commands/doctor.js.map +1 -1
- package/dist/bin/commands/init.js +7 -0
- package/dist/bin/commands/init.js.map +1 -1
- package/dist/bin/commands/slack.js +232 -0
- package/dist/bin/commands/slack.js.map +1 -0
- package/dist/bin/commands/tui/slash-command-runner.js +11 -2
- package/dist/bin/commands/tui/slash-command-runner.js.map +1 -1
- package/dist/server.js +18 -2
- package/dist/server.js.map +1 -1
- package/dist/src/cli/commands.js +68 -15
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/opencodex-models.js +67 -14
- package/dist/src/cli/opencodex-models.js.map +1 -1
- package/dist/src/cli/registry-live.js +55 -2
- package/dist/src/cli/registry-live.js.map +1 -1
- package/dist/src/cli/registry.js +8 -2
- package/dist/src/cli/registry.js.map +1 -1
- package/dist/src/core/config.js +26 -1
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/messaging/channel-health.js +6 -0
- package/dist/src/messaging/channel-health.js.map +1 -1
- package/dist/src/messaging/channel-validate.js +85 -0
- package/dist/src/messaging/channel-validate.js.map +1 -0
- package/dist/src/routes/messaging.js +9 -0
- package/dist/src/routes/messaging.js.map +1 -1
- package/dist/src/routes/system.js +5 -0
- package/dist/src/routes/system.js.map +1 -1
- package/dist/src/slack/api.js +32 -3
- package/dist/src/slack/api.js.map +1 -1
- package/dist/src/slack/bot.js +37 -1
- package/dist/src/slack/bot.js.map +1 -1
- package/dist/src/slack/events.js +23 -0
- package/dist/src/slack/events.js.map +1 -1
- package/dist/src/slack/hot-notify.js +33 -0
- package/dist/src/slack/hot-notify.js.map +1 -0
- package/dist/src/slack/manifest.js +85 -0
- package/dist/src/slack/manifest.js.map +1 -0
- package/dist/src/slack/progress.js +98 -0
- package/dist/src/slack/progress.js.map +1 -0
- package/dist/src/slack/send-only-client.js +3 -2
- package/dist/src/slack/send-only-client.js.map +1 -1
- package/dist/src/slack/slack-file.js +4 -2
- package/dist/src/slack/slack-file.js.map +1 -1
- package/package.json +3 -1
- package/public/css/sidebar.css +200 -0
- package/public/dist/assets/Agent-BjbZVZ9p.js +1 -0
- package/public/dist/assets/{Heartbeat-vDGhPfct.js → Heartbeat-DDmZARWw.js} +1 -1
- package/public/dist/assets/{Memory-B_UZ-iCA.js → Memory-CUbviW7N.js} +1 -1
- package/public/dist/assets/ModelProvider-ngg8xTuB.js +1 -0
- package/public/dist/assets/{agent-meta-C8DqYXM6.js → agent-meta-Y3DE6sxh.js} +1 -1
- package/public/dist/assets/app-BzD_ZLvt.js +55 -0
- package/public/dist/assets/{app-Nu2VPaO5.css → app-DX4e6isX.css} +1 -1
- package/public/dist/assets/bgtask-badge-Dqbp-Y4e.js +1 -0
- package/public/dist/assets/{bgtask-badge-DXca8E7c.js → bgtask-badge-qMkKOZx0.js} +1 -1
- package/public/dist/assets/{constants-CSZ011-j.js → constants-Df7-7Rky.js} +1 -1
- package/public/dist/assets/{employees-BL4qMxJj.js → employees-BnobWM_2.js} +1 -1
- package/public/dist/assets/{iframe-renderer-DUI0LYBy.js → iframe-renderer-C5vRbbl9.js} +2 -2
- package/public/dist/assets/iframe-renderer-Df4KIVyx.js +1 -0
- package/public/dist/assets/{manager-B-gpf-3j.js → manager-DL1GygQL.js} +1 -1
- package/public/dist/assets/memory-D-bULTHl.js +1 -0
- package/public/dist/assets/{memory-B_Y0L0KD.js → memory-DF4kPwwC.js} +1 -1
- package/public/dist/assets/{message-history-Dl8dJzez.js → message-history-C4WOisGx.js} +1 -1
- package/public/dist/assets/message-history-ZBoza6ew.js +1 -0
- package/public/dist/assets/{render-BI1aqKoc.js → render-DYX6GRQ0.js} +1 -1
- package/public/dist/assets/{settings-B0Sj4sUH.js → settings-DbJpGoaX.js} +1 -1
- package/public/dist/assets/settings-SreihUje.js +1 -0
- package/public/dist/assets/settings-core-B1Y5Nb4Z.js +1 -0
- package/public/dist/assets/settings-core-DobZWIZ7.js +140 -0
- package/public/dist/assets/{sidebar-DOR9fTgN.js → sidebar-Cp22g0Gv.js} +3 -3
- package/public/dist/assets/skills-DwPIP08e.js +1 -0
- package/public/dist/assets/{skills-wt0fkV9Y.js → skills-IGquen_j.js} +1 -1
- package/public/dist/assets/slash-commands-BBVfwcnW.js +1 -0
- package/public/dist/assets/{slash-commands-BZbqTg3v.js → slash-commands-ZRDOL99E.js} +1 -1
- package/public/dist/assets/{trace-drawer-kMYzaVFW.js → trace-drawer-D8To8fYt.js} +1 -1
- package/public/dist/assets/ui-CAicpUZc.js +1 -0
- package/public/dist/assets/{ui-D4D5HUfo.js → ui-Hx_mNoDf.js} +1 -1
- package/public/dist/index.html +90 -47
- package/public/dist/manager/index.html +1 -1
- package/public/index.html +88 -45
- package/public/js/constants.ts +50 -0
- package/public/js/features/channel-onboarding-flow.ts +197 -0
- package/public/js/features/channel-onboarding.ts +322 -0
- package/public/js/features/channel-setup-guide.ts +26 -0
- package/public/js/features/channel-setup-rules.ts +32 -0
- package/public/js/features/notifications.ts +38 -0
- package/public/js/features/settings-channel.ts +4 -0
- package/public/js/features/settings-core.ts +73 -9
- package/public/js/features/settings-discord.ts +2 -0
- package/public/js/features/settings-slack.ts +60 -2
- package/public/js/features/settings-telegram.ts +2 -0
- package/public/js/features/settings-types.ts +1 -1
- package/public/js/features/settings.ts +1 -1
- package/public/js/features/transport-status-row.ts +7 -3
- package/public/js/main.ts +5 -1
- package/public/locales/en.json +65 -3
- package/public/locales/ja.json +76 -3
- package/public/locales/ko.json +65 -3
- package/public/locales/zh.json +76 -3
- package/public/manager/src/settings/pages/Agent.tsx +25 -4
- package/public/manager/src/settings/pages/components/PerCliRow.tsx +13 -5
- package/public/manager/src/settings/pages/components/agent/agent-meta.ts +98 -0
- package/scripts/bundle-sidecar.sh +13 -4
- package/scripts/check-sidecar-prune-safety.mjs +167 -0
- package/scripts/pick-gyp-python.sh +61 -0
- package/scripts/release-gates.mjs +14 -0
- package/public/dist/assets/Agent-BgkP4EnS.js +0 -1
- package/public/dist/assets/ModelProvider-CgUvs6pE.js +0 -1
- package/public/dist/assets/app-BGIAy4he.js +0 -55
- package/public/dist/assets/bgtask-badge-GRSiNILQ.js +0 -1
- package/public/dist/assets/iframe-renderer-BUSk0Rw9.js +0 -1
- package/public/dist/assets/memory-CX6YahTc.js +0 -1
- package/public/dist/assets/message-history-Det7u76S.js +0 -1
- package/public/dist/assets/settings-C7NutP6X.js +0 -1
- package/public/dist/assets/settings-core-CyX-px6_.js +0 -100
- package/public/dist/assets/settings-core-DVpFmrhY.js +0 -1
- package/public/dist/assets/skills-BWhKreQ2.js +0 -1
- package/public/dist/assets/slash-commands-Di4NDA4w.js +0 -1
- package/public/dist/assets/ui-BghexWrB.js +0 -1
|
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
|
|
4
4
|
import { TextField, SelectField, ToggleField } from '../../fields';
|
|
5
5
|
import type { SettingsClient } from '../../types';
|
|
6
6
|
import type { DirtyEntry } from '../../types';
|
|
7
|
+
import { coerceEffortForModel, effortChoicesForModel } from './agent/agent-meta';
|
|
7
8
|
import type { CliMeta, PerCliEntry } from './agent/agent-meta';
|
|
8
9
|
import { PiProfileDialog } from './PiProfileDialog';
|
|
9
10
|
import { piModelOptions, piProfileOptions, type PiSettingsView } from './pi-profile';
|
|
@@ -36,9 +37,10 @@ export function PerCliRow({ cli, meta, original, value, setValue, setEntry, clie
|
|
|
36
37
|
: hasProviders
|
|
37
38
|
? (meta.modelsByProvider?.[provider] ?? meta.models)
|
|
38
39
|
: meta.models;
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const providerEfforts = hasProviders ? (meta.effortsByProvider?.[provider] ?? meta.efforts) : undefined;
|
|
41
|
+
// Per-model effort sets from a live opencodex catalog win over the
|
|
42
|
+
// provider/registry lists; the chosen value is forwarded to the wire.
|
|
43
|
+
const effortOptions = effortChoicesForModel(meta, value.model || '', providerEfforts, hasProviders ? provider : undefined);
|
|
42
44
|
|
|
43
45
|
return (
|
|
44
46
|
<div className="settings-percli-row" data-cli={cli}>
|
|
@@ -68,7 +70,9 @@ export function PerCliRow({ cli, meta, original, value, setValue, setEntry, clie
|
|
|
68
70
|
const nextModels = meta.modelsByProvider?.[next] ?? [];
|
|
69
71
|
const nextEfforts = meta.effortsByProvider?.[next] ?? [];
|
|
70
72
|
const nextModel = nextModels.includes(value.model || '') ? (value.model || '') : (nextModels[0] || '');
|
|
71
|
-
|
|
73
|
+
// Resolve against the NEW provider+model pair: the same
|
|
74
|
+
// model id can allow different efforts per provider.
|
|
75
|
+
const nextEffort = coerceEffortForModel(meta, nextModel, value.effort ?? '', nextEfforts, next);
|
|
72
76
|
setValue({ ...value, provider: next, model: nextModel, effort: nextEffort });
|
|
73
77
|
setEntry(`perCli.${cli}.provider`, entryFor(next, original.provider ?? meta.defaultProvider ?? 'claude'));
|
|
74
78
|
setEntry(`perCli.${cli}.model`, entryFor(nextModel, original.model ?? ''));
|
|
@@ -84,8 +88,12 @@ export function PerCliRow({ cli, meta, original, value, setValue, setEntry, clie
|
|
|
84
88
|
value={value.model ?? modelOptions[0] ?? ''}
|
|
85
89
|
options={modelOptions.map((m) => ({ value: m, label: m }))}
|
|
86
90
|
onChange={(next) => {
|
|
87
|
-
|
|
91
|
+
const nextEffort = coerceEffortForModel(meta, next, value.effort ?? '', providerEfforts, hasProviders ? provider : undefined);
|
|
92
|
+
setValue({ ...value, model: next, effort: nextEffort });
|
|
88
93
|
setEntry(`perCli.${cli}.model`, entryFor(next, original.model ?? ''));
|
|
94
|
+
if (nextEffort !== (value.effort ?? '')) {
|
|
95
|
+
setEntry(`perCli.${cli}.effort`, entryFor(nextEffort, original.effort ?? ''));
|
|
96
|
+
}
|
|
89
97
|
}}
|
|
90
98
|
/>
|
|
91
99
|
) : (
|
|
@@ -6,6 +6,22 @@ export type CliMeta = {
|
|
|
6
6
|
providers?: ReadonlyArray<string>;
|
|
7
7
|
modelsByProvider?: Record<string, ReadonlyArray<string>>;
|
|
8
8
|
effortsByProvider?: Record<string, ReadonlyArray<string>>;
|
|
9
|
+
/**
|
|
10
|
+
* Per-model reasoning-effort sets from a live opencodex catalog. An entry
|
|
11
|
+
* that exists but is EMPTY means the model takes no effort at all, so it
|
|
12
|
+
* must not fall back to `efforts`.
|
|
13
|
+
*/
|
|
14
|
+
effortsByModel?: Record<string, ReadonlyArray<string>>;
|
|
15
|
+
defaultEffortByModel?: Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* Provider-scoped per-model efforts, used by runtimes whose model list is
|
|
18
|
+
* split by provider (`ai-e`). A flat map would collide on ids shared across
|
|
19
|
+
* providers — `gpt-5.6-sol` exists under both codex and kiro with different
|
|
20
|
+
* allowed efforts.
|
|
21
|
+
*/
|
|
22
|
+
effortsByModelByProvider?: Record<string, Record<string, ReadonlyArray<string>>>;
|
|
23
|
+
defaultEffortByModelByProvider?: Record<string, Record<string, string>>;
|
|
24
|
+
modelSource?: string;
|
|
9
25
|
effortNote?: string;
|
|
10
26
|
modelNote?: string;
|
|
11
27
|
};
|
|
@@ -230,6 +246,35 @@ function stringArrayRecord(value: unknown): Record<string, string[]> | undefined
|
|
|
230
246
|
return out;
|
|
231
247
|
}
|
|
232
248
|
|
|
249
|
+
function stringRecord(value: unknown): Record<string, string> | undefined {
|
|
250
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
251
|
+
const out: Record<string, string> = {};
|
|
252
|
+
for (const [key, raw] of Object.entries(value as Record<string, unknown>)) {
|
|
253
|
+
if (typeof raw === 'string') out[key] = raw;
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function nestedStringArrayRecord(value: unknown): Record<string, Record<string, string[]>> | undefined {
|
|
259
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
260
|
+
const out: Record<string, Record<string, string[]>> = {};
|
|
261
|
+
for (const [key, raw] of Object.entries(value as Record<string, unknown>)) {
|
|
262
|
+
const inner = stringArrayRecord(raw);
|
|
263
|
+
if (inner) out[key] = inner;
|
|
264
|
+
}
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function nestedStringRecord(value: unknown): Record<string, Record<string, string>> | undefined {
|
|
269
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
270
|
+
const out: Record<string, Record<string, string>> = {};
|
|
271
|
+
for (const [key, raw] of Object.entries(value as Record<string, unknown>)) {
|
|
272
|
+
const inner = stringRecord(raw);
|
|
273
|
+
if (inner) out[key] = inner;
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
|
|
233
278
|
export function normalizeCliMetaRegistry(raw: unknown): Record<string, CliMeta> {
|
|
234
279
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
|
|
235
280
|
const out: Record<string, CliMeta> = {};
|
|
@@ -238,6 +283,10 @@ export function normalizeCliMetaRegistry(raw: unknown): Record<string, CliMeta>
|
|
|
238
283
|
const record = value as Record<string, unknown>;
|
|
239
284
|
const modelsByProvider = stringArrayRecord(record['modelsByProvider']);
|
|
240
285
|
const effortsByProvider = stringArrayRecord(record['effortsByProvider']);
|
|
286
|
+
const effortsByModel = stringArrayRecord(record['effortsByModel']);
|
|
287
|
+
const defaultEffortByModel = stringRecord(record['defaultEffortByModel']);
|
|
288
|
+
const effortsByModelByProvider = nestedStringArrayRecord(record['effortsByModelByProvider']);
|
|
289
|
+
const defaultEffortByModelByProvider = nestedStringRecord(record['defaultEffortByModelByProvider']);
|
|
241
290
|
out[key] = {
|
|
242
291
|
label: typeof record['label'] === 'string' ? record['label'] : key,
|
|
243
292
|
models: stringArray(record['models']),
|
|
@@ -246,6 +295,11 @@ export function normalizeCliMetaRegistry(raw: unknown): Record<string, CliMeta>
|
|
|
246
295
|
...(record['providers'] ? { providers: stringArray(record['providers']) } : {}),
|
|
247
296
|
...(modelsByProvider ? { modelsByProvider } : {}),
|
|
248
297
|
...(effortsByProvider ? { effortsByProvider } : {}),
|
|
298
|
+
...(effortsByModel ? { effortsByModel } : {}),
|
|
299
|
+
...(defaultEffortByModel ? { defaultEffortByModel } : {}),
|
|
300
|
+
...(effortsByModelByProvider ? { effortsByModelByProvider } : {}),
|
|
301
|
+
...(defaultEffortByModelByProvider ? { defaultEffortByModelByProvider } : {}),
|
|
302
|
+
...(typeof record['modelSource'] === 'string' ? { modelSource: record['modelSource'] } : {}),
|
|
249
303
|
...(typeof record['effortNote'] === 'string' ? { effortNote: record['effortNote'] } : {}),
|
|
250
304
|
...(typeof record['modelNote'] === 'string' ? { modelNote: record['modelNote'] } : {}),
|
|
251
305
|
};
|
|
@@ -253,6 +307,50 @@ export function normalizeCliMetaRegistry(raw: unknown): Record<string, CliMeta>
|
|
|
253
307
|
return out;
|
|
254
308
|
}
|
|
255
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Effort choices for one model.
|
|
312
|
+
*
|
|
313
|
+
* A live opencodex catalog advertises a different effort set per model, and the
|
|
314
|
+
* chosen value reaches the wire as `-c model_reasoning_effort=`, so the
|
|
315
|
+
* per-model set wins. An entry that exists but is EMPTY means the model takes
|
|
316
|
+
* no effort and must not fall back to the provider/registry lists.
|
|
317
|
+
*/
|
|
318
|
+
export function effortChoicesForModel(
|
|
319
|
+
meta: CliMeta,
|
|
320
|
+
model: string,
|
|
321
|
+
providerEfforts?: ReadonlyArray<string>,
|
|
322
|
+
provider?: string,
|
|
323
|
+
): ReadonlyArray<string> {
|
|
324
|
+
const key = (model || '').trim();
|
|
325
|
+
// Provider-scoped map wins for provider-split runtimes. When the map exists
|
|
326
|
+
// but the model is absent (e.g. a Claude model under ai-e/claude), fall back
|
|
327
|
+
// to the provider list rather than inventing an empty "no effort" set.
|
|
328
|
+
if (provider) {
|
|
329
|
+
const scoped = meta.effortsByModelByProvider?.[provider]?.[key];
|
|
330
|
+
if (scoped) return scoped;
|
|
331
|
+
}
|
|
332
|
+
const byModel = meta.effortsByModel?.[key];
|
|
333
|
+
if (byModel) return byModel;
|
|
334
|
+
return providerEfforts ?? meta.efforts;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Keep a saved effort only when the model still advertises it. */
|
|
338
|
+
export function coerceEffortForModel(
|
|
339
|
+
meta: CliMeta,
|
|
340
|
+
model: string,
|
|
341
|
+
effort: string,
|
|
342
|
+
providerEfforts?: ReadonlyArray<string>,
|
|
343
|
+
provider?: string,
|
|
344
|
+
): string {
|
|
345
|
+
const choices = effortChoicesForModel(meta, model, providerEfforts, provider);
|
|
346
|
+
if (effort && choices.includes(effort)) return effort;
|
|
347
|
+
const key = (model || '').trim();
|
|
348
|
+
const fallback = (provider ? meta.defaultEffortByModelByProvider?.[provider]?.[key] : undefined)
|
|
349
|
+
?? meta.defaultEffortByModel?.[key]
|
|
350
|
+
?? '';
|
|
351
|
+
return choices.includes(fallback) ? fallback : '';
|
|
352
|
+
}
|
|
353
|
+
|
|
256
354
|
export function metaFor(cli: string, registry?: Record<string, CliMeta> | null): CliMeta {
|
|
257
355
|
return registry?.[cli] || CLI_META[cli] || { label: cli, models: [], efforts: [] };
|
|
258
356
|
}
|
|
@@ -18,6 +18,11 @@ install_locked_production_dependencies() {
|
|
|
18
18
|
|
|
19
19
|
echo "=== Bundling sidecar: $PLATFORM-$ARCH ==="
|
|
20
20
|
|
|
21
|
+
# Refuse to build a sidecar whose prune list would strip a runtime dependency.
|
|
22
|
+
# Failing here costs a few seconds; failing later costs a shipped app that dies
|
|
23
|
+
# on first use, which is exactly what happened with node-fetch.
|
|
24
|
+
node "$PROJECT_ROOT/scripts/check-sidecar-prune-safety.mjs"
|
|
25
|
+
|
|
21
26
|
rm -rf "$SIDECAR_DIR"
|
|
22
27
|
mkdir -p "$SIDECAR_DIR/bin"
|
|
23
28
|
|
|
@@ -57,14 +62,20 @@ cd "$SIDECAR_DIR"
|
|
|
57
62
|
install_locked_production_dependencies
|
|
58
63
|
|
|
59
64
|
echo "Pruning frontend-only dependencies..."
|
|
65
|
+
# Every entry here is deleted from the bundled sidecar, so a package the server
|
|
66
|
+
# imports must never appear. It did: node-fetch sat in this list from the commit
|
|
67
|
+
# that created this script while src/telegram/bot.ts imports it, and every
|
|
68
|
+
# packaged desktop app died with ERR_MODULE_NOT_FOUND the moment that module
|
|
69
|
+
# loaded. check-sidecar-prune-safety.mjs now fails the build when this list and
|
|
70
|
+
# the server's real imports disagree.
|
|
60
71
|
PRUNE_PKGS=(
|
|
61
72
|
"@codemirror/autocomplete" "@codemirror/lang-markdown" "@codemirror/language"
|
|
62
73
|
"@codemirror/language-data" "@codemirror/state" "@codemirror/view"
|
|
63
74
|
"@lezer/highlight" "@lucide/icons" "@milkdown/kit" "@replit/codemirror-vim"
|
|
64
75
|
"@tanstack/virtual-core" "@uiw/react-codemirror" "@xterm/addon-fit" "@xterm/xterm"
|
|
65
|
-
"d3" "dompurify" "
|
|
76
|
+
"d3" "dompurify" "katex" "marked-highlight" "mermaid"
|
|
66
77
|
"react" "react-dom" "react-markdown" "rehype-katex" "rehype-sanitize"
|
|
67
|
-
"remark-breaks" "remark-gfm" "remark-math"
|
|
78
|
+
"remark-breaks" "remark-gfm" "remark-math"
|
|
68
79
|
)
|
|
69
80
|
for pkg in "${PRUNE_PKGS[@]}"; do
|
|
70
81
|
rm -rf "$SIDECAR_DIR/node_modules/$pkg" 2>/dev/null || true
|
|
@@ -77,8 +88,6 @@ rm -rf "$SIDECAR_DIR/node_modules/@vue" 2>/dev/null || true
|
|
|
77
88
|
rm -rf "$SIDECAR_DIR/node_modules/cytoscape" 2>/dev/null || true
|
|
78
89
|
rm -rf "$SIDECAR_DIR/node_modules/cytoscape-fcose" 2>/dev/null || true
|
|
79
90
|
rm -rf "$SIDECAR_DIR/node_modules/es-toolkit" 2>/dev/null || true
|
|
80
|
-
rm -rf "$SIDECAR_DIR/node_modules/lodash" 2>/dev/null || true
|
|
81
|
-
rm -rf "$SIDECAR_DIR/node_modules/web-streams-polyfill" 2>/dev/null || true
|
|
82
91
|
|
|
83
92
|
echo "Removing stale .bin symlinks after dependency pruning..."
|
|
84
93
|
find "$SIDECAR_DIR/node_modules/.bin" -type l ! -exec test -e {} \; -print -delete 2>/dev/null || true
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fail when the sidecar prune list would delete something the server imports.
|
|
4
|
+
*
|
|
5
|
+
* `scripts/bundle-sidecar.sh` ships a hand-written list of "frontend-only"
|
|
6
|
+
* packages and `rm -rf`s them out of the bundled sidecar. The list was wrong
|
|
7
|
+
* from the commit that introduced it: `node-fetch` sat in it while
|
|
8
|
+
* `src/telegram/bot.ts` imports it, so any packaged desktop app died the
|
|
9
|
+
* moment that module loaded --
|
|
10
|
+
*
|
|
11
|
+
* ERR_MODULE_NOT_FOUND: Cannot find package 'node-fetch'
|
|
12
|
+
* imported from .../Resources/server/dist/src/telegram/bot.js
|
|
13
|
+
*
|
|
14
|
+
* The dependency graph cannot answer this on its own: frontend and server
|
|
15
|
+
* packages both live in `dependencies`, so "reachable from package.json" marks
|
|
16
|
+
* 34 of the 37 pruned entries as unsafe and tells you nothing. What actually
|
|
17
|
+
* separates them is whether SERVER source imports the package -- so that is
|
|
18
|
+
* what this checks, and it also follows each kept package's own dependencies,
|
|
19
|
+
* because deleting a transitive one breaks the import just as thoroughly.
|
|
20
|
+
* `web-streams-polyfill` is exactly that case: nothing in src/ names it, but
|
|
21
|
+
* node-fetch -> fetch-blob -> web-streams-polyfill, and it was on the list too.
|
|
22
|
+
*/
|
|
23
|
+
import fs from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
|
|
27
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Trees whose imports must survive pruning.
|
|
31
|
+
*
|
|
32
|
+
* `dist` is what the sidecar actually runs, so it is the authority; the
|
|
33
|
+
* TypeScript sources are scanned too because a fresh checkout may not have
|
|
34
|
+
* built yet and a checker that silently passes on a missing directory is worse
|
|
35
|
+
* than one that over-approximates.
|
|
36
|
+
*/
|
|
37
|
+
const SERVER_SOURCES = ['dist', 'src', 'bin', 'server.ts'];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Packages loaded by a computed specifier, which no regex over the source can
|
|
41
|
+
* find. `src/agent/jwc-runtime.ts` does `import(spec)` where spec comes from an
|
|
42
|
+
* env var or a default constant, and the audit was right that the scan misses
|
|
43
|
+
* it. Listing them here is a deliberate escape hatch: each entry names why it
|
|
44
|
+
* cannot be discovered automatically.
|
|
45
|
+
*/
|
|
46
|
+
const RUNTIME_LOADED = [
|
|
47
|
+
// JWC SDK: `import(spec)` with spec from JWC_SDK_PATH or DEFAULT_JWC_SDK_SPEC.
|
|
48
|
+
'@jawcode/engine-sdk',
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/** Packages the prune step removes, read from the shell script itself. */
|
|
52
|
+
function prunedPackages() {
|
|
53
|
+
const script = fs.readFileSync(path.join(repoRoot, 'scripts/bundle-sidecar.sh'), 'utf8');
|
|
54
|
+
|
|
55
|
+
const listBlock = script.match(/PRUNE_PKGS=\(([\s\S]*?)\n\)/);
|
|
56
|
+
if (!listBlock) throw new Error('PRUNE_PKGS array not found in bundle-sidecar.sh');
|
|
57
|
+
const listed = [...listBlock[1].matchAll(/"([^"]+)"/g)].map((m) => m[1]);
|
|
58
|
+
|
|
59
|
+
// The script also removes a handful of packages with individual rm -rf
|
|
60
|
+
// lines below the loop. Those are just as capable of breaking the sidecar,
|
|
61
|
+
// so they are checked with the same rule rather than trusted.
|
|
62
|
+
const individual = [...script.matchAll(
|
|
63
|
+
/rm -rf "\$SIDECAR_DIR\/node_modules\/([^"$]+)"/g)].map((m) => m[1]);
|
|
64
|
+
|
|
65
|
+
return [...new Set([...listed, ...individual])];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Every file under the server source trees. */
|
|
69
|
+
function serverFiles() {
|
|
70
|
+
const files = [];
|
|
71
|
+
const walk = (rel) => {
|
|
72
|
+
const abs = path.join(repoRoot, rel);
|
|
73
|
+
if (!fs.existsSync(abs)) return;
|
|
74
|
+
if (fs.statSync(abs).isFile()) { files.push(abs); return; }
|
|
75
|
+
for (const entry of fs.readdirSync(abs)) {
|
|
76
|
+
if (entry === 'node_modules' || entry.startsWith('.')) continue;
|
|
77
|
+
walk(path.join(rel, entry));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
SERVER_SOURCES.forEach(walk);
|
|
81
|
+
return files.filter((f) => /\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(f));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Bare package specifiers imported anywhere in the server sources. */
|
|
85
|
+
function serverImports() {
|
|
86
|
+
const found = new Set();
|
|
87
|
+
const patterns = [
|
|
88
|
+
/\bfrom\s+['"]([^'".][^'"]*)['"]/g,
|
|
89
|
+
/\brequire\(\s*['"]([^'".][^'"]*)['"]\s*\)/g,
|
|
90
|
+
/\bimport\(\s*['"]([^'".][^'"]*)['"]\s*\)/g,
|
|
91
|
+
];
|
|
92
|
+
for (const file of serverFiles()) {
|
|
93
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
94
|
+
for (const re of patterns) {
|
|
95
|
+
for (const m of text.matchAll(re)) {
|
|
96
|
+
const spec = m[1];
|
|
97
|
+
if (spec.startsWith('node:')) continue;
|
|
98
|
+
// Scoped names keep two segments, plain names keep one.
|
|
99
|
+
const name = spec.startsWith('@')
|
|
100
|
+
? spec.split('/').slice(0, 2).join('/')
|
|
101
|
+
: spec.split('/')[0];
|
|
102
|
+
found.add(name);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return found;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Everything reachable from `roots` through installed package dependencies. */
|
|
110
|
+
function closure(roots) {
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const stack = [...roots];
|
|
113
|
+
while (stack.length > 0) {
|
|
114
|
+
const name = stack.pop();
|
|
115
|
+
if (seen.has(name)) continue;
|
|
116
|
+
seen.add(name);
|
|
117
|
+
const manifest = path.join(repoRoot, 'node_modules', name, 'package.json');
|
|
118
|
+
if (!fs.existsSync(manifest)) continue;
|
|
119
|
+
let manifestJson;
|
|
120
|
+
try {
|
|
121
|
+
manifestJson = JSON.parse(fs.readFileSync(manifest, 'utf8'));
|
|
122
|
+
} catch { continue; }
|
|
123
|
+
// Follow optional and peer edges too. A package that resolves one of
|
|
124
|
+
// them at runtime fails identically to a missing hard dependency, and
|
|
125
|
+
// over-keeping costs disk while under-keeping ships a broken app.
|
|
126
|
+
const deps = {
|
|
127
|
+
...(manifestJson.dependencies || {}),
|
|
128
|
+
...(manifestJson.optionalDependencies || {}),
|
|
129
|
+
...(manifestJson.peerDependencies || {}),
|
|
130
|
+
};
|
|
131
|
+
for (const dep of Object.keys(deps)) if (!seen.has(dep)) stack.push(dep);
|
|
132
|
+
}
|
|
133
|
+
return seen;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function findUnsafePrunes() {
|
|
137
|
+
const pruned = prunedPackages();
|
|
138
|
+
const imported = serverImports();
|
|
139
|
+
for (const pkg of RUNTIME_LOADED) imported.add(pkg);
|
|
140
|
+
// Only walk from packages the server actually imports; the closure of every
|
|
141
|
+
// dependency would sweep the frontend back in.
|
|
142
|
+
const required = closure([...imported]);
|
|
143
|
+
|
|
144
|
+
return pruned
|
|
145
|
+
.filter((pkg) => required.has(pkg))
|
|
146
|
+
.map((pkg) => ({
|
|
147
|
+
pkg,
|
|
148
|
+
direct: imported.has(pkg),
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
153
|
+
const unsafe = findUnsafePrunes();
|
|
154
|
+
if (unsafe.length === 0) {
|
|
155
|
+
console.log('✅ sidecar prune list removes nothing the server needs');
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
console.error(`❌ sidecar prune list — ${unsafe.length} package(s) the server needs:`);
|
|
159
|
+
for (const { pkg, direct } of unsafe) {
|
|
160
|
+
console.error(` ${pkg} — ${direct
|
|
161
|
+
? 'imported directly by server source'
|
|
162
|
+
: 'required transitively by a package the server imports'}`);
|
|
163
|
+
}
|
|
164
|
+
console.error('\nRemove them from scripts/bundle-sidecar.sh, or the packaged app will');
|
|
165
|
+
console.error('die with ERR_MODULE_NOT_FOUND the first time that code path runs.');
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Print a python3 that node-gyp can actually run under.
|
|
3
|
+
#
|
|
4
|
+
# node-gyp's bundled gyp still does `from distutils.version import StrictVersion`,
|
|
5
|
+
# and distutils was removed in Python 3.12. On a machine whose default python3 is
|
|
6
|
+
# Homebrew 3.12+ the native rebuild dies with:
|
|
7
|
+
#
|
|
8
|
+
# ModuleNotFoundError: No module named 'distutils'
|
|
9
|
+
# ⨯ node-gyp failed to rebuild '.../node_modules/node-pty'
|
|
10
|
+
#
|
|
11
|
+
# Observed on macOS 27 with Homebrew Python 3.14.6 while building the desktop app
|
|
12
|
+
# on a second machine. macOS still ships /usr/bin/python3 (3.9.6), which has
|
|
13
|
+
# distutils, so a working interpreter is usually present -- it just is not the
|
|
14
|
+
# one on PATH.
|
|
15
|
+
#
|
|
16
|
+
# CI already knew: .github/workflows/desktop-release.yml pins actions/setup-python
|
|
17
|
+
# to 3.11 with exactly this reasoning in a comment. The local build path never got
|
|
18
|
+
# the same treatment, so `npm run electron:dist:mac` on a developer machine was the
|
|
19
|
+
# one route into node-gyp with no defense.
|
|
20
|
+
#
|
|
21
|
+
# Candidates are TESTED, not assumed: a hardcoded path is the same bet that broke
|
|
22
|
+
# here, one Python release later. An explicit $PYTHON still wins, because the
|
|
23
|
+
# caller may know something this script does not.
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
|
|
26
|
+
has_distutils() {
|
|
27
|
+
[ -x "$1" ] || return 1
|
|
28
|
+
"$1" -c 'import distutils' >/dev/null 2>&1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if [ -n "${PYTHON:-}" ]; then
|
|
32
|
+
printf '%s\n' "$PYTHON"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
for candidate in \
|
|
37
|
+
/usr/bin/python3 \
|
|
38
|
+
"$(command -v python3.11 2>/dev/null || true)" \
|
|
39
|
+
"$(command -v python3.10 2>/dev/null || true)" \
|
|
40
|
+
"$(command -v python3.9 2>/dev/null || true)" \
|
|
41
|
+
"$(command -v python3 2>/dev/null || true)"
|
|
42
|
+
do
|
|
43
|
+
[ -n "$candidate" ] || continue
|
|
44
|
+
if has_distutils "$candidate"; then
|
|
45
|
+
printf '%s\n' "$candidate"
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
# Nothing usable. Fall back to whatever python3 exists so the caller fails with
|
|
51
|
+
# node-gyp's own message rather than an empty variable, and say why.
|
|
52
|
+
fallback="$(command -v python3 2>/dev/null || true)"
|
|
53
|
+
if [ -n "$fallback" ]; then
|
|
54
|
+
echo "[gyp-python] no python3 with distutils found; falling back to $fallback" >&2
|
|
55
|
+
echo "[gyp-python] node-gyp will likely fail. Install python@3.11 or set PYTHON=<path>." >&2
|
|
56
|
+
printf '%s\n' "$fallback"
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
echo "[gyp-python] no python3 on PATH at all" >&2
|
|
61
|
+
exit 1
|
|
@@ -67,6 +67,10 @@ const GATES = {
|
|
|
67
67
|
'tests/unit/browser-web-ai-source-audit.test.ts',
|
|
68
68
|
'tests/unit/browser-web-ai-cli-contract.test.ts',
|
|
69
69
|
'tests/unit/release-gates.test.ts',
|
|
70
|
+
// Guards the release/build scripts themselves, so it belongs in
|
|
71
|
+
// the suite gate:all actually runs rather than npm test only.
|
|
72
|
+
'tests/unit/electron-version-sync.test.ts',
|
|
73
|
+
'tests/unit/gyp-python-pick.test.ts',
|
|
70
74
|
].filter((p) => fs.existsSync(path.join(repoRoot, p)));
|
|
71
75
|
if (targets.length === 0) {
|
|
72
76
|
return { ok: false, detail: 'no Phase 22 mirror tests found' };
|
|
@@ -414,6 +418,16 @@ const GATES = {
|
|
|
414
418
|
return { ok: true, detail: 'desktop artifacts will carry the release version' };
|
|
415
419
|
},
|
|
416
420
|
},
|
|
421
|
+
'sidecar-prune-safety': {
|
|
422
|
+
description: 'the sidecar prune list never deletes a package the server imports',
|
|
423
|
+
check() {
|
|
424
|
+
const r = run('node', ['scripts/check-sidecar-prune-safety.mjs'], { timeout: 60_000 });
|
|
425
|
+
if (r.status !== 0) {
|
|
426
|
+
return { ok: false, detail: (r.stdout || r.stderr || '').trim().slice(-800) };
|
|
427
|
+
}
|
|
428
|
+
return { ok: true, detail: 'packaged sidecar keeps every runtime dependency' };
|
|
429
|
+
},
|
|
430
|
+
},
|
|
417
431
|
'gate-docs': {
|
|
418
432
|
description: 'structure/INDEX.md documents exactly the gates that exist, and each is npm-addressable',
|
|
419
433
|
check() {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-XQCOJYun.js";import{n as t,t as n}from"./jsx-runtime-gFMgZ0TQ.js";import{a as r,i,n as a,r as o,t as s}from"./page-shell-LF7vVVfG.js";import{i as c,s as l}from"./fields-zkeByFQW.js";import{a as u,n as d,o as f}from"./Permissions-CnKt44dN.js";import{a as p,i as m,n as h,o as g,r as _,s as v,t as y}from"./agent-meta-C8DqYXM6.js";import{t as b}from"./path-utils-LsJeDCKF.js";var x=e(t(),1),S=n();function C({cli:e,cliOptions:t,provider:n=``,providerOptions:r=[],model:a,modelOptions:o,effort:s,effortOptions:u,workingDir:d,workingDirError:f,cliMeta:m,onCliChange:g,onProviderChange:_,onModelChange:v,onEffortChange:b,onWorkingDirChange:x}){let C=p(t),w=C.filter(e=>y.includes(e)).length;return(0,S.jsx)(i,{title:`Agent runtime`,hint:`Active CLI, model, effort, and workspace used by this instance.`,children:(0,S.jsxs)(`div`,{className:`settings-agent-runtime-grid`,children:[(0,S.jsx)(c,{id:`agent-cli`,label:`Active CLI`,value:e,options:C.map(e=>({value:e,label:h(e,m).label||e})),collapsedAfter:w,onChange:g}),r.length>0?(0,S.jsx)(c,{id:`agent-${e}-provider`,label:`Provider`,value:n,options:r.map(e=>({value:e,label:e})),onChange:e=>_?.(e)}):null,(0,S.jsx)(c,{id:`agent-model`,label:`Active model`,value:a,options:o.length>0?o:[{value:``,label:`(default)`}],onChange:v}),(0,S.jsx)(c,{id:`agent-effort`,label:`Effort`,value:s,options:[{value:``,label:`(default)`},...u.map(e=>({value:e,label:e}))],disabled:u.length===0,onChange:b}),(0,S.jsx)(l,{id:`agent-workingDir`,label:`Working directory`,value:d,error:f,placeholder:`/path/to/project`,onChange:x})]})})}var w=[{value:`auto`,label:`Auto`},{value:`custom`,label:`Custom allowlist`}];function ee({value:e,onChange:t}){let n=u(e),r=n.mode===`custom`?`custom`:`auto`,a=n.mode===`custom`?n.tokens:[],o=n.mode===`custom`?`${a.length} explicit token${a.length===1?``:`s`}`:`Runtime resolves allowed capabilities`;return(0,S.jsxs)(i,{title:`Permissions`,hint:`Quick runtime mode. Open Permissions for detailed allowlist editing.`,children:[(0,S.jsx)(c,{id:`agent-permissions-mode`,label:`Mode`,value:r,options:w,onChange:e=>{t(e===`auto`?`auto`:a.length>0&&d(a)?a:f(null))}}),(0,S.jsx)(`p`,{className:`settings-agent-note`,children:o})]})}function te({activeCli:e,flushCli:t,flushModel:n,cliOptions:r,modelOptions:a,cliMeta:o,loading:s,error:l,onFlushCliChange:u,onFlushModelChange:d}){let f=t||e,p=f?h(f,o).label||f:`active`;return(0,S.jsx)(i,{title:`Flush Agent`,hint:`Separate summary agent used for context flush and compact work.`,children:(0,S.jsxs)(`details`,{className:`settings-agent-flush`,children:[(0,S.jsxs)(`summary`,{children:[(0,S.jsx)(`span`,{children:`Flush runtime`}),(0,S.jsxs)(`code`,{children:[p,n&&n!==`default`?` / ${n}`:``]})]}),s?(0,S.jsx)(`p`,{className:`settings-agent-note`,children:`Loading flush settings...`}):null,l?(0,S.jsx)(`p`,{className:`settings-field-error`,role:`alert`,children:l}):null,(0,S.jsxs)(`div`,{className:`settings-agent-runtime-grid`,children:[(0,S.jsx)(c,{id:`agent-flush-cli`,label:`Flush CLI`,value:t,options:[{value:``,label:`(active CLI)`},...r.map(e=>({value:e,label:h(e,o).label||e}))],onChange:u}),(0,S.jsx)(c,{id:`agent-flush-model`,label:`Flush model`,value:n,options:[{value:``,label:`(default)`},...a],onChange:d})]})]})})}function T(e){return e.source===`static`||e.id.startsWith(`static:`)}function E(){return`new:${Date.now().toString(36)}:${Math.random().toString(36).slice(2,8)}`}function D(e,t){let n=e[0]||`claude`,r=t?.[n]?.models?.[0]||`default`;return{id:E(),name:`New Agent`,cli:n,model:r,role:``,status:`idle`,source:`db`}}function ne(e){return Array.isArray(e)?e.map(O).filter(Boolean):e.data.map(O).filter(Boolean)}function O(e){if(!e||typeof e!=`object`)return null;let t=e,n=typeof t.id==`string`?t.id:``,r=typeof t.name==`string`?t.name:``;if(!n||!r)return null;let i=t.source===`static`||n.startsWith(`static:`)?`static`:`db`,a={id:n,name:r,cli:typeof t.cli==`string`&&t.cli?t.cli:`claude`,model:typeof t.model==`string`&&t.model?t.model:`default`,role:typeof t.role==`string`?t.role:``,source:i};return typeof t.status==`string`&&(a.status=t.status),a}function k(e){return e.name.trim()?e.cli.trim()?e.model.trim()?null:`Model is required`:`CLI is required`:`Name is required`}function A(e){return e.some(e=>k(e)!==null)}function re(e,t){return JSON.stringify(e.map(I))===JSON.stringify(t.map(I))}function j(e,t){let n=new Map(e.map(e=>[e.id,e])),r=new Set(t.map(e=>e.id)),i=[],a=[],o=[];for(let e of t){let t=n.get(e.id);if(!t||e.id.startsWith(`new:`)){i.push(e);continue}let r=P(t,e);Object.keys(r).length>0&&a.push({before:t,after:e,patch:r})}for(let t of e)!r.has(t.id)&&!T(t)&&o.push(t);return{added:i,updated:a,removed:o}}function M(e,t){let n=j(e,t);return{added:n.added.length,updated:n.updated.length,removed:n.removed.length}}async function N(e,t,n){let r=j(t,n);for(let t of r.removed)await e.delete(`/api/employees/${encodeURIComponent(t.id)}`);for(let t of r.added)await e.post(`/api/employees`,F(t));for(let t of r.updated)await e.put(`/api/employees/${encodeURIComponent(t.after.id)}`,t.patch)}function P(e,t){if(T(t))return e.model===t.model?{}:{model:t.model};let n={};for(let r of[`name`,`cli`,`model`,`role`,`status`])(e[r]||``)!==(t[r]||``)&&(n[r]=t[r]||``);return n}function F(e){return{name:e.name.trim()||`New Agent`,cli:e.cli.trim()||`claude`,model:e.model.trim()||`default`,role:e.role.trim()}}function I(e){let t={id:e.id,name:e.name,cli:e.cli,model:e.model,role:e.role,source:e.source};return e.status!==void 0&&(t.status=e.status),t}function L({employee:e,index:t,cliOptions:n,cliMeta:r,onChange:i,onRemove:a}){let o=T(e),s=m(h(e.cli,r).models,e.model),u=k(e),d=Array.from(new Set([...n,e.cli,`claude`])).filter(Boolean);return(0,S.jsxs)(`fieldset`,{className:`settings-runtime-employee-row`,children:[(0,S.jsxs)(`legend`,{children:[e.name||`Employee ${t+1}`,o?(0,S.jsx)(`span`,{children:`static`}):null]}),(0,S.jsxs)(`div`,{className:`settings-runtime-employee-grid`,children:[(0,S.jsx)(l,{id:`runtime-employee-${e.id}-name`,label:`Name`,value:e.name,disabled:o,error:u===`Name is required`?u:null,onChange:e=>i({name:e})}),(0,S.jsx)(c,{id:`runtime-employee-${e.id}-cli`,label:`CLI`,value:e.cli,disabled:o,options:d.map(e=>({value:e,label:h(e,r).label||e})),onChange:e=>{i({cli:e,model:h(e,r).models[0]||`default`})}}),(0,S.jsx)(c,{id:`runtime-employee-${e.id}-model`,label:`Model`,value:e.model,options:s.length>0?s:[{value:`default`,label:`default`}],onChange:e=>i({model:e})}),(0,S.jsx)(l,{id:`runtime-employee-${e.id}-role`,label:`Role`,value:e.role,disabled:o,onChange:e=>i({role:e})})]}),(0,S.jsxs)(`div`,{className:`settings-runtime-employee-footer`,children:[(0,S.jsxs)(`span`,{children:[e.source,e.status?` / ${e.status}`:``]}),(0,S.jsx)(`button`,{type:`button`,className:`settings-action settings-action-discard`,disabled:o,onClick:a,"aria-label":`Remove employee ${e.name||t+1}`,children:`Remove`})]})]})}function ie({roster:e,original:t,cliOptions:n,cliMeta:r,loading:a,error:o,onRosterChange:s}){let c=M(t,e),l=A(e);function u(t,n){s(e.map((e,r)=>r===t?{...e,...n}:e))}function d(t){s(e.filter((e,n)=>n!==t))}return(0,S.jsxs)(i,{title:`Employees`,hint:`Runtime dispatch roster. Static employees keep their locked identity; database employees can be edited.`,children:[(0,S.jsxs)(`div`,{className:`settings-runtime-employee-summary`,children:[(0,S.jsxs)(`span`,{children:[`+`,c.added]}),(0,S.jsxs)(`span`,{children:[`~`,c.updated]}),(0,S.jsxs)(`span`,{children:[`-`,c.removed]}),l?(0,S.jsx)(`strong`,{children:`Fix invalid rows before saving.`}):null]}),a?(0,S.jsx)(`p`,{className:`settings-agent-note`,children:`Loading employees...`}):null,o?(0,S.jsx)(`p`,{className:`settings-field-error`,role:`alert`,children:o}):null,e.length===0?(0,S.jsx)(`p`,{className:`settings-empty`,children:`No runtime employees configured.`}):(0,S.jsx)(`div`,{className:`settings-runtime-employee-list`,children:e.map((e,t)=>(0,S.jsx)(L,{employee:e,index:t,cliOptions:n,cliMeta:r??null,onChange:e=>u(t,e),onRemove:()=>d(t)},e.id))}),(0,S.jsx)(`div`,{className:`settings-employee-footer-bar`,children:(0,S.jsx)(`button`,{type:`button`,className:`settings-action settings-action-discard`,onClick:()=>s([...e,D(n,r)]),children:`+ Add employee`})})]})}var R=new Set([`runtimeEmployees`,`flushCli`,`flushModel`]);function z(e){let t={},n=null,r=null;for(let[i,a]of Object.entries(e))i===`runtimeEmployees`?r=a:i===`flushCli`||i===`flushModel`?(n??={},n[i===`flushCli`?`cli`:`model`]=String(a??``)):R.has(i)||(t[i]=a);return{settingsBundle:t,flushPatch:n,runtimeEmployeesNext:r}}async function ae(e){let{client:t,bundle:n,employeeDraft:r,employeeOriginal:i}=e;if(A(r))throw Error(`runtime employees: fix invalid rows before saving`);let{settingsBundle:a,flushPatch:o,runtimeEmployeesNext:s}=z(n),c=null;try{if(Object.keys(a).length>0){let e=await t.put(`/api/settings`,b(a));c=e&&typeof e==`object`&&`data`in e?e.data:e}}catch(e){throw Error(`settings save failed: ${e instanceof Error?e.message:String(e)}`)}try{o&&await t.put(`/api/memory-files/settings`,o)}catch(e){throw Error(`flush settings save failed: ${e instanceof Error?e.message:String(e)}`)}try{s&&await N(t,i,s)}catch(e){throw Error(`runtime employees save failed: ${e instanceof Error?e.message:String(e)}`)}return c}function B({port:e,client:t,dirty:n,registerSave:i}){let{state:c,refresh:l,setData:d}=r(t,`/api/settings`),[f,p]=(0,x.useState)({cli:``,provider:``,model:``,effort:``,workingDir:``,permissions:`auto`}),[y,b]=(0,x.useState)({}),[w,T]=(0,x.useState)({}),[E,D]=(0,x.useState)(!0),[O,k]=(0,x.useState)(null),[j,M]=(0,x.useState)([]),[N,P]=(0,x.useState)([]),[F,I]=(0,x.useState)(!0),[L,R]=(0,x.useState)(null),[z,B]=(0,x.useState)(null),V=(0,x.useCallback)(async()=>{try{let e=await t.get(`/api/cli-registry`);B(_(e&&typeof e==`object`&&`data`in e?e.data:e))}catch{B(null)}},[t]),H=(0,x.useCallback)(async()=>{D(!0),k(null);try{let e=await t.get(`/api/memory-files`),n={cli:e.cli||``,model:e.model||``};b(n),T(n)}catch(e){k(e instanceof Error?e.message:String(e))}finally{D(!1)}},[t]),U=(0,x.useCallback)(async()=>{I(!0),R(null);try{let e=ne(await t.get(`/api/employees`));M(e),P(e)}catch(e){R(e instanceof Error?e.message:String(e))}finally{I(!1)}},[t]);(0,x.useEffect)(()=>{V(),H(),U()},[V,U,H]),(0,x.useEffect)(()=>{if(c.kind!==`ready`)return;let e=Object.keys(c.data.perCli||{}),t=c.data.cli||e[0]||``,n=u(c.data.permissions),r=h(t,z);p({cli:t,provider:c.data.perCli?.[t]?.provider||r.defaultProvider||``,model:v(t,c.data.perCli,c.data.activeOverrides),effort:g(t,c.data.perCli,c.data.activeOverrides),workingDir:c.data.workingDir||``,permissions:n.mode===`custom`?n.tokens:`auto`})},[z,c]),(0,x.useEffect)(()=>()=>{for(let e of Array.from(n.pending.keys()))(e===`cli`||e===`workingDir`||e===`permissions`||e===`runtimeEmployees`||e===`flushCli`||e===`flushModel`||e.startsWith(`activeOverrides.`))&&n.remove(e)},[n]);let W=(0,x.useCallback)((e,t)=>n.set(e,t),[n]),G=(0,x.useCallback)(async()=>{let e=n.saveBundle();if(Object.keys(e).length===0)return;let r=await ae({client:t,bundle:e,employeeDraft:N,employeeOriginal:j});n.clear(),r&&d(r),await l(),await V(),await H(),await U()},[t,n,N,j,V,U,H,l,d]);(0,x.useEffect)(()=>{if(i)return i(G),()=>i(null)},[i,G]);let K=c.kind===`ready`?c.data:{},q=K.perCli||{},J=K.activeOverrides||{},Y=(0,x.useMemo)(()=>Object.keys(q),[q]),X=h(f.cli,z),Z=f.provider||X.defaultProvider||X.providers?.[0]||``,Q=f.cli===`pi`,oe=!Q&&(X.providers?.length??0)>0,se=m(oe&&X.modelsByProvider?.[Z]||X.models,f.model),ce=oe&&X.effortsByProvider?.[Z]||X.efforts,le=f.workingDir.trim()?null:`Required`;if(c.kind===`loading`)return(0,S.jsx)(a,{});if(c.kind===`offline`)return(0,S.jsx)(o,{port:e});if(c.kind===`error`)return(0,S.jsx)(s,{message:c.message});function ue(){for(let e of Array.from(n.pending.keys()))e.startsWith(`activeOverrides.`)&&n.remove(e)}function $(e){p(e)}return(0,S.jsxs)(`form`,{className:`settings-page-form`,onSubmit:e=>{e.preventDefault(),G()},children:[(0,S.jsx)(C,{cli:f.cli,cliOptions:Y.length>0?Y:[f.cli||`claude`],provider:Z,providerOptions:Q?[]:X.providers||[],model:f.model,modelOptions:se,effort:f.effort,effortOptions:ce,workingDir:f.workingDir,workingDirError:le,cliMeta:z,onCliChange:e=>{let t=h(e,z),n={...f,cli:e,provider:q[e]?.provider||t.defaultProvider||``,model:v(e,q,J),effort:g(e,q,J)};ue(),$(n),W(`cli`,{value:e,original:K.cli||``,valid:!0})},onProviderChange:e=>{let t=X.modelsByProvider?.[e]||[],n=X.effortsByProvider?.[e]||[],r=t.includes(f.model)?f.model:t[0]||``,i=n.includes(f.effort)?f.effort:``;$({...f,provider:e,model:r,effort:i}),W(`perCli.${f.cli}.provider`,{value:e,original:q[f.cli]?.provider||X.defaultProvider||``,valid:!0}),W(`activeOverrides.${f.cli}.model`,{value:r,original:v(f.cli,q,J),valid:r.trim().length>0}),W(`activeOverrides.${f.cli}.effort`,{value:i,original:g(f.cli,q,J),valid:!0})},onModelChange:e=>{$({...f,model:e}),W(`activeOverrides.${f.cli}.model`,{value:e,original:v(f.cli,q,J),valid:e.trim().length>0})},onEffortChange:e=>{$({...f,effort:e}),W(`activeOverrides.${f.cli}.effort`,{value:e,original:g(f.cli,q,J),valid:!0})},onWorkingDirChange:e=>{$({...f,workingDir:e}),W(`workingDir`,{value:e,original:K.workingDir||``,valid:e.trim().length>0})}}),(0,S.jsx)(ee,{value:f.permissions,onChange:e=>{$({...f,permissions:e}),W(`permissions`,{value:e,original:K.permissions??`auto`,valid:e===`auto`||e.length>0})}}),(0,S.jsx)(te,{activeCli:f.cli,flushCli:w.cli||``,flushModel:w.model||``,cliOptions:Y,cliMeta:z,modelOptions:m(h(w.cli||f.cli,z).models,w.model||``),loading:E,error:O,onFlushCliChange:e=>{let t=e&&h(e,z).models[0]||``;T({cli:e,model:t}),W(`flushCli`,{value:e,original:y.cli||``,valid:!0}),W(`flushModel`,{value:t,original:y.model||``,valid:!0})},onFlushModelChange:e=>{T({...w,model:e}),W(`flushModel`,{value:e,original:y.model||``,valid:!0})}}),(0,S.jsx)(ie,{roster:N,original:j,cliOptions:Y,cliMeta:z,loading:F,error:L,onRosterChange:e=>{if(P(e),re(e,j)){n.remove(`runtimeEmployees`);return}n.set(`runtimeEmployees`,{value:e,original:j,valid:!A(e)})}})]})}export{B as default,z as splitAgentSaveBundle};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-XQCOJYun.js";import{n as t,t as n}from"./jsx-runtime-gFMgZ0TQ.js";import{a as r,i,n as a,r as o,t as s}from"./page-shell-LF7vVVfG.js";import{a as c,i as l,n as u,r as d,s as f}from"./fields-zkeByFQW.js";import{n as p,r as m}from"./agent-meta-C8DqYXM6.js";import{t as h}from"./path-utils-LsJeDCKF.js";var g=e(t(),1),_=n(),v=[`basic`,`openai`,`anthropic`,`vertex`];function y(e){return e===`anthropic`?`https://api.anthropic.com/v1/messages`:e===`vertex`?`https://LOCATION-aiplatform.googleapis.com/v1`:`http://127.0.0.1:18645/v1`}function b({client:e,pi:t,provider:n,model:r,onClose:i,onRegistered:a}){let o=(0,g.useMemo)(()=>t?.profiles?.find(e=>e.id===n)||t?.profiles?.[0],[t,n]),[s,c]=(0,g.useState)(o?.mode||`basic`),[u,d]=(0,g.useState)(o?.id||n||`progrok`),[p,m]=(0,g.useState)(o?.endpoint||y(s)),[h,b]=(0,g.useState)(r||o?.model||`grok-composer-2.5-fast`),[x,S]=(0,g.useState)(``),[C,w]=(0,g.useState)(!1),[T,E]=(0,g.useState)(null);(0,g.useEffect)(()=>{m(e=>e||y(s))},[s]);let D=async()=>{w(!0),E(null);try{let t=await e.post(`/api/pi/profiles/register`,{id:u,label:u,mode:s,endpoint:p,model:h,apiKey:x});a({provider:u,model:h,models:t.models||[],pi:t.settings?.pi}),i()}catch(e){E(e instanceof Error?e.message:String(e))}finally{w(!1)}};return(0,_.jsx)(`div`,{className:`settings-memory-modal`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`pi-profile-title`,children:(0,_.jsxs)(`div`,{className:`settings-memory-modal-card`,children:[(0,_.jsxs)(`header`,{className:`settings-memory-modal-header`,children:[(0,_.jsx)(`h3`,{id:`pi-profile-title`,children:`Pi Settings`}),(0,_.jsx)(`button`,{type:`button`,className:`settings-action settings-action-discard`,onClick:i,children:`Close`})]}),(0,_.jsxs)(`div`,{className:`settings-page-form`,children:[(0,_.jsx)(l,{id:`pi-profile-mode`,label:`Mode`,value:s,options:v.map(e=>({value:e,label:e})),onChange:e=>c(e)}),(0,_.jsx)(f,{id:`pi-profile-id`,label:`Provider`,value:u,onChange:d,placeholder:`progrok`}),(0,_.jsx)(f,{id:`pi-profile-endpoint`,label:`Endpoint`,value:p,onChange:m,placeholder:y(s)}),(0,_.jsx)(f,{id:`pi-profile-model`,label:`Model`,value:h,onChange:b,placeholder:`grok-composer-2.5-fast`}),(0,_.jsx)(f,{id:`pi-profile-key`,label:`API Key`,value:x,onChange:S,placeholder:o?.apiKeySet?`set (${o.apiKeyLast4||`****`})`:`empty for local proxy`}),(0,_.jsx)(`p`,{className:`settings-empty`,children:`Default: grok-composer-2.5-fast. Bare grok-composer-2.5 currently has no verified team access.`}),T?(0,_.jsx)(`p`,{className:`settings-field-error`,children:T}):null,(0,_.jsx)(`button`,{type:`button`,className:`settings-action settings-action-save`,disabled:C,onClick:()=>void D(),children:C?`Registering...`:`Register`})]})]})})}function x(e,t,n=``){let r=new Set;n&&r.add(n);for(let n of e?.discoveredModels?.[t]||[])r.add(n);let i=e?.profiles?.find(e=>e.id===t);return i?.model&&r.add(i.model),[...r]}function S(e,t=``){let n=new Set;t&&n.add(t);for(let t of e?.profiles||[])n.add(t.id);return[...n]}function C(e,t,n=!0){return{value:e,original:t,valid:n}}function w({cli:e,meta:t,original:n,value:r,setValue:i,setEntry:a,client:o,pi:s,setPi:u}){let d=`percli-${e}-models`,p=e===`pi`,[m,h]=(0,g.useState)(!1),v=r.provider||t.defaultProvider||t.providers?.[0]||`claude`,y=p?x(s,v,r.model||``):[],w=!p&&(t.providers?.length??0)>0,T=p?y:w?t.modelsByProvider?.[v]??t.models:t.models,E=w?t.effortsByProvider?.[v]??t.efforts:t.efforts;return(0,_.jsxs)(`div`,{className:`settings-percli-row`,"data-cli":e,children:[(0,_.jsx)(`h3`,{className:`settings-percli-title`,children:t.label}),(0,_.jsxs)(`div`,{className:`settings-percli-grid`,children:[p&&S(s,v).length?(0,_.jsx)(l,{id:`percli-${e}-provider`,label:`Provider`,value:v,options:S(s,v).map(e=>({value:e,label:e})),onChange:t=>{let o=x(s,t),c=o.includes(r.model||``)?r.model||``:o[0]||``;i({...r,provider:t,model:c}),a(`perCli.${e}.provider`,C(t,n.provider??`progrok`)),a(`perCli.${e}.model`,C(c,n.model??``))}}):w?(0,_.jsx)(l,{id:`percli-${e}-provider`,label:`Provider`,value:v,options:(t.providers??[]).map(e=>({value:e,label:e})),onChange:o=>{let s=t.modelsByProvider?.[o]??[],c=t.effortsByProvider?.[o]??[],l=s.includes(r.model||``)?r.model||``:s[0]||``,u=c.includes(r.effort||``)&&r.effort||``;i({...r,provider:o,model:l,effort:u}),a(`perCli.${e}.provider`,C(o,n.provider??t.defaultProvider??`claude`)),a(`perCli.${e}.model`,C(l,n.model??``)),a(`perCli.${e}.effort`,C(u,n.effort??``))}}):null,(0,_.jsxs)(`div`,{className:`settings-percli-model`,children:[p&&T.length>0?(0,_.jsx)(l,{id:`percli-${e}-model`,label:`Model`,value:r.model??T[0]??``,options:T.map(e=>({value:e,label:e})),onChange:t=>{i({...r,model:t}),a(`perCli.${e}.model`,C(t,n.model??``))}}):(0,_.jsx)(f,{id:`percli-${e}-model`,label:`Model`,value:r.model??``,onChange:t=>{i({...r,model:t}),a(`perCli.${e}.model`,C(t,n.model??``))},placeholder:T[0]??`model id`}),t.modelNote?(0,_.jsx)(`p`,{className:`settings-percli-note`,children:t.modelNote}):null,T.length>0?(0,_.jsx)(`datalist`,{id:d,children:T.map(e=>(0,_.jsx)(`option`,{value:e},e))}):null]}),E.length>0?(0,_.jsxs)(`div`,{className:`settings-percli-effort`,children:[(0,_.jsx)(l,{id:`percli-${e}-effort`,label:`Effort`,value:r.effort??``,options:[{value:``,label:`(default)`},...E.map(e=>({value:e,label:e}))],onChange:t=>{i({...r,effort:t}),a(`perCli.${e}.effort`,C(t,n.effort??``))}}),t.effortNote?(0,_.jsx)(`p`,{className:`settings-percli-note`,children:t.effortNote}):null]}):t.effortNote?(0,_.jsx)(`p`,{className:`settings-percli-note settings-percli-note--effort`,children:t.effortNote}):null,(0,_.jsx)(c,{id:`percli-${e}-fastmode`,label:`Fast mode`,value:!!r.fastMode,onChange:t=>{i({...r,fastMode:t}),a(`perCli.${e}.fastMode`,C(t,!!n.fastMode))}}),p&&o?(0,_.jsx)(`button`,{type:`button`,className:`settings-action`,onClick:()=>h(!0),children:`Settings`}):null]}),p&&o&&m?(0,_.jsx)(b,{client:o,pi:s,provider:v,model:r.model||`grok-composer-2.5-fast`,onClose:()=>h(!1),onRegistered:({provider:t,model:o,pi:s})=>{s&&u&&u(s),i({...r,provider:t,model:o}),a(`perCli.${e}.provider`,C(t,n.provider??`progrok`)),a(`perCli.${e}.model`,C(o,n.model??``))}}):null]})}function T(e){return[...e].sort((e,t)=>e===`pi`?-1:t===`pi`?1:e===`ai-e`?t===`pi`?1:-1:t===`ai-e`?e===`pi`?-1:1:0)}function E(e){let t=new Set;for(let n of Object.keys(e.activeOverrides||{}))t.add(n);for(let n of Object.keys(e.perCli||{}))t.add(n);let n={};for(let e of t)n[e]={model:``,effort:``};return{activeOverrides:n}}function D({port:e,client:t,dirty:n,registerSave:c}){let{state:l,refresh:f,setData:v}=r(t,`/api/settings`),[y,b]=(0,g.useState)({}),[x,S]=(0,g.useState)(void 0),[C,D]=(0,g.useState)([]),[O,k]=(0,g.useState)({}),[A,j]=(0,g.useState)(!1),[M,N]=(0,g.useState)(null),[P,F]=(0,g.useState)(null),I=(0,g.useCallback)(async()=>{try{let e=await t.get(`/api/cli-registry`);F(m(e&&typeof e==`object`&&`data`in e?e.data:e))}catch{F(null)}},[t]);(0,g.useEffect)(()=>{I()},[I]),(0,g.useEffect)(()=>{if(l.kind!==`ready`)return;b({...l.data.perCli||{}}),S(l.data.pi),D([...l.data.fallbackOrder||[]]);let e=l.data.perCli?.codex||{},t={};typeof e.contextWindowSize==`number`&&(t.contextWindowSize=e.contextWindowSize),typeof e.contextWindowCompactLimit==`number`&&(t.contextWindowCompactLimit=e.contextWindowCompactLimit),k(t)},[l]),(0,g.useEffect)(()=>()=>{for(let e of Array.from(n.pending.keys()))(e===`fallbackOrder`||e.startsWith(`perCli.`))&&n.remove(e)},[n]);let L=(0,g.useCallback)((e,t)=>n.set(e,t),[n]),R=(0,g.useCallback)(async()=>{let e=n.saveBundle();if(Object.keys(e).length===0)return;let r=h(e),i=await t.put(`/api/settings`,r),a=i&&typeof i==`object`&&`data`in i?i.data:i;n.clear(),v(a),b({...a.perCli||{}}),D([...a.fallbackOrder||[]]),await f(),await I()},[t,n,I,f,v]);(0,g.useEffect)(()=>{if(c)return c(R),()=>c(null)},[c,R]);let z=(0,g.useCallback)(async()=>{if(l.kind===`ready`&&window.confirm(`Reset all active overrides?`)){j(!0),N(null);try{let e=E(l.data),n=await t.put(`/api/settings`,e);v(n&&typeof n==`object`&&`data`in n?n.data:n),await f()}catch(e){N(e instanceof Error?e.message:String(e))}finally{j(!1)}}},[t,f,v,l]);if(l.kind===`loading`)return(0,_.jsx)(a,{});if(l.kind===`offline`)return(0,_.jsx)(o,{port:e});if(l.kind===`error`)return(0,_.jsx)(s,{message:l.message});let B=l.data,V=B.perCli||{},H=T(Object.keys(V)),U=V.codex||{},W=B.activeOverrides||{},G=Object.entries(W);return(0,_.jsxs)(`form`,{className:`settings-page-form`,onSubmit:e=>{e.preventDefault(),R()},children:[(0,_.jsx)(i,{title:`Model defaults`,hint:`Per-CLI defaults applied when no active override is set on the Agent page.`,children:H.length===0?(0,_.jsx)(`p`,{className:`settings-empty`,children:`No CLIs registered for this instance.`}):H.map(e=>(0,_.jsx)(w,{cli:e,meta:p(e,P),original:V[e]||{},value:y[e]||V[e]||{},setValue:t=>b({...y,[e]:t}),setEntry:L,client:t,pi:x,setPi:S},e))}),V.codex?(0,_.jsxs)(i,{title:`Codex context window`,hint:`Codex-only sliders. Other CLIs ignore these values.`,children:[(0,_.jsx)(d,{id:`model-codex-ctx`,label:`Context window size`,value:O.contextWindowSize??(typeof U.contextWindowSize==`number`?U.contextWindowSize:1e6),min:0,step:1e4,onChange:e=>{k({...O,contextWindowSize:e}),L(`perCli.codex.contextWindowSize`,{value:e,original:U.contextWindowSize??1e6,valid:Number.isFinite(e)&&e>=0})}}),(0,_.jsx)(d,{id:`model-codex-compact`,label:`Compact limit`,value:O.contextWindowCompactLimit??(typeof U.contextWindowCompactLimit==`number`?U.contextWindowCompactLimit:9e5),min:0,step:1e4,onChange:e=>{k({...O,contextWindowCompactLimit:e}),L(`perCli.codex.contextWindowCompactLimit`,{value:e,original:U.contextWindowCompactLimit??9e5,valid:Number.isFinite(e)&&e>=0})}})]}):null,(0,_.jsx)(i,{title:`Fallback order`,hint:`Order of CLIs used when the active CLI fails. Press Enter to add a chip; Backspace to remove the last.`,children:(0,_.jsx)(u,{id:`model-fallbackOrder`,label:`Fallback order`,value:C,onChange:e=>{D(e),L(`fallbackOrder`,{value:e,original:B.fallbackOrder||[],valid:!0})},placeholder:`cli name`})}),(0,_.jsxs)(i,{title:`Active overrides`,hint:`Per-session overrides applied on top of per-CLI defaults.`,children:[G.length===0?(0,_.jsx)(`p`,{className:`settings-empty`,children:`No active overrides.`}):(0,_.jsxs)(`table`,{className:`settings-overrides-table`,children:[(0,_.jsx)(`thead`,{children:(0,_.jsxs)(`tr`,{children:[(0,_.jsx)(`th`,{scope:`col`,children:`CLI`}),(0,_.jsx)(`th`,{scope:`col`,children:`Model`}),(0,_.jsx)(`th`,{scope:`col`,children:`Effort`})]})}),(0,_.jsx)(`tbody`,{children:G.map(([e,t])=>(0,_.jsxs)(`tr`,{children:[(0,_.jsx)(`td`,{children:e}),(0,_.jsx)(`td`,{children:(0,_.jsx)(`code`,{children:t?.model||`—`})}),(0,_.jsx)(`td`,{children:(0,_.jsx)(`code`,{children:t?.effort||`—`})})]},e))})]}),(0,_.jsxs)(`div`,{className:`settings-overrides-actions`,children:[(0,_.jsx)(`button`,{type:`button`,className:`settings-action settings-action-discard`,onClick:()=>void z(),disabled:A||G.length===0,children:A?`Resetting…`:`Reset overrides`}),M?(0,_.jsx)(`span`,{className:`settings-field-error`,role:`alert`,children:M}):null]})]})]})}export{E as buildResetOverridesPatch,D as default,T as orderModelCliKeys};
|