Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/.impeccable/surfaces/src-app-tsx.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ related_targets: ["src/ConsoleApp.tsx"]
# Administrator console (operate)

Scope: the signed-in console shell and every page behind it, including Getting started on the Overview and the optional console tour. Visitor mode: Operate.
Audience: the administrator of one Parsar Core deployment. Task: judge health, capacity, usage and failures; inspect and delete or copy project assets; manage projects, keys and nodes.
Audience: the administrator of one Parsar Core deployment. Task: judge health, capacity, usage and failures; inspect and delete or copy project assets; manage projects, keys, nodes and each harness's default model.
Constraints: Web API only (`/core/v1`); missing data stays visibly missing; no small print, explanations live in help tips; API terms stay English in Chinese copy; zh-CN and English, light and dark.

Information architecture: Monitor (Overview, Agent metrics, Sandbox metrics, Session log) · Resources (Agents, Environment templates, Skills, Files, Vaults) · Platform (Projects and keys, Nodes, System).
Expand Down
24 changes: 0 additions & 24 deletions apps/web/.shoot-click.mjs

This file was deleted.

35 changes: 21 additions & 14 deletions apps/web/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,15 +607,15 @@ orbits hold their places, the grid is a still frame and no transition runs.

### Getting started
The first card on the Overview while any step is to do: a card header ("Getting
started", "n of 3 done", a help tip, then a ghost Take the tour button and an icon
button that hides it) over three rows split by Faint Rules. Each row has a 22px
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 Idle Gray, 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; Create project
(which continues to the new project's first key) or Issue key; Projects and keys.
Add node, Create project and Issue key open their page with the dialog already
open. Every step done turns it into one line, "You're set", with Take the tour and
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;
Projects and keys. Add node, Create project and Issue key open their page with the
dialog already open; Open System focuses the default harness's Set or Replace. 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
Expand Down Expand Up @@ -644,14 +644,21 @@ offered as Current); another backend starts from its standard size and this
console's Runtime, and E2B always needs its key again. Rules sit behind help tips.

### System page
Read-only facts in three sections, each saying where it changes. Installation: the
public address, API base URL, installation ID and source commit as a fact card.
Sandboxes: the shared sandbox configuration, with a "Change on the Nodes page"
text action in the section header. 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, each saying where it changes. Installation: the public address, API
base URL, installation ID and source commit as a fact card. Default model, 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 provider's 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 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. Sandboxes: the shared sandbox configuration, with a "Change on
the Nodes page" text action in the section header. 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.

### Loading and motion
The console has no spinners and no "Loading…" lines. Reads are cached (TanStack
Expand Down
15 changes: 9 additions & 6 deletions apps/web/PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@ workbench.
template build's), a review, and advanced settings
with the complete form — then the node list with each node's capacity, host
figures and allocations, enrollment, renaming, sandbox limits and removal; Add node
asks for the node's sandbox limits before it issues the one-time command), System (read-only: the
installation's public address, API base URL, installation ID and source commit; Core's
startup settings from config.json, with the file and the apply command that change them;
and the sandbox configuration every project shares, with a link to Nodes where it changes).
asks for the node's sandbox limits before it issues the one-time command), System (the
installation's public address, API base URL, installation ID and source commit, read-only;
each harness's default model, set, replaced or cleared there beside its read-only startup
state; the sandbox configuration every project shares, with a link to Nodes where it
changes; and Core's startup settings from config.json, with the file and the apply command
that change them).
- A node whose provider is not ready names the reason (Docker unreachable, no Docker
limits, missing Runtime image, no KVM, missing microsandbox components, a host too
small) and its fix in the help tip beside its status, wherever that status shows.
Expand All @@ -95,9 +97,10 @@ workbench.
about when Core reclaims it. Docker never suspends and shows none of it.
- **Getting started**: signing in opens the console on the Overview; nothing is
forced first. While a step is to do, a Getting started checklist on the Overview
shows three steps, in any order, each with its state and one action: sandboxes
shows four steps, in any order, each with its state and one action: sandboxes
ready (a saved deployment and a node online and ready, or a saved E2B deployment
whose template build is not reported as not ready),
whose template build is not reported as not ready), a default model on the default
harness (on any enabled harness when none is default),
a project with an active key, and a first Session. Completion comes from reads the
console already makes. It can be hidden; Show Getting started in the sidebar
opens it again, and it ends with a brief "You're set". The optional
Expand Down
20 changes: 20 additions & 0 deletions apps/web/e2e/access.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ test("opens a fresh install on the Overview's Getting started: a project and its
const step = (name: string) => page.getByRole("region", { name: "Getting started" }).getByRole("listitem").filter({ hasText: name });
// The fixture deployment already has a ready node.
await expect(step("Get sandboxes ready")).toContainText("Done");
await expect(step("Set a default model")).toContainText("To do");
await expect(step("Create a project and issue a key")).toContainText("To do");
await expect(step("Run the first Session")).toContainText("To do");

Expand Down Expand Up @@ -82,6 +83,25 @@ test("opens a fresh install on the Overview's Getting started: a project and its
expect(stored).not.toContain(FIXTURE_CORE_KEY);
});

test("leads from Getting started to the default model, and counts it done once the default harness has one", async ({ page, request }) => {
await openConsole(page, request, "overview", { fresh: true });
const step = page.getByRole("region", { name: "Getting started" }).getByRole("listitem").filter({ hasText: "Set a default model" });
await expect(step).toContainText("To do");
await step.getByRole("button", { name: "Open System" }).click();
await expect(page.getByRole("heading", { name: "System", level: 1 })).toBeVisible();
// It arrives on the default harness's action.
const set = page.getByRole("button", { name: "Set the default model for Codex" });
await expect(set).toBeFocused();
await set.click();
const form = page.getByRole("dialog", { name: "Set default model for Codex" });
await form.getByLabel("Base URL").fill("https://model.example/v1");
await form.getByLabel("API key").fill("sk-fixture-getting-started");
await form.getByRole("button", { name: "Save" }).click();
await expect(form).toBeHidden();
await page.getByRole("button", { name: "Overview", exact: true }).click();
await expect(step).toContainText("Done");
});

test("shows every page empty on a fresh install, and Getting started hides, comes back and leads to sandbox setup", async ({ page, request }) => {
await openConsole(page, request, "overview", { fresh: true, sandbox: "none", nodes: "none" });
// What each page shows once its reads are done: an empty state, Core's own figures, or sandbox setup.
Expand Down
7 changes: 4 additions & 3 deletions apps/web/e2e/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ export const FIXTURE_CORE_KEY = "fixture-core-key-3f9a2c71";
* `sandbox` the sandbox deployment, `nodes: "none"` a deployment no node has joined, and
* `installation` how config.json's public_url is set: "public" (HTTPS, the default), "local"
* (loopback: only the Core machine reaches the API, and E2B is rejected) or "stale" (public,
* with a node enrolled with an earlier address).
* with a node enrolled with an earlier address), and `credentials: "none"` a Core without a
* credential encryption key, which cannot store a model provider's key.
*/
export interface FixtureOptions { fresh?: boolean; sandbox?: "configured" | "none" | "e2b"; nodes?: "none"; installation?: "public" | "local" | "stale" }
export interface FixtureOptions { fresh?: boolean; sandbox?: "configured" | "none" | "e2b"; nodes?: "none"; installation?: "public" | "local" | "stale"; credentials?: "none" }

/** Fresh fixture state: signed out ("login") or already signed in ("authenticated"). */
export async function resetFixture(request: APIRequestContext, auth: "login" | "authenticated" = "authenticated", options: FixtureOptions = {}) {
await request.post(`${fixture}/__fixture/reset?auth=${auth}${options.fresh ? "&projects=none" : ""}&sandbox=${options.sandbox ?? "configured"}${options.nodes ? `&nodes=${options.nodes}` : ""}&installation=${options.installation ?? "public"}`);
await request.post(`${fixture}/__fixture/reset?auth=${auth}${options.fresh ? "&projects=none" : ""}&sandbox=${options.sandbox ?? "configured"}${options.nodes ? `&nodes=${options.nodes}` : ""}&installation=${options.installation ?? "public"}${options.credentials ? `&credentials=${options.credentials}` : ""}`);
}

/** Opens the console already signed in, in English. */
Expand Down
Loading
Loading