@dotobokuri/fleet-console 1.17.1 → 1.19.0
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 +10 -9
- package/dist/cli.d.ts +13 -14
- package/dist/cli.mjs +1181 -2428
- package/dist/client/assets/SymbolsNerdFontMono-Regular-CoUzE12l.woff2 +0 -0
- package/dist/client/assets/{_baseUniq-B7sqIsqm.js → _baseUniq-C4_iQlZS.js} +1 -1
- package/dist/client/assets/{arc-CvtYLN8U.js → arc-BBAWxYi5.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-rlI7pNrR.js → architectureDiagram-Q4EWVU46-_oQqhexV.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-nwx9BNRz.js → blockDiagram-DXYQGD6D-3xT9VFiD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-CRL_0Ism.js → c4Diagram-AHTNJAMY-S-IMcMW2.js} +1 -1
- package/dist/client/assets/channel-5tV1napa.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DEkncDR3.js → chunk-4BX2VUAB-tS9Ys0P9.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-Bnot0Ti1.js → chunk-4TB4RGXK-YZgAG7SW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-EgKmMTlI.js → chunk-55IACEB6-m-CtqHoO.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-BKiKnVQE.js → chunk-EDXVE4YY-yoymxaEV.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BRC398vL.js → chunk-FMBD7UC4-REUkrQAG.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-D82gQaXN.js → chunk-OYMX7WX6-DzMuL105.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-BYdI5fky.js → chunk-QZHKN3VN-DXFFll8-.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-BhBgOoc0.js → chunk-YZCP3GAM-CmY7W-gg.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-WDM904ap.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-WDM904ap.js +1 -0
- package/dist/client/assets/clone-BJg9p0cC.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-J8dfEdWD.js → cose-bilkent-S5V4N54A-BupE-a40.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-zGd_6_Kv.js → dagre-KV5264BT-Jn67LxMS.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-BQpQGgZ3.js → diagram-5BDNPKRD-BAeFEORH.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-BbgPVITm.js → diagram-G4DWMVQ6-CnZrDABY.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-lJsMf0Ic.js → diagram-MMDJMWI5-DefG-kXU.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-BpEKTzOh.js → diagram-TYMM5635-BPkXKB-Q.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-CHHVwJ_a.js → erDiagram-SMLLAGMA-Bi4jlild.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-Cun8a4NF.js → flowDiagram-DWJPFMVM-BoZyLO7h.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CGCwH_yf.js → ganttDiagram-T4ZO3ILL-LgzSNjrh.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BkY4qdoV.js → gitGraphDiagram-UUTBAWPF-B4fH-hPT.js} +1 -1
- package/dist/client/assets/{graph-HbRzXQtt.js → graph-D3ZAYMLG.js} +1 -1
- package/dist/client/assets/index-DctP_CJP.css +1 -0
- package/dist/client/assets/index-vgEn_EkG.js +404 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-B21Xtz8v.js → infoDiagram-42DDH7IO-pENdunXd.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DiABngEH.js → ishikawaDiagram-UXIWVN3A-WakLHKte.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-BKZ-Uivu.js → journeyDiagram-VCZTEJTY-CrqXKtzk.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-CoJzTtsH.js → kanban-definition-6JOO6SKY-DdSC9eJ-.js} +1 -1
- package/dist/client/assets/{layout-D5tmQpxt.js → layout-BI1Oko_a.js} +1 -1
- package/dist/client/assets/{linear-B_riNOXk.js → linear-KUjZtfMe.js} +1 -1
- package/dist/client/assets/{mermaid.core-CyRCP3U3.js → mermaid.core-DNlIf4_3.js} +4 -4
- package/dist/client/assets/{min-Cn8LZZXB.js → min-DCAecveI.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BN_7mZUt.js → mindmap-definition-QFDTVHPH-BSCyQA-4.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-DPP2-sYA.js → pieDiagram-DEJITSTG-C_1I63z5.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CQ_oo-f9.js → quadrantDiagram-34T5L4WZ-CtqeYPcA.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-BkGq7nxD.js → requirementDiagram-MS252O5E-Cpakdg-s.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-BVcoktBR.js → sankeyDiagram-XADWPNL6-CK_eMH3e.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-B5rr_VYJ.js → sequenceDiagram-FGHM5R23-D2YygHqF.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-BV8RqzB9.js → stateDiagram-FHFEXIEX-DEDhnbhU.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-ji50PgJQ.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-CJ8TM-3t.js → timeline-definition-GMOUNBTQ-Brgny70W.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-BETSq-Pu.js → vennDiagram-DHZGUBPP-B1I5E8Hy.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-CyT2qapq.js → wardley-RL74JXVD-P0VCYfKI.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CWHaiUEG.js → wardleyDiagram-NUSXRM2D-DhYC5Sus.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-oVPlZ_Yn.js → xychartDiagram-5P7HB3ND-ByJX99rJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/diff/routes.mjs +260 -32
- package/dist/fleet-plugins/file-explorer/routes.mjs +1 -1
- package/dist/fleet-plugins/skills/routes.mjs +108 -201
- package/dist/fleet-plugins/terminal/routes.mjs +821 -1283
- package/package.json +1 -1
- package/dist/client/assets/channel-Ct9EqqyG.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CPMS8Qpo.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CPMS8Qpo.js +0 -1
- package/dist/client/assets/clone-C5XNYXHD.js +0 -1
- package/dist/client/assets/index-CfNSyAUJ.css +0 -1
- package/dist/client/assets/index-CzCMC0e-.js +0 -402
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Bx_fpQWQ.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
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 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
|
|
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 a resident collapsible stream dock pinned to the panel bottom — live output always visible without a click, per-browser collapsed state persisted — plus a detail modal for full track history accessed via the dock header, 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
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.
|
|
16
16
|
- The observer-side client contract: REST snapshot fetches and the SSE consumption loop with reconnect/resync.
|
|
17
17
|
- The streaming view model: the event reducer that folds `CarrierJobStreamEvent` timelines into per-job, per-track views with incremental text accumulation.
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
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
50
|
- `runtime/fleet-console/core/client/**` must not import `@dotobokuri/fleet-carriers`, carrier persona modules, deep carrier paths, or Node-only carrier runtime modules.
|
|
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
|
|
51
|
+
- Fleet Console may render and edit display-safe carrier settings data such as carrier id, display name, role/category, resolved CLI/model/effort, and Task Force backend count/configuration.
|
|
52
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.
|
|
53
53
|
- Console must not deep-import `@dotobokuri/fleet-carriers/src/**`, `packages/fleet-carriers/src/**`, `runtime/fleet-cli/**`, or `@dotobokuri/fleet-cli`.
|
|
54
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.
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
## Settings Plugin Boundary
|
|
57
57
|
|
|
58
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
59
|
- 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
60
|
- SDK settings descriptors remain minimal: `{ id, title, render }`. Do not add grouping, ordering, plugin ownership, or sensitivity metadata to `SettingsSectionDescriptor`.
|
|
62
61
|
|
|
@@ -127,7 +126,7 @@ These guards are robustness measures on top of the trust model, not a replacemen
|
|
|
127
126
|
- `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
127
|
- 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`). All mounted terminal panels subscribe directly and react instantly to settings changes. **Terminal Renderer** is persisted in localStorage under `fleet-plugin.terminal.renderer` (per-browser volatile). **Terminal Font** (name + size) is persisted on the console server under `plugins.terminal.font` via `ClientSettingsCapability` (`GET`/`PUT /api/v1/settings/plugins/terminal`); it survives browser changes and console restarts. On first load the store seeds the server from `fleet-plugin.terminal.font` localStorage if the server has no value (1-time migration), then removes the local key. `fleet-plugin.terminal.*` localStorage keys are Terminal plugin-owned; core and other plugins must not read or write them.
|
|
129
128
|
- **Preferences vs Settings semantics**: `ClientPreferencesCapability` is per-browser volatile (localStorage). `ClientSettingsCapability` is per-server durable (stored in the console `settings.json` `plugins` record; the server is the SSoT). Use preferences for transient display choices (e.g. renderer backend); use settings for durable configuration (e.g. font, feature toggles) that must survive browser changes and console restarts.
|
|
130
|
-
- **Plugin settings safety**: Values written through `ClientSettingsCapability` must not contain secrets, tokens, credentials, or raw filesystem paths. The Token Boundary applies to plugin settings payloads the same as to all other browser-facing payloads. Model API keys persist through `@dotobokuri/
|
|
129
|
+
- **Plugin settings safety**: Values written through `ClientSettingsCapability` must not contain secrets, tokens, credentials, or raw filesystem paths. The Token Boundary applies to plugin settings payloads the same as to all other browser-facing payloads. Model API keys persist through `@dotobokuri/core-infra` auth storage, not plugin settings.
|
|
131
130
|
- 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.
|
|
132
131
|
|
|
133
132
|
## Window System
|
|
@@ -136,7 +135,7 @@ Operation chrome (maximize, minimize, focus, and the Operations Left SideBar) is
|
|
|
136
135
|
|
|
137
136
|
- **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.
|
|
138
137
|
- **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.
|
|
139
|
-
- **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
|
+
- **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. Plugins may declare which launch kind created an Operation through `payload.launchKindId`; when multiple kinds share the same type and no matching launch kind is present, the chip shows the neutral fallback glyph. 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.
|
|
140
139
|
- **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.
|
|
141
140
|
- **`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.
|
|
142
141
|
|
|
@@ -157,8 +156,8 @@ Operation chrome (maximize, minimize, focus, and the Operations Left SideBar) is
|
|
|
157
156
|
- `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.
|
|
158
157
|
- `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/`).
|
|
159
158
|
- `../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.
|
|
160
|
-
- `../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/`.
|
|
161
|
-
- `../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.
|
|
159
|
+
- `../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/`. **Extended Bridge layout**: selecting a file or commit expands the panel 400 px to the left via `ctx.requestExtraWidth(400)` (single `useLayoutEffect` call site) — the full extra width goes to the left diff document pane (`hunk-view.tsx` with `hunk-parse.ts` line-number gutter and `@@` summary labels); the right file list pane stays a **fixed px column** (default 248 px, drag minimum 220 px, persisted per browser as `fleet-console.diff.listPaneWidth`). A 4 px drag divider adjusts list width; drag clamping is a pure function in `client/rail-layout.ts` (inversion guard: no-op when the container cannot satisfy both pane minimums). The split grid is `minmax(0, 1fr) 4px minmax(0, min(<listPaneWidth>px, calc(100% - 144px)))` — when the container narrows, the document pane minimum (140 px) is preserved and the list column shrinks instead (the stored width value is unchanged). History commit rows (`.diff-tree-pane`) use container queries: author/time meta hides at ≤300 px; badges hide at ≤248 px. Pane grids (`.diff-tree-pane`, `.diff-hunk-pane`) declare `grid-template-columns: minmax(0, 1fr)` explicitly — undeclared columns default to implicit `auto` tracks sized to content min-width (toolbar fixed-width sums can exceed the pane and overflow the row). Deselecting or closing resets extra width to 0. The repository picker opens as an opaque in-panel Command Deck (`var(--ink-deep)` background, `inset: 0`) with worktree children always rendered inline, ROOT badge = brass, WORKTREE badge = aurora, and DEPTH + Rescan footer preserved.
|
|
160
|
+
- `../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. **Two-tier layout**: with no file selected the panel is tree-only (single column, no extra width); selecting a file switches to a 2-pane split via a single `useLayoutEffect` calling `ctx.requestExtraWidth(360)` — left viewer pane + right fixed tree column (default 248 px, drag minimum 160 px, persisted per browser as `fleet-console.file-explorer.treePaneWidth`). Closing the viewer returns to tree-only and clears extra width. The viewer minimum (200 px) is preserved by the same CSS `min()` clamp pattern as Diff (`minmax(0, min(<treePaneWidth>px, calc(100% - 204px)))`). File Explorer does not use `RailPanelDescriptor.preferredExtraWidth` (the SDK field remains for other panels). Pane grids declare `grid-template-columns: minmax(0, 1fr)` for the same implicit-auto overflow pitfall as Diff.
|
|
162
161
|
- `../fleet-plugins/skills/` — the built-in Skills plugin package. It wraps the Vercel Labs `skills` CLI (pinned version, self-bootstrapped into the plugin data directory via `npm install --prefix --global=false --force=false` and executed directly as `node <…>/node_modules/skills/bin/cli.mjs` — never `npx`, whose multi-bin resolution breaks under non-default user npm configs) to provide skill list, install, update, remove, and SKILL.md preview routes under `/plugins/skills/*`, proxies the skills.sh registry search API, manages an in-memory job registry with 750ms cursor polling, and renders the Skills Activity Rail panel (Installed/Find tabs, inline install flow, reading overlay).
|
|
163
162
|
- `tests/` — vitest suites for the reducer, SSE parser, store, terminal, and CLI lifecycle.
|
|
164
163
|
|
|
@@ -197,7 +196,8 @@ These CSS variables are declared in `rail.css` and must not be inlined elsewhere
|
|
|
197
196
|
- 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.
|
|
198
197
|
- Rail panel id deduplication is enforced in `plugin-registry.ts#createPluginRegistry`; the first plugin wins and subsequent duplicates are warned and skipped.
|
|
199
198
|
- `apiVersion` compatibility gate applies to external plugins only (enforced in `plugin-registry.ts#loadExternalPlugin`).
|
|
200
|
-
- `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 (
|
|
199
|
+
- `RailPanelDescriptor.preferredExtraWidth?: number` — **static extra width**: when set, the host adds this many px to the active panel slot width at all times while the panel is open; the host reads only the declared value and remains panel-id-agnostic (additive with `useCodexSplitExtraWidth`).
|
|
200
|
+
- `RailPanelContext.requestExtraWidth?: (px: number | null) => void` — **dynamic extra width**: the active panel calls this to request additional width based on its own state changes (e.g. file selection). SSoT is `rail-store.ts` `panelExtraWidth`; semantics: positive integer = extra px to add, `null` = reset to 0. Guards: (1) only the active panelId is honoured (stale-closure calls from inactive panels are dropped); (2) `px` is normalized to `Math.max(0, Math.round(px))`, non-finite and null → 0; (3) viewport clamp: `Math.min(normalized, Math.max(0, window.innerWidth - 548))` (skipped in Node/test env); (4) same-value no-op (no listener notify); (5) render-time backstop: `.right-rail-panel-slot` carries `max-width: calc(100vw - 148px)` so a viewport shrink after the request never pushes the slot over the canvas. Reset paths: `setActiveRailPanel`, `toggleRailPanel`, and `closeRailPanel` all zero `panelExtraWidth` on state change. Value is transient — never persisted to localStorage. Call site must be a single `useLayoutEffect` keyed on state that triggers the request; do not scatter calls across event handlers.
|
|
201
201
|
|
|
202
202
|
**Layout contract**:
|
|
203
203
|
|
|
@@ -211,7 +211,7 @@ These CSS variables are declared in `rail.css` and must not be inlined elsewhere
|
|
|
211
211
|
- Active icon = `--brass`; idle = `--ink-fog`; hover = `--ink-spectral`. Do **not** use `--aurora` for rail icon states.
|
|
212
212
|
- Active icon carries a left-edge 2px `--brass` bar (`.right-rail-ico.is-active::before`).
|
|
213
213
|
- Panel open/close transition = `width var(--duration-base) var(--ease-spring)`. `prefers-reduced-motion: reduce` must short-circuit to `0.01ms`.
|
|
214
|
-
- Panel chrome (header 46px min-height + body 1fr) follows the mock `.panel` pattern. Panel body content is plugin-owned; chrome is host-owned.
|
|
214
|
+
- Panel chrome (header 46px min-height + body 1fr; head and body `min-width: 240px`, aligned with `MIN_PANEL_WIDTH` in `right-rail.tsx`) follows the mock `.panel` pattern. Panel body content is plugin-owned; chrome is host-owned.
|
|
215
215
|
- Viewer-surface syntax/diff colors go in new surface CSS files (e.g., `rail-viewer.css`); `theme.css` is immutable.
|
|
216
216
|
|
|
217
217
|
## Design Identity — "Maritime Console"
|
|
@@ -244,6 +244,7 @@ imports -> types/interfaces -> constants -> functions/components
|
|
|
244
244
|
- 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.
|
|
245
245
|
- **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`.
|
|
246
246
|
- **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.
|
|
247
|
+
- **Plugin external-bundle module-singleton split**: a `@dotobokuri/*` package holding module-scoped mutable singleton state (e.g. `fleet-carriers` `state-io.ts` `runtimeState`) is inlined into `dist/cli.mjs` for host code (tsup `noExternal`) but loaded as a **separate esbuild-external copy** for plugins — so host code and plugin code see **different singleton instances**. State the host sets (e.g. a boot-time `initStore(dir)` in `server.ts`) is invisible to the plugin's copy and vice versa. Symptom: a mutation routed through one copy silently no-ops or writes to the wrong path while the other copy holds the initialized state (PR#175: console carrier-settings PATCH returned 200 but never wrote `carriers.json`). Fix: do **not** depend on cross-boundary singleton state — make the resolver **self-contained** (`runtimeState.storeDir ?? getFleetDataDir()`) so both copies converge regardless of which one had its setter called. Unit tests import a single source singleton and stay green; only a **dist real-boot e2e** catches this.
|
|
247
248
|
|
|
248
249
|
## Tests
|
|
249
250
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -71,21 +71,9 @@ interface ConsoleStatusDeps {
|
|
|
71
71
|
interface ConsoleStopDeps {
|
|
72
72
|
readonly lifecycle?: Pick<ReturnType<typeof createConsoleDaemonLifecycle>, "stop">;
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
readonly lifecycle?: Pick<ReturnType<typeof createConsoleDaemonLifecycle>, "stop" | "ensureDaemon" | "probe">;
|
|
76
|
-
readonly openBrowser?: (url: string, deps?: OpenBrowserDeps) => void;
|
|
77
|
-
}
|
|
78
|
-
interface BuildConsoleHelpTextOptions {
|
|
79
|
-
readonly env?: NodeJS.ProcessEnv;
|
|
80
|
-
readonly isTTY?: boolean;
|
|
81
|
-
readonly release?: string;
|
|
82
|
-
}
|
|
83
|
-
declare function parseConsoleCliMode(argv: readonly string[]): ConsoleCliMode;
|
|
84
|
-
declare function parseConsoleHookCommand(argv: readonly string[]): {
|
|
74
|
+
type ConsoleHookCommand = {
|
|
85
75
|
readonly command: "capture-session";
|
|
86
76
|
readonly provider: string;
|
|
87
|
-
} | {
|
|
88
|
-
readonly command: "subagents-context";
|
|
89
77
|
} | {
|
|
90
78
|
readonly command: "turn-start";
|
|
91
79
|
} | {
|
|
@@ -95,6 +83,17 @@ declare function parseConsoleHookCommand(argv: readonly string[]): {
|
|
|
95
83
|
} | {
|
|
96
84
|
readonly command: "auto-name";
|
|
97
85
|
};
|
|
86
|
+
interface ConsoleRestartDeps {
|
|
87
|
+
readonly lifecycle?: Pick<ReturnType<typeof createConsoleDaemonLifecycle>, "stop" | "ensureDaemon" | "probe">;
|
|
88
|
+
readonly openBrowser?: (url: string, deps?: OpenBrowserDeps) => void;
|
|
89
|
+
}
|
|
90
|
+
interface BuildConsoleHelpTextOptions {
|
|
91
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
92
|
+
readonly isTTY?: boolean;
|
|
93
|
+
readonly release?: string;
|
|
94
|
+
}
|
|
95
|
+
declare function parseConsoleCliMode(argv: readonly string[]): ConsoleCliMode;
|
|
96
|
+
declare function parseConsoleHookCommand(argv: readonly string[]): ConsoleHookCommand;
|
|
98
97
|
declare function buildConsoleHelpText(options?: BuildConsoleHelpTextOptions): string;
|
|
99
98
|
declare function createConsoleDaemonLifecycle(deps?: ConsoleDaemonLifecycleDeps): {
|
|
100
99
|
ensureDaemon: () => Promise<string>;
|
|
@@ -114,4 +113,4 @@ declare function runConsoleStop(deps?: ConsoleStopDeps): Promise<string>;
|
|
|
114
113
|
declare function runConsoleRestart(deps?: ConsoleRestartDeps): Promise<OpenFleetConsoleResult>;
|
|
115
114
|
declare function main(): Promise<void>;
|
|
116
115
|
|
|
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 };
|
|
116
|
+
export { type BuildConsoleHelpTextOptions, type ConsoleCliMode, type ConsoleDaemonLifecycleDeps, type ConsoleHookCommand, type ConsoleRestartDeps, type ConsoleStatusDeps, type ConsoleStopDeps, type OpenFleetConsoleDeps, type OpenFleetConsoleResult, buildConsoleHelpText, createConsoleDaemonLifecycle, main, openFleetConsole, parseConsoleCliMode, parseConsoleHookCommand, runConsoleRestart, runConsoleStatus, runConsoleStop };
|