From 63a12e599d189d4e2b0211c2bad7a75bad505ea6 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 7 Oct 2026 14:51:02 +0000 Subject: [PATCH] Delete the empty installation configuration fields --- apps/web/DESIGN.md | 6 +- apps/web/PRODUCT.md | 2 +- apps/web/e2e/fixture-console.mjs | 3 +- apps/web/e2e/public-url.spec.ts | 3 +- .../components/InstallationNotice.test.tsx | 2 +- .../src/features/system/StartupSettings.tsx | 93 ++++++++----------- apps/web/src/features/system/system.css | 33 ------- apps/web/src/i18n/locales/en/system.ts | 5 - apps/web/src/i18n/locales/zh-CN/system.ts | 5 - apps/web/src/lib/locale-strings.ts | 3 - contracts/agents-api/admin-api.md | 2 +- contracts/agents-api/core.openapi.yaml | 13 +-- contracts/agents-api/zh/admin-api.md | 4 +- docs/web/console-api-usage.md | 6 +- docs/zh/web/console-api-usage.md | 8 +- .../agents-client/src/admin-client.test.ts | 7 +- .../agents-client/src/admin-projection.ts | 15 +-- packages/agents-client/src/admin-types.ts | 12 +-- services/core/cmd/server/installation.go | 2 +- services/core/internal/api/installation.go | 17 +--- .../core/internal/api/installation_test.go | 4 +- 21 files changed, 74 insertions(+), 171 deletions(-) diff --git a/apps/web/DESIGN.md b/apps/web/DESIGN.md index 86c08ae95..d4430a411 100644 --- a/apps/web/DESIGN.md +++ b/apps/web/DESIGN.md @@ -399,7 +399,7 @@ On Overview and Session log, failed reads that leave a section unavailable repla A failed action whose outcome needs a decision (a sandbox change with no answer, a timeout or a 5xx) opens an error dialog with the reason and the next step as its primary button. Failed refreshes and project reads also raise an error toast; other failed actions, Core's clear refusal of a sandbox change among them, are reported there with the reason. A refusal leaves the page usable as it was. Errors inside a dialog or a form stay beside what they concern. Coverage notes (Margin Gray, Hairline ring, 8px corners, 12.5px Graphite) state bounded aggregation. Standing warnings that need action use an amber-tinted line at the top of the page body. On Nodes, this names nodes still bound to an old Core address; each of those nodes' status reads Old address (amber dot) with "Remove and add again" under it in 12px Graphite. Partial-data chips are amber-tinted pills with a help tip. Safety notices (a key shown once, a destructive consequence) stay visible in body text. -A local-only installation has the same amber notice on Overview, Nodes and System: other machines cannot connect, followed by Core's configuration path and apply command as copyable values. If Core has no configuration snapshot, state that those instructions are unavailable; never fill in a path or command. Add node is disabled with its reason beside the action, and Getting started leaves its first step to do with the address fix visible. A pending or failed installation read cannot complete that step; a failed read shows Unknown and Retry. +A local-only installation has the same amber notice on Overview, Nodes and System: other machines cannot connect, followed by Review the public address, which leads to System. Add node is disabled with its reason beside the action, and Getting started leaves its first step to do with the address fix visible. A pending or failed installation read cannot complete that step; a failed read shows Unknown and Retry. ### Onboarding Signing in and the console tour share one frame: a dark stage on the left (always dark, whatever the theme) and the task panel on the right, which follows the theme. The stage is the product's one authored moment: a flickering indigo dot grid under slow light rays (Magic UI's flickering grid and light rays), Core as the OpenAgentCore mark on a tile with a travelling border beam, and two orbits of Agents, Sessions, Skills, Vaults, files, templates and machines around it; the OpenAgentCore mark is itself nodes on a ring. Brand copy sits bottom-left in solid ink; it is a paragraph, not a heading, because the panel's title names the task. Signing in asks for one thing, the deployment's Core key, in a single password field; a copyable Docker Compose command to read the key stays visible beneath it, with a reminder to substitute a custom installation directory. The key’s authority stays in a help tip. A refused key, too many attempts or an unavailable console is an error beside the field. Signing in opens the console on the Overview. The optional tour has three chapters — Monitor, Resources, Platform — whose stage shows a real dark screenshot of those pages, tilted towards the panel; it takes the place of the console until its last button, Skip or Escape, and then returns the focus to the control that opened it. Entering the console or the tour, and leaving the tour, happen inside a View Transition: the old page dissolves forward and the new one is revealed in a circle growing from the pressed button. With reduced motion the orbits hold their places, the grid is a still frame and no transition runs. @@ -408,7 +408,7 @@ Signing in and the console tour share one frame: a dark stage on the left (alway The first card on the Overview while any step is to do: a card header ("Getting started", "n of 4 done", a help tip, then a ghost Take the tour button and an icon button that hides it) over four rows split by Faint Rules. Each row has a 22px numbered ring (a check on the tile wash when done), a 13px/600 title over one 12.5px Graphite line, a status dot (Done in green, To do in Pencil, Checking pending, Unknown for a failed read) and one outline action while the step is to do: Set up sandboxes, Add node, Open Nodes or Open sandbox backend; Open System; Create project (which continues to the new project's first key) or Issue key; See how to call (the newest active project, preferring one with an active key), or Projects and keys without an active project. Add node, Create project and Issue key open their page with the dialog already open; Open System brings the Default model provider section to the top of the page body and focuses the default harness's Set or Replace; See how to call opens the project and, once its keys, usage and address are read, brings its How to call heading to the top of the page body, focused. Only the page body scrolls; the page header stays. Every step done turns it into one line, "You're set", with Take the tour and Dismiss; it stays, through the tour, until dismissed, and the checklist does not come back on its own. The choice is kept per installation in the browser, also while the deployment cannot be read; Show Getting started, a quiet row above the sidebar's account controls, opens it again at any time. ### Sandbox setup -Setting up hosted sandboxes is a set of pages inside System’s Sandbox configuration secondary page, one decision each: where sandboxes run (own machines or E2B), then the backend or the E2B account, then the size of each sandbox (three presets; E2B skips it, since each sandbox takes the template build's size), then a review. Choices are large cards that advance on a click; short indigo dashes show the progress; pages slide and blur across. The backend page compares microsandbox and Docker behind a help tip; microsandbox comes first, preselected (a saved backend stays selected), with a neutral Recommended pill beside its title. Docker takes a confirmation (see Dialogs) once per visit to setup; a saved Docker deployment has already made it. The review states where sandboxes run, the size, the Runtime (taken from this console's distribution manifest) and the Core address, read-only: it is config.json's `public_url`, and the console never asks for it. A loopback address carries an amber line under it: only the Core machine reaches it. When Core rejects the configuration for it (E2B with a loopback `public_url`), a red-tinted block under the review keeps Core's message and adds the config file and apply command as copyable values. A save attempt clears the transient E2B key. Initial setup then asks for it again, with a link to that step; an update may leave it blank to keep the committed key. Advanced settings, one link away, hold the complete form: resources (not for E2B), the Runtime release and the E2B template. A change keeps the saved size and Runtime while the backend stays the same (a saved size outside the presets is offered as Current). Same-backend editing starts at size or E2B credentials with the provider fixed. It is an online configuration update, including when older sandboxes remain: existing node identities and resource ownership are retained. Changing the backend or E2B team requires reset and then a new setup. E2B updates can omit the key to retain it; every explicitly entered key takes the verified replacement path and advances the target generation on success, including the same value. Rejections remain inline with a safe reason and a deliberate way back to reset; never infer teams from a key, auto-reset or auto-resubmit. Optional explanations sit behind help tips; errors and safety consequences remain visible. +Setting up hosted sandboxes is a set of pages inside System’s Sandbox configuration secondary page, one decision each: where sandboxes run (own machines or E2B), then the backend or the E2B account, then the size of each sandbox (three presets; E2B skips it, since each sandbox takes the template build's size), then a review. Choices are large cards that advance on a click; short indigo dashes show the progress; pages slide and blur across. The backend page compares microsandbox and Docker behind a help tip; microsandbox comes first, preselected (a saved backend stays selected), with a neutral Recommended pill beside its title. Docker takes a confirmation (see Dialogs) once per visit to setup; a saved Docker deployment has already made it. The review states where sandboxes run, the size, the Runtime (taken from this console's distribution manifest) and the Core address, read-only: it is config.json's `public_url`, and the console never asks for it. A loopback address carries an amber line under it: only the Core machine reaches it. When Core rejects the configuration for it (E2B with a loopback `public_url`), a red-tinted block under the review keeps Core's message and adds Managed in System, which leads to System. A save attempt clears the transient E2B key. Initial setup then asks for it again, with a link to that step; an update may leave it blank to keep the committed key. Advanced settings, one link away, hold the complete form: resources (not for E2B), the Runtime release and the E2B template. A change keeps the saved size and Runtime while the backend stays the same (a saved size outside the presets is offered as Current). Same-backend editing starts at size or E2B credentials with the provider fixed. It is an online configuration update, including when older sandboxes remain: existing node identities and resource ownership are retained. Changing the backend or E2B team requires reset and then a new setup. E2B updates can omit the key to retain it; every explicitly entered key takes the verified replacement path and advances the target generation on success, including the same value. Rejections remain inline with a safe reason and a deliberate way back to reset; never infer teams from a key, auto-reset or auto-resubmit. Optional explanations sit behind help tips; errors and safety consequences remain visible. ### Configuration generations A single rollout row opens a details dialog for Core's target generation, previous-generation sandboxes and rollout counts. Poll rapidly only while Core reports preparing, or while the independent reset is active. Settled is preparation state, not proof that all nodes are ready or all older Sessions have ended. Retained old resources alone must not keep rapid polling alive. Render failed, update-required and unknown target states distinctly. Keep offline/live-provider status separate from a node's durable serving-generation pin; the pin alone never means the node is online or ready. Node detail shows the serving generation and target preparation; allocation detail shows the owned configuration generation. Do not calculate rollout completion from these rows or promise immediate placement on the target. @@ -423,7 +423,7 @@ A persistent progress panel uses Core's busy, idle and cleanup counts, deadline Read deployment progress independently of node details. Partial failures retain successful facts with a visible stale/unavailable notice. An uncertain write opens the recovery dialog and requires a new authoritative read before another mutation; refresh reads state and never resubmits the write. The connection's QueryClient owns both the authoritative deployment and pending or uncertain writes across route transitions. Leaving Sandbox configuration cannot cancel or forget a submitted reset, and a cached node snapshot cannot replace a newer reset or completion learned on Overview. Returning to Nodes or Sandbox configuration reads the shared deployment immediately and refreshes node evidence separately. Only a successful authoritative read begun after the write settles can release the mutation block; an earlier or still-pending read cannot. Submitting consumes the reset confirmation even if its outcome is uncertain; recovery uses the separate read-and-review dialog. Observation retries preserve applicable non-secret configuration drafts. A changed installation, owner epoch, backend, mode or generation discards the prior draft and confirmation. Logout clears this connection-scoped state. ### System page -Four sections, each saying where it changes. Installation: the public address, API base URL, installation ID and source commit as a fact card, with an outline action that opens the Domain and HTTPS secondary page. Default model configuration, the one section changed here: one card per harness in an auto-fill grid, its header holding the harness name and outline actions (Set, or Replace and Clear); fact rows give the harness's read-only startup state (a status dot and a Default pill, its source behind a help tip), then the default model ID, provider protocol, base URL, whether a key is configured, token limits when set and the update time, or Not set. Set and Replace open one form dialog. The model ID is required; advanced settings disclose an optional JSON object editor with formatting and inline syntax errors, plus token limits. The harness list supplies supported protocols, native protocols, JSON support and required limits from one adapter declaration. The form uses those fields without harness-specific branches. Nonempty JSON requires a native protocol; the form explains an incompatible selection beside the editor. Help tips explain the scope of native settings. Changing the model ID, provider URL or protocol clears the native JSON so settings cannot follow an unrelated model by accident. Re-entering the required write-only API key alone does not change model identity. The key field is a required password input, never prefilled or shown and forgotten when the form closes. Core's rejection stays in red inside the form; Clear is a ConfirmDialog. Usage details opens Core’s observations in a separate dialog. Sandboxes: one navigation row to the Sandbox configuration secondary page; do not repeat its configuration facts on System. Startup settings: a line naming the config file and the apply command as copyable chips, with when they were last applied, over a table of each setting, its value and the services a change restarts. Sensitive settings show only Configured or Not set; Default and Fixed after install are neutral pills beside the value. +Four sections; the page help says where sandboxes and startup settings change. Installation: the public address, API base URL, installation ID and source commit as a fact card, with an outline action that opens the Domain and HTTPS secondary page. Default model configuration, the one section changed here: one card per harness in an auto-fill grid, its header holding the harness name and outline actions (Set, or Replace and Clear); fact rows give the harness's read-only startup state (a status dot and a Default pill, its source behind a help tip), then the default model ID, provider protocol, base URL, whether a key is configured, token limits when set and the update time, or Not set. Set and Replace open one form dialog. The model ID is required; advanced settings disclose an optional JSON object editor with formatting and inline syntax errors, plus token limits. The harness list supplies supported protocols, native protocols, JSON support and required limits from one adapter declaration. The form uses those fields without harness-specific branches. Nonempty JSON requires a native protocol; the form explains an incompatible selection beside the editor. Help tips explain the scope of native settings. Changing the model ID, provider URL or protocol clears the native JSON so settings cannot follow an unrelated model by accident. Re-entering the required write-only API key alone does not change model identity. The key field is a required password input, never prefilled or shown and forgotten when the form closes. Core's rejection stays in red inside the form; Clear is a ConfirmDialog. Usage details opens Core’s observations in a separate dialog. Sandboxes: one navigation row to the Sandbox configuration secondary page; do not repeat its configuration facts on System. Startup settings: a line saying these are the settings Core loaded, over a table of each setting, its value and the services a change restarts. Sensitive settings show only Configured or Not set; Default and Fixed after install are neutral pills beside the value. ### One place for each task A configuration or operation has one home. Other pages link to it instead of repeating the same panel. System links to the Sandbox configuration secondary page; Nodes contains node management. Keep the configuration page flat: the resource editor is a dialog, and rollout is one status row with a details action. Put low-frequency counts and generation metadata in that dialog. Explanatory prose belongs in help tips, not rows of small print. Keep actionable errors and unresolved state visible without duplicating the whole workflow. diff --git a/apps/web/PRODUCT.md b/apps/web/PRODUCT.md index 090ca06fd..b1fe280d9 100644 --- a/apps/web/PRODUCT.md +++ b/apps/web/PRODUCT.md @@ -59,7 +59,7 @@ The console runs beside the administrator's own Core, with execution, files and - **Connect a host.** The Linux/macOS and PowerShell commands come from Core's installation read for the Session's environment; the console shows them as they are, with a link to the native installation guide, and never builds one itself. A command downloads the matching installer, installs the chosen Harnesses, starts the daemon and checks its connection. Its authorization expires after 30 minutes; the console reads a fresh one every 20 minutes, and says the command is unavailable when Core has none. No model readiness is implied. Rotating a credential requires stopping the installed daemon, replacing the configured file and starting that same daemon again. A disconnected daemon may still be running; `start` alone does not replace it. - **Typed write errors.** Known Core codes use shared bilingual copy and safe typed details. Exact Core field paths attach definite refusals to the relevant input. Unknown codes retain Core's fallback message; uncertain write outcomes stay form-level and are never retried automatically. - **Read failures.** Overview and Session log distinguish unavailable reads from successful empty results. Failed reads have a visible retry; retained or partial data says it may be incomplete or out of date, and Session filter totals stay missing while any required read has failed. Only successful empty reads show zero. -- **Local-only address.** Overview, Nodes and System warn when Core reports `local_only`, with the configuration path and apply command Core supplies as copyable instructions. Without a configuration snapshot they state what is missing. Add node is unavailable with a reason; Getting started keeps the first step to do until the public address is fixed. An unread installation address cannot complete that step, and a failed read offers Retry. +- **Local-only address.** Overview, Nodes and System warn when Core reports `local_only` and lead to System to review the public address. Add node is unavailable with a reason; Getting started keeps the first step to do until the public address is fixed. An unread installation address cannot complete that step, and a failed read offers Retry. - **Figures.** Project, Agent and key usage comes from Core's summary; Agent run, tool and activity figures are still assembled in the browser from bounded reads and state their coverage. Metrics that would need new Core endpoints are not simulated. Usage is cumulative per Session and is not billing. - Runtime CPU and memory exist only for Core-managed hosted sandboxes. - Preserve workflow safety: confirmed deletion, no automatic retry of uncertain writes, no secrets in browser storage. diff --git a/apps/web/e2e/fixture-console.mjs b/apps/web/e2e/fixture-console.mjs index a3de5cafa..6d57e3cc2 100644 --- a/apps/web/e2e/fixture-console.mjs +++ b/apps/web/e2e/fixture-console.mjs @@ -40,7 +40,7 @@ const publicUrl = () => (state.installation === "local" ? LOCAL_URL : PUBLIC_URL /** Core reports one installation ID, a canonical UUID, in the installation and the deployment. */ const INSTALLATION_ID = "7f3c2a90-5b1e-4c2d-9e3f-0a1b2c3d4e5f"; -/** GET /core/v1/installation: the address, the startup settings from config.json and what is bound to the address. */ +/** GET /core/v1/installation: the address, the startup settings Core loaded and what is bound to the address. */ function installation() { const local = state.installation === "local"; const setting = (key, value, fallback, restarts, extra = {}) => ({ key, value, default: fallback, changeable: true, sensitive: false, restarts, ...extra }); @@ -51,7 +51,6 @@ function installation() { object: "core.installation", installation_id: INSTALLATION_ID, public_url: publicUrl(), api_base_url: `${publicUrl()}/v1`, local_only: local, source_commit: release.source_commit, configuration: { - path: "/opt/oac/config.json", apply_command: "sudo oac apply", applied_at: "2026-09-24T09:30:00Z", settings: [ setting("public_url", publicUrl(), LOCAL_URL, ["core", "web"]), setting("listen_address", "127.0.0.1:8091", "127.0.0.1:8091", ["core"]), diff --git a/apps/web/e2e/public-url.spec.ts b/apps/web/e2e/public-url.spec.ts index 14dda4215..2b3a36a27 100644 --- a/apps/web/e2e/public-url.spec.ts +++ b/apps/web/e2e/public-url.spec.ts @@ -42,12 +42,11 @@ test("explains an E2B rejection in the wizard, with a link to domain setup", asy await expect(page.getByRole("heading", { name: "Connect E2B" })).toBeVisible(); }); -test("lists the startup settings on System with where to change them", async ({ page, request }) => { +test("lists the startup settings on System", async ({ page, request }) => { await openConsole(page, request, "system"); const installation = page.getByRole("region", { name: "Installation" }); await expect(installation).toContainText("https://core.example.com/v1"); const startup = page.getByRole("region", { name: "Startup settings" }); - await expect(startup).toContainText("Change these in /opt/oac/config.json, then run sudo oac apply"); const settings = startup.getByRole("table", { name: "Startup settings" }); await expect(settings.getByRole("row", { name: /^log_level/ })).toContainText("debug"); await expect(settings.getByRole("row", { name: /^listen_address/ })).toContainText("Default"); diff --git a/apps/web/src/components/InstallationNotice.test.tsx b/apps/web/src/components/InstallationNotice.test.tsx index 68efdcef7..81a8ab535 100644 --- a/apps/web/src/components/InstallationNotice.test.tsx +++ b/apps/web/src/components/InstallationNotice.test.tsx @@ -5,7 +5,7 @@ import { InstallationNotice } from "./InstallationNotice"; const installation: CoreInstallation = { object: "core.installation", installation_id: null, public_url: "http://127.0.0.1:8091", api_base_url: "http://127.0.0.1:8091/v1", - source_commit: null, local_only: true, configuration: null, + source_commit: null, local_only: true, configuration: { settings: [] }, address_bindings: { nodes: 0, nodes_on_other_address: 0, hosted_sandboxes: 0, self_hosted_executors: 0 }, }; diff --git a/apps/web/src/features/system/StartupSettings.tsx b/apps/web/src/features/system/StartupSettings.tsx index 9d999e2f5..b506b7364 100644 --- a/apps/web/src/features/system/StartupSettings.tsx +++ b/apps/web/src/features/system/StartupSettings.tsx @@ -1,10 +1,8 @@ import type { CoreInstallationConfiguration, CoreInstallationSetting } from "@oac/agents-client"; -import { Trans, useTranslation } from "react-i18next"; +import { useTranslation } from "react-i18next"; import { ValuePill } from "../../components/atoms/ValuePill"; import { Section } from "../../components/console-ui"; -import { CopyableId } from "../../components/list-ui"; -import { formatDateTime } from "../../lib/format"; function display(value: unknown): string | null { if (value === null || value === undefined) return null; @@ -16,62 +14,45 @@ function isDefault(setting: CoreInstallationSetting): boolean { } /** - * Platform › System: Core's startup settings, read-only. They live in - * config.json and take effect with the apply command; the console only says - * where to change them. A sensitive setting shows whether it is set, never - * its value. + * Platform › System: the process settings Core loaded, read-only. A + * sensitive setting shows whether it is set, never its value. */ -export function StartupSettings({ configuration }: { configuration: CoreInstallationConfiguration | null }) { - const { t, i18n } = useTranslation("system"); - const locale = i18n.resolvedLanguage; +export function StartupSettings({ configuration }: { configuration: CoreInstallationConfiguration }) { + const { t } = useTranslation("system"); return (
- {configuration === null ?

{t("startup.none")}

: <> -

- {configuration.path ? - , - command: , - }} - /> - : {t("startup.effective")}} - {configuration.applied_at ? {t("startup.appliedAt", { time: formatDateTime(Date.parse(configuration.applied_at) / 1000, locale) })} : null} -

-
- - - - - - - - - - {configuration.settings.filter((setting) => setting.key !== "public_url").map((setting) => { - const value = setting.sensitive ? null : display(setting.value); - return ( - - - - - - ); - })} - -
{t("startup.columns.key")}{t("startup.columns.value")}{t("startup.columns.restarts")}
{setting.key} - - {setting.sensitive - ? t(setting.configured ? "startup.configured" : "startup.notSet") - : value === null ? {t("startup.notSet")} : {value}} - {isDefault(setting) ? {t("startup.default")} : null} - {setting.changeable ? null : {t("startup.fixed")}} - - {setting.restarts.length ? setting.restarts.join(", ") : {t("startup.noRestart")}}
-
- } +

{t("startup.effective")}

+
+ + + + + + + + + + {configuration.settings.filter((setting) => setting.key !== "public_url").map((setting) => { + const value = setting.sensitive ? null : display(setting.value); + return ( + + + + + + ); + })} + +
{t("startup.columns.key")}{t("startup.columns.value")}{t("startup.columns.restarts")}
{setting.key} + + {setting.sensitive + ? t(setting.configured ? "startup.configured" : "startup.notSet") + : value === null ? {t("startup.notSet")} : {value}} + {isDefault(setting) ? {t("startup.default")} : null} + {setting.changeable ? null : {t("startup.fixed")}} + + {setting.restarts.length ? setting.restarts.join(", ") : {t("startup.noRestart")}}
+
); } diff --git a/apps/web/src/features/system/system.css b/apps/web/src/features/system/system.css index 1731597a4..07f783f17 100644 --- a/apps/web/src/features/system/system.css +++ b/apps/web/src/features/system/system.css @@ -59,45 +59,12 @@ color: var(--ink-3); } -/* Where startup settings are changed: the file and the apply command, both copyable. */ -.system-where { - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: 4px 12px; - margin: 0; - color: var(--ink-2); - font-size: 13px; - line-height: 24px; -} - -/* The file and the command read as chips that carry their own copy button. */ -.system-where .copyable-id { - margin: 0 2px; - padding-left: 8px; - vertical-align: middle; - background: var(--surface); - border-radius: var(--radius-chip); - box-shadow: var(--shadow-hairline); -} - -.system-where .copyable-id code { - color: var(--fg); - font-family: var(--font-mono); - font-size: 12px; -} - /* Setting names and values are the table's data, not secondary IDs. */ .data-table.system-settings code { color: var(--ink); font-size: 12px; } -.system-applied { - color: var(--ink-3); - font-size: 12.5px; -} - .system-note { margin: 0; color: var(--ink-2); diff --git a/apps/web/src/i18n/locales/en/system.ts b/apps/web/src/i18n/locales/en/system.ts index 01539ccb9..dda2183a5 100644 --- a/apps/web/src/i18n/locales/en/system.ts +++ b/apps/web/src/i18n/locales/en/system.ts @@ -19,12 +19,7 @@ export const system = { startup: { title: "Startup settings", help: "Core reports the process settings it loaded. A sensitive setting shows only whether it is set.", - none: "Core did not report startup settings.", effective: "These are the settings this Core process loaded.", - where: "Change these in , then run ", - copyPath: "Copy path", - copyCommand: "Copy command", - appliedAt: "Last applied {{time}}", columns: { key: "Setting", value: "Value", diff --git a/apps/web/src/i18n/locales/zh-CN/system.ts b/apps/web/src/i18n/locales/zh-CN/system.ts index 072fd56c5..770c992ce 100644 --- a/apps/web/src/i18n/locales/zh-CN/system.ts +++ b/apps/web/src/i18n/locales/zh-CN/system.ts @@ -21,12 +21,7 @@ export const system: TranslationShape = { startup: { title: "启动设置", help: "Core 报告它加载的进程设置。敏感设置只显示是否已设置。", - none: "Core 没有报告启动设置。", effective: "这些是这个 Core 进程加载的设置。", - where: "在 中修改,然后运行 ", - copyPath: "复制路径", - copyCommand: "复制命令", - appliedAt: "上次应用于 {{time}}", columns: { key: "设置", value: "值", diff --git a/apps/web/src/lib/locale-strings.ts b/apps/web/src/lib/locale-strings.ts index a9e7a6171..715e1e4d8 100644 --- a/apps/web/src/lib/locale-strings.ts +++ b/apps/web/src/lib/locale-strings.ts @@ -426,9 +426,6 @@ export const chinese = { "Change the sandbox configuration": "修改沙箱配置", "The address nodes and sandboxes use to reach Core.": "节点和沙箱访问 Core 使用的地址。", "Managed in System": "在系统中管理", - "Config file": "配置文件", - "Then run": "然后运行", - "Copy path": "复制路径", "Set a public address before connecting remote nodes; E2B sandboxes need an HTTPS one.": "连接远程节点前,请先设置公开地址;E2B 沙箱需要 HTTPS 地址。", "Enter the E2B key again to save.": "请重新输入 E2B key 后再保存。", "Enter the key": "输入 key", diff --git a/contracts/agents-api/admin-api.md b/contracts/agents-api/admin-api.md index fe39e5e66..f31771c1f 100644 --- a/contracts/agents-api/admin-api.md +++ b/contracts/agents-api/admin-api.md @@ -137,7 +137,7 @@ Core writes this record in the same transaction that creates the Session. Later | `api_base_url` | `public_url` followed by `/v1`, the `OPENAI_BASE_URL` for Project API keys. Null when `public_url` is null | | `local_only` | True when `public_url` names a loopback host, which only the Core host reaches | | `source_commit` | The full source commit Core was built from; null for development builds | -| `configuration` | The process settings Core loaded from its environment. `path` and `apply_command` are empty, and `applied_at` is null | +| `configuration` | The process settings Core loaded from its environment, under `settings` | | `address_bindings` | What a change of `public_url` affects, counted on each read | `configuration.settings` has one entry per setting Core loaded, with its dotted `key`, effective `value`, `default`, whether it is `changeable`, whether it is `sensitive`, and the services it `restarts` (`core`, `web`, `database`). diff --git a/contracts/agents-api/core.openapi.yaml b/contracts/agents-api/core.openapi.yaml index c3412ffd4..f7c597938 100644 --- a/contracts/agents-api/core.openapi.yaml +++ b/contracts/agents-api/core.openapi.yaml @@ -437,8 +437,7 @@ definitions: configuration: allOf: - $ref: '#/definitions/api.InstallationConfiguration' - description: The process settings Core loaded. path and apply_command are empty, and applied_at is null, because Core reports its environment rather than an installer file. - x-nullable: true + description: The process settings Core loaded. installation_id: description: The ID in OAC_INSTALLATION_ID_FILE; null when Core runs without the sandbox manager. type: string @@ -461,16 +460,6 @@ definitions: type: object api.InstallationConfiguration: properties: - applied_at: - description: Null when Core reports its own environment. - type: string - x-nullable: true - apply_command: - description: Command that applies config.json changes. Empty when Core reports its own environment. - type: string - path: - description: Absolute host path of config.json. Empty when Core reports its own environment. - type: string settings: items: $ref: '#/definitions/api.InstallationSetting' diff --git a/contracts/agents-api/zh/admin-api.md b/contracts/agents-api/zh/admin-api.md index c86c1de67..eabea2d5b 100644 --- a/contracts/agents-api/zh/admin-api.md +++ b/contracts/agents-api/zh/admin-api.md @@ -1,7 +1,7 @@ --- title: "Core 管理 API" source: contracts/agents-api/admin-api.md -source_hash: 3fc6573b19b9c78ca8a3b275122793b1a99e31f739d83ff25ff56624013dc428 +source_hash: 22ac83d8596bcee671a4f94c81d2fe65e109ab9c0b3759e17cf30626bd5d41df --- Core 管理 API(`/core/v1`)用于管理安装实例:Project 及其 API 密钥、Project 资源的读取和删除、执行器凭据、部署默认模型、沙箱部署及其节点、监控和审计。Web 的[控制台服务器](../../../docs/zh/web/console-server.md#forwarding-to-core)会为已登录的管理员调用它;运维人员则从 Core 主机上的脚本调用它([编写 Core API 脚本](../../../docs/zh/getting-started/operations.md#script-the-core-api))。生成的架构是 [core.openapi.yaml](../core.openapi.yaml),所有错误都使用 [Core 错误封装](core-errors.md)。 @@ -139,7 +139,7 @@ Core 会在创建 Session 的同一事务中写入此记录。之后的 Agent | `api_base_url` | 在 `public_url` 后附加 `/v1`,即 Project API 密钥使用的 `OPENAI_BASE_URL`。当 `public_url` 为 null 时为 null | | `local_only` | 当 `public_url` 指向回环主机时为 True,该主机只能由 Core 主机访问 | | `source_commit` | Core 构建所依据的完整源代码提交;开发构建为 null | -| `configuration` | Core 从环境加载的进程设置。`path` 和 `apply_command` 为空,`applied_at` 为 null | +| `configuration` | Core 从环境加载的进程设置,位于 `settings` 中 | | `address_bindings` | 更改 `public_url` 所影响的内容,每次读取都会重新统计 | `configuration.settings` 为 Core 加载的每项设置一条记录,包含以点分隔的 `key`、生效的 `value`、`default`、是否 `changeable`、是否 `sensitive`,以及会 `restarts` 的服务(`core`、`web`、`database`)。 diff --git a/docs/web/console-api-usage.md b/docs/web/console-api-usage.md index 60195f8b6..ba8faa171 100644 --- a/docs/web/console-api-usage.md +++ b/docs/web/console-api-usage.md @@ -72,10 +72,10 @@ In an archived project the section hides **Issue credential** and **Rotate** beh | Resource owners | `GET /core/v1/projects/{project_id}/resource-owners` | The Creator column of every resource list and the creator fact of detail pages, in batches of up to 100 IDs: the creating key's name, **Admin copy** for an owner with source `admin_copy`, or **Unknown** when Core has no record | | Write operations | `GET /core/v1/projects/{project_id}/write-operations` | A project's write history, newest first, filtered by key and resource type, 50 per page | | Summary | `GET /core/v1/summary` | Overview (per project), the Agents list (`group_by=agent`), a project's page (per project and `group_by=key`), Agent metrics (to skip idle projects, and usage by creating key since the start of the range), the Projects list (last activity) | -| Installation | `GET /core/v1/installation` | System's Installation facts (`public_url`, `api_base_url`, `installation_id`, `source_commit`) and read-only Startup settings (`configuration.settings` under its `path`, `apply_command` and `applied_at`; a sensitive setting shows only whether it is `configured`); `api_base_url` in the call samples; `public_url` as the download origin and `--source-url` of the node install and uninstall commands (and the install command's `--core-url`); `path` and `apply_command` beside a sandbox configuration Core rejected. A sensitive setting with a value, or an unknown member, fails the read; `configuration: null` shows a note | +| Installation | `GET /core/v1/installation` | System's Installation facts (`public_url`, `api_base_url`, `installation_id`, `source_commit`) and read-only Startup settings (`configuration.settings`; a sensitive setting shows only whether it is `configured`); `api_base_url` in the call samples; `public_url` as the download origin and `--source-url` of the node install and uninstall commands (and the install command's `--core-url`). A sensitive setting with a value, or an unknown member, fails the read | | Core metrics | `GET /core/v1/metrics?range=` | Core metrics page; the Core popover on Overview. A Core without the route (404) is shown as not reporting, and the popover then shows only Core's status. The [Core metrics contract](../../contracts/agents-api/core-metrics.md) defines every measurement | -`local_only`, or no `public_url`, stops Add node from issuing a command and Clean up the host from giving one. Overview, Nodes and System then show a visible warning with Core's configuration path and apply command as copyable values; when `configuration` is null, they state that the path and command are unavailable. Nodes disables Add node with a visible reason, and Getting started leaves its sandbox step to do. +`local_only`, or no `public_url`, stops Add node from issuing a command and Clean up the host from giving one. Overview, Nodes and System then show a visible warning, with Review the public address leading to System. Nodes disables Add node with a visible reason, and Getting started leaves its sandbox step to do. Wherever a new key is shown, and without any key on an active project's page, the console gives shell exports of `OPENAI_BASE_URL` (the installation's `api_base_url`) and `OPENAI_API_KEY` (the new key, or a placeholder for a key of the project), with `curl` and Python examples for `GET /v1/agents` and `POST /v1/agents/sessions`, and sends none of them. When the installation is `local_only` it says the API is reachable only on the Core machine, and without an `api_base_url` it says to set `public_url`. @@ -95,7 +95,7 @@ The list carries each harness's configuration, so the console does not read `GET | Operation | Route | Console use | | --- | --- | --- | -| Deployment | `GET`, `POST`, `PUT /core/v1/sandbox/deployment` | Read the provider, the read-only `core_url` (`OAC_PUBLIC_URL`, shown in the setup review and never sent), reset state, installation ID and specification; a 409 `sandbox_configuration_error` (E2B with a loopback `public_url`) shows the shared client's fixed safe address-configuration message in the setup wizard, with the installation's config file and apply command, and leaves nothing to confirm; initialize the deployment with `resources` and the Docker or microsandbox `runtime` release, or with the E2B account and no `resources` (Core adopts the template build's CPU and memory); change its settings with the expected generation. E2B's `metadata.template_build` (status, CPU, memory, disk) shows on System, the Sandbox configuration summary and Sandbox metrics, and sizes each sandbox when `specification.resources` is missing; microsandbox's `suspension` (idle and retention seconds) shows on System and the Nodes summary | +| Deployment | `GET`, `POST`, `PUT /core/v1/sandbox/deployment` | Read the provider, the read-only `core_url` (`OAC_PUBLIC_URL`, shown in the setup review and never sent), reset state, installation ID and specification; a 409 `sandbox_configuration_error` (E2B with a loopback `public_url`) shows the shared client's fixed safe address-configuration message in the setup wizard, with Managed in System leading to System, and leaves nothing to confirm; initialize the deployment with `resources` and the Docker or microsandbox `runtime` release, or with the E2B account and no `resources` (Core adopts the template build's CPU and memory); change its settings with the expected generation. E2B's `metadata.template_build` (status, CPU, memory, disk) shows on System, the Sandbox configuration summary and Sandbox metrics, and sizes each sandbox when `specification.resources` is missing; microsandbox's `suspension` (idle and retention seconds) shows on System and the Nodes summary | | E2B discovery | `POST /core/v1/sandbox/providers/e2b/discovery` | The setup wizard lists the templates the entered E2B key can see, then the selected template's ready builds. The key travels only in these request bodies and the deployment write | | Reset | `POST`, `DELETE /core/v1/sandbox/deployment/reset` | Explicitly clear hosted resources, or cancel the remaining clear at the observed generation; show Core's remaining and offline projection | | Nodes | `GET /core/v1/sandbox/nodes` | Nodes page; fleet on Overview; node capacity on Sandbox metrics. An online node's `diagnostic` (`docker_unavailable`, `docker_limits_unsupported`, `runtime_image_unavailable`, `kvm_unavailable`, `microsandbox_artifacts_unavailable`, `capacity_insufficient`, `provider_unavailable`; any other value reads as `provider_unavailable`) marks it degraded and names the reason and fix in the help tip beside its status on each of these and on the node's page. A node whose `core_url` (the address it enrolled with) differs from the deployment's `core_url` is named on the Nodes page as bound to an old address, to be removed and added again, and its status there and on its page reads Old address instead of its health; an empty `core_url` (a node Core did not enroll) is unknown, not old. **Add node** follows only the node whose `enrollment_id` equals its command's | diff --git a/docs/zh/web/console-api-usage.md b/docs/zh/web/console-api-usage.md index 09d91aff8..d855d7ee6 100644 --- a/docs/zh/web/console-api-usage.md +++ b/docs/zh/web/console-api-usage.md @@ -1,7 +1,7 @@ --- title: "控制台 API 使用" source: docs/web/console-api-usage.md -source_hash: 2a4be7b081286e5a95c14380acc517d2d4b4ba955c0bd61338b0977d84c9df16 +source_hash: 3b54852843f5afccd6ce47a532a9c82f6a4a0dbffdac9b96892c5026f5f78c20 --- 本页列出各控制台页面读取和写入的 Core 路由,以及控制台如何限定读取范围。[administrator API contract](../../../contracts/agents-api/zh/admin-api.md) 定义了路由、响应结构、分页和审计记录;[API namespaces and credentials](../api/index.md) 定义了本文使用的术语。 @@ -74,10 +74,10 @@ source_hash: 2a4be7b081286e5a95c14380acc517d2d4b4ba955c0bd61338b0977d84c9df16 | 资源所有者 | `GET /core/v1/projects/{project_id}/resource-owners` | 每个资源列表的 Creator 列和详情页的创建者信息,每批最多处理 100 个 ID:创建密钥的名称;来源为 `admin_copy` 的所有者显示 **Admin copy**;Core 无记录时显示 **Unknown** | | 写入操作 | `GET /core/v1/projects/{project_id}/write-operations` | 项目的写入历史,按最新优先,可按密钥和资源类型筛选,每页 50 条 | | 汇总 | `GET /core/v1/summary` | Overview(按项目)、Agents 列表(`group_by=agent`)、项目页面(按项目并使用 `group_by=key`)、Agent 指标(跳过空闲项目,并统计从范围开始以来按创建密钥划分的使用量)、Projects 列表(最近活动) | -| 安装 | `GET /core/v1/installation` | System 的 Installation 信息(`public_url`、`api_base_url`、`installation_id`、`source_commit`)和只读 Startup 设置(`path` 下的 `configuration.settings`,以及 `apply_command` 和 `applied_at`;敏感设置仅显示其是否为 `configured`);调用示例中的 `api_base_url`;作为下载来源以及节点安装和卸载命令中 `--source-url` 的 `public_url`(还包括安装命令中的 `--core-url`);Core 拒绝的 Sandbox 配置旁的 `path` 和 `apply_command`。如果敏感设置包含值,或存在未知成员,读取会失败;`configuration: null` 会显示一条说明 | +| 安装 | `GET /core/v1/installation` | System 的 Installation 信息(`public_url`、`api_base_url`、`installation_id`、`source_commit`)和只读 Startup 设置(`configuration.settings`;敏感设置仅显示其是否为 `configured`);调用示例中的 `api_base_url`;作为下载来源以及节点安装和卸载命令中 `--source-url` 的 `public_url`(还包括安装命令中的 `--core-url`)。如果敏感设置包含值,或存在未知成员,读取会失败 | | Core 指标 | `GET /core/v1/metrics?range=` | Core 指标页面;Overview 上的 Core 弹出内容。不存在该路由的 Core(404)会显示为未报告数据,此时弹出内容仅显示 Core 状态。[Core metrics contract](../../../contracts/agents-api/zh/core-metrics.md) 定义了每项度量 | -如果为 `local_only`,或者没有 `public_url`,Add node 将无法签发命令,Clean up the host 也无法提供命令。随后 Overview、Nodes 和 System 会显示醒目警告,其中 Core 的配置路径和 apply command 为可复制值;当 `configuration` 为 null 时,它们会说明路径和命令不可用。Nodes 会禁用 Add node 并显示明确原因,Getting started 则将 sandbox 步骤保留为待办项。 +如果为 `local_only`,或者没有 `public_url`,Add node 将无法签发命令,Clean up the host 也无法提供命令。随后 Overview、Nodes 和 System 会显示醒目警告,并通过 Review the public address 前往 System。Nodes 会禁用 Add node 并显示明确原因,Getting started 则将 sandbox 步骤保留为待办项。 无论是在显示新密钥时,还是在活动项目页面没有显示任何密钥时,控制台都会提供 `OPENAI_BASE_URL`(安装的 `api_base_url`)和 `OPENAI_API_KEY`(新密钥,或项目密钥的占位符)的 shell 导出变量,以及针对 `GET /v1/agents` 和 `POST /v1/agents/sessions` 的 `curl` 和 Python 示例,但不会发送其中任何调用。当安装为 `local_only` 时,控制台会说明 API 只能在 Core 所在计算机上访问;当缺少 `api_base_url` 时,则会提示设置 `public_url`。 @@ -97,7 +97,7 @@ source_hash: 2a4be7b081286e5a95c14380acc517d2d4b4ba955c0bd61338b0977d84c9df16 | 操作 | 路由 | 控制台用途 | | --- | --- | --- | -| 部署 | `GET`、`POST`、`PUT /core/v1/sandbox/deployment` | 读取提供商、只读 `core_url`(即 `OAC_PUBLIC_URL`,会显示在设置审核中且绝不发送)、重置状态、安装 ID 和规范;409 `sandbox_configuration_error`(E2B 搭配回环地址形式的 `public_url`)会在设置向导中显示共享客户端固定的安全地址配置消息,并同时显示安装的配置文件和 apply command,且无需确认;使用 `resources` 以及 Docker 或 microsandbox 的 `runtime` release 初始化部署,或者使用 E2B 账户且不提供 `resources`(Core 采用模板构建的 CPU 和内存);使用预期的 generation 更改设置。E2B 的 `metadata.template_build`(状态、CPU、内存、磁盘)会显示在 System、Sandbox 配置摘要和 Sandbox metrics 中;当缺少 `specification.resources` 时,它还会确定每个 Sandbox 的大小;microsandbox 的 `suspension`(空闲和保留秒数)会显示在 System 和 Nodes 摘要中 | +| 部署 | `GET`、`POST`、`PUT /core/v1/sandbox/deployment` | 读取提供商、只读 `core_url`(即 `OAC_PUBLIC_URL`,会显示在设置审核中且绝不发送)、重置状态、安装 ID 和规范;409 `sandbox_configuration_error`(E2B 搭配回环地址形式的 `public_url`)会在设置向导中显示共享客户端固定的安全地址配置消息,并通过 Managed in System 前往 System,且无需确认;使用 `resources` 以及 Docker 或 microsandbox 的 `runtime` release 初始化部署,或者使用 E2B 账户且不提供 `resources`(Core 采用模板构建的 CPU 和内存);使用预期的 generation 更改设置。E2B 的 `metadata.template_build`(状态、CPU、内存、磁盘)会显示在 System、Sandbox 配置摘要和 Sandbox metrics 中;当缺少 `specification.resources` 时,它还会确定每个 Sandbox 的大小;microsandbox 的 `suspension`(空闲和保留秒数)会显示在 System 和 Nodes 摘要中 | | E2B 发现 | `POST /core/v1/sandbox/providers/e2b/discovery` | 设置向导先列出输入的 E2B 密钥可见的模板,再列出所选模板的可用构建。该密钥只会通过这些请求体和部署写入请求传输 | | 重置 | `POST`、`DELETE /core/v1/sandbox/deployment/reset` | 显式清除托管资源,或在观测到的 generation 处取消剩余清除;显示 Core 的剩余资源和离线预测 | | Nodes | `GET /core/v1/sandbox/nodes` | Nodes 页面;Overview 上的机群;Sandbox metrics 中的节点容量。在线节点的 `diagnostic`(`docker_unavailable`、`docker_limits_unsupported`、`runtime_image_unavailable`、`kvm_unavailable`、`microsandbox_artifacts_unavailable`、`capacity_insufficient`、`provider_unavailable`;任何其他值均读取为 `provider_unavailable`)会将其标记为降级,并在上述每个页面及节点页面中,紧邻状态的帮助提示里说明原因和修复方法。如果节点的 `core_url`(其注册时使用的地址)与部署的 `core_url` 不同,Nodes 页面会将其标记为绑定到旧地址,需要移除后重新添加;此时它在该页面和节点页面中的状态会显示 Old address,而不是健康状态;如果 `core_url` 为空(Core 未注册该节点),则状态为未知,而不是旧地址。**Add node** 仅跟踪 `enrollment_id` 与其命令所含 `enrollment_id` 相等的节点 | diff --git a/packages/agents-client/src/admin-client.test.ts b/packages/agents-client/src/admin-client.test.ts index 5089dfdff..aba044f90 100644 --- a/packages/agents-client/src/admin-client.test.ts +++ b/packages/agents-client/src/admin-client.test.ts @@ -328,19 +328,20 @@ describe("AdminClient installation", () => { const installation = { object: "core.installation", installation_id: resourceId, public_url: "https://core.example", api_base_url: "https://core.example/v1", local_only: false, source_commit: "a".repeat(40), - configuration: { path: "/home/alice/.oac/core/config.json", apply_command: "/home/alice/.oac/core/oac apply", applied_at: "2026-09-25T09:30:00Z", settings: [port, headers] }, + configuration: { settings: [port, headers] }, address_bindings: { nodes: 2, nodes_on_other_address: 1, hosted_sandboxes: 3, self_hosted_executors: 1 }, }; it("reads installation facts before any deployment and rejects inconsistent snapshots", async () => { expect(await clientWith(installation).client.retrieveInstallation()).toEqual(installation); - expect(await clientWith({ ...installation, installation_id: null, public_url: null, api_base_url: null, source_commit: null, configuration: null }).client.retrieveInstallation()).toMatchObject({ public_url: null }); - const configuration = (settings: unknown[]) => ({ ...installation, configuration: { ...installation.configuration, settings } }); + expect(await clientWith({ ...installation, installation_id: null, public_url: null, api_base_url: null, source_commit: null }).client.retrieveInstallation()).toMatchObject({ public_url: null }); + const configuration = (settings: unknown[]) => ({ ...installation, configuration: { settings } }); for (const invalid of [ configuration([port, { ...headers, value: { authorization: "leak" } }]), configuration([port, { key: headers.key, value: null, default: null, changeable: true, sensitive: true, restarts: ["core"] }]), configuration([port, port]), { ...installation, address_bindings: { ...installation.address_bindings, nodes_on_other_address: 3 } }, { ...installation, token: "leak" }, + { ...installation, configuration: null }, configuration([{ ...port, configured: true }]), ]) { await expect(clientWith(invalid).client.retrieveInstallation()).rejects.toMatchObject({ code: "invalid_admin_response" }); diff --git a/packages/agents-client/src/admin-projection.ts b/packages/agents-client/src/admin-projection.ts index 67f509a60..a4196bb8c 100644 --- a/packages/agents-client/src/admin-projection.ts +++ b/packages/agents-client/src/admin-projection.ts @@ -292,14 +292,9 @@ export function projectInstallation(value: unknown): CoreInstallation { const bindings = record(installation.address_bindings, ["nodes", "nodes_on_other_address", "hosted_sandboxes", "self_hosted_executors"]); if (![bindings.nodes, bindings.nodes_on_other_address, bindings.hosted_sandboxes, bindings.self_hosted_executors].every(isNonnegativeInteger) || (bindings.nodes_on_other_address as number) > (bindings.nodes as number)) return invalidAdminResponse(); - let configuration: CoreInstallation["configuration"] = null; - if (installation.configuration !== null) { - const applied = record(installation.configuration, ["path", "apply_command", "applied_at", "settings"]); - if (typeof applied.path !== "string" || (applied.path !== "" && !applied.path.startsWith("/")) || typeof applied.apply_command !== "string" || - (applied.applied_at !== null && (typeof applied.applied_at !== "string" || !date(applied.applied_at))) || !Array.isArray(applied.settings)) return invalidAdminResponse(); - const settings = applied.settings.map(projectInstallationSetting); - if (new Set(settings.map((setting) => setting.key)).size !== settings.length) return invalidAdminResponse(); - configuration = { path: applied.path, apply_command: applied.apply_command, applied_at: applied.applied_at, settings }; - } - return { ...installation, address_bindings: { ...bindings }, configuration } as unknown as CoreInstallation; + const configuration = record(installation.configuration, ["settings"]); + if (!Array.isArray(configuration.settings)) return invalidAdminResponse(); + const settings = configuration.settings.map(projectInstallationSetting); + if (new Set(settings.map((setting) => setting.key)).size !== settings.length) return invalidAdminResponse(); + return { ...installation, address_bindings: { ...bindings }, configuration: { settings } } as unknown as CoreInstallation; } diff --git a/packages/agents-client/src/admin-types.ts b/packages/agents-client/src/admin-types.ts index 53701d598..e09c60f7a 100644 --- a/packages/agents-client/src/admin-types.ts +++ b/packages/agents-client/src/admin-types.ts @@ -200,14 +200,8 @@ export interface CoreInstallationSetting { /** Services that restart when the setting changes. */ restarts: ("core" | "web" | "database")[]; } -/** Where process settings change, and their last applied values. */ +/** The process settings Core loaded. */ export interface CoreInstallationConfiguration { - /** Absolute host path of config.json. Empty when Core reports the environment it loaded. */ - path: string; - /** Command that applies config.json changes. Empty when Core reports the environment it loaded. */ - apply_command: string; - /** Null when Core reports the environment it loaded. */ - applied_at: string | null; settings: CoreInstallationSetting[]; } /** `GET /core/v1/installation`: available before any sandbox deployment exists. */ @@ -222,8 +216,8 @@ export interface CoreInstallation { local_only: boolean; /** Full source commit Core was built from; null for development builds. */ source_commit: string | null; - /** Null when Core was not started. Core reports the process settings it loaded; path and apply_command are empty unless a snapshot was supplied. */ - configuration: CoreInstallationConfiguration | null; + /** The process settings Core loaded. */ + configuration: CoreInstallationConfiguration; address_bindings: CoreAddressBindings; } diff --git a/services/core/cmd/server/installation.go b/services/core/cmd/server/installation.go index d7bd089c4..a927a4e3c 100644 --- a/services/core/cmd/server/installation.go +++ b/services/core/cmd/server/installation.go @@ -33,6 +33,6 @@ func installationFacts(publicURL string) (api.Installation, error) { if err != nil { return facts, err } - facts.Configuration = &api.InstallationConfiguration{Settings: settings} + facts.Configuration = api.InstallationConfiguration{Settings: settings} return facts, nil } diff --git a/services/core/internal/api/installation.go b/services/core/internal/api/installation.go index 229da8835..ec1ba9f55 100644 --- a/services/core/internal/api/installation.go +++ b/services/core/internal/api/installation.go @@ -3,7 +3,6 @@ package api import ( "context" "net/http" - "time" "github.com/MiniMax-AI/OpenAgentCore/services/core/internal/deployment" ) @@ -22,22 +21,14 @@ type Installation struct { LocalOnly bool `json:"local_only"` // Full source commit Core was built from; null for development builds. SourceCommit *string `json:"source_commit" extensions:"x-nullable"` - // The process settings Core loaded. path and apply_command are empty, and applied_at is null, because Core reports its environment rather than an installer file. - Configuration *InstallationConfiguration `json:"configuration" extensions:"x-nullable"` + // The process settings Core loaded. + Configuration InstallationConfiguration `json:"configuration"` AddressBindings deployment.AddressBindings `json:"address_bindings"` } -// InstallationConfiguration is the process settings Core loaded. Path and -// ApplyCommand are empty, and AppliedAt is null, unless a caller built a -// snapshot itself. +// InstallationConfiguration is the process settings Core loaded. type InstallationConfiguration struct { - // Absolute host path of config.json. Empty when Core reports its own environment. - Path string `json:"path"` - // Command that applies config.json changes. Empty when Core reports its own environment. - ApplyCommand string `json:"apply_command"` - // Null when Core reports its own environment. - AppliedAt *time.Time `json:"applied_at" extensions:"x-nullable"` - Settings []InstallationSetting `json:"settings"` + Settings []InstallationSetting `json:"settings"` } type InstallationSetting struct { diff --git a/services/core/internal/api/installation_test.go b/services/core/internal/api/installation_test.go index 721703bf8..c8c047e89 100644 --- a/services/core/internal/api/installation_test.go +++ b/services/core/internal/api/installation_test.go @@ -19,7 +19,7 @@ func TestInstallationReadNeedsOnlyTheCoreKey(t *testing.T) { fakes.projectsReader.resolveAPIKey = projectKeys(t, callerBinding()).ResolveAPIKey deps.CoreKeys = coreKeys(t, "administrator") public, id := "https://core.example", "5b7c0f3e-0000-4000-8000-000000000001" - settings := &InstallationConfiguration{Path: "/home/alice/.oac/core/config.json", Settings: []InstallationSetting{{Key: "ports.core", Value: 8091, Default: 8091, Changeable: true, Restarts: []string{"core"}}}} + settings := InstallationConfiguration{Settings: []InstallationSetting{{Key: "ports.core", Value: 8091, Default: 8091, Changeable: true, Restarts: []string{"core"}}}} fakes.installationBindings.addressBindings = func(context.Context) (deployment.AddressBindings, error) { return deployment.AddressBindings{Nodes: 2, NodesOnOtherAddress: 1}, nil } @@ -40,7 +40,7 @@ func TestInstallationReadNeedsOnlyTheCoreKey(t *testing.T) { var body map[string]any if result.Code != http.StatusOK || json.Unmarshal(result.Body.Bytes(), &body) != nil || body["object"] != "core.installation" || body["public_url"] != public || body["address_bindings"].(map[string]any)["nodes_on_other_address"] != float64(1) || - body["configuration"].(map[string]any)["path"] != "/home/alice/.oac/core/config.json" { + body["configuration"].(map[string]any)["settings"].([]any)[0].(map[string]any)["key"] != "ports.core" { t.Fatal(result.Code, result.Body.String()) } }