@dotobokuri/fleet-console 1.15.0 → 1.16.1
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/AGENTS.md +160 -23
- package/README.md +18 -16
- package/dist/cli.d.ts +1 -20
- package/dist/cli.mjs +14116 -22237
- package/dist/client/assets/{_baseUniq-cnqj2OOU.js → _baseUniq-Bvbp_jlP.js} +1 -1
- package/dist/client/assets/{arc-DKvQCH7P.js → arc-BIE5Buf8.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-iJqnsZ6O.js → architectureDiagram-Q4EWVU46-D5XMG93D.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-CkJhTJZi.js → blockDiagram-DXYQGD6D-DF6L79td.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-DHkUfhoy.js → c4Diagram-AHTNJAMY-DhF1h0OW.js} +1 -1
- package/dist/client/assets/channel-BSjWiXu9.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-CJmFkUz-.js → chunk-4BX2VUAB-HIDhmpsY.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-Dp00Zwg3.js → chunk-4TB4RGXK-WM3UvlRe.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DFBvwrH_.js → chunk-55IACEB6-D9kCAM0-.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-Dik0hYho.js → chunk-EDXVE4YY-CHGSf6YA.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DYcmg67a.js → chunk-FMBD7UC4-saa1UCmo.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-B9bhXkOc.js → chunk-OYMX7WX6-BrlxxClZ.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CW_BXRdy.js → chunk-QZHKN3VN-CVnd5MX8.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-uJM9wQJs.js → chunk-YZCP3GAM-BpsFNlbM.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DIEVc5gp.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-DIEVc5gp.js +1 -0
- package/dist/client/assets/clone-VuQnnBA0.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BpsaUYJS.js → cose-bilkent-S5V4N54A-AdFIrmD1.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-CLaMczco.js → dagre-KV5264BT-YPE_DPjG.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-CFOnN2pM.js → diagram-5BDNPKRD-sW7JSe_o.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-CkLTTvmR.js → diagram-G4DWMVQ6-CdoUA7HM.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-c8R_KOlQ.js → diagram-MMDJMWI5-Dr_Q44k2.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-Cfz4vDC5.js → diagram-TYMM5635-CgWLDdO4.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-BZFJRBcT.js → erDiagram-SMLLAGMA-BS5jKQII.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-CSbwVNst.js → flowDiagram-DWJPFMVM-DM_ZIfsi.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-Z3BPe1vZ.js → ganttDiagram-T4ZO3ILL-D1ASi2pt.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-7Ka8NeSE.js → gitGraphDiagram-UUTBAWPF-DTS8qu5f.js} +1 -1
- package/dist/client/assets/{graph-DG6E-jsV.js → graph-BSP3V12O.js} +1 -1
- package/dist/client/assets/index-D3a51i1X.js +402 -0
- package/dist/client/assets/index-DNtncJ2Y.css +1 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-CN7rF9WG.js → infoDiagram-42DDH7IO-C6rS44Vv.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-jus38Ki6.js → ishikawaDiagram-UXIWVN3A-BysMV0Yj.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-CCn-AdDe.js → journeyDiagram-VCZTEJTY-Cq1tDTjK.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-BOfals_F.js → kanban-definition-6JOO6SKY-CV7fuoIo.js} +1 -1
- package/dist/client/assets/{layout-B2qTAIS5.js → layout-mBwNdhT9.js} +1 -1
- package/dist/client/assets/{linear-BmsYolMA.js → linear-B8Mqzhqu.js} +1 -1
- package/dist/client/assets/{mermaid.core-D0OYO4L1.js → mermaid.core-mTVIRAe-.js} +4 -4
- package/dist/client/assets/{min-jZihhkYo.js → min-LkS4GBrL.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-35wK1xy9.js → mindmap-definition-QFDTVHPH-CuK4DHsN.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-BynWLFT0.js → pieDiagram-DEJITSTG-yKtYS0aG.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-w5UoFlnr.js → quadrantDiagram-34T5L4WZ-CQYBuUXw.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-BQ9zR_XY.js → requirementDiagram-MS252O5E-DExCsGb_.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-BL1Lj8dz.js → sankeyDiagram-XADWPNL6-kqoN-VmQ.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-jC78BST4.js → sequenceDiagram-FGHM5R23-Dsk6zp0F.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-iVYQ2VCC.js → stateDiagram-FHFEXIEX-Bg-kY2ci.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Dlqv8bJ9.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-CYAlka_x.js → timeline-definition-GMOUNBTQ-DkeszYPW.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-BFcj10GU.js → vennDiagram-DHZGUBPP-a9aq_CaS.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-CIUgshxb.js → wardley-RL74JXVD-BioIIFLl.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-BEV516Os.js → wardleyDiagram-NUSXRM2D-Bpy2nx7x.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-D1DFk2ax.js → xychartDiagram-5P7HB3ND-N7vhpanC.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/diff/routes.mjs +304 -0
- package/dist/fleet-plugins/file-explorer/routes.mjs +446 -0
- package/dist/{cli-bin.mjs → fleet-plugins/terminal/routes.mjs} +17313 -21404
- package/package.json +7 -6
- package/dist/cli-bin.d.ts +0 -2
- package/dist/client/assets/channel-C3r_coHh.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DmoF5itQ.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-DmoF5itQ.js +0 -1
- package/dist/client/assets/clone-CJPQGOBM.js +0 -1
- package/dist/client/assets/index-B-bmeDQZ.js +0 -777
- package/dist/client/assets/index-BfTQyXzZ.css +0 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BEYzM5Cb.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -4,26 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
## Owns
|
|
6
6
|
|
|
7
|
-
- The console HTTP backend (loopback-only): its own server skeleton, security headers, static `/console/` serving of its own `dist/client/`, the browser observer REST/SSE surface (including the `validateHost`-gated `GET /
|
|
7
|
+
- The console HTTP backend (loopback-only): its own server skeleton, security headers, static `/console/` serving of its own `dist/client/`, the browser observer REST/SSE surface (including the `validateHost`-gated `GET /api/v1/settings/api-catalog` route-catalog introspection that backs the Settings backend-API list), generic plugin route/upgrade registration, and console-owned Theater folder selection. The console no longer imports or launches the retired gateway package.
|
|
8
8
|
- The server lifecycle: lock file, runtime paths, health probe, and build-stale detection are console-owned. `fleet-console` is the daemon, not a launcher over someone else's daemon.
|
|
9
9
|
- The `fleet-console` CLI entry point (`./cli` export, `dist/cli.mjs`): `start` (the default when no subcommand is given) ensures the local console server and opens the console URL in a browser; if a healthy daemon already exists, it opens that daemon's URL without starting a new server or erroring. `stop` stops the console server; `restart` stops the console server then starts a fresh one and opens it; `status` prints server health, endpoint, console URL, and registered-workspace count. The server binds to an OS-assigned random loopback port and records the actual port in the lock file, so consumers discover the port from the lock endpoint. `--help`/`-h` prints the banner-style help. `fleet console <args>` in `fleet-cli` relays the full argument list to this CLI as a child process, so every subcommand works through both `fleet console …` and the standalone `fleet-console …` binary. The root `pnpm fleet-console` script runs it from source via `tsx`; unpublished local runs isolate runtime files under the project workspace `.fleet/console` unless `FLEET_CONSOLE_DIR` is set, while published stable builds continue using the OS temp directory.
|
|
10
|
-
-
|
|
11
|
-
- Console durable state is persisted in the console data directory's `state.json` (`createDurableJsonStore`, `sensitivity: "sensitive"`). The data directory follows the release channel: published stable builds use `~/.fleet/console/state.json`, while unpublished local runs (`pnpm fleet-console`/`tsx`) use the project workspace `.fleet/console/state.json` co-located with the lock, so a dev console never shares Theaters/Operations with a globally installed one. Setting `FLEET_CONSOLE_DIR` relocates the durable state (and captures) into that directory too — co-located with the lock, matching the runtime-file escape hatch — so a read-only checkout can point its writable runtime slot away from the project tree. The console server is the sole writer; the file stores `{ version:
|
|
10
|
+
- Terminal sessions are spawned server-side by the Terminal plugin and observed in-process; carrier events flow directly into the observer store and browser SSE without a separate fleet-cli registration channel.
|
|
11
|
+
- Console durable state is persisted in the console data directory's `state.json` (`createDurableJsonStore`, `sensitivity: "sensitive"`). The data directory follows the release channel: published stable builds use `~/.fleet/console/state.json`, while unpublished local runs (`pnpm fleet-console`/`tsx`) use the project workspace `.fleet/console/state.json` co-located with the lock, so a dev console never shares Theaters/Operations with a globally installed one. Setting `FLEET_CONSOLE_DIR` relocates the durable state (and captures) into that directory too — co-located with the lock, matching the runtime-file escape hatch — so a read-only checkout can point its writable runtime slot away from the project tree. The console server is the sole writer; the file stores `{ version: 2, theaters, operations }`. On startup the server restores Theaters and Operations into a dormant state (no PTY), and Operation state survives console restarts.
|
|
12
12
|
- A capture inbound channel writes `{fleetSessionId}.json` under the same console data directory's `captures/` (`~/.fleet/console/captures/` for stable, the project `.fleet/console/captures/` for local runs). Provider CLI SessionStart hooks record provider session ids via `fleet-console hook capture-session <provider>`; the server reads the capture file, merges the provider session id into durable state, and cleans up the capture file. This is separate from the console lock file in `os.tmpdir()` (stable) / the project `.fleet/console` lock (local).
|
|
13
13
|
- The React SPA served from the console backend at `/console/`: layout, components, styles, and visual identity. The global navigation bar owns **Theater** selection — a project root directory that groups console-owned terminal sessions and Codex wiki context. The Operations surface renders a single spatial Map canvas of terminal sessions filtered to the active Theater; operators create sessions directly on the canvas (Shift-drag or right-click), choosing an Agent CLI before a new terminal session starts. Each session panel lists its active carrier jobs in a floating job dock, and selecting a job opens a centered streaming overlay scoped to that session's jobs. The canvas also hosts free shell panels (right-click) that resolve their cwd from the active Theater. A console-wide Operation quick-search (Cmd/Ctrl+K) searches terminal sessions across all Theaters, switches to the selected Operation's Theater, selects that Operation, and navigates to `/operations`; on `/codex` paths the same shortcut yields to Codex's own search.
|
|
14
|
-
- The
|
|
14
|
+
- The built-in Terminal plugin (`runtime/fleet-plugins/terminal`, package `@fleet-plugins/terminal`): one plugin id, `terminal`, owns the Shell and Agent operation kinds plus plugin-scoped WebSocket, ticket registry, PTY session lifecycle, and launch runtime. It absorbs the server-shared and client-shared helpers, keeps operation type ids `shell` and `agent` (carrier streaming renders inside the Agent panel as an in-panel summary plus a detail modal, not a separate child operation), serves plugin HTTP routes under `/plugins/terminal/{shell,agent}/*`, and uses the plugin-scoped `ws` route for PTY transport. The Shell launch label is `Shell`.
|
|
15
|
+
- The Codex/Fleet Wiki web surface under `/console/codex`: the console-owned Codex server gateway, workspace registry, wiki API routes, and migrated vanilla TypeScript Maritime Codex client. The console-level **TheaterRegistry** is the source of truth for project roots and does not require a Fleet Wiki knowledge root; the Codex `WorkspaceRegistry` is the subset of Theaters whose directories contain a Fleet Wiki knowledge root. Codex is mounted as a Right Rail built-in panel under the Fleet Console GNB without an iframe or proxy daemon.
|
|
15
16
|
- The observer-side client contract: REST snapshot fetches and the SSE consumption loop with reconnect/resync.
|
|
16
17
|
- The streaming view model: the event reducer that folds `CarrierJobStreamEvent` timelines into per-job, per-track views with incremental text accumulation.
|
|
17
|
-
- Console self-update (`POST /
|
|
18
|
+
- Console self-update (`POST /api/v1/updates/apply`): the console backend owns an independent detached self-update worker that re-installs the globally published `fleet-cli` and `fleet-console` packages, writes a status file and a log file under the console data directory. The route enforces exact loopback Origin, rejects local/unpublished builds, re-checks the latest release before accepting, and returns `202 { status: "accepted" }` only after the worker has been spawned and detached. The worker shuts down the console server and re-opens a browser on the new server at a fresh OS-assigned random loopback port once the install completes; the old tab is intentionally not preserved. Console must never import or depend on `@dotobokuri/fleet-cli` for this flow.
|
|
18
19
|
|
|
19
20
|
## Codex / Fleet Wiki Surface
|
|
20
21
|
|
|
21
|
-
- Canonical Codex workspace routes live under `/console/codex/w/:ws/...`; MRU-compatible API routes live under `/console/codex/api/...`. Do not reintroduce global `/api/...` wiki routes because console owns its own local API namespace.
|
|
22
|
+
- Canonical Codex workspace routes live under `/console/codex/w/:ws/...`; MRU-compatible API routes live under `/console/codex/api/...`. Do not reintroduce global `/api/...` wiki routes because console owns its own local API namespace. The Codex server exposes exactly 4 REST resources: `GET /api/search` (empty `q` = full index via `listWiki`; non-empty `q` = `briefingQuery`), `GET /api/entry/:id` (with optional `?include=raw` to embed raw source content inline — raw is never served as a separate endpoint), `GET|POST /api/drydock[/:id[/decision]]` (replaces the former `/api/queue*` surface; all queue actions now go through `drydock/:id/decision`), and `GET /api/conflicts[/:id]`. All deprecated paths — `/api/health`, `/api/workspaces`, `/api/index`, `/api/index-md`, `/api/log`, `/api/raw`, and `/api/queue*` — return 404 or 405 and must not be reintroduced.
|
|
22
23
|
- The console-level **Theater** is the parent concept for project roots. Codex workspaces share the same id space (`workspaceHash(realpath(dir))`) but are a strict subset of the Theater registry (`hasWiki=true`). The Codex left workspace switcher is removed; Theater selection in the global navigation bar is the only workspace switch, and Theaters without a Fleet Wiki knowledge root render a "Codex 없음" state instead of mounting a wiki surface.
|
|
23
|
-
- The migrated Codex client stays Vanilla TypeScript under `client/src/codex/**`; do not rewrite it into React state or components beyond the React mount host.
|
|
24
|
-
- Codex
|
|
25
|
-
- The Codex reading surface
|
|
26
|
-
- Preserve Maritime Codex
|
|
24
|
+
- The migrated Codex client stays Vanilla TypeScript under `core/client/src/codex/**`; do not rewrite it into React state or components beyond the React mount host.
|
|
25
|
+
- Codex is a **Right Rail built-in panel** (after ALERTS; `BUILT_IN_RAIL_PANELS = [alertsPanel, codexPanel]`). The rail renders a single-column **Navigator** (search input + entry list + Drydock pending-count badge + Conflicts entry) — there is no `app-shell` 3-pane grid, container-responsive pane-shedding, or pane-collapse toggle. A single shared mount host (`codex-host.ts`) relocates the Vanilla singleton into the rail container with `appendChild` via `mountNavigatorInto` + a `setOnRequestOpenReader` callback + `setNavigatorTheater`; `destroy()`+remount is never called. Workspace selection is driven by Theater state (`ctx.theaterId` from `RailPanelContext`); the left workspace switcher is removed. Mount paths: Theater auto-registration (`server.ts` `codex.registerWorkspace(cwd)`) and restart restoration (`server.ts` `restoreCodexWorkspaces`). The admin workspace registration endpoint, bearer-token surface, standalone `fleet-wiki` binary, Vanilla `router.ts`, `setCodexPresentationMode`/`setCodexPaneCollapsed`, and the `fleet-console.codex.nav-collapsed`/`rail-collapsed` localStorage keys are all removed.
|
|
26
|
+
- The Codex reading surface has two tiers. Selecting a Navigator entry opens an **inline 2-pane split** inside the rail panel: the rail slot widens (the user's nav width + a ~360px document pane, driven by `useCodexSplitExtraWidth` reading `codexReader` state) and renders the preserved markdown reader on the **left** (`.codex-doc-pane`, ~46ch measure, with a collapsible inline ToC and a compact `⤢ Expand` button) while the Navigator stays on the **right** (`.codex-nav-pane`, still browsable — selecting another entry swaps the left document). The `⤢ Expand` button opens the **Reading overlay** (`CodexReadingSheet`, `createPortal` to `document.body`) — a centered modal "large view" over the Operations canvas at a fixed comfortable width (`min(960px, calc(100vw - 120px))`, no width toggle), gated on `codexReader != null && codexReaderExpanded`. The overlay is `role="dialog"` + `aria-modal="true"`, focus-trapped, animated with `codex-rise-centered` (which preserves the base `translateX(-50%)` centering — plain `codex-rise` sets only `translateY` and would clobber it), marks the background canvas/SideBar inert via `body[data-codex-reading]`, carries a 200px ToC rail with an `IntersectionObserver` scroll-spy (brass = active section), and closes on Esc/scrim/✕ back to the **2-pane split** (not nav-only) with focus restored to the Expand button. A single Vanilla reader instance (`codex-host` `readerHostNode`/`tocHostNode`) relocates between the split doc-pane and the overlay via `appendChild`, preserving content and scroll. The reading measure is owned by `.markdown-body` and its reading-wrapper siblings (`.document-header`, `.related-list`), never a shell grid. Codex typography is tokenized through `--font-size-*`; brass = active/location and aurora = live/link semantics, glass surfaces, self-hosted Fraunces/Manrope/JetBrains Mono, and `prefers-reduced-motion` short-circuits are unchanged. The deprecated `app-shell`/`app-shell--wide`/`app-shell--raw` grid, container queries, document-level ToC drawer, Manifest panel, `⌘\`/`Ctrl+\` width toggle, `.codex-reading-sheet.is-wide`, and the `Esc · ⌘\` hint are removed.
|
|
27
|
+
- Preserve Maritime Codex reading aesthetic: the markdown render pipeline (`renderMarkdown` — marked + DOMPurify + hljs + wiki links; Mermaid hydrator with `securityLevel: "strict"`) and the `.markdown-body`/`.document-header`/`.related-list`/`.code-block` styling are preserved verbatim and reused in both the inline split doc-pane and the Reading overlay. These are now owned by `@fleet-console/markdown` (`runtime/fleet-console/markdown/`) — the shared workspace package that is the new SSoT for `renderMarkdown`, `installDiagramHydrator`, and the shared CSS. Surfaces: Navigator (search + entry list + Drydock + conflicts), the inline 2-pane split (document + browsable navigator), and the Reading overlay (large markdown view + ToC scroll-spy + copy-context actions + diagram lightbox), with brass/aurora roles and self-hosted fonts. The standalone raw viewer route, index/log views, command palette, Manifest panel, and document-level ToC drawer are removed; raw source is embedded inline via `GET /api/entry/:id?include=raw`.
|
|
27
28
|
- Preserve wiki security invariants: Host allowlist, Origin guard, write-surface loopback gate, DOMPurify markdown sanitization, Mermaid `securityLevel: "strict"` with `htmlLabels: false` and no `bindFunctions`, path containment, and lockfile bearer auth for admin workspace registration.
|
|
28
29
|
- Browser payloads must not expose MCP/session tokens, terminal tickets, or Codex admin tokens.
|
|
29
30
|
|
|
@@ -39,31 +40,129 @@
|
|
|
39
40
|
- Terminal HTTP routes do not use browser bearer tokens, but must retain the terminal Origin check; the terminal WebSocket is reached through a one-use ticket.
|
|
40
41
|
- MCP session tokens must never reach browser code, URL query strings, SSE payloads, terminal tickets, logs, or static assets.
|
|
41
42
|
- Theater routes likewise do not expose admin bearer tokens, MCP/session tokens, terminal tickets, folder-grant identifiers, raw working-directory paths, provider session ids, or transcript paths to the browser.
|
|
42
|
-
-
|
|
43
|
+
- Console-owned Theater folder browser routes (`POST /api/v1/theaters/folder-listings`, `POST /api/v1/theaters/folder-grants`) are gated by `validateHost` then `isTerminalAuthorized` (the same Origin boundary as terminal routes). No adminToken or bearer auth is added. Folder selection is browser UI plus loopback fs APIs; no OS-native dialog or child process is spawned. Selected absolute paths appear only in list and grant responses; they are not included in session, Theater, observer, or SSE payloads. When a Theater is registered the resolved cwd is stored in durable local state (`sensitivity: "sensitive"`) as before — it is not transmitted to the browser.
|
|
44
|
+
- **Symlink containment** — Theater file/image routes must verify containment _after_ resolving symlinks: call `fs.promises.realpath()` on both the target path and the Theater root, then re-check that the real path is within the real root before stat/read. Symptom without this: a symlink inside the Theater can point to `/etc/passwd` and pass the initial string-prefix check. Why: `path.resolve` + string prefix only catches traversal in the nominal path; `realpath` follows the actual OS link chain.
|
|
45
|
+
- **Git ref option injection** — Any `ref` string passed to Theater diff routes must be validated against `isSafeGitRef()` (SHA or branch/tag — no leading `-`). Even with `shell: false`, git options like `--output=` or `--no-index` are interpreted by git as flags when passed as ref arguments. Symptom without this: a crafted `ref` can redirect git output or trigger unintended git operations. Why: git parses its own argv before the shell; format whitelisting is the only reliable guard.
|
|
43
46
|
|
|
44
47
|
## Carrier Readiness/Settings Boundary
|
|
45
48
|
|
|
46
|
-
- `runtime/fleet-console/
|
|
47
|
-
- `runtime/fleet-console/client/**` must not import `@dotobokuri/fleet-carriers`, carrier persona modules, deep carrier paths, or Node-only carrier runtime modules.
|
|
49
|
+
- `runtime/fleet-console/core/host/**` may import `@dotobokuri/fleet-carriers` public root exports to consume Carrier Readiness read models and mutate global Carrier Settings through the carrier store.
|
|
50
|
+
- `runtime/fleet-console/core/client/**` must not import `@dotobokuri/fleet-carriers`, carrier persona modules, deep carrier paths, or Node-only carrier runtime modules.
|
|
48
51
|
- Fleet Console may render and edit display-safe carrier settings data such as carrier id, display name, role/category, resolved CLI/model/effort, Task Force backend count/configuration, and subagent mode/tag.
|
|
49
52
|
- `fleet-carriers` remains the source of truth for carrier persona defaults, carrier-store interpretation, store mutation, and carrier read-model construction. Console must not copy or reconstruct carrier persona policy or carrier runtime state.
|
|
50
53
|
- Console must not deep-import `@dotobokuri/fleet-carriers/src/**`, `packages/fleet-carriers/src/**`, `runtime/fleet-cli/**`, or `@dotobokuri/fleet-cli`.
|
|
51
54
|
- Carrier readiness/settings browser payloads must not serialize prompt bodies, raw persona instructions, executor tool allowlists, tokens, credential values, auth env details, terminal/session/admin tickets, or raw filesystem paths.
|
|
52
55
|
|
|
56
|
+
## Settings Plugin Boundary
|
|
57
|
+
|
|
58
|
+
- Core Settings owns the `Console` group and core console settings such as the console port controls.
|
|
59
|
+
- Model sign-in settings are Terminal plugin-owned, not core-owned.
|
|
60
|
+
- Plugin Settings sections are discovered from `plugins[].settingsSections` and rendered under the `Plugins` group. The host derives plugin ownership from the plugin registration id and normalizes plugin active ids as `${pluginId}:${sectionId}`.
|
|
61
|
+
- SDK settings descriptors remain minimal: `{ id, title, render }`. Do not add grouping, ordering, plugin ownership, or sensitivity metadata to `SettingsSectionDescriptor`.
|
|
62
|
+
|
|
53
63
|
## Server-side Dependency Boundary
|
|
54
64
|
|
|
55
|
-
- `runtime/fleet-console/
|
|
65
|
+
- `runtime/fleet-console/core/host/**` may import `@dotobokuri/fleet-admiral` public root exports for server-only Agent CLI launch/runtime assembly. `runtime/fleet-console/core/client/**` must not import `@dotobokuri/fleet-admiral`.
|
|
66
|
+
- Fleet Console core host, core client, and built-in plugins may import `@fleet-console/sdk` domain subpaths. The SDK is the plugin-facing contract SSoT and must not import back from core, `@dotobokuri/*`, or `@fleet-plugins/*`.
|
|
67
|
+
|
|
68
|
+
## External Client Plugins
|
|
69
|
+
|
|
70
|
+
Fleet Console can load third-party client plugins from `~/.fleet/plugins/<id>/` alongside the built-in Terminal plugin. Built-in plugins remain statically resolved through `virtual:fleet-plugins`; only external plugins are discovered and loaded at runtime.
|
|
71
|
+
|
|
72
|
+
### Discovery & Manifest
|
|
73
|
+
|
|
74
|
+
- External plugins are discovered under `~/.fleet/plugins/<id>/`. Each plugin directory must contain a `plugin.json` manifest.
|
|
75
|
+
- Required and optional manifest fields:
|
|
76
|
+
- `id` (required): plugin identifier, must match `^[a-z0-9][a-z0-9-]*$`.
|
|
77
|
+
- `name?`: human-readable plugin name.
|
|
78
|
+
- `apiVersion?`: integer major version. External plugins must match the console SDK major version (`SDK_API_VERSION`); a mismatch causes the plugin to be hard-skipped.
|
|
79
|
+
- `client?`: relative path to the browser client entry (e.g. `client/index.tsx`).
|
|
80
|
+
- `routes?`: relative path to the Node server route module (e.g. `routes.ts`).
|
|
81
|
+
- `sensitiveFields?`: list of operation payload fields the plugin considers sensitive; these augment core sanitization for observer responses.
|
|
82
|
+
- Plugin entry paths are validated to stay within the plugin root; absolute paths or `..` segments are rejected.
|
|
83
|
+
|
|
84
|
+
### Server Runtime Routes
|
|
85
|
+
|
|
86
|
+
- Core owns three dedicated routes under `/plugin-runtime/`:
|
|
87
|
+
- `GET /plugin-runtime/manifest` returns the external-plugin catalog DTO `{ plugins: { id, name?, clientUrl, apiVersion }[] }`.
|
|
88
|
+
- `GET /plugin-runtime/client/<id>.mjs` serves the bundled external plugin client.
|
|
89
|
+
- `GET /plugin-runtime/shim/<name>.mjs` serves React/SDK shims that re-export the console runtime singleton.
|
|
90
|
+
- These routes are intentionally **not** under `/plugins/` or `/console/` to avoid overlap with plugin-scoped routes and static fallback handling.
|
|
91
|
+
- External route modules (`.ts`) are bundled with esbuild into temporary ESM bundles; plain `.mjs`/`.js` files are served directly. `react`, `react/jsx-runtime`, `@fleet-console/sdk/*`, `@dotobokuri/*`, `@fleet-plugins/*`, and Node built-ins are external for server route bundles.
|
|
92
|
+
|
|
93
|
+
### Client Loading & Singleton Runtime
|
|
94
|
+
|
|
95
|
+
- `main.tsx` publishes the console's own React and SDK browser modules to `globalThis.__fleetConsoleRuntime__` before render.
|
|
96
|
+
- Shim modules served from `/plugin-runtime/shim/<name>.mjs` read that global object and re-export the same React/SDK instances, so external plugins share the console's React singleton and avoid dual-React errors.
|
|
97
|
+
- External plugin client sources (`.ts`/`.tsx`) are bundled with esbuild at server startup. `react`, `react/jsx-runtime`, and all `@fleet-console/sdk/*` specifiers are rewritten to shim URLs; import maps are not used.
|
|
98
|
+
- The server host must **not** statically import `react`, `react/jsx-runtime`, or any `@fleet-console/sdk/*/browser` module at runtime. The shim's named-export keys come from a build-time-generated manifest (`core/host/plugin-host/shim-keys.generated.ts`, gitignored, produced by `generate:shim-keys` which is prepended to `build`/`typecheck`/`test`/`predev`). A static host-side `react` import re-adds a `react` external to the tsup `dist/cli.mjs` bundle, but the published manifest keeps only `node-pty`/`ws` as dependencies — so the console would crash at boot with `ERR_MODULE_NOT_FOUND: react`. Verify with `npm pack` + isolated install after touching shim/runtime-publishing code.
|
|
99
|
+
- During bootstrap `loadPluginRegistry()` fetches `/plugin-runtime/manifest`, dynamically imports each listed client module, and merges the resulting `operationKinds`, `settingsSections`, and `notificationKinds` into the client plugin registry.
|
|
100
|
+
|
|
101
|
+
### Trust Model
|
|
102
|
+
|
|
103
|
+
- **Installing an external plugin equals trusting it.** External plugins run with the same privileges as the console host process on loopback and share the browser origin. This is the same trust model as npm packages or IDE extensions.
|
|
104
|
+
- The console intentionally does **not** sandbox external plugin server routes or client code. Third-party plugins may register full backend routes under their `/plugins/<id>/` namespace and execute in the host Node process.
|
|
105
|
+
- Operators must not install plugins they do not trust. The platform assumes the principal who can write to `~/.fleet/plugins` already has local code-execution capability.
|
|
106
|
+
|
|
107
|
+
### Safety Guards
|
|
108
|
+
|
|
109
|
+
These guards are robustness measures on top of the trust model, not a replacement for it:
|
|
110
|
+
|
|
111
|
+
- `apiVersion` compatibility gate applies only to external plugins; built-in plugins are exempt.
|
|
112
|
+
- Duplicate plugin ids are resolved built-in-first; external duplicates are skipped with a warning.
|
|
113
|
+
- **External plugin boot failures are quarantined**: a failing external plugin is skipped and logs a warning, but the console continues to boot. Built-in plugin boot failures remain hard failures.
|
|
114
|
+
- Client bundling enforces **plugin-root containment** with symlink awareness: transitive imports that escape the plugin root are rejected at bundle time.
|
|
115
|
+
- Manifest, shim, and bundled client payloads must not leak tokens, terminal tickets, raw filesystem paths, provider session ids, transcript paths, or sensitive fields. Bundle path comments are normalized relative to the plugin root so absolute home paths do not appear in browser payloads.
|
|
116
|
+
- Three-layer error boundaries isolate plugin failures: loader-level, settings-section level, and operation-panel level (`PluginErrorBoundary` from `@fleet-console/sdk/react/browser`).
|
|
117
|
+
|
|
118
|
+
### Sample
|
|
119
|
+
|
|
120
|
+
- `examples/plugins/notes/` demonstrates a minimal external plugin with a custom Operation panel, a Settings section, and a backend route. To exercise the discovery path, copy the directory to `~/.fleet/plugins/notes` in an isolated HOME.
|
|
121
|
+
|
|
122
|
+
## Host↔Plugin Reactive Channel
|
|
123
|
+
|
|
124
|
+
- The host bridges plugin capabilities to console client state through `createHostCapabilities` (`core/client/src/plugin-capabilities.ts`). It overwrites the SDK no-op defaults for `notifications` and `status` with store-bound implementations:
|
|
125
|
+
- `notifications.emit` feeds `operationNotifications` (ALERTS are shown only for Operations that are not currently visible).
|
|
126
|
+
- `status.set` / `status.clear` feed `operationStatus`, which drives the running-panel perimeter progress rim and beacon.
|
|
127
|
+
- `theme` is passed to plugins through `OperationRenderContext` (not through a capability), so plugins receive the current `ConsoleTheme` reactively on every canvas render. Host token boundaries remain unchanged: these are transient client-only values; no tokens, paths, or credentials cross into plugin code.
|
|
128
|
+
- Terminal renderer and font preferences are **not** in `OperationRenderContext`. The Terminal plugin owns these prefs end-to-end through a module-scoped `useSyncExternalStore` store (`client/shared/terminal-prefs-store.ts`), stored under `fleet-plugin.terminal.renderer` and `fleet-plugin.terminal.font` localStorage keys. All mounted terminal panels subscribe directly and react instantly to settings changes. `fleet-plugin.terminal.*` keys are Terminal plugin-owned; core and other plugins must not read or write them.
|
|
129
|
+
- The SDK contract is invariant. Host code may only extend capability implementations; it must not add new capability surface areas or require plugins to import core modules.
|
|
130
|
+
|
|
131
|
+
## Window System
|
|
132
|
+
|
|
133
|
+
Operation chrome (maximize, minimize, focus, and the Operations Left SideBar) is host-owned. Plugins render only their panel body through `OperationRenderContext` and do not participate in window-state decisions.
|
|
134
|
+
|
|
135
|
+
- **Per-panel maximize** (`maximizedOperationId` in `core/client/src/canvas/canvas-store.ts`) is orthogonal to **map fullscreen** (`mapFullscreen`, renamed from the earlier map-level maximize while keeping the same storage key). A maximized panel renders in a temporary full-canvas geometry — the map canvas column (col2), which excludes the SideBar (col1) and the Activity Rail (col3) — over the same instance without remounting. Drag, resize, and geometry persistence are blocked while maximized.
|
|
136
|
+
- **Minimize preserves PTY**: minimized panels stay in the DOM with `visibility:hidden` and `inert` instead of unmounting, preserving terminal PTY / WebSocket state. Because the host chrome handles this, all plugins benefit uniformly and plugins need not be PTY-aware.
|
|
137
|
+
- **Operations Left SideBar = all-panel chip list**: the left progressive 3-tier SideBar (`rail` 56px / `list` 180px / `detail` 280px) shows every Operation in the current Theater as a vertical chip, sorted by `operationOrder` (drag reorder / Alt+Shift+↑↓). Chips show active highlight (brass), minimized dim, close button (two-step ARM), accent (`--chip-accent` border + 1px ring), and notification count. **Chip leading slot**: a 24×24 non-interactive `<span aria-hidden>` hosts the Operation's kind icon (resolved via `renderKindIcon` — no new SDK surface); if no icon resolves, a neutral fallback glyph is shown. Underway/status state is reflected through CSS class modifiers on the chip (`side-bar-chip--underway-{live|turn|awaiting}`, `side-bar-chip--underway-ring`). Right-clicking a chip opens the accent popover (`onContextMenu` on `<li>`). Accent tints the chip's focus-border channel only — never the icon fill. In `rail` tier, chips are horizontally centred (justify-content:center, padding 0) and labels are hidden. The SideBar header hosts **+New** (opens a `createPortal` global overlay with `mode="launch"` — Operations catalog only — positioned to the right of the button at `rect.right+8, rect.top`; no transient sidebar width expansion) and a **⚙ Settings** button (active, rail tier hidden) that opens a `createPortal` global overlay with `mode="controls"` — Map fullscreen, Radar sweep, Panel pulse, and Shortcuts (collapsed in `<details>`) — positioned to the right of the button. There is no footer on the SideBar; Map/Radar/PanelPulse controls live exclusively in the ⚙ controls overlay. Width persists per browser at `fleet-console.operations.side-width`; collapsed state at `fleet-console.operations.side-collapsed`. Clicking a chip focuses the Operation (restoring it if minimized); if a panel is currently maximized and the clicked chip is a different Operation, the maximized panel is switched to the clicked Operation (`setMaximizedOperationId`) without triggering a canvas pan. Double-clicking a chip's name opens an inline rename input that reuses the canvas panel rename flow through the shared `useInlineRename` hook (Enter commits, Escape cancels, blur commits); the rename input is hidden in `rail` tier alongside the name, so rename is only reachable from `list`/`detail` tiers.
|
|
138
|
+
- **Active Operation SSoT**: `activeOperationId` (`core/client/src/store.ts`) is the single source of truth for active highlight and for `Alt + Left / Right` cycling. The cycle order is the same as the Left SideBar's visible order — the shared `sortOperationsByOrder` helper (`store.ts`) ranks by the canvas `operationOrder` (drag reorder) and falls back to createdAt for unranked Operations — so `Alt + Left / Right` never diverges from the chip list. If the active Operation is minimized, active is cleared. When a new panel is added while maximized, the maximized state is kept and the new panel becomes the maximized one. The same maximize-preserving rule applies to the one-shot jump path — ALERTS "Open" and the Cmd/Ctrl+K quick-search, both routed through `pendingOperationFocus` consumed in `operations.tsx`: when a panel is maximized as the jump resolves, the maximized target is swapped to the destination Operation (`setMaximizedOperationId`) instead of clearing maximize, matching the chip-click and `Alt + Left / Right` policy. The jump handlers themselves must not call `clearMaximizedOperationId()` directly — the consume effect owns that decision after `loadForTheater` restores the destination Theater's maximize state.
|
|
139
|
+
- **`operationsHydrated`**: the canvas prunes stale Operation geometries only after the first `operations` fetch has set `operationsHydrated` to `true` (`core/client/src/store.ts`). Until then, pruning is deferred so restored geometry is not discarded during initial load.
|
|
140
|
+
|
|
141
|
+
## Plugin-Shared Libraries
|
|
142
|
+
|
|
143
|
+
`runtime/fleet-console/markdown/` (`@fleet-console/markdown`) is a private, source-only workspace package that is the single SSoT for the shared markdown rendering and diagram hydration libraries. It sits alongside `@fleet-console/sdk` (peer workspace package) and is consumed by both the Codex reading surface and built-in plugins (e.g. file-explorer).
|
|
144
|
+
|
|
145
|
+
- **Owned dependencies**: `marked`, `dompurify`, `highlight.js`, `mermaid`. These must not appear in `runtime/fleet-console/package.json` `dependencies` or in built-in plugin `package.json` `dependencies`; all are transitive through this package.
|
|
146
|
+
- **Exports**: `@fleet-console/markdown/core` (`renderMarkdown`, `encodeMermaidSource`, `decodeMermaidSource`), `@fleet-console/markdown/mermaid` (`installDiagramHydrator`, `cssColorToHex`), `@fleet-console/markdown/styles.css` (all `.markdown-body`/`.code-block`/`.diagram-block`/`.diagram-lightbox` rules + `highlight.js` github-dark theme).
|
|
147
|
+
- **React-free**: the package returns HTML strings; it does not import React. Built-in plugins that are React-based wrap the output in `dangerouslySetInnerHTML`.
|
|
148
|
+
- **Codex DI pattern**: `renderMarkdown(body, { resolveWikiLink: (id) => entryPath(id) })` injects the codex router via option; file-explorer omits it so wiki link patterns fall through as escaped text. No codex/wiki vocabulary inside the package.
|
|
149
|
+
- **Security invariants** (must never be weakened): DOMPurify double-sanitize + `sanitizeConfig` ALLOWED_URI_REGEXP/ADD_ATTR verbatim; Mermaid `securityLevel: "strict"`, `htmlLabels: false`, no `bindFunctions`; SVG sanitize `USE_PROFILES.svg`; `installDiagramHydrator` inert/focus-trap for the lightbox.
|
|
150
|
+
- **tsup bundling**: `noExternal: [/^@fleet-console\/(sdk|markdown)(\/|$)/]` ensures the package is bundled inline on `npm publish`; the published manifest's `dependencies` stays `node-pty` + `ws` only.
|
|
151
|
+
- **External plugin shim**: external plugins under `~/.fleet/plugins/` cannot yet import `@fleet-console/markdown` (esbuild shim not yet added); that is a separate follow-up.
|
|
56
152
|
|
|
57
153
|
## Layout
|
|
58
154
|
|
|
59
|
-
- `
|
|
60
|
-
- `client/` — the Vite React SPA (`client/src/`, `client/index.html`, `client/vite.config.ts`). Must not import Node-only modules or the console backend (`
|
|
155
|
+
- `core/host/` — Node-side backend and CLI lifecycle: the HTTP server (`server.ts`), security headers, static serving (`static-console.ts`), observer routes (including Theater registry and cascading Operation/capture removal), generic plugin route and upgrade registration, Theater folder routes (`theater-folder-browser.ts`, `theater-folder-grants.ts`), the SSE helper, `codex/` (Fleet Wiki/Codex API gateway and workspace registration), `theaters.ts` (console-level in-memory TheaterRegistry backed by durable state), `theater.ts` (Theater id hash, realpath canonicalization, and label helpers), the lifecycle modules (`lock.ts`, `paths.ts`, `health.ts`, `stale.ts`), the self-update orchestrator (`update-apply.ts`), and the CLI (`cli.ts`, `cli-bin.ts`, `browser.ts`, `help-style.ts`). Terminal PTY tickets, WebSocket upgrade, session lifecycle, shell launch, and agent launch live in the Terminal plugin under `../fleet-plugins/terminal/server/`. Built by tsup to `dist/cli.mjs` and `dist/cli-bin.mjs`. `help-style.ts` is a CLI-help-only **self-hosted** style helper shared by the console and Codex compatibility CLIs; it must not import from `fleet-cli`, `packages/*`, or `core/client/`, and changes to the shared banner/SGR vocabulary require manual sync across those copies.
|
|
156
|
+
- `core/client/` — the Vite React SPA (`core/client/src/`, `core/client/index.html`, `core/client/vite.config.ts`). Must not import Node-only modules or the console backend (`core/host/`).
|
|
157
|
+
- `../fleet-plugins/terminal/` — the built-in Terminal plugin package. It provides the Shell and Agent browser panels, plugin route handlers, launch metadata, and absorbed shared/server-shared/client-shared helpers for the single `terminal` plugin.
|
|
158
|
+
- `../fleet-plugins/diff/` — the built-in Diff plugin package. It owns git diff backend routes (`/plugins/diff/changed`, `/plugins/diff/file`), `isSafeGitRef` validation, and the Diff rail panel client. Server code (`server/`) and client code (`client/`) are self-contained; no git or file helpers live in `core/host/`.
|
|
159
|
+
- `../fleet-plugins/file-explorer/` — the built-in File Explorer plugin package. It owns file listing (`/plugins/file-explorer/files/list`), file reading (`/plugins/file-explorer/files/read`), image serving (`/plugins/file-explorer/files/image`), symlink-aware containment, and the File Explorer rail panel client. Server code (`server/`) and client code (`client/`) are self-contained.
|
|
61
160
|
- `tests/` — vitest suites for the reducer, SSE parser, store, terminal, and CLI lifecycle.
|
|
62
161
|
|
|
63
162
|
## Tech Stack (deliberate)
|
|
64
163
|
|
|
65
164
|
- **React 19 + Vite + TypeScript.** Chosen because the console's core requirement is smooth incremental streaming UI and the package is slated to grow into the unified Fleet GUI. Do not replace with hand-rolled DOM rendering. `react-router-dom` (`BrowserRouter` with `basename="/console"`) is the sanctioned client router. Routes: `/` redirects to `/operations`; `/operations` renders the carrier observation surface (the Map canvas of session panels + JobView); unknown paths redirect to `/operations`. The console backend already serves extensionless `/console/*` paths as `index.html` (SPA fallback in `static-console.ts`), so client-side routes require **no** backend change. Routing state belongs to react-router; observation data stays in the external `store.ts`. Do not add a state-management library until that store proves insufficient.
|
|
66
|
-
- State lives in a framework-agnostic external store (`client/src/store.ts`) bridged via `useSyncExternalStore`. Pure reduction logic stays in `client/src/reduce.ts` and must remain React-free and unit-tested.
|
|
165
|
+
- State lives in a framework-agnostic external store (`core/client/src/store.ts`) bridged via `useSyncExternalStore`. Pure reduction logic stays in `core/client/src/reduce.ts` and must remain React-free and unit-tested.
|
|
67
166
|
- Web fonts are self-hosted via `@fontsource-variable/*`. External font CDNs are forbidden.
|
|
68
167
|
- Browser launch must use OS-level commands via `child_process.spawn`; do not add an `open` dependency.
|
|
69
168
|
|
|
@@ -75,20 +174,57 @@
|
|
|
75
174
|
- The output view keeps pin-to-bottom follow behavior: pinned within slack distance, released on upward scroll, restored via the follow button. Removing this is a UX regression.
|
|
76
175
|
- `sentTextLength` tracks emitted length from `textLength` metadata so retention clamping on the console-backend side stays visible to the operator.
|
|
77
176
|
|
|
177
|
+
## Right Rail (Activity Rail)
|
|
178
|
+
|
|
179
|
+
The right rail (`core/client/src/rail/`) is a persistent 48px vertical icon column at the right edge of the Operations canvas. Selecting an icon opens an inboard panel (312px) beside the icons; re-clicking the active icon collapses the panel while keeping the rail visible.
|
|
180
|
+
|
|
181
|
+
**Right-edge handle separation** — two handles share the right edge and must not overlap:
|
|
182
|
+
|
|
183
|
+
| Handle | z-index token | Element | Notes |
|
|
184
|
+
|--------|--------------|---------|-------|
|
|
185
|
+
| Activity Rail | `--z-rail: 10` | `.right-rail` | Lowest; canvas-grid column 2 |
|
|
186
|
+
| Codex Side | `--z-codex-side: 20` | `.codex-side-panel` | Overlay above canvas |
|
|
187
|
+
|
|
188
|
+
These CSS variables are declared in `rail.css` and must not be inlined elsewhere. ALERTS and Codex are both built-in rail panels; the former floating right-edge ALERTS dock and its `--z-alerts-dropdown` token were removed.
|
|
189
|
+
|
|
190
|
+
**RailPanel registration**:
|
|
191
|
+
|
|
192
|
+
- `RailPanelDescriptor` is the sole SDK surface for rail panels, defined in `@fleet-console/sdk/rail`.
|
|
193
|
+
- **Core built-in panels** (`rail/built-in-panels.ts` → `BUILT_IN_RAIL_PANELS = [alertsPanel, codexPanel]`) are core-owned, do **not** pass through the plugin registry, and render **above** the plugin panels in the icon column, separated by a `.right-rail-divider`. ALERTS (`id: "alerts"`) is the first built-in panel; Codex (`id: "codex"`) is the second. A built-in panel may ignore the `RailPanelContext` host capabilities and read console state directly (ALERTS does this; Codex uses `ctx.theaterId`).
|
|
194
|
+
- Plugins — both statically-resolved built-in plugins (File Explorer, Diff) and external plugins — register rail panels via `FleetClientPlugin.railPanels[]`, which flow through `rail-registry.ts` → `useRailPanels()` and render **after** the divider. `right-rail.tsx` resolves the active panel against the combined `[...builtInPanels, ...pluginPanels]` set.
|
|
195
|
+
- Rail panel id deduplication is enforced in `plugin-registry.ts#createPluginRegistry`; the first plugin wins and subsequent duplicates are warned and skipped.
|
|
196
|
+
- `apiVersion` compatibility gate applies to external plugins only (enforced in `plugin-registry.ts#loadExternalPlugin`).
|
|
197
|
+
- `RailPanelDescriptor.preferredExtraWidth?: number` — when set, the host adds this many px to the active panel slot width; the host reads only the declared value and remains panel-id-agnostic (same mechanism as `useCodexSplitExtraWidth`, additive).
|
|
198
|
+
|
|
199
|
+
**Layout contract**:
|
|
200
|
+
|
|
201
|
+
- `.console-body.is-canvas` is `display:grid; grid-template-columns: auto minmax(0,1fr) auto` with `column-gap: var(--space-3)`. Col1 = `<OperationsSideBar>` (width driven by `--side-bar-width` CSS variable); col2 = the Operations map canvas; col3 = `<RightRail>` (floating glass card).
|
|
202
|
+
- The SideBar (col1), the Operations map (col2), and the Activity Rail (col3) are **independent**: per-panel maximize (`maximizedOperationId !== null`) is confined to the map canvas (col2) and does **not** collapse the SideBar or Rail columns. The maximized panel fills col2 (which already excludes the SideBar and Rail), so both stay visible and reflow the canvas if opened/closed while a panel is maximized. `.console-body.is-canvas` no longer takes an `is-map-fullscreen` modifier; that modifier remains only on `.console-shell` for map fullscreen (topbar collapse).
|
|
203
|
+
- The `canvas.tsx` `ResizeObserver` naturally reflows the canvas width when the rail opens/closes — no manual layout math is needed.
|
|
204
|
+
- `.console-shell` grid (64px header + 1fr body) is immutable; only `.console-body.is-canvas` grid changes.
|
|
205
|
+
|
|
206
|
+
**Design rules for RailPanel chrome**:
|
|
207
|
+
|
|
208
|
+
- Active icon = `--brass`; idle = `--ink-fog`; hover = `--ink-spectral`. Do **not** use `--aurora` for rail icon states.
|
|
209
|
+
- Active icon carries a left-edge 2px `--brass` bar (`.right-rail-ico.is-active::before`).
|
|
210
|
+
- Panel open/close transition = `width var(--duration-base) var(--ease-spring)`. `prefers-reduced-motion: reduce` must short-circuit to `0.01ms`.
|
|
211
|
+
- Panel chrome (header 46px min-height + body 1fr) follows the mock `.panel` pattern. Panel body content is plugin-owned; chrome is host-owned.
|
|
212
|
+
- Viewer-surface syntax/diff colors go in new surface CSS files (e.g., `rail-viewer.css`); `theme.css` is immutable.
|
|
213
|
+
|
|
78
214
|
## Design Identity — "Maritime Console"
|
|
79
215
|
|
|
80
216
|
The console is the operations variant of Fleet Wiki's **Maritime Codex** language: same deep-water ink, brass instrumentation, aurora life signals, glass surfaces, and codex motion grammar, but tuned for live observation rather than reading. It is a command instrument over the same sea, not an editorial document view.
|
|
81
217
|
|
|
82
|
-
- **Relationship to Maritime Codex**: `client/src/codex/**` owns the migrated Maritime Codex reading surface. Console may translate the vocabulary for operations needs, but it must stay visibly related through the shared token system, glass atmosphere, brass/aurora pairing, Fraunces display type, and `codex-rise` motion.
|
|
218
|
+
- **Relationship to Maritime Codex**: `core/client/src/codex/**` owns the migrated Maritime Codex reading surface. Console may translate the vocabulary for operations needs, but it must stay visibly related through the shared token system, glass atmosphere, brass/aurora pairing, Fraunces display type, and `codex-rise` motion.
|
|
83
219
|
- **Color semantics**:
|
|
84
220
|
- `brass` means "지금 보고 있는 곳" — selected job brass dot indicator, active navigation, structural decoration, and non-live active/focus-adjacent emphasis.
|
|
85
221
|
- `aurora` means "지금 살아있는 것" — streaming status, live dots, tenant beacons, stream caret, follow button, and `connection-chip--live`. Unlike Fleet Wiki, console does not reserve aurora only for document linkage because there is no document-link concept here.
|
|
86
222
|
- `coral` means error/bad; `--warn` (amber, near `oklch(80% 0.13 85)`) means warning/connecting; neutral ink means idle.
|
|
87
|
-
- User accents
|
|
223
|
+
- User accents are a user-chosen hue (any hue across the wheel — red/amber/green included) that **occupies the same outer-edge channel the focus highlight uses** — the border outline plus its 1px ring — on **both the SideBar chip and its canvas Operation panel**, fed by `--chip-accent` / `--op-accent`. When an accent is set it **takes over that focus channel in the accent color and shows permanently, regardless of focus**: an accented panel/chip always wears the accent on its border (and focus ring), so it reads as "focused in the user's hue" whether or not it is the active Operation, and focusing it keeps the accent instead of reverting to brass. Focus brass is used only when no accent is set (active → brass border/ring; idle → neutral rim). Status keeps its own separate channel — the underway/in-progress aurora/warn conic rides the edge `::before` (a minimized chip's `::before` carries the rotating conic; a visible operation's rotation is owned by its canvas panel) and the running glow/wake is unchanged — so an accented Operation shows its accent border together with the running conic. The SideBar chip's active brass *fill* stays as a secondary focus highlight beneath the accent outline. The accent never animates on its own (an infinitely orbiting ring is reserved for the functional in-progress signal) and never tints the panel/chip fill, name, beacon, count, close, or the status conic.
|
|
88
224
|
- **Typography**: `Fraunces Variable` is display type for the topbar brand, job titles, idle marks, and large headings. `Manrope Variable` is the default UI family. `JetBrains Mono Variable` is for stream output, job ids, timelines, and eyebrow labels with uppercase tracked styling. **Exception**: the Operations terminal (xterm) uses `Cascadia Code` — a terminal-tuned face for box-drawing/Powerline glyph alignment — rendered via the xterm WebGL addon with DOM fallback. This is the sole surface where the console mono identity deliberately diverges from JetBrains Mono; the terminal font lives in the `terminal.tsx` xterm options (xterm takes a JS font string, not a CSS token), so it is not a `theme.css` variable.
|
|
89
225
|
- **Surface and atmosphere**: `body::before` owns the multi-radial cold teal + brass afterglow field, and `body::after` owns the `feTurbulence` grain overlay. Sidebar, selected-job stage, timeline dock, and job summary are glass cards using `backdrop-filter: blur(18px) saturate(140%)`, `--surface-glass`, `--surface-rim`, `--shadow-soft`, and `--radius-xl`/large-radius surfaces.
|
|
90
|
-
- **Motion**: panes use one first-paint `codex-rise` reveal (720ms, `--ease-spring`, topbar/sidebar/stage staggered 40/120/200ms). Live dots use aurora pulse, the stream caret keeps its blink, and ambient infinite motion is forbidden. `prefers-reduced-motion` must continue to short-circuit animation. Exception: the Operations canvas background may render a user-toggleable ambient radar sweep
|
|
91
|
-
- **Hard bans**: no font CDN; no `Inter`/`Roboto`/`Arial`/`system-ui` as the first font family; no solid `#fff` or `#000` backgrounds; no card/button/chip radius at or below 4px; no removal of `prefers-reduced-motion`; no mixing brass and aurora roles; no reintroduction of `--carbon-*` or `--signal-*` token families;
|
|
226
|
+
- **Motion**: panes use one first-paint `codex-rise` reveal (720ms, `--ease-spring`, topbar/sidebar/stage staggered 40/120/200ms). Live dots use aurora pulse, the stream caret keeps its blink, and ambient infinite motion is forbidden. `prefers-reduced-motion` must continue to short-circuit animation. Exception: the Operations canvas background may render a user-toggleable ambient radar sweep, paired with a user-toggleable panel-pulse perimeter animation, that persists per browser; both default on in the stable channel but default off in the local (unpublished `pnpm`) channel so dev restarts start quiet, an explicit per-browser toggle preference always wins over the channel default, and both remain short-circuited by `prefers-reduced-motion`. Second exception: a running Operation panel renders a continuous perimeter "wake" ripple as a functional status signal (not ambient decoration) — it is driven by the plugin `ClientOperationStatusCapability` value reported through `operationStatus` (`running` = amber `--warn` turn, including carrier streaming; `awaiting` = aurora), appears only while work is in progress (`idle` and `dormant` render no perimeter wake), and stays short-circuited by `prefers-reduced-motion`, degrading to a static tinted rim and glow. Like the live-dot pulse, this is allowed functional status motion, not forbidden ambient motion.
|
|
227
|
+
- **Hard bans**: no font CDN; no `Inter`/`Roboto`/`Arial`/`system-ui` as the first font family; no solid `#fff` or `#000` backgrounds; no card/button/chip radius at or below 4px; no removal of `prefers-reduced-motion`; no mixing brass and aurora roles; no reintroduction of `--carbon-*` or `--signal-*` token families; the user accent lives only in the focus outline channel it shares with brass (border + focus ring) — when set it overrides the focus brass there and shows permanently; no accent leak into the panel/chip fill, name, beacon, count, close, or the status (underway conic / aurora-warn / running glow) channel.
|
|
92
228
|
- CSS stays in three layers: `theme.css` (tokens/reset/keyframes only), `layout.css` (shell grid/breakpoints only), `components.css` (every concrete surface).
|
|
93
229
|
|
|
94
230
|
## TypeScript File Structure
|
|
@@ -101,9 +237,10 @@ imports -> types/interfaces -> constants -> functions/components
|
|
|
101
237
|
|
|
102
238
|
## Build & Serve Contract
|
|
103
239
|
|
|
104
|
-
- `pnpm --filter @dotobokuri/fleet-console build` runs tsup (`
|
|
240
|
+
- `pnpm --filter @dotobokuri/fleet-console build` runs tsup (`core/host/cli.ts` → `dist/cli.mjs`, `core/host/cli-bin.ts` → `dist/cli-bin.mjs`, `../fleet-plugins/terminal/routes.ts` → `dist/fleet-plugins/terminal/routes.mjs`, `../fleet-plugins/diff/routes.ts` → `dist/fleet-plugins/diff/routes.mjs`, `../fleet-plugins/file-explorer/routes.ts` → `dist/fleet-plugins/file-explorer/routes.mjs`) and Vite (`core/client/` → `dist/client/` with `base: "/console/"`). There is **no** embed step: the console backend serves its own `dist/client/` directly under `/console/` (loopback-only). Changing `base` or the output layout breaks the static-serving contract.
|
|
105
241
|
- This package **is** the HTTP server. The backend owns its own loopback server, lifecycle, and `/console/` serving; the CLI starts and stops that server rather than launching a separate daemon.
|
|
106
|
-
- **npm publish contract**: tsup bundles every `@dotobokuri/*` workspace dependency inline (`noExternal: [/^@dotobokuri
|
|
242
|
+
- **npm publish contract**: tsup bundles every `@dotobokuri/*` workspace dependency and `@fleet-console/sdk` inline (`noExternal: [/^@dotobokuri\//, /^@fleet-console\/sdk(\/|$)/]`) so the published package is self-contained; only `node-pty` (native binding) and `ws` (dynamic `require`) stay external. `scripts/publish-fleet-console.mjs` drops `private`, replaces `dependencies` with just those two externals, and injects the `node-pty` `postinstall`. Do **not** add a statically-imported workspace package without confirming it bundles, and re-verify the published manifest with `npm pack` after touching `noExternal` or runtime deps — leaving a `workspace:*` dependency in the manifest breaks `npm install`.
|
|
243
|
+
- **Plugin native/external module resolution**: a built-in plugin's server code that `require()`s a native or `external` module (`node-pty`, `ws`) must resolve it against the `@dotobokuri/fleet-console` package, not via a bare `createRequire(import.meta.url)`. Plugins load from an esbuild **cache bundle** (`node_modules/.cache/fleet-console-plugin-*`), so a `createRequire` rooted there can resolve a **stale copy from a parent-workspace `node_modules`** and fail at runtime with `posix_spawnp failed` — typecheck and build stay green, so only a runtime/e2e run catches it. The Terminal plugin's `server/shared/pty.ts` (`findConsolePackageRequire`) is the reference, guarded by a `launch.test.ts` regression test.
|
|
107
244
|
|
|
108
245
|
## Tests
|
|
109
246
|
|
package/README.md
CHANGED
|
@@ -1,39 +1,41 @@
|
|
|
1
1
|
# Fleet Console
|
|
2
2
|
|
|
3
|
-
Standalone loopback web console for observing carrier jobs, live output streams, and
|
|
3
|
+
Standalone loopback web console for observing carrier jobs, live output streams, and plugin-owned PTY terminal workspaces.
|
|
4
4
|
|
|
5
5
|
## What It Does
|
|
6
6
|
|
|
7
|
-
Fleet Console owns its own local HTTP server. Terminal
|
|
7
|
+
Fleet Console owns its own local HTTP server. The Terminal plugin owns Shell and Agent PTY runtime, tickets, launch, and WebSocket transport; carrier events are streamed to the browser through the observer API.
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- Plugin-owned terminal sessions and observed jobs in a navigable rail.
|
|
10
10
|
- Workspace hub sessions created through an in-console directory browser — no OS-native dialog.
|
|
11
|
-
-
|
|
11
|
+
- Terminal plugin-spawned Agent CLI PTYs with in-process observation.
|
|
12
12
|
- Per-job carrier tracks with incremental output text, reasoning folds, and tool-call activity.
|
|
13
13
|
- Codex/Fleet Wiki browsing under the shared Console GNB at `/console/codex`.
|
|
14
14
|
- Browser observer snapshots and SSE streams backed by console-owned global observed ids.
|
|
15
15
|
- Browser terminal access through short-lived tickets over WebSocket.
|
|
16
16
|
|
|
17
|
+
The built-in terminal plugin lives at `runtime/fleet-plugins/terminal` (`@fleet-plugins/terminal`). It is the single built-in plugin id `terminal`, provides operation types `shell`, `agent`, and `agent.streaming`, owns plugin-scoped WebSocket, ticket, PTY session, and launch runtime, and serves Shell/Agent plugin routes under `/plugins/terminal/{shell,agent}/*`. The console owns Theater folder selection through `/api/v1/theaters/folder-listings` and `/api/v1/theaters/folder-grants`. The Shell launch title is `Shell`.
|
|
18
|
+
|
|
17
19
|
## Runtime Channels
|
|
18
20
|
|
|
19
21
|
| Channel | Purpose | Token Boundary |
|
|
20
22
|
|---|---|---|
|
|
21
23
|
| `/observer/*` | Browser snapshot and SSE observer surface. | Loopback-only; no browser bearer token. |
|
|
22
|
-
| `POST /
|
|
23
|
-
| `POST /
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
24
|
+
| `POST /api/v1/theaters/folder-listings` | Returns a directory listing (`{ path, parentPath, roots, entries, truncated? }`) for the given path, or the server home directory when `path` is null. Directories only, non-recursive, capped at 500 entries. | Requires the terminal Origin boundary (`isTerminalAuthorized`); no adminToken. |
|
|
25
|
+
| `POST /api/v1/theaters/folder-grants` | Validates the client-supplied absolute path through `validateAbsoluteDirectory` and returns a one-use `{ folderGrantId }`. | Requires the terminal Origin boundary; no adminToken. |
|
|
26
|
+
| `/plugins/terminal/shell/*` | Shell launch and ticket routes for the `shell` operation type. | Shell cwd is resolved server-side from the selected Theater; browser receives only one-use terminal tickets. |
|
|
27
|
+
| `/plugins/terminal/agent/*` | Agent launch, session, ticket, job, event, tenant, and state routes for the `agent` and `agent.streaming` operation types. | Requires the terminal Origin boundary; MCP/session tokens remain server-only. |
|
|
28
|
+
| Terminal plugin WebSocket route | Terminal plugin-owned browser PTY WebSocket transport used by Shell and Agent operations under the plugin namespace. | Browser reaches it through a one-use ticket from the terminal plugin routes. |
|
|
27
29
|
| `/console/` | Static React client served from this package's `dist/client`. | Served directly from the loopback console URL. |
|
|
28
30
|
| `/console/codex/*` | Console-owned Codex/Fleet Wiki web, workspace API, and migrated Maritime Codex client. | Admin workspace registration uses the lock bearer token; browser reads stay token-free on allowed local origins. |
|
|
29
31
|
|
|
30
|
-
`/observer/tenants` may include `terminalSessionId` for
|
|
32
|
+
`/observer/tenants` may include `terminalSessionId` for plugin-owned terminal sessions. Shell and Agent HTTP routes plus WebSocket transport live under `/plugins/terminal/*`.
|
|
31
33
|
|
|
32
34
|
## Session Binding
|
|
33
35
|
|
|
34
|
-
When the
|
|
36
|
+
When the Terminal plugin creates a terminal session, it generates a session id, resolves the selected Agent CLI through the shared fleet-admiral runtime, and keeps the selected absolute cwd server-side. The plugin records non-secret session metadata for observer hydration through generic console operation and event capabilities.
|
|
35
37
|
|
|
36
|
-
Folder selection is handled entirely in the browser UI: the React directory browser modal calls `POST /
|
|
38
|
+
Folder selection is handled entirely in the browser UI: the React directory browser modal calls the console-owned `POST /theaters/folders/list` route to browse the server's local filesystem, then calls `POST /theaters/folders/grants` once the operator confirms a directory. The resulting one-use grant is consumed by Theater registration; Shell and Agent launches resolve cwd from the Theater server-side. No OS-native dialog or child process is involved. The browser modal works in remote and headless browser sessions without any OS-level dialog support.
|
|
37
39
|
|
|
38
40
|
Folder grants are one-use and in-memory. Browser-side cancellation stays local to the modal and does not call the server grant endpoint.
|
|
39
41
|
|
|
@@ -41,7 +43,7 @@ Folder grants are one-use and in-memory. Browser-side cancellation stays local t
|
|
|
41
43
|
|
|
42
44
|
HTTP surfaces are loopback-only. Browser observer routes are directly available on loopback and terminal routes retain their Origin boundary (`isTerminalAuthorized`). MCP session tokens, bootstrap tokens, and selected absolute paths are not exposed through browser payloads, URL query strings, SSE frames, terminal tickets, logs, or static assets.
|
|
43
45
|
|
|
44
|
-
`POST /
|
|
46
|
+
`POST /theaters/folders/list` and `POST /theaters/folders/grants` both require `validateHost` and `isTerminalAuthorized`. No adminToken or bearer auth is used for folder endpoints. Selected absolute paths appear only in list and grant API responses; they are not included in session, Theater, observer, or SSE payloads. When a Theater is registered, the resolved cwd is stored in durable local state (`~/.fleet/console/state.json`, `sensitivity: "sensitive"`) exactly as before; this is a sensitive local file and is not transmitted to the browser.
|
|
45
47
|
|
|
46
48
|
Codex/Fleet Wiki routes preserve the migrated wiki security boundary: Host allowlist, Origin checks for write routes, loopback write gates, path containment, DOMPurify markdown sanitization, strict Mermaid rendering, and lockfile bearer auth for workspace registration.
|
|
47
49
|
|
|
@@ -52,14 +54,14 @@ fleet console # via fleet-cli
|
|
|
52
54
|
fleet-console # standalone binary
|
|
53
55
|
fleet-console status
|
|
54
56
|
fleet-console stop
|
|
55
|
-
fleet wiki # opens the console-owned Codex surface
|
|
56
|
-
fleet-wiki # standalone compatibility binary from this package
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
The launcher ensures the local console server is running and opens `/console/` directly without browser token fragments.
|
|
60
60
|
|
|
61
61
|
## Development
|
|
62
62
|
|
|
63
|
+
Source is split under `core/host/` for the Node CLI/backend and `core/client/` for the Vite React SPA. The built-in Terminal plugin package lives at `../fleet-plugins/terminal/`. The private `@fleet-console/sdk` package under `sdk/` is the shared plugin contract surface for core and built-in plugins.
|
|
64
|
+
|
|
63
65
|
```bash
|
|
64
66
|
pnpm --filter @dotobokuri/fleet-console dev
|
|
65
67
|
pnpm --filter @dotobokuri/fleet-console test
|
|
@@ -67,6 +69,6 @@ pnpm --filter @dotobokuri/fleet-console typecheck
|
|
|
67
69
|
pnpm --filter @dotobokuri/fleet-console build
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
`build` emits `dist/cli.mjs` and `dist/
|
|
72
|
+
`build` emits `dist/cli.mjs`, `dist/cli-bin.mjs`, `dist/client/`, and `dist/fleet-plugins/terminal/routes.mjs`. There is no external embed step.
|
|
71
73
|
|
|
72
74
|
See `AGENTS.md` for ownership, token-boundary, and streaming invariants.
|
package/dist/cli.d.ts
CHANGED
|
@@ -75,20 +75,6 @@ interface ConsoleRestartDeps {
|
|
|
75
75
|
readonly lifecycle?: Pick<ReturnType<typeof createConsoleDaemonLifecycle>, "stop" | "ensureDaemon" | "probe">;
|
|
76
76
|
readonly openBrowser?: (url: string, deps?: OpenBrowserDeps) => void;
|
|
77
77
|
}
|
|
78
|
-
interface OpenFleetWikiWorkspaceOptions {
|
|
79
|
-
readonly cwd: string;
|
|
80
|
-
readonly host?: string;
|
|
81
|
-
readonly port?: number;
|
|
82
|
-
}
|
|
83
|
-
interface OpenFleetWikiWorkspaceResult {
|
|
84
|
-
readonly host: string;
|
|
85
|
-
readonly pid: number;
|
|
86
|
-
readonly port: number;
|
|
87
|
-
readonly url: string;
|
|
88
|
-
}
|
|
89
|
-
interface ProbeFleetWikiDaemonOptions {
|
|
90
|
-
readonly cwd: string;
|
|
91
|
-
}
|
|
92
78
|
interface BuildConsoleHelpTextOptions {
|
|
93
79
|
readonly env?: NodeJS.ProcessEnv;
|
|
94
80
|
readonly isTTY?: boolean;
|
|
@@ -110,7 +96,6 @@ declare function parseConsoleHookCommand(argv: readonly string[]): {
|
|
|
110
96
|
readonly command: "auto-name";
|
|
111
97
|
};
|
|
112
98
|
declare function buildConsoleHelpText(options?: BuildConsoleHelpTextOptions): string;
|
|
113
|
-
declare function buildWikiHelpText(options?: BuildConsoleHelpTextOptions): string;
|
|
114
99
|
declare function createConsoleDaemonLifecycle(deps?: ConsoleDaemonLifecycleDeps): {
|
|
115
100
|
ensureDaemon: () => Promise<string>;
|
|
116
101
|
probe: () => Promise<{
|
|
@@ -124,13 +109,9 @@ declare function createConsoleDaemonLifecycle(deps?: ConsoleDaemonLifecycleDeps)
|
|
|
124
109
|
stop: () => Promise<void>;
|
|
125
110
|
};
|
|
126
111
|
declare function openFleetConsole(deps?: OpenFleetConsoleDeps): Promise<OpenFleetConsoleResult>;
|
|
127
|
-
declare function openFleetWikiWorkspace(options: OpenFleetWikiWorkspaceOptions): Promise<OpenFleetWikiWorkspaceResult>;
|
|
128
|
-
declare function probeFleetWikiDaemon(_options: ProbeFleetWikiDaemonOptions): Promise<OpenFleetWikiWorkspaceResult | null>;
|
|
129
|
-
declare function stopDaemon(): Promise<void>;
|
|
130
112
|
declare function runConsoleStatus(deps?: ConsoleStatusDeps): Promise<string>;
|
|
131
113
|
declare function runConsoleStop(deps?: ConsoleStopDeps): Promise<string>;
|
|
132
114
|
declare function runConsoleRestart(deps?: ConsoleRestartDeps): Promise<OpenFleetConsoleResult>;
|
|
133
115
|
declare function main(): Promise<void>;
|
|
134
|
-
declare function mainWiki(argv?: readonly string[]): Promise<void>;
|
|
135
116
|
|
|
136
|
-
export { type BuildConsoleHelpTextOptions, type ConsoleCliMode, type ConsoleDaemonLifecycleDeps, type ConsoleRestartDeps, type ConsoleStatusDeps, type ConsoleStopDeps, type OpenFleetConsoleDeps, type OpenFleetConsoleResult,
|
|
117
|
+
export { type BuildConsoleHelpTextOptions, type ConsoleCliMode, type ConsoleDaemonLifecycleDeps, type ConsoleRestartDeps, type ConsoleStatusDeps, type ConsoleStopDeps, type OpenFleetConsoleDeps, type OpenFleetConsoleResult, buildConsoleHelpText, createConsoleDaemonLifecycle, main, openFleetConsole, parseConsoleCliMode, parseConsoleHookCommand, runConsoleRestart, runConsoleStatus, runConsoleStop };
|