cartethyia 2.7.5 → 2.7.6
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/.env.example +4 -1
- package/README.md +1 -1
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-D5rol2j2.js → ConsoleLogPage-KRYjHZsX.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-B5nZvmf0.js → InjectSystemPromptPanel-d5v3K_Yc.js} +245 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-DnQPSIA8.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-5SnUSy3j.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BJtRZ4qD.js → SettingsPage-BW4AKj2H.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BfAb5-o_.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-BNzeZ2Vk.js → UsagePage-B7M7LvMM.js} +13 -13
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0K-c4eo.js → assert-CidQVoNo.js} +1 -1
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DfbhQU4k.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BllT4LcF.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/{card-CtkX66Wd.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CQXVMn4P.js → cli-tools-Bwv5-zGZ.js} +1 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-BQqBQd2n.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BrTEZ8zu.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BAACA727.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-FQlj0HUT.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-C4gU5iOL.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-BHHX_Tmm.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CajrYUnD.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Bx2I7XUe.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/{live-DNNqRbj2.js → live-7_QpnYGu.js} +1 -1
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/{network-C5UBACrJ.js → network-CuMDaTzT.js} +1 -1
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-Xsi9Qx04.js → pagination-B5aOoweZ.js} +1 -1
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/{routing-in0f_IfK.js → routing-fnCsxgNO.js} +1 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BLULNqTp.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CcGczj6I.js → settings-BxslsErn.js} +1 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/{stack-D2lIR0tg.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-DTARjfiW.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-c0oPSnqW.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-m8WGNPrz.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +57 -57
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +1 -1
- package/src/boot/dependencies.ts +10 -2
- package/src/config/providers.ts +13 -7
- package/src/config/spec.ts +18 -11
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/routes.ts +19 -23
- package/src/console/cli-tools/contracts.ts +39 -295
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +6 -6
- package/src/console/cli-tools/injectors/driver.ts +8 -105
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +28 -5
- package/src/console/cli-tools/routes.ts +6 -0
- package/src/console/cli-tools/service.ts +7 -6
- package/src/console/observability/contracts.ts +6 -0
- package/src/console/observability/store.ts +3 -0
- package/src/console/share/share-stats.ts +7 -9
- package/src/observability/event-log.ts +7 -0
- package/src/observability/event-query.ts +2 -0
- package/src/observability/telemetry-buffer.ts +7 -0
- package/src/persistence/schema.ts +8 -0
- package/src/persistence/stores/api-key-store.ts +19 -1
- package/src/protocol/dsml.ts +5 -76
- package/src/providers/adapters/compatible-adapter.ts +29 -1
- package/src/providers/default-registry.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -14
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +20 -11
- package/src/providers/integrations/cursor/cursor.ts +2 -3
- package/src/providers/integrations/github/github-discovery.ts +15 -2
- package/src/providers/integrations/github/github-oauth.ts +178 -9
- package/src/providers/integrations/github/github.ts +95 -14
- package/src/providers/integrations/kiro/kiro.ts +1 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +9 -3
- package/src/providers/integrations/qoder/qoder.ts +147 -88
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/operations/account-error-classification.ts +67 -17
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/provider-usage.ts +15 -49
- package/src/security/admission/service.ts +11 -4
- package/src/security/api-key-auth.ts +5 -3
- package/src/transport/dispatch/attempt-finalize.ts +69 -21
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/failure-policy.ts +15 -1
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/request/state.ts +6 -0
- package/src/transport/routing/router.ts +7 -0
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/vision-handoff.ts +83 -28
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js +0 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +0 -1
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +0 -1
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js +0 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +0 -1
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js +0 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +0 -1
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +0 -2
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/app-Bgd3zANQ.js +0 -9
- package/dist/dashboard/assets/app-Bgd3zANQ.js.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BEK7KP0H.js +0 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +0 -1
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/entry-EBHJ3DrE.js +0 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +0 -4
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js +0 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +0 -2
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +0 -1
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
|
@@ -6,12 +6,11 @@ import { ensureV1Suffix } from "./fs-ops";
|
|
|
6
6
|
* CLI Tools type contracts — shared across registry, injectors, service,
|
|
7
7
|
* dashboard, and API routes.
|
|
8
8
|
*
|
|
9
|
-
* Each supported CLI coding agent ([CC], Codex,
|
|
10
|
-
* with static metadata and
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* without clobbering user settings.
|
|
9
|
+
* Each supported CLI coding agent ([CC], Codex, Kilo, OpenCode) has a `ToolDef`
|
|
10
|
+
* with static metadata and a paired `ToolInjector` that reads and writes the
|
|
11
|
+
* tool's config files on the host filesystem. The dashboard picks an API key +
|
|
12
|
+
* models, POSTs to the backend, and the injector merges Cartethyia-specific
|
|
13
|
+
* fields into the tool's config without clobbering user settings.
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -22,7 +21,7 @@ import { ensureV1Suffix } from "./fs-ops";
|
|
|
22
21
|
type CliToolSurface = "chat" | "responses" | "messages";
|
|
23
22
|
|
|
24
23
|
/** How the tool's config is managed. */
|
|
25
|
-
type ConfigType = "env" | "custom"
|
|
24
|
+
type ConfigType = "env" | "custom";
|
|
26
25
|
|
|
27
26
|
/** A native model slot exposed by a CLI tool. */
|
|
28
27
|
interface ToolModelDef {
|
|
@@ -53,6 +52,13 @@ export interface CliMappingInput {
|
|
|
53
52
|
/** The user-facing routing mode for a CLI tool: `remote` routes are resolved by the gateway, `local` routes are baked into the tool's own config file. */
|
|
54
53
|
export type CliMappingMode = "remote" | "local";
|
|
55
54
|
|
|
55
|
+
/**
|
|
56
|
+
* The default runtime SDK package for OpenCode: its native
|
|
57
|
+
* `@opencode/ai/providers/openai-compatible` package (bundled with OpenCode
|
|
58
|
+
* itself, not the AI SDK).
|
|
59
|
+
*/
|
|
60
|
+
export const DEFAULT_SDK: string = "@opencode/ai/providers/openai-compatible";
|
|
61
|
+
|
|
56
62
|
/** Persisted mapping settings for one CLI tool + API key pair. */
|
|
57
63
|
export interface CliMappingSettings {
|
|
58
64
|
readonly toolId: string;
|
|
@@ -68,21 +74,6 @@ interface ToolNote {
|
|
|
68
74
|
readonly text: string;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
/** A guide step for guide-only tools. */
|
|
72
|
-
interface GuideStep {
|
|
73
|
-
readonly step: number;
|
|
74
|
-
readonly title: string;
|
|
75
|
-
readonly desc?: string;
|
|
76
|
-
readonly value?: string;
|
|
77
|
-
readonly type?: "apiKeySelector" | "modelSelector";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** A code block for guide-only tools. */
|
|
81
|
-
interface GuideCodeBlock {
|
|
82
|
-
readonly language: string;
|
|
83
|
-
readonly code: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
77
|
/** Static metadata for a CLI tool — consumed by both backend and frontend. */
|
|
87
78
|
export interface ToolDef {
|
|
88
79
|
readonly id: string;
|
|
@@ -95,8 +86,6 @@ export interface ToolDef {
|
|
|
95
86
|
readonly settingsFile?: string;
|
|
96
87
|
readonly docsUrl?: string;
|
|
97
88
|
readonly notes?: readonly ToolNote[];
|
|
98
|
-
readonly guideSteps?: readonly GuideStep[];
|
|
99
|
-
readonly codeBlock?: GuideCodeBlock;
|
|
100
89
|
/**
|
|
101
90
|
* How this tool's slot→route mapping behaves, which also decides whether the
|
|
102
91
|
* tool has a mapping surface at all.
|
|
@@ -109,9 +98,7 @@ export interface ToolDef {
|
|
|
109
98
|
* so a slot has one value (written to the file) rather than a separate
|
|
110
99
|
* target. There is nothing to persist; the model is baked into the file.
|
|
111
100
|
*
|
|
112
|
-
* Absent means the tool exposes no configurable model slots at all
|
|
113
|
-
* guide-only tool). This replaces a separate `mappingSupported` flag that
|
|
114
|
-
* had to be kept consistent with the mode by hand — the mode already says it.
|
|
101
|
+
* Absent means the tool exposes no configurable model slots at all.
|
|
115
102
|
*/
|
|
116
103
|
readonly mappingMode?: CliMappingMode;
|
|
117
104
|
}
|
|
@@ -137,8 +124,6 @@ export type ToolRegistryEntry = Pick<
|
|
|
137
124
|
| "settingsFile"
|
|
138
125
|
| "docsUrl"
|
|
139
126
|
| "notes"
|
|
140
|
-
| "guideSteps"
|
|
141
|
-
| "codeBlock"
|
|
142
127
|
> & { readonly mappingSupported: boolean };
|
|
143
128
|
|
|
144
129
|
/** Runtime status of a CLI tool on the host. */
|
|
@@ -175,6 +160,17 @@ export interface ApplyInput {
|
|
|
175
160
|
readonly mapping?: CliMappingInput;
|
|
176
161
|
/** Enables [CC]'s bypass-permissions (YOLO) mode when explicitly selected. */
|
|
177
162
|
readonly bypassPermissions?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* The runtime SDK package to write into the tool's config (e.g. OpenCode's
|
|
165
|
+
* `provider.*.npm`). Absent = the tool's default.
|
|
166
|
+
*/
|
|
167
|
+
readonly sdk?: string;
|
|
168
|
+
/**
|
|
169
|
+
* WebSearch provider selection written into OpenCode's config. "carte"
|
|
170
|
+
* routes search through the Cartethyia gateway; "native" (or absent) leaves
|
|
171
|
+
* OpenCode's own provider selection in place.
|
|
172
|
+
*/
|
|
173
|
+
readonly websearch?: string;
|
|
178
174
|
}
|
|
179
175
|
|
|
180
176
|
/** Result of an apply or reset operation. */
|
|
@@ -182,6 +178,8 @@ export interface ApplyResult {
|
|
|
182
178
|
readonly success: boolean;
|
|
183
179
|
readonly settingsPath?: string;
|
|
184
180
|
readonly message: string;
|
|
181
|
+
/** Non-fatal notes for the operator, e.g. preserved pre-existing providers. */
|
|
182
|
+
readonly warnings?: readonly string[];
|
|
185
183
|
}
|
|
186
184
|
|
|
187
185
|
/**
|
|
@@ -195,9 +193,9 @@ export interface ApplyResult {
|
|
|
195
193
|
* persisted routing rows on each `/v1/*` call. Works when the gateway is
|
|
196
194
|
* remote, containerised, or the CLI lives on another machine.
|
|
197
195
|
* - `both` — the tool's config was written and a remote route was recorded.
|
|
198
|
-
* - `none` — nothing ran: the tool has no injectable config
|
|
199
|
-
*
|
|
200
|
-
*
|
|
196
|
+
* - `none` — nothing ran: the tool has no injectable config and the request
|
|
197
|
+
* asked for no remote route. Reported rather than folded into `file`, so
|
|
198
|
+
* the UI never claims a write that did not happen.
|
|
201
199
|
*/
|
|
202
200
|
export type ApplyOutcome = "file" | "remote" | "both" | "none";
|
|
203
201
|
|
|
@@ -210,6 +208,8 @@ export interface ApplyConfigResult {
|
|
|
210
208
|
readonly savedRemoteRoute: boolean;
|
|
211
209
|
readonly settingsPath?: string;
|
|
212
210
|
readonly message: string;
|
|
211
|
+
/** Non-fatal notes for the operator (see `ApplyResult.warnings`). */
|
|
212
|
+
readonly warnings?: readonly string[];
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
/** Result of a download-config operation. */
|
|
@@ -220,8 +220,7 @@ export interface DownloadResult {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
|
-
* Injector contract — one per
|
|
224
|
-
* Guide-only tools share a single GuideInjector that generates config text.
|
|
223
|
+
* Injector contract — one per tool.
|
|
225
224
|
*/
|
|
226
225
|
export interface ToolInjector {
|
|
227
226
|
readonly toolId: string;
|
|
@@ -325,8 +324,12 @@ export interface InjectorSpec {
|
|
|
325
324
|
/** Extract status details from existing settings file(s) */
|
|
326
325
|
readonly readStatus: (path: string) => Promise<StatusDetails> | StatusDetails;
|
|
327
326
|
|
|
328
|
-
/**
|
|
329
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Apply Cartethyia configuration to the settings file(s). Return an array of
|
|
329
|
+
* non-fatal warning strings to surface them to the operator (e.g. preserved
|
|
330
|
+
* pre-existing providers).
|
|
331
|
+
*/
|
|
332
|
+
readonly apply: (input: ApplyInput, path: string) => Promise<readonly string[] | void> | readonly string[] | void;
|
|
330
333
|
|
|
331
334
|
/**
|
|
332
335
|
* Reset Cartethyia configuration from settings file(s).
|
|
@@ -449,32 +452,6 @@ export const TOOL_REGISTRY = {
|
|
|
449
452
|
],
|
|
450
453
|
},
|
|
451
454
|
|
|
452
|
-
cline: {
|
|
453
|
-
id: "cline",
|
|
454
|
-
name: "Cline",
|
|
455
|
-
color: "#00D1B2",
|
|
456
|
-
description: "Cline AI Coding Assistant (VS Code)",
|
|
457
|
-
configType: "custom" as const,
|
|
458
|
-
surface: "chat" as const,
|
|
459
|
-
settingsFile: "~/.cline/data/globalState.json",
|
|
460
|
-
defaultModels: [
|
|
461
|
-
{
|
|
462
|
-
id: "claude/claude-sonnet-5",
|
|
463
|
-
name: "Claude Sonnet 5",
|
|
464
|
-
alias: "sonnet",
|
|
465
|
-
roleLabel: "Primary Agent",
|
|
466
|
-
roleKind: "primary",
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
id: "claude/claude-opus-4-8",
|
|
470
|
-
name: "Claude Opus",
|
|
471
|
-
alias: "opus",
|
|
472
|
-
roleLabel: "Advanced Agent",
|
|
473
|
-
roleKind: "secondary",
|
|
474
|
-
},
|
|
475
|
-
],
|
|
476
|
-
},
|
|
477
|
-
|
|
478
455
|
opencode: {
|
|
479
456
|
id: "opencode",
|
|
480
457
|
name: "OpenCode",
|
|
@@ -501,86 +478,6 @@ export const TOOL_REGISTRY = {
|
|
|
501
478
|
],
|
|
502
479
|
},
|
|
503
480
|
|
|
504
|
-
droid: {
|
|
505
|
-
id: "droid",
|
|
506
|
-
name: "Factory Droid",
|
|
507
|
-
color: "#00D4FF",
|
|
508
|
-
description: "Factory Droid AI Assistant",
|
|
509
|
-
configType: "custom" as const,
|
|
510
|
-
surface: "chat" as const,
|
|
511
|
-
settingsFile: "~/.factory/settings.json",
|
|
512
|
-
defaultModels: [
|
|
513
|
-
{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" },
|
|
514
|
-
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", alias: "gpt-5.6-sol" },
|
|
515
|
-
],
|
|
516
|
-
},
|
|
517
|
-
|
|
518
|
-
hermes: {
|
|
519
|
-
id: "hermes",
|
|
520
|
-
name: "Hermes Agent",
|
|
521
|
-
color: "#8B5CF6",
|
|
522
|
-
description: "Nous Research self-improving AI agent",
|
|
523
|
-
configType: "custom" as const,
|
|
524
|
-
surface: "chat" as const,
|
|
525
|
-
settingsFile: "~/.hermes/config.yaml",
|
|
526
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
527
|
-
},
|
|
528
|
-
|
|
529
|
-
"grok-build": {
|
|
530
|
-
id: "grok-build",
|
|
531
|
-
name: "Grok Build",
|
|
532
|
-
color: "#1DA1F2",
|
|
533
|
-
description: "xAI Grok Build CLI",
|
|
534
|
-
configType: "custom" as const,
|
|
535
|
-
surface: "chat" as const,
|
|
536
|
-
settingsFile: "~/.grok/config.toml",
|
|
537
|
-
defaultModels: [{ id: "grok/grok-4", name: "Grok 4", alias: "grok-4" }],
|
|
538
|
-
},
|
|
539
|
-
|
|
540
|
-
copilot: {
|
|
541
|
-
id: "copilot",
|
|
542
|
-
name: "GitHub Copilot",
|
|
543
|
-
color: "#1F6FEB",
|
|
544
|
-
description: "GitHub Copilot Chat (VS Code chatLanguageModels.json)",
|
|
545
|
-
configType: "custom" as const,
|
|
546
|
-
surface: "chat" as const,
|
|
547
|
-
settingsFile: "Code/User/chatLanguageModels.json",
|
|
548
|
-
defaultModels: [
|
|
549
|
-
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", alias: "gpt-5.6-sol" },
|
|
550
|
-
{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" },
|
|
551
|
-
],
|
|
552
|
-
},
|
|
553
|
-
|
|
554
|
-
"deepseek-tui": {
|
|
555
|
-
id: "deepseek-tui",
|
|
556
|
-
name: "DeepSeek TUI",
|
|
557
|
-
color: "#4D6BFE",
|
|
558
|
-
description: "DeepSeek Terminal Coding Agent (Rust TUI)",
|
|
559
|
-
configType: "custom" as const,
|
|
560
|
-
surface: "chat" as const,
|
|
561
|
-
settingsFile: "~/.deepseek/config.toml",
|
|
562
|
-
defaultModels: [
|
|
563
|
-
{ id: "deepseek/deepseek-chat", name: "DeepSeek Chat", alias: "deepseek-chat" },
|
|
564
|
-
{
|
|
565
|
-
id: "deepseek/deepseek-reasoner",
|
|
566
|
-
name: "DeepSeek Reasoner",
|
|
567
|
-
alias: "deepseek-reasoner",
|
|
568
|
-
},
|
|
569
|
-
],
|
|
570
|
-
},
|
|
571
|
-
|
|
572
|
-
jcode: {
|
|
573
|
-
id: "jcode",
|
|
574
|
-
name: "jcode",
|
|
575
|
-
color: "#FF6B35",
|
|
576
|
-
description: "High-performance Rust-based coding agent harness",
|
|
577
|
-
configType: "custom" as const,
|
|
578
|
-
surface: "chat" as const,
|
|
579
|
-
settingsFile: "~/.jcode/config.toml",
|
|
580
|
-
docsUrl: "https://github.com/1jehuang/jcode",
|
|
581
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
582
|
-
},
|
|
583
|
-
|
|
584
481
|
kilo: {
|
|
585
482
|
id: "kilo",
|
|
586
483
|
name: "Kilo Code",
|
|
@@ -591,159 +488,6 @@ export const TOOL_REGISTRY = {
|
|
|
591
488
|
settingsFile: "~/.local/share/kilo/auth.json",
|
|
592
489
|
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
593
490
|
},
|
|
594
|
-
|
|
595
|
-
openclaw: {
|
|
596
|
-
id: "openclaw",
|
|
597
|
-
name: "Open Claw",
|
|
598
|
-
color: "#FF6B35",
|
|
599
|
-
description: "Open Claw AI Assistant",
|
|
600
|
-
configType: "custom" as const,
|
|
601
|
-
surface: "messages" as const,
|
|
602
|
-
settingsFile: "~/.openclaw/openclaw.json",
|
|
603
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
604
|
-
},
|
|
605
|
-
|
|
606
|
-
cowork: {
|
|
607
|
-
id: "cowork",
|
|
608
|
-
name: "Claude Cowork",
|
|
609
|
-
color: "#D97757",
|
|
610
|
-
description: "Claude Desktop Cowork (third-party inference)",
|
|
611
|
-
configType: "custom" as const,
|
|
612
|
-
surface: "messages" as const,
|
|
613
|
-
settingsFile: "Claude/configLibrary/_meta.json",
|
|
614
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
615
|
-
},
|
|
616
|
-
// ── Guide-only tools (no fs injection — show steps + downloadable config) ──
|
|
617
|
-
roo: {
|
|
618
|
-
id: "roo",
|
|
619
|
-
name: "Roo",
|
|
620
|
-
color: "#FF6B6B",
|
|
621
|
-
description: "Roo AI Assistant",
|
|
622
|
-
configType: "guide" as const,
|
|
623
|
-
surface: "chat" as const,
|
|
624
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
625
|
-
guideSteps: [
|
|
626
|
-
{ step: 1, title: "Open Settings", desc: "Go to Roo Settings panel" },
|
|
627
|
-
{ step: 2, title: "Select Provider", desc: "Choose API Provider → Ollama" },
|
|
628
|
-
{ step: 3, title: "Base URL", value: "{{baseUrl}}" },
|
|
629
|
-
{ step: 4, title: "API Key", type: "apiKeySelector" },
|
|
630
|
-
{ step: 5, title: "Select Model", type: "modelSelector" },
|
|
631
|
-
],
|
|
632
|
-
},
|
|
633
|
-
|
|
634
|
-
continue: {
|
|
635
|
-
id: "continue",
|
|
636
|
-
name: "Continue",
|
|
637
|
-
color: "#7C3AED",
|
|
638
|
-
description: "Continue AI Assistant",
|
|
639
|
-
configType: "guide" as const,
|
|
640
|
-
surface: "chat" as const,
|
|
641
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
642
|
-
guideSteps: [
|
|
643
|
-
{ step: 1, title: "Open Config", desc: "Open Continue configuration file" },
|
|
644
|
-
{ step: 2, title: "API Key", type: "apiKeySelector" },
|
|
645
|
-
{ step: 3, title: "Select Model", type: "modelSelector" },
|
|
646
|
-
{
|
|
647
|
-
step: 4,
|
|
648
|
-
title: "Add Model Config",
|
|
649
|
-
desc: "Add the following configuration to your models array:",
|
|
650
|
-
},
|
|
651
|
-
],
|
|
652
|
-
codeBlock: {
|
|
653
|
-
language: "json",
|
|
654
|
-
code: `{
|
|
655
|
-
"apiBase": "{{baseUrl}}",
|
|
656
|
-
"title": "{{model}}",
|
|
657
|
-
"model": "{{model}}",
|
|
658
|
-
"provider": "openai",
|
|
659
|
-
"apiKey": "{{apiKey}}"
|
|
660
|
-
}`,
|
|
661
|
-
},
|
|
662
|
-
},
|
|
663
|
-
|
|
664
|
-
amp: {
|
|
665
|
-
id: "amp",
|
|
666
|
-
name: "Amp CLI",
|
|
667
|
-
color: "#F97316",
|
|
668
|
-
description: "Sourcegraph Amp coding assistant CLI",
|
|
669
|
-
configType: "guide" as const,
|
|
670
|
-
surface: "chat" as const,
|
|
671
|
-
docsUrl: "/docs?section=cli-tools&tool=amp",
|
|
672
|
-
defaultModels: [{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" }],
|
|
673
|
-
notes: [
|
|
674
|
-
{
|
|
675
|
-
type: "info",
|
|
676
|
-
text: "Use Cartethyia model aliases to keep Amp shorthand mappings stable across provider updates.",
|
|
677
|
-
},
|
|
678
|
-
],
|
|
679
|
-
guideSteps: [
|
|
680
|
-
{
|
|
681
|
-
step: 1,
|
|
682
|
-
title: "Install Amp",
|
|
683
|
-
desc: "Install the Amp CLI using the package manager supported by your environment.",
|
|
684
|
-
},
|
|
685
|
-
{ step: 2, title: "API Key", type: "apiKeySelector" },
|
|
686
|
-
{ step: 3, title: "Base URL", value: "{{baseUrl}}" },
|
|
687
|
-
{ step: 4, title: "Select Model", type: "modelSelector" },
|
|
688
|
-
{
|
|
689
|
-
step: 5,
|
|
690
|
-
title: "Add Shorthands",
|
|
691
|
-
desc: "Map Amp shorthand names to Cartethyia aliases in your local config.",
|
|
692
|
-
},
|
|
693
|
-
],
|
|
694
|
-
codeBlock: {
|
|
695
|
-
language: "bash",
|
|
696
|
-
code: `export OPENAI_API_KEY="{{apiKey}}"
|
|
697
|
-
export OPENAI_BASE_URL="{{baseUrl}}"
|
|
698
|
-
amp --model "{{model}}"`,
|
|
699
|
-
},
|
|
700
|
-
},
|
|
701
|
-
|
|
702
|
-
qwen: {
|
|
703
|
-
id: "qwen",
|
|
704
|
-
name: "Qwen Code",
|
|
705
|
-
color: "#10B981",
|
|
706
|
-
description: "Alibaba Qwen Code CLI — OpenAI-compatible via Cartethyia",
|
|
707
|
-
configType: "guide" as const,
|
|
708
|
-
surface: "chat" as const,
|
|
709
|
-
docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/configuration/model-providers/",
|
|
710
|
-
defaultModels: [
|
|
711
|
-
{ id: "claude/claude-sonnet-5", name: "Claude Sonnet 5", alias: "sonnet" },
|
|
712
|
-
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", alias: "gpt-5.6-sol" },
|
|
713
|
-
],
|
|
714
|
-
notes: [
|
|
715
|
-
{
|
|
716
|
-
type: "warning",
|
|
717
|
-
text: "Config path: Linux/macOS ~/.qwen/settings.json - Windows %USERPROFILE%\\.qwen\\settings.json",
|
|
718
|
-
},
|
|
719
|
-
],
|
|
720
|
-
guideSteps: [
|
|
721
|
-
{ step: 1, title: "Install Qwen Code", desc: "npm install -g @qwen-code/qwen-code" },
|
|
722
|
-
{ step: 2, title: "API Key", type: "apiKeySelector" },
|
|
723
|
-
{ step: 3, title: "Base URL", value: "{{baseUrl}}" },
|
|
724
|
-
{ step: 4, title: "Select Model", type: "modelSelector" },
|
|
725
|
-
{
|
|
726
|
-
step: 5,
|
|
727
|
-
title: "Save Config",
|
|
728
|
-
desc: "Copy the JSON below to your ~/.qwen/settings.json file.",
|
|
729
|
-
},
|
|
730
|
-
],
|
|
731
|
-
codeBlock: {
|
|
732
|
-
language: "json",
|
|
733
|
-
code: `{
|
|
734
|
-
"security": {
|
|
735
|
-
"auth": {
|
|
736
|
-
"selectedType": "openai",
|
|
737
|
-
"apiKey": "{{apiKey}}",
|
|
738
|
-
"baseUrl": "{{baseUrl}}"
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
"model": {
|
|
742
|
-
"name": "{{model}}"
|
|
743
|
-
}
|
|
744
|
-
}`,
|
|
745
|
-
},
|
|
746
|
-
},
|
|
747
491
|
} satisfies Record<string, ToolDef>;
|
|
748
492
|
|
|
749
493
|
export type ToolId = keyof typeof TOOL_REGISTRY;
|
|
@@ -54,6 +54,17 @@ export async function writeJsonFile(path: string, data: unknown): Promise<void>
|
|
|
54
54
|
await Bun.write(path, JSON.stringify(data, null, 2));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Write JSON to a file, but first preserve the current contents at `path.bak`
|
|
59
|
+
* when the file already exists. Callers use this for tools whose config is
|
|
60
|
+
* precious (anything the user hand-edited): a broken merge must never destroy
|
|
61
|
+
* the only copy of that file, and the operator can always restore `.bak`.
|
|
62
|
+
*/
|
|
63
|
+
export async function writeJsonFileWithBackup(path: string, data: unknown): Promise<void> {
|
|
64
|
+
await backupExistingFile(path);
|
|
65
|
+
await writeJsonFile(path, data);
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
/** Read a text file, returning null if it doesn't exist. */
|
|
58
69
|
export async function readTextFile(path: string): Promise<string | null> {
|
|
59
70
|
try {
|
|
@@ -68,6 +79,24 @@ export async function writeTextFile(path: string, content: string): Promise<void
|
|
|
68
79
|
await Bun.write(path, content);
|
|
69
80
|
}
|
|
70
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Write text to a file, preserving the current contents at `path.bak` first.
|
|
84
|
+
* See `writeJsonFileWithBackup`.
|
|
85
|
+
*/
|
|
86
|
+
export async function writeTextFileWithBackup(path: string, content: string): Promise<void> {
|
|
87
|
+
await backupExistingFile(path);
|
|
88
|
+
await writeTextFile(path, content);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Copy an existing file to `path.bak` (overwriting an old backup). Missing
|
|
93
|
+
* source files are skipped — there is nothing to protect yet.
|
|
94
|
+
*/
|
|
95
|
+
async function backupExistingFile(path: string): Promise<void> {
|
|
96
|
+
if (!(await fileExists(path))) return;
|
|
97
|
+
await Bun.write(`${path}.bak`, await Bun.file(path).text());
|
|
98
|
+
}
|
|
99
|
+
|
|
71
100
|
/** Remove a file if it exists. */
|
|
72
101
|
export async function removeFile(path: string): Promise<void> {
|
|
73
102
|
await rm(path, { force: true });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {} from "../contracts";
|
|
2
2
|
import { jsonDownload } from "../contracts";
|
|
3
|
-
import { homeDir, isLocalEndpoint, readJsonFile, stripV1Suffix,
|
|
3
|
+
import { homeDir, isLocalEndpoint, readJsonFile, stripV1Suffix, writeJsonFileWithBackup } from "../fs-ops";
|
|
4
4
|
import type { ApplyInput, InjectorSpec } from "../contracts";
|
|
5
5
|
|
|
6
6
|
|
|
@@ -94,7 +94,7 @@ export const claudeSpec: InjectorSpec = {
|
|
|
94
94
|
settings.env = env;
|
|
95
95
|
settings.model = claudeModel(input);
|
|
96
96
|
for (const [key, value] of Object.entries(CLAUDE_BASELINE)) settings[key] = value;
|
|
97
|
-
await
|
|
97
|
+
await writeJsonFileWithBackup(path, settings);
|
|
98
98
|
},
|
|
99
99
|
|
|
100
100
|
async reset(path) {
|
|
@@ -106,7 +106,7 @@ export const claudeSpec: InjectorSpec = {
|
|
|
106
106
|
}
|
|
107
107
|
delete settings.model;
|
|
108
108
|
delete settings.smallModel;
|
|
109
|
-
await
|
|
109
|
+
await writeJsonFileWithBackup(path, settings);
|
|
110
110
|
},
|
|
111
111
|
|
|
112
112
|
download(input) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {} from "../contracts";
|
|
2
|
-
import { ensureV1Suffix, homeDir, readJsonFile, readTextFile, removeFile, textGet, textHas, textRemove, textUpsert,
|
|
2
|
+
import { ensureV1Suffix, homeDir, readJsonFile, readTextFile, removeFile, textGet, textHas, textRemove, textUpsert, writeJsonFileWithBackup, writeTextFileWithBackup } from "../fs-ops";
|
|
3
3
|
import type { InjectorSpec } from "../contracts";
|
|
4
4
|
|
|
5
5
|
|
|
@@ -93,12 +93,12 @@ export const codexSpec: InjectorSpec = {
|
|
|
93
93
|
` refresh_interval_ms = 0`,
|
|
94
94
|
].join("\n"),
|
|
95
95
|
);
|
|
96
|
-
await
|
|
96
|
+
await writeTextFileWithBackup(path, text);
|
|
97
97
|
|
|
98
98
|
const auth = ((await readJsonFile(codexAuthPath())) as Record<string, string> | null) ?? {};
|
|
99
99
|
auth[CODEX_PROVIDER] = input.apiKey;
|
|
100
|
-
await
|
|
101
|
-
await
|
|
100
|
+
await writeJsonFileWithBackup(codexAuthPath(), auth);
|
|
101
|
+
await writeTextFileWithBackup(codexAuthHelperPath(), CODEX_AUTH_HELPER_SOURCE);
|
|
102
102
|
},
|
|
103
103
|
|
|
104
104
|
async reset(path) {
|
|
@@ -118,12 +118,12 @@ export const codexSpec: InjectorSpec = {
|
|
|
118
118
|
if (textGet(text, { kind: "flat", key: "model_provider" }) === CODEX_PROVIDER) {
|
|
119
119
|
text = textUpsert(text, { kind: "flat", key: "model_provider", insertAtTop: true }, "openai");
|
|
120
120
|
}
|
|
121
|
-
await
|
|
121
|
+
await writeTextFileWithBackup(path, text);
|
|
122
122
|
|
|
123
123
|
const auth = (await readJsonFile(codexAuthPath())) as Record<string, string> | null;
|
|
124
124
|
if (auth?.[CODEX_PROVIDER]) {
|
|
125
125
|
delete auth[CODEX_PROVIDER];
|
|
126
|
-
await
|
|
126
|
+
await writeJsonFileWithBackup(codexAuthPath(), auth);
|
|
127
127
|
}
|
|
128
128
|
await removeFile(codexAuthHelperPath());
|
|
129
129
|
},
|