dsh-packycode-api 1.3.3 → 1.3.5

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/SECURITY.md CHANGED
@@ -13,12 +13,21 @@
13
13
  - Balance requests carry a timeout (`AbortSignal.timeout`) and `redirect: 'error'`, and go only to the configured hosts plus the PackyAPI fallback host.
14
14
  - Model traffic goes to the configured model host through the plugin's pi-ai adapter. Protocol-specific compatibility headers are applied host-side; credentials remain scoped to the selected plugin key.
15
15
 
16
- ## Native Claude requests
16
+ ## Claude requests
17
17
 
18
- - Claude uses the plugin's HTTP Messages route. The plugin has no Claude CLI runner, installer, delegation tool or local MCP bridge. Existing standalone Claude Code installations and configuration are untouched.
19
- - The compatibility headers and session metadata do not load a CLI login or external configuration. Bearer authentication uses only the plugin's selected conversation key; images keep their separate credential record.
20
- - DSH owns tool execution, permissions, plan mode, attachments and history. Wire tool aliases are restored only for tools offered on that request; unknown aliases are not decoded into host tools. Saved images from older CLI-backed sessions remain readable without executing their old tools.
21
- - Captured client-policy refusals do not disable keys or switch providers. The specific pre-stream anomalies covered by the native transport may replay the exact bytes and credential once; ordinary policy refusals, HTTP 429 and accepted streams are excluded. Cancellation aborts the replay.
18
+ - Claude requests use one of two transports, chosen by the request key's PackyCode groups (or the user's pin): keys in `cc` send them through this device's official Claude Code CLI; every other key uses the plugin's HTTP Messages route. Both carry the same payload; neither has any fallback to the other, to another provider, or to another credential.
19
+ - The HTTP route's compatibility headers and session metadata do not load a CLI login or external configuration. Bearer authentication uses only the plugin's selected conversation key; images keep their separate credential record.
20
+ - DSH owns tool execution, permissions, plan mode, attachments and history on both routes. Wire tool aliases are restored only for tools offered on that request; unknown aliases are not decoded into host tools. Saved images from older CLI-backed sessions remain readable without executing their old tools.
21
+ - Captured client-policy refusals do not disable keys or switch providers or transports. The specific pre-stream anomalies covered by the HTTP route may replay the exact bytes and credential once; ordinary policy refusals, HTTP 429 and accepted streams are excluded. Cancellation aborts the replay.
22
+
23
+ ## The Claude Code CLI transport
24
+
25
+ - The CLI is a transport, not an agent: it runs with all of its own tools disabled (`--tools ""`), so it cannot read, write or execute anything itself. The tools it can see are the ones DSH offered for that request, served by an MCP bridge; every call is answered with the result DSH produced after running the tool under its own permissions and sandbox.
26
+ - The bridge is a per-process Streamable HTTP server bound to `127.0.0.1` on a random port. It requires a random 256-bit capability on every request, rejects foreign `Host` and `Origin` headers, and closes with its process. The capability lives only in an owner-only (`0600`) config file inside an owner-only run directory under the CLI home; it never appears in a command line.
27
+ - The CLI runs with an isolated config home (`~/.dsh/packycode-claude-cli`), `--setting-sources ""`, `--strict-mcp-config`, `--disable-slash-commands`, `--no-session-persistence`, and CLAUDE.md, auto-memory, auto-update, telemetry and non-essential traffic disabled. Inherited `ANTHROPIC_*`, `CLAUDE_*`, `MCP_*`, `DISABLE_*` and `ENABLE_*` variables are removed, so the user's own Claude Code login, API key, base URL, model and settings never reach it.
28
+ - The request key reaches the child only as `ANTHROPIC_AUTH_TOKEN` in its environment, with the endpoint as `ANTHROPIC_BASE_URL`; it is never written to a file, an argument, a transcript or a log. The DSH system prompt and the conversation transcript the CLI resumes are written to that owner-only run directory and removed when the process ends; directories left by a crashed host are removed after a day.
29
+ - The CLI's own retries, model fallback, refusal fallback and compaction are disabled: one DSH step is one API request, and retries follow DSH's policy. Processes are stopped on cancellation, after 20 idle minutes, beyond eight live conversations, and when the plugin unloads.
30
+ - Installing the CLI from the settings panel runs `npm install -g @anthropic-ai/claude-code` with constant arguments; on Windows the npm launcher is resolved to the program it forwards to, so no shell parses user text.
22
31
 
23
32
  ## Reporting
24
33
 
@@ -1,13 +1,15 @@
1
1
  {
2
- "latestTested": "0.1.5-rc.2",
2
+ "latestTested": "0.2.1-rc.2",
3
3
  "acceptedRuntimeRange": ">=0.1.5-rc.1 <0.3.0-0",
4
4
  "supported": [
5
5
  "0.1.5-rc.2",
6
- "0.2.0-rc.2"
6
+ "0.2.0-rc.2",
7
+ "0.2.1-rc.2"
7
8
  ],
8
- "piAi": "0.85.1",
9
+ "piAi": "1.1.0",
9
10
  "validation": {
10
11
  "0.1.5-rc.2": "36 offline tests against the published host packages: the key ring (add/select/disable/rotate/enable/remove and a hand-edited record), balance parsing and host fallback with a fake fetch, failure classification against the gateway's real Chinese wording (including that a raw 403 quota message must not read as auth), the catalog contract (protocol, base URL, and vendor effort levels per model), the stream guard (terminal events, pre-chunk throws, and that a mid-stream failure never blames a key), and the real PiAiAdapter resolving every advertised model and preparing its call without reading a credential or opening a stream. Live check against the gateway: balance read (account and per-key), one answer from each of the three models over its own protocol, a rotation rehearsal where an invalid key was disabled and the next attempt answered with the real key, and the host adapter classifying a rejected key as AUTH.",
11
- "0.2.0-rc.2": "Declared in the accepted range; checked against the bundled 0.2.0-rc.2 host packages, not yet in a live boot on this line. Present and used: llm.registerAdapter (fiber-scoped, duplicates refused), llm.registerConfigurableProviders, credential records (credentials-local keeps records in memory, updated on modifyRecord and by a file watcher), connection.rpc.handle, client slots, web.registerSearchProvider with web.searchProviders, loader.entries(), profileContext.patchPath, hmr.runExclusive (refuses nested transactions), and the SettingsForms service: settings.update(rowId, patch) for volatile Config fields, settings.configure, and loader/volatile-update. settings.installSection does NOT exist on this line (an earlier note said it did), so the host overrides (apiHost/statusHost, not volatile) are read-only here while the volatile web search preference is writable."
12
+ "0.2.0-rc.2": "Declared in the accepted range; checked against the bundled 0.2.0-rc.2 host packages, not yet in a live boot on this line. Present and used: llm.registerAdapter (fiber-scoped, duplicates refused), llm.registerConfigurableProviders, credential records (credentials-local keeps records in memory, updated on modifyRecord and by a file watcher), connection.rpc.handle, client slots, web.registerSearchProvider with web.searchProviders, loader.entries(), profileContext.patchPath, hmr.runExclusive (refuses nested transactions), and the SettingsForms service: settings.update(rowId, patch) for volatile Config fields, settings.configure, and loader/volatile-update. settings.installSection does NOT exist on this line (an earlier note said it did), so the host overrides (apiHost/statusHost, not volatile) are read-only here while the volatile web search preference is writable.",
13
+ "0.2.1-rc.2": "The development host since 2026-10-17: devDependencies pin the packages DeepSeek Harness 0.2.1-rc.2 ships (dsh-* 0.2.1-rc.2, cordis 4.0.5, schemastery 3.18.5, pi-ai 1.1.0), and the client and connection tests also run on 0.2.0-rc.2 through the host-v020 fixture. Before the switch the whole suite also passed against the packages extracted from the desktop app's app.asar. What changed for this plugin: PiAiAdapter.listModels resolves the key first and lists nothing on MISSING_CREDENTIAL; pi-ai 1.x Models folds systemPrompt and tools into a leading system message (toolsAdded) before the provider sees a request; dsh-tools runs PTC programs on ptcRuntime with workingDirectory; Connection admission asks browser auth for authenticateApi; credential-record changes refresh the browser's model catalog. Through the real host adapter, request bodies matched the pi-ai 0.85 baseline for 9 of 10 models (deepseek-flash's tool loses strict: false) and only SDK version headers differ. Not yet checked live or in a running DSH."
12
14
  }
13
15
  }
Binary file
Binary file
package/lib/client.js CHANGED
@@ -122,6 +122,45 @@ window.__ModuleLoader__.load({
122
122
  groupsMiss: "不可用",
123
123
  groupsPartial: "部分",
124
124
  groupsStale: "分组为上次读取的结果",
125
+ keyClaudeCli: "Claude 经官方 CLI",
126
+ keyClaudeHttp: "Claude 经 HTTP",
127
+ keyClaudeCliHint: "分组 {groups} 只接受官方 Claude Code 客户端,这个 Key 的 Claude 请求由本机 Claude Code CLI 发出。",
128
+ keyClaudeHttpHint: "这个 Key 的 Claude 请求走插件内置的 HTTP 通道。",
129
+ cliTitle: "Claude Code CLI",
130
+ cliIntro: "cc 分组只接受官方 Claude Code 客户端,这些 Key 的 Claude 请求由本机的 Claude Code CLI 发出;cc-sale、cc-expensive、claude-officially 等分组继续走插件内置的 HTTP 通道。通道由 Key 所在的分组决定。两种通道都由 DSH 执行工具、审批权限、计划模式、附件和历史,体验一致,CLI 只负责把请求发给 PackyCode。",
131
+ cliProcesses: "运行中的 CLI 会话 {live} 个 · 已启动 {opened} 次 · 续接 {continued} 次",
132
+ cliChecking: "正在检测本机的 Claude Code CLI…",
133
+ cliInstalled: "已安装",
134
+ cliVersion: "版本",
135
+ cliPath: "位置",
136
+ cliRecheck: "重新检测",
137
+ cliMissing: "未检测到 Claude Code CLI",
138
+ cliMissingHint: "cc 分组的 Key 需要它才能使用 Claude 模型(claude-opus-5-5、claude-sonnet-5-5、claude-haiku-5-5)。其它分组和其它模型不受影响。",
139
+ cliConfiguredMissing: "设置里指定的 CLI 路径不存在:{path}",
140
+ cliBroken: "找到了 CLI,但无法运行",
141
+ cliInstall: "安装 Claude Code CLI",
142
+ cliReinstall: "重新安装",
143
+ cliInstalling: "正在安装…(通常需要一两分钟,请勿关闭 DSH)",
144
+ cliInstallCommand: "将在本机执行:{command}(需要 Node.js 18 或更高版本)",
145
+ cliInstallSucceeded: "安装完成。",
146
+ cliInstallFailed: "安装失败",
147
+ cliNpmMissing: "没有找到 npm。请先从 https://nodejs.org 安装 Node.js(18 或更高版本),然后再点安装。",
148
+ cliPermission: "npm 没有权限写入全局目录。请在终端执行 sudo {command},或把 npm 的全局目录改到用户目录后重试。",
149
+ cliPermissionWindows: "npm 没有权限写入全局目录。请以管理员身份打开终端执行 {command},然后点「重新检测」。",
150
+ cliNetwork: "下载失败,请检查网络或 npm 镜像设置后重试。",
151
+ cliTimeout: "安装超时,请检查网络后重试。",
152
+ cliNotFoundAfterInstall: "npm 显示安装完成,但仍找不到 claude 命令。请在终端执行 claude --version 确认,然后点「重新检测」。",
153
+ cliWindowsNote: "Windows 上 Claude Code 还需要 Git for Windows(https://git-scm.com/downloads/win)。",
154
+ cliOutdated: "Claude Code CLI 版本过低",
155
+ cliOutdatedHint: "插件需要 {minVersion} 或更新版本。更新前,cc 分组 Key 的 Claude 请求无法发出;其它分组和其它模型不受影响。",
156
+ cliUpdate: "更新 Claude Code CLI",
157
+ cliUpdating: "正在更新…(通常需要一两分钟,请勿关闭 DSH)",
158
+ cliUpdateCommand: "将在本机执行:{command}",
159
+ cliUpdateSucceeded: "已更新到 {version}。",
160
+ cliUpdateFailed: "更新失败",
161
+ cliUpdateNotInstalled: "没有找到要更新的 CLI,请点「重新检测」。",
162
+ cliUpdateStillOutdated: "更新命令已执行,但版本仍低于 {minVersion}。请在终端执行 {command},看它给出的提示(例如用 Homebrew 安装的要执行 brew upgrade claude-code),然后点「重新检测」。",
163
+ cliUpdateBroken: "更新后 CLI 无法运行。请点「重新安装」,或在终端执行 {command}。",
125
164
  modelsTitle: "显示的模型",
126
165
  modelsIntro: "切换模型时只显示已勾选的 PackyCode 模型,默认全选。修改后立即生效;已有会话仍可继续使用原模型。",
127
166
  modelsChecking: "正在读取模型设置…",
@@ -295,6 +334,45 @@ window.__ModuleLoader__.load({
295
334
  groupsMiss: "Not",
296
335
  groupsPartial: "partly",
297
336
  groupsStale: "groups from the last read",
337
+ keyClaudeCli: "Claude via official CLI",
338
+ keyClaudeHttp: "Claude via HTTP",
339
+ keyClaudeCliHint: "Group {groups} only serves the official Claude Code client, so this key's Claude requests are sent by this device's Claude Code CLI.",
340
+ keyClaudeHttpHint: "This key's Claude requests use the plugin's built-in HTTP route.",
341
+ cliTitle: "Claude Code CLI",
342
+ cliIntro: "The cc group only serves the official Claude Code client, so Claude requests on cc keys go through this device's Claude Code CLI; cc-sale, cc-expensive, claude-officially and other groups keep the built-in HTTP route. The key's groups decide the route. Either way DSH runs the tools, permissions, plan mode, attachments and history the same; the CLI only carries the request to PackyCode.",
343
+ cliProcesses: "{live} CLI session(s) running · started {opened} · continued {continued}",
344
+ cliChecking: "Checking this device for the Claude Code CLI…",
345
+ cliInstalled: "Installed",
346
+ cliVersion: "Version",
347
+ cliPath: "Location",
348
+ cliRecheck: "Check again",
349
+ cliMissing: "Claude Code CLI not found",
350
+ cliMissingHint: "Keys in the cc group need it to use the Claude models (claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5). Other groups and models are unaffected.",
351
+ cliConfiguredMissing: "The CLI path set in settings does not exist: {path}",
352
+ cliBroken: "The CLI was found but could not run",
353
+ cliInstall: "Install Claude Code CLI",
354
+ cliReinstall: "Reinstall",
355
+ cliInstalling: "Installing… (usually a minute or two; keep DSH open)",
356
+ cliInstallCommand: "Runs on this device: {command} (requires Node.js 18 or later)",
357
+ cliInstallSucceeded: "Installed.",
358
+ cliInstallFailed: "Install failed",
359
+ cliNpmMissing: "npm was not found. Install Node.js 18 or later from https://nodejs.org, then click Install again.",
360
+ cliPermission: "npm cannot write its global directory. Run sudo {command} in a terminal, or point npm’s global prefix at a folder you own, then retry.",
361
+ cliPermissionWindows: "npm cannot write its global directory. Open a terminal as administrator, run {command}, then click Check again.",
362
+ cliNetwork: "The download failed. Check your network or npm registry settings and retry.",
363
+ cliTimeout: "The install timed out. Check your network and retry.",
364
+ cliNotFoundAfterInstall: "npm reported success, but the claude command still cannot be found. Run claude --version in a terminal, then click Check again.",
365
+ cliWindowsNote: "On Windows, Claude Code also needs Git for Windows (https://git-scm.com/downloads/win).",
366
+ cliOutdated: "Claude Code CLI is too old",
367
+ cliOutdatedHint: "The plugin needs {minVersion} or later. Until it is updated, Claude requests on cc keys cannot be sent; other groups and models are unaffected.",
368
+ cliUpdate: "Update Claude Code CLI",
369
+ cliUpdating: "Updating… (usually a minute or two; keep DSH open)",
370
+ cliUpdateCommand: "Runs on this device: {command}",
371
+ cliUpdateSucceeded: "Updated to {version}.",
372
+ cliUpdateFailed: "Update failed",
373
+ cliUpdateNotInstalled: "No CLI was found to update. Click Check again.",
374
+ cliUpdateStillOutdated: "The update ran, but the version is still below {minVersion}. Run {command} in a terminal and follow what it says (for a Homebrew install, brew upgrade claude-code), then click Check again.",
375
+ cliUpdateBroken: "The CLI no longer runs after the update. Click Reinstall, or run {command} in a terminal.",
298
376
  advanced: "Advanced: endpoints",
299
377
  apiHost: "Model endpoint",
300
378
  statusHost: "Balance endpoint",
@@ -1796,6 +1874,13 @@ window.__ModuleLoader__.load({
1796
1874
  const INVITE_URL = "https://www.packyapi.ai/register?aff=Im3l";
1797
1875
  /** How long after a search change the panel re-reads what DSH applied (the patch hot-reloads). */
1798
1876
  const SEARCH_SETTLE_MS = 1500;
1877
+ /** While the CLI installs, the panel polls its status this often. */
1878
+ const INSTALL_POLL_MS = 2e3;
1879
+ const CLAUDE_MODELS = [
1880
+ "claude-opus-5-5",
1881
+ "claude-sonnet-5-5",
1882
+ "claude-haiku-5-5"
1883
+ ];
1799
1884
  const OWN_SEARCH_IDS = {
1800
1885
  auto: "packycode-api-auto",
1801
1886
  packycode: "packycode-api"
@@ -1932,12 +2017,31 @@ window.__ModuleLoader__.load({
1932
2017
  ]
1933
2018
  }, family.name))
1934
2019
  }),
2020
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClaudeRoute, {
2021
+ group,
2022
+ t
2023
+ }),
1935
2024
  group.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1936
2025
  className: "pkc-meta",
1937
2026
  children: t("groupsStale")
1938
2027
  })
1939
2028
  ] });
1940
2029
  }
2030
+ /** Which transport this key's Claude requests take, when the key reaches Claude. */
2031
+ function ClaudeRoute({ group, t }) {
2032
+ const route = group.claudeTransport;
2033
+ if (route === void 0 || !CLAUDE_MODELS.some((id) => group.coverage?.[id] === true)) return null;
2034
+ const hint = route.transport === "cli" ? fill(t("keyClaudeCliHint"), { groups: (route.groups ?? []).join(" / ") || "cc" }) : t("keyClaudeHttpHint");
2035
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2036
+ className: "pkc-groups",
2037
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2038
+ className: "pkc-chip",
2039
+ "data-tone": route.transport === "cli" ? "info" : "neutral",
2040
+ title: hint,
2041
+ children: route.transport === "cli" ? t("keyClaudeCli") : t("keyClaudeHttp")
2042
+ })
2043
+ });
2044
+ }
1941
2045
  function KeyRow({ entry, row, group, descriptions, groupsLoading, busy, rename, onAction, t }) {
1942
2046
  const editing = rename.renaming?.id === entry.id;
1943
2047
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2479,6 +2583,232 @@ window.__ModuleLoader__.load({
2479
2583
  ]
2480
2584
  });
2481
2585
  }
2586
+ const INSTALL_FAILURES = {
2587
+ "npm-missing": () => "cliNpmMissing",
2588
+ permission: (cli) => cli.platform === "win32" ? "cliPermissionWindows" : "cliPermission",
2589
+ network: () => "cliNetwork",
2590
+ timeout: () => "cliTimeout",
2591
+ "not-found-after-install": () => "cliNotFoundAfterInstall",
2592
+ "not-installed": () => "cliUpdateNotInstalled",
2593
+ "still-outdated": () => "cliUpdateStillOutdated",
2594
+ "broken-after-update": () => "cliUpdateBroken"
2595
+ };
2596
+ /** The sentence explaining a failed install or update, by the code the host classified. */
2597
+ function installFailure(cli, t) {
2598
+ const key = INSTALL_FAILURES[cli.install?.code]?.(cli);
2599
+ const command = cli.install?.action === "update" ? cli.updateCommand : cli.command;
2600
+ return key === void 0 ? void 0 : fill(t(key), {
2601
+ command: command ?? "",
2602
+ minVersion: cli.minVersion ?? ""
2603
+ });
2604
+ }
2605
+ /**
2606
+ * Whether this device has the Claude Code CLI that Claude requests on cc
2607
+ * keys go through: an installer without one, an updater for one too old.
2608
+ */
2609
+ function ClaudeCliCard({ call, t }) {
2610
+ const [cli, setCli] = react.useState();
2611
+ const [checking, setChecking] = react.useState(false);
2612
+ const [error, setError] = react.useState();
2613
+ const check = react.useCallback((force = false) => {
2614
+ setChecking(true);
2615
+ setError(void 0);
2616
+ return call("claude-cli/status", { force }).then(setCli).catch((failure) => setError(clearMessage(failure))).finally(() => setChecking(false));
2617
+ }, [call]);
2618
+ react.useEffect(() => {
2619
+ check(false);
2620
+ }, [check]);
2621
+ const running = cli?.install?.state === "running";
2622
+ react.useEffect(() => {
2623
+ if (!running) return void 0;
2624
+ let live = true;
2625
+ const timer = window.setInterval(() => {
2626
+ call("claude-cli/status").then((value) => {
2627
+ if (live) setCli(value);
2628
+ }).catch(() => void 0);
2629
+ }, INSTALL_POLL_MS);
2630
+ return () => {
2631
+ live = false;
2632
+ window.clearInterval(timer);
2633
+ };
2634
+ }, [call, running]);
2635
+ const install = () => {
2636
+ setError(void 0);
2637
+ call("claude-cli/install").then(setCli).catch((failure) => setError(clearMessage(failure)));
2638
+ };
2639
+ const update = () => {
2640
+ setError(void 0);
2641
+ call("claude-cli/update").then(setCli).catch((failure) => setError(clearMessage(failure)));
2642
+ };
2643
+ const usable = cli?.installed === true && cli.version !== void 0;
2644
+ const outdated = usable && cli.outdated === true;
2645
+ const action = cli?.install?.action ?? "install";
2646
+ const failed = cli?.install?.state === "failed";
2647
+ const failure = cli === void 0 ? void 0 : installFailure(cli, t);
2648
+ const processes = cli?.processes;
2649
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
2650
+ title: t("cliTitle"),
2651
+ aside: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
2652
+ disabled: checking || running,
2653
+ onClick: () => check(true),
2654
+ children: checking ? t("refreshing") : t("cliRecheck")
2655
+ }),
2656
+ children: [
2657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2658
+ className: "pkc-note",
2659
+ children: t("cliIntro")
2660
+ }),
2661
+ cli === void 0 && error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2662
+ className: "pkc-note",
2663
+ children: t("cliChecking")
2664
+ }),
2665
+ usable && !outdated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2666
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2667
+ className: "pkc-status",
2668
+ children: [
2669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2670
+ className: "pkc-dot",
2671
+ "data-tone": "success"
2672
+ }),
2673
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("cliInstalled") }),
2674
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${t("cliVersion")} ${cli.version}` })
2675
+ ]
2676
+ }),
2677
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2678
+ className: "pkc-meta",
2679
+ children: [
2680
+ t("cliPath"),
2681
+ " ",
2682
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2683
+ className: "pkc-mono",
2684
+ children: cli.path
2685
+ })
2686
+ ]
2687
+ }),
2688
+ processes !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2689
+ className: "pkc-meta",
2690
+ children: fill(t("cliProcesses"), processes)
2691
+ }),
2692
+ cli.install?.state === "succeeded" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2693
+ className: "pkc-note",
2694
+ children: action === "update" ? fill(t("cliUpdateSucceeded"), { version: cli.install.to ?? cli.version }) : t("cliInstallSucceeded")
2695
+ })
2696
+ ] }),
2697
+ outdated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2698
+ className: "pkc-callout",
2699
+ children: [
2700
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2701
+ className: "pkc-status",
2702
+ children: [
2703
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2704
+ className: "pkc-dot",
2705
+ "data-tone": "warn"
2706
+ }),
2707
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("cliOutdated") }),
2708
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: `${t("cliVersion")} ${cli.version}` })
2709
+ ]
2710
+ }),
2711
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2712
+ className: "pkc-meta",
2713
+ children: [
2714
+ t("cliPath"),
2715
+ " ",
2716
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2717
+ className: "pkc-mono",
2718
+ children: cli.path
2719
+ })
2720
+ ]
2721
+ }),
2722
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2723
+ className: "pkc-note",
2724
+ children: fill(t("cliOutdatedHint"), { minVersion: cli.minVersion ?? "" })
2725
+ }),
2726
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2727
+ className: "pkc-split",
2728
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
2729
+ variant: "primary",
2730
+ size: "md",
2731
+ disabled: running || checking,
2732
+ onClick: update,
2733
+ children: running && action === "update" ? t("cliUpdating") : t("cliUpdate")
2734
+ })
2735
+ }),
2736
+ cli.updateCommand !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2737
+ className: "pkc-note",
2738
+ children: fill(t("cliUpdateCommand"), { command: cli.updateCommand })
2739
+ })
2740
+ ]
2741
+ }),
2742
+ failed && action === "update" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2743
+ className: "pkc-error",
2744
+ children: failure === void 0 ? t("cliUpdateFailed") : `${t("cliUpdateFailed")} · ${failure}`
2745
+ }), cli.install.detail !== void 0 && cli.install.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
2746
+ className: "pkc-detail",
2747
+ children: cli.install.detail
2748
+ })] }),
2749
+ cli !== void 0 && !usable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2750
+ className: "pkc-callout",
2751
+ children: [
2752
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2753
+ className: "pkc-status",
2754
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2755
+ className: "pkc-dot",
2756
+ "data-tone": cli.installed ? "danger" : "warn"
2757
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: cli.installed ? t("cliBroken") : t("cliMissing") })]
2758
+ }),
2759
+ cli.installed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2760
+ className: "pkc-meta",
2761
+ children: [
2762
+ t("cliPath"),
2763
+ " ",
2764
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2765
+ className: "pkc-mono",
2766
+ children: cli.path
2767
+ })
2768
+ ]
2769
+ }), cli.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
2770
+ className: "pkc-detail",
2771
+ children: cli.error
2772
+ })] }),
2773
+ cli.configuredPath !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2774
+ className: "pkc-note",
2775
+ children: fill(t("cliConfiguredMissing"), { path: cli.configuredPath })
2776
+ }),
2777
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2778
+ className: "pkc-note",
2779
+ children: t("cliMissingHint")
2780
+ }),
2781
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2782
+ className: "pkc-split",
2783
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
2784
+ variant: "primary",
2785
+ size: "md",
2786
+ disabled: running || checking,
2787
+ onClick: install,
2788
+ children: running && action === "install" ? t("cliInstalling") : cli.installed ? t("cliReinstall") : t("cliInstall")
2789
+ })
2790
+ }),
2791
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2792
+ className: "pkc-note",
2793
+ children: [fill(t("cliInstallCommand"), { command: cli.command ?? "" }), cli.platform === "win32" && ` ${t("cliWindowsNote")}`]
2794
+ })
2795
+ ]
2796
+ }),
2797
+ failed && action === "install" && !usable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2798
+ className: "pkc-error",
2799
+ children: failure === void 0 ? t("cliInstallFailed") : `${t("cliInstallFailed")} · ${failure}`
2800
+ }), cli.install.detail !== void 0 && cli.install.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
2801
+ className: "pkc-detail",
2802
+ children: cli.install.detail
2803
+ })] }),
2804
+ error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2805
+ className: "pkc-error",
2806
+ role: "alert",
2807
+ children: `${t("error")}: ${error}`
2808
+ })
2809
+ ]
2810
+ });
2811
+ }
2482
2812
  /** A release-age wait can end tomorrow, so the date is part of the time. */
2483
2813
  function readDateTime(ms) {
2484
2814
  try {
@@ -2657,6 +2987,10 @@ window.__ModuleLoader__.load({
2657
2987
  call,
2658
2988
  t
2659
2989
  }),
2990
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClaudeCliCard, {
2991
+ call,
2992
+ t
2993
+ }),
2660
2994
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
2661
2995
  call,
2662
2996
  t