@dotobokuri/fleet-console 1.18.0 → 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 +6 -5
- package/dist/cli.d.ts +13 -12
- package/dist/cli.mjs +143 -175
- package/dist/client/assets/SymbolsNerdFontMono-Regular-CoUzE12l.woff2 +0 -0
- package/dist/client/assets/{_baseUniq-DFEDX4NY.js → _baseUniq-C4_iQlZS.js} +1 -1
- package/dist/client/assets/{arc-CGwMrR6o.js → arc-BBAWxYi5.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-BqLyvIOK.js → architectureDiagram-Q4EWVU46-_oQqhexV.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-DoiPMLdv.js → blockDiagram-DXYQGD6D-3xT9VFiD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-DfGxDkah.js → c4Diagram-AHTNJAMY-S-IMcMW2.js} +1 -1
- package/dist/client/assets/channel-5tV1napa.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-3zXv74oY.js → chunk-4BX2VUAB-tS9Ys0P9.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-MkXUmjUE.js → chunk-4TB4RGXK-YZgAG7SW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-mhkgqlI_.js → chunk-55IACEB6-m-CtqHoO.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-B0_ZDihs.js → chunk-EDXVE4YY-yoymxaEV.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BQAuE4pL.js → chunk-FMBD7UC4-REUkrQAG.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-CY8Eyj9X.js → chunk-OYMX7WX6-DzMuL105.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CaZoMwKH.js → chunk-QZHKN3VN-DXFFll8-.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-DV1YOEUH.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-Cy3v0C_N.js → cose-bilkent-S5V4N54A-BupE-a40.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-CGJ6x_VR.js → dagre-KV5264BT-Jn67LxMS.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-MyyXeHKQ.js → diagram-5BDNPKRD-BAeFEORH.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-D52Q2132.js → diagram-G4DWMVQ6-CnZrDABY.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-DCypusCr.js → diagram-MMDJMWI5-DefG-kXU.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-c5UBqE9-.js → diagram-TYMM5635-BPkXKB-Q.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-BlF0Lxy7.js → erDiagram-SMLLAGMA-Bi4jlild.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-lo8xyzfx.js → flowDiagram-DWJPFMVM-BoZyLO7h.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BXVZMpon.js → ganttDiagram-T4ZO3ILL-LgzSNjrh.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-DB1A6uYk.js → gitGraphDiagram-UUTBAWPF-B4fH-hPT.js} +1 -1
- package/dist/client/assets/{graph-Ck2AX1p2.js → graph-D3ZAYMLG.js} +1 -1
- package/dist/client/assets/{index-Cf9MmpwN.css → index-DctP_CJP.css} +1 -1
- package/dist/client/assets/index-vgEn_EkG.js +404 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-NEVE42fw.js → infoDiagram-42DDH7IO-pENdunXd.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-MbFr_8pL.js → ishikawaDiagram-UXIWVN3A-WakLHKte.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-Sq2ja3q8.js → journeyDiagram-VCZTEJTY-CrqXKtzk.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-C63qPhtK.js → kanban-definition-6JOO6SKY-DdSC9eJ-.js} +1 -1
- package/dist/client/assets/{layout-CFEuVj4N.js → layout-BI1Oko_a.js} +1 -1
- package/dist/client/assets/{linear-B626G8Op.js → linear-KUjZtfMe.js} +1 -1
- package/dist/client/assets/{mermaid.core-BT7SVKBR.js → mermaid.core-DNlIf4_3.js} +4 -4
- package/dist/client/assets/{min-MbEOApMw.js → min-DCAecveI.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-DMpht5Mg.js → mindmap-definition-QFDTVHPH-BSCyQA-4.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-B4XS0haZ.js → pieDiagram-DEJITSTG-C_1I63z5.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CReo5Ajb.js → quadrantDiagram-34T5L4WZ-CtqeYPcA.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-A50aqG-K.js → requirementDiagram-MS252O5E-Cpakdg-s.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-471VOdlW.js → sankeyDiagram-XADWPNL6-CK_eMH3e.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-BAvOyUC-.js → sequenceDiagram-FGHM5R23-D2YygHqF.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-gCtZrvR-.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-CTHf-uxq.js → timeline-definition-GMOUNBTQ-Brgny70W.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-5gvXi0dO.js → vennDiagram-DHZGUBPP-B1I5E8Hy.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-XR-qQTWZ.js → wardley-RL74JXVD-P0VCYfKI.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-EwPsMHdt.js → wardleyDiagram-NUSXRM2D-DhYC5Sus.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-CePwuUb7.js → xychartDiagram-5P7HB3ND-ByJX99rJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/diff/routes.mjs +207 -34
- package/dist/fleet-plugins/file-explorer/routes.mjs +1 -1
- package/dist/fleet-plugins/skills/routes.mjs +101 -83
- package/dist/fleet-plugins/terminal/routes.mjs +556 -325
- package/package.json +1 -1
- package/dist/client/assets/channel-DlT8zCVq.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-B-UN9f9r.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-B-UN9f9r.js +0 -1
- package/dist/client/assets/clone-CDPRFCO0.js +0 -1
- package/dist/client/assets/index-DWgndVkX.js +0 -404
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Bdb0YU66.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -126,7 +126,7 @@ These guards are robustness measures on top of the trust model, not a replacemen
|
|
|
126
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.
|
|
127
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.
|
|
128
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.
|
|
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/
|
|
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.
|
|
130
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.
|
|
131
131
|
|
|
132
132
|
## Window System
|
|
@@ -135,7 +135,7 @@ Operation chrome (maximize, minimize, focus, and the Operations Left SideBar) is
|
|
|
135
135
|
|
|
136
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.
|
|
137
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.
|
|
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. 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.
|
|
139
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.
|
|
140
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.
|
|
141
141
|
|
|
@@ -156,8 +156,8 @@ Operation chrome (maximize, minimize, focus, and the Operations Left SideBar) is
|
|
|
156
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.
|
|
157
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/`).
|
|
158
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.
|
|
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 expands the panel 400 px to the left via `ctx.requestExtraWidth(400)` (single `useLayoutEffect` call site)
|
|
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.
|
|
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.
|
|
161
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).
|
|
162
162
|
- `tests/` — vitest suites for the reducer, SSE parser, store, terminal, and CLI lifecycle.
|
|
163
163
|
|
|
@@ -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,17 +71,7 @@ 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
77
|
} | {
|
|
@@ -93,6 +83,17 @@ declare function parseConsoleHookCommand(argv: readonly string[]): {
|
|
|
93
83
|
} | {
|
|
94
84
|
readonly command: "auto-name";
|
|
95
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;
|
|
96
97
|
declare function buildConsoleHelpText(options?: BuildConsoleHelpTextOptions): string;
|
|
97
98
|
declare function createConsoleDaemonLifecycle(deps?: ConsoleDaemonLifecycleDeps): {
|
|
98
99
|
ensureDaemon: () => Promise<string>;
|
|
@@ -112,4 +113,4 @@ declare function runConsoleStop(deps?: ConsoleStopDeps): Promise<string>;
|
|
|
112
113
|
declare function runConsoleRestart(deps?: ConsoleRestartDeps): Promise<OpenFleetConsoleResult>;
|
|
113
114
|
declare function main(): Promise<void>;
|
|
114
115
|
|
|
115
|
-
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 };
|
package/dist/cli.mjs
CHANGED
|
@@ -21,6 +21,93 @@ var __export = (target, all) => {
|
|
|
21
21
|
for (var name in all)
|
|
22
22
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
23
23
|
};
|
|
24
|
+
var DEFAULT_WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
25
|
+
var WINDOWS_PATH_SEPARATOR = ";";
|
|
26
|
+
var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat"]);
|
|
27
|
+
var CMD_EXPANSION_SENSITIVE_PATTERN = /[%^]/;
|
|
28
|
+
function resolvePathBinary(command2, env, options = {}) {
|
|
29
|
+
const resolved = findBinaryPath(command2, env, options);
|
|
30
|
+
return resolved ? wrapWindowsShim(resolved, env, options.platform ?? process.platform) : void 0;
|
|
31
|
+
}
|
|
32
|
+
function findBinaryPath(command2, env, options = {}) {
|
|
33
|
+
const platform = options.platform ?? process.platform;
|
|
34
|
+
const isWindows2 = platform === "win32";
|
|
35
|
+
const pathValue = isWindows2 ? env.Path ?? env.PATH ?? "" : env.PATH ?? "";
|
|
36
|
+
const pathExts = isWindows2 ? parsePathExt(env.PATHEXT) : [""];
|
|
37
|
+
return findOnPath(command2, pathValue, pathExts, platform);
|
|
38
|
+
}
|
|
39
|
+
function wrapWindowsShim(resolved, env, platform) {
|
|
40
|
+
if (platform !== "win32" || !WINDOWS_SHIM_EXTENSIONS.has(path25__default.extname(resolved).toLowerCase())) {
|
|
41
|
+
return { bin: resolved, prefixArgs: [] };
|
|
42
|
+
}
|
|
43
|
+
rejectCmdExpansionSensitiveShim(resolved);
|
|
44
|
+
return {
|
|
45
|
+
bin: env.ComSpec ?? "cmd.exe",
|
|
46
|
+
prefixArgs: ["/d", "/s", "/c", "call", forceWindowsArgQuoting(resolved)]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function rejectCmdExpansionSensitiveShim(resolved) {
|
|
50
|
+
if (CMD_EXPANSION_SENSITIVE_PATTERN.test(resolved)) {
|
|
51
|
+
throw new Error(`Refusing to run Windows shim path with cmd.exe expansion-sensitive characters (% or ^): ${resolved}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function forceWindowsArgQuoting(value) {
|
|
55
|
+
return `${value} `;
|
|
56
|
+
}
|
|
57
|
+
function findOnPath(bin, pathValue, pathExts, platform) {
|
|
58
|
+
const pathSeparator = platform === "win32" ? WINDOWS_PATH_SEPARATOR : path25__default.delimiter;
|
|
59
|
+
if (hasPathSeparator(bin, platform) || path25__default.isAbsolute(bin)) {
|
|
60
|
+
return resolveWithExtensions(bin, pathExts, platform);
|
|
61
|
+
}
|
|
62
|
+
for (const entry of pathValue.split(pathSeparator)) {
|
|
63
|
+
const searchDir = entry.length === 0 ? "." : entry;
|
|
64
|
+
const candidate = resolveWithExtensions(path25__default.join(searchDir, bin), pathExts, platform);
|
|
65
|
+
if (candidate) {
|
|
66
|
+
return candidate;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
function resolveWithExtensions(candidate, pathExts, platform) {
|
|
72
|
+
if (platform !== "win32" || path25__default.extname(candidate).length > 0) {
|
|
73
|
+
return isAccessibleExecutable(candidate, platform) ? candidate : void 0;
|
|
74
|
+
}
|
|
75
|
+
for (const ext of pathExts) {
|
|
76
|
+
if (ext.length === 0) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const withExt = `${candidate}${ext}`;
|
|
80
|
+
if (existsSync(withExt)) {
|
|
81
|
+
return withExt;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return isAccessibleExecutable(candidate, platform) ? candidate : void 0;
|
|
85
|
+
}
|
|
86
|
+
function isAccessibleExecutable(candidate, platform) {
|
|
87
|
+
if (!existsSync(candidate)) return false;
|
|
88
|
+
if (platform === "win32") return true;
|
|
89
|
+
try {
|
|
90
|
+
accessSync(candidate, constants.X_OK);
|
|
91
|
+
return true;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function hasPathSeparator(value, platform) {
|
|
97
|
+
if (value.includes("/")) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return platform === "win32" && value.includes("\\");
|
|
101
|
+
}
|
|
102
|
+
function parsePathExt(pathExt) {
|
|
103
|
+
const raw = pathExt && pathExt.trim().length > 0 ? pathExt : DEFAULT_WINDOWS_PATHEXT;
|
|
104
|
+
return raw.split(WINDOWS_PATH_SEPARATOR).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ../../packages/core-process/src/hidden.ts
|
|
108
|
+
function withHidden(options) {
|
|
109
|
+
return { ...options ?? {}, windowsHide: true };
|
|
110
|
+
}
|
|
24
111
|
function openBrowser(url2, deps = {}) {
|
|
25
112
|
const platform = deps.platform ?? os4__default.platform();
|
|
26
113
|
const spawnBrowser = deps.spawnBrowser ?? ((command2, args, options) => {
|
|
@@ -348,7 +435,7 @@ function ensureSafeDirectory(dirPath) {
|
|
|
348
435
|
fs11.chmodSync(dirPath, SECURE_DIR_MODE);
|
|
349
436
|
}
|
|
350
437
|
|
|
351
|
-
// ../../packages/
|
|
438
|
+
// ../../packages/core-infra/src/fs-store/atomic-write.ts
|
|
352
439
|
var DEFAULT_MAX_ATTEMPTS = 10;
|
|
353
440
|
var IGNORED_FSYNC_ERROR_CODES = /* @__PURE__ */ new Set(["EPERM", "EINVAL", "ENOSYS"]);
|
|
354
441
|
function buildTempPath(filePath) {
|
|
@@ -595,7 +682,7 @@ function readAuthStoreFile(filePath) {
|
|
|
595
682
|
}
|
|
596
683
|
}
|
|
597
684
|
|
|
598
|
-
// ../../packages/
|
|
685
|
+
// ../../packages/core-infra/src/auth/auth-storage.ts
|
|
599
686
|
var DEFAULT_AUTH_PATH = path25.join(getFleetDataDir(), "auth.json");
|
|
600
687
|
var AUTH_LOCK_OWNER_FILE_NAME = "owner.json";
|
|
601
688
|
var AUTH_LOCK_TIMEOUT_MS = 5e3;
|
|
@@ -766,7 +853,7 @@ function ensureSafeDirectoryIfSensitive(dir, sensitivity) {
|
|
|
766
853
|
}
|
|
767
854
|
}
|
|
768
855
|
|
|
769
|
-
// ../../packages/
|
|
856
|
+
// ../../packages/core-infra/src/data-dir/settings/store.ts
|
|
770
857
|
var GLOBAL_OPTIONS_VERSION = 1;
|
|
771
858
|
var GLOBAL_OPTIONS_FILE_NAME = "settings.json";
|
|
772
859
|
var LOCK_DIR_NAME = "settings.json.lock";
|
|
@@ -808,18 +895,17 @@ function sanitizeGlobalOptionsData(value) {
|
|
|
808
895
|
}
|
|
809
896
|
const data = {
|
|
810
897
|
version: GLOBAL_OPTIONS_VERSION,
|
|
811
|
-
...typeof value.replaceSystemPrompt === "boolean" ? { replaceSystemPrompt: value.replaceSystemPrompt } : {},
|
|
812
898
|
...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {}
|
|
813
899
|
};
|
|
814
|
-
const allowedKeys = /* @__PURE__ */ new Set(["version", "
|
|
815
|
-
const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "
|
|
900
|
+
const allowedKeys = /* @__PURE__ */ new Set(["version", "enableMetaphor"]);
|
|
901
|
+
const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean";
|
|
816
902
|
return { data, changed };
|
|
817
903
|
}
|
|
818
904
|
function isRecord(value) {
|
|
819
905
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
820
906
|
}
|
|
821
907
|
|
|
822
|
-
// ../../packages/
|
|
908
|
+
// ../../packages/core-infra/src/data-dir/settings/service.ts
|
|
823
909
|
function createGlobalOptionsService(deps = {}) {
|
|
824
910
|
const store2 = deps.store ?? createGlobalOptionsStore({ dataDir: deps.dataDir });
|
|
825
911
|
return {
|
|
@@ -835,7 +921,7 @@ function updateGlobalOptions(store2, mutate) {
|
|
|
835
921
|
return store2.update(mutate);
|
|
836
922
|
}
|
|
837
923
|
|
|
838
|
-
// ../../packages/
|
|
924
|
+
// ../../packages/core-infra/src/index.ts
|
|
839
925
|
function createInfraServices(deps = {}) {
|
|
840
926
|
const globalOptionsService = createGlobalOptionsService();
|
|
841
927
|
const authService = createAuthService({ authPath: deps.authPath ?? DEFAULT_AUTH_PATH });
|
|
@@ -17782,12 +17868,7 @@ function killProcess(child, forceTimeoutMs = 3e3) {
|
|
|
17782
17868
|
markIntentionalKill(child);
|
|
17783
17869
|
if (isWindows()) {
|
|
17784
17870
|
try {
|
|
17785
|
-
execSync(`taskkill /PID ${child.pid} /T /F`, {
|
|
17786
|
-
stdio: "pipe",
|
|
17787
|
-
timeout: 5e3,
|
|
17788
|
-
// 콘솔 없는 호스트에서 taskkill 호출 시 새 콘솔 창이 깜빡이는 것을 방지한다.
|
|
17789
|
-
windowsHide: true
|
|
17790
|
-
});
|
|
17871
|
+
execSync(`taskkill /PID ${child.pid} /T /F`, withHidden({ stdio: "pipe", timeout: 5e3 }));
|
|
17791
17872
|
} catch {
|
|
17792
17873
|
child.kill("SIGKILL");
|
|
17793
17874
|
}
|
|
@@ -17890,17 +17971,12 @@ var BaseConnection = class extends EventEmitter {
|
|
|
17890
17971
|
const child = isWindows() ? spawn(
|
|
17891
17972
|
this.env.ComSpec ?? "cmd.exe",
|
|
17892
17973
|
buildWindowsCmdArgs(this.command, this.args),
|
|
17893
|
-
{
|
|
17974
|
+
withHidden({
|
|
17894
17975
|
cwd: this.cwd,
|
|
17895
17976
|
stdio: ["pipe", "pipe", "pipe"],
|
|
17896
17977
|
env: this.env,
|
|
17897
|
-
windowsVerbatimArguments: true
|
|
17898
|
-
|
|
17899
|
-
// Windows가 새 콘솔 창을 할당해 깜빡인다. stdio는 모두 pipe라 가시 콘솔이
|
|
17900
|
-
// 불필요하므로 CREATE_NO_WINDOW로 창 생성을 억제한다. (fleet-cli처럼 콘솔이
|
|
17901
|
-
// 이미 있는 경우에도 무해하다.)
|
|
17902
|
-
windowsHide: true
|
|
17903
|
-
}
|
|
17978
|
+
windowsVerbatimArguments: true
|
|
17979
|
+
})
|
|
17904
17980
|
) : spawn(this.command, this.args, {
|
|
17905
17981
|
cwd: this.cwd,
|
|
17906
17982
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -18781,35 +18857,14 @@ function createIdleTimeoutRace(promise2, idleMs, label) {
|
|
|
18781
18857
|
});
|
|
18782
18858
|
return [wrapped, resetTimer];
|
|
18783
18859
|
}
|
|
18860
|
+
|
|
18861
|
+
// ../../packages/core-unified-agent/src/utils/npx.ts
|
|
18784
18862
|
function resolveNpxPath(env) {
|
|
18785
|
-
const
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
const result = execSync(whichCmd, {
|
|
18789
|
-
encoding: "utf-8",
|
|
18790
|
-
stdio: "pipe",
|
|
18791
|
-
timeout: 5e3,
|
|
18792
|
-
env,
|
|
18793
|
-
// 콘솔 없는 호스트에서 `where npx` 실행 시 콘솔 창이 깜빡이는 것을 방지한다.
|
|
18794
|
-
windowsHide: true
|
|
18795
|
-
}).trim();
|
|
18796
|
-
const candidates = result.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
18797
|
-
if (windows) {
|
|
18798
|
-
const executable = candidates.find((p) => /\.(cmd|bat|exe)$/i.test(p));
|
|
18799
|
-
if (executable) {
|
|
18800
|
-
return executable;
|
|
18801
|
-
}
|
|
18802
|
-
}
|
|
18803
|
-
if (candidates.length > 0) {
|
|
18804
|
-
return candidates[0];
|
|
18805
|
-
}
|
|
18806
|
-
return windows ? "npx.cmd" : "npx";
|
|
18807
|
-
} catch {
|
|
18808
|
-
if (windows) {
|
|
18809
|
-
return "npx.cmd";
|
|
18810
|
-
}
|
|
18811
|
-
return "npx";
|
|
18863
|
+
const resolved = findBinaryPath("npx", env ?? process.env);
|
|
18864
|
+
if (resolved) {
|
|
18865
|
+
return resolved;
|
|
18812
18866
|
}
|
|
18867
|
+
return isWindows() ? "npx.cmd" : "npx";
|
|
18813
18868
|
}
|
|
18814
18869
|
function buildNpxArgs(packageName, preferOffline = false) {
|
|
18815
18870
|
const args = ["--yes"];
|
|
@@ -18831,7 +18886,7 @@ var models_default = {
|
|
|
18831
18886
|
updatedAt: "2026-07-04T00:00:00Z",
|
|
18832
18887
|
providers: {
|
|
18833
18888
|
claude: {
|
|
18834
|
-
name: "Claude Code
|
|
18889
|
+
name: "Claude Code",
|
|
18835
18890
|
defaultModel: "opus[1m]",
|
|
18836
18891
|
models: [
|
|
18837
18892
|
{ modelId: "haiku", name: "Claude Haiku", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "low" } },
|
|
@@ -18844,7 +18899,7 @@ var models_default = {
|
|
|
18844
18899
|
]
|
|
18845
18900
|
},
|
|
18846
18901
|
codex: {
|
|
18847
|
-
name: "
|
|
18902
|
+
name: "Codex",
|
|
18848
18903
|
defaultModel: "gpt-5.4",
|
|
18849
18904
|
models: [
|
|
18850
18905
|
{ modelId: "gpt-5.4", name: "GPT-5.4", effort: { supported: true, levels: ["low", "medium", "high", "xhigh"], default: "high" } },
|
|
@@ -19309,25 +19364,23 @@ var CliDetector = class {
|
|
|
19309
19364
|
* @returns 사용 가능 여부
|
|
19310
19365
|
*/
|
|
19311
19366
|
async isCliAvailable(command2) {
|
|
19312
|
-
const
|
|
19313
|
-
|
|
19314
|
-
await this.execCommand(whichCommand, [command2], 3e3);
|
|
19367
|
+
const resolved = findBinaryPath(command2, process.env);
|
|
19368
|
+
if (resolved !== void 0) {
|
|
19315
19369
|
return true;
|
|
19316
|
-
}
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
}
|
|
19370
|
+
}
|
|
19371
|
+
if (process.platform === "win32") {
|
|
19372
|
+
try {
|
|
19373
|
+
await this.execCommand(
|
|
19374
|
+
"powershell",
|
|
19375
|
+
["-NoProfile", "-Command", `Get-Command -All ${command2}`],
|
|
19376
|
+
5e3
|
|
19377
|
+
);
|
|
19378
|
+
return true;
|
|
19379
|
+
} catch {
|
|
19380
|
+
return false;
|
|
19328
19381
|
}
|
|
19329
|
-
return false;
|
|
19330
19382
|
}
|
|
19383
|
+
return false;
|
|
19331
19384
|
}
|
|
19332
19385
|
/**
|
|
19333
19386
|
* CLI의 버전을 감지합니다.
|
|
@@ -19351,13 +19404,8 @@ var CliDetector = class {
|
|
|
19351
19404
|
* @returns 전체 경로 또는 커맨드 이름
|
|
19352
19405
|
*/
|
|
19353
19406
|
async getCliPath(command2) {
|
|
19354
|
-
const
|
|
19355
|
-
|
|
19356
|
-
const result = await this.execCommand(whichCommand, [command2], 3e3);
|
|
19357
|
-
return result.split(/\r?\n/)[0].trim();
|
|
19358
|
-
} catch {
|
|
19359
|
-
return command2;
|
|
19360
|
-
}
|
|
19407
|
+
const resolved = findBinaryPath(command2, process.env);
|
|
19408
|
+
return resolved ?? command2;
|
|
19361
19409
|
}
|
|
19362
19410
|
/**
|
|
19363
19411
|
* 외부 명령 실행 결과(stdout)를 문자열로 반환합니다.
|
|
@@ -19367,11 +19415,7 @@ var CliDetector = class {
|
|
|
19367
19415
|
execFile(
|
|
19368
19416
|
command2,
|
|
19369
19417
|
args,
|
|
19370
|
-
{
|
|
19371
|
-
encoding: "utf-8",
|
|
19372
|
-
timeout,
|
|
19373
|
-
windowsHide: true
|
|
19374
|
-
},
|
|
19418
|
+
withHidden({ encoding: "utf-8", timeout }),
|
|
19375
19419
|
(error51, stdout) => {
|
|
19376
19420
|
if (error51) {
|
|
19377
19421
|
reject(error51);
|
|
@@ -22363,75 +22407,6 @@ function assertNoAuthorizationHeader(config2) {
|
|
|
22363
22407
|
throw new Error(`Builtin external MCP server "${config2.name}" must not define headers.`);
|
|
22364
22408
|
}
|
|
22365
22409
|
}
|
|
22366
|
-
var DEFAULT_WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
22367
|
-
var WINDOWS_PATH_SEPARATOR = ";";
|
|
22368
|
-
var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat"]);
|
|
22369
|
-
var CMD_EXPANSION_SENSITIVE_PATTERN = /[%^]/;
|
|
22370
|
-
function resolvePathBinary(command2, env, options = {}) {
|
|
22371
|
-
const platform = options.platform ?? process.platform;
|
|
22372
|
-
const isWindows2 = platform === "win32";
|
|
22373
|
-
const pathValue = isWindows2 ? env.Path ?? env.PATH ?? "" : env.PATH ?? "";
|
|
22374
|
-
const pathExts = isWindows2 ? parsePathExt(env.PATHEXT) : [""];
|
|
22375
|
-
const resolved = findOnPath(command2, pathValue, pathExts, platform);
|
|
22376
|
-
return resolved ? wrapWindowsShim(resolved, env, platform) : void 0;
|
|
22377
|
-
}
|
|
22378
|
-
function wrapWindowsShim(resolved, env, platform) {
|
|
22379
|
-
if (platform !== "win32" || !WINDOWS_SHIM_EXTENSIONS.has(path25__default.extname(resolved).toLowerCase())) {
|
|
22380
|
-
return { bin: resolved, prefixArgs: [] };
|
|
22381
|
-
}
|
|
22382
|
-
rejectCmdExpansionSensitiveShim(resolved);
|
|
22383
|
-
return {
|
|
22384
|
-
bin: env.ComSpec ?? "cmd.exe",
|
|
22385
|
-
prefixArgs: ["/d", "/s", "/c", "call", forceWindowsArgQuoting(resolved)]
|
|
22386
|
-
};
|
|
22387
|
-
}
|
|
22388
|
-
function rejectCmdExpansionSensitiveShim(resolved) {
|
|
22389
|
-
if (CMD_EXPANSION_SENSITIVE_PATTERN.test(resolved)) {
|
|
22390
|
-
throw new Error(`Refusing to run Windows shim path with cmd.exe expansion-sensitive characters (% or ^): ${resolved}`);
|
|
22391
|
-
}
|
|
22392
|
-
}
|
|
22393
|
-
function forceWindowsArgQuoting(value) {
|
|
22394
|
-
return `${value} `;
|
|
22395
|
-
}
|
|
22396
|
-
function findOnPath(bin, pathValue, pathExts, platform) {
|
|
22397
|
-
const pathSeparator = platform === "win32" ? WINDOWS_PATH_SEPARATOR : path25__default.delimiter;
|
|
22398
|
-
if (hasPathSeparator(bin, platform) || path25__default.isAbsolute(bin)) {
|
|
22399
|
-
return resolveWithExtensions(bin, pathExts, platform);
|
|
22400
|
-
}
|
|
22401
|
-
for (const entry of pathValue.split(pathSeparator)) {
|
|
22402
|
-
const searchDir = entry.length === 0 ? "." : entry;
|
|
22403
|
-
const candidate = resolveWithExtensions(path25__default.join(searchDir, bin), pathExts, platform);
|
|
22404
|
-
if (candidate) {
|
|
22405
|
-
return candidate;
|
|
22406
|
-
}
|
|
22407
|
-
}
|
|
22408
|
-
return void 0;
|
|
22409
|
-
}
|
|
22410
|
-
function resolveWithExtensions(candidate, pathExts, platform) {
|
|
22411
|
-
if (platform !== "win32" || path25__default.extname(candidate).length > 0) {
|
|
22412
|
-
return existsSync(candidate) ? candidate : void 0;
|
|
22413
|
-
}
|
|
22414
|
-
for (const ext of pathExts) {
|
|
22415
|
-
if (ext.length === 0) {
|
|
22416
|
-
continue;
|
|
22417
|
-
}
|
|
22418
|
-
const withExt = `${candidate}${ext}`;
|
|
22419
|
-
if (existsSync(withExt)) {
|
|
22420
|
-
return withExt;
|
|
22421
|
-
}
|
|
22422
|
-
}
|
|
22423
|
-
return existsSync(candidate) ? candidate : void 0;
|
|
22424
|
-
}
|
|
22425
|
-
function hasPathSeparator(value, platform) {
|
|
22426
|
-
if (value.includes("/")) {
|
|
22427
|
-
return true;
|
|
22428
|
-
}
|
|
22429
|
-
return platform === "win32" && value.includes("\\");
|
|
22430
|
-
}
|
|
22431
|
-
function parsePathExt(pathExt) {
|
|
22432
|
-
const raw = pathExt && pathExt.trim().length > 0 ? pathExt : DEFAULT_WINDOWS_PATHEXT;
|
|
22433
|
-
return raw.split(WINDOWS_PATH_SEPARATOR).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
22434
|
-
}
|
|
22435
22410
|
|
|
22436
22411
|
// ../../packages/core-agent/src/internal/session-errors.ts
|
|
22437
22412
|
var DEAD_SESSION_PATTERNS = [
|
|
@@ -23572,7 +23547,6 @@ __export(state_io_exports, {
|
|
|
23572
23547
|
withStoreLock: () => withStoreLock
|
|
23573
23548
|
});
|
|
23574
23549
|
var FILENAME = "carriers.json";
|
|
23575
|
-
var DEFAULT_STORE_DIRNAME = ".fleet";
|
|
23576
23550
|
var STALE_SUBAGENT_MODE_FILENAME = "carrier-subagent.json";
|
|
23577
23551
|
var LOCK_DIRNAME = "carriers.json.lock";
|
|
23578
23552
|
var LOCK_OWNER_FILENAME = "owner.json";
|
|
@@ -23582,10 +23556,11 @@ var runtimeState = {
|
|
|
23582
23556
|
lastLocalWriteFingerprint: null
|
|
23583
23557
|
};
|
|
23584
23558
|
function initStore(dir) {
|
|
23585
|
-
|
|
23586
|
-
|
|
23559
|
+
const resolvedDir = dir ?? getFleetDataDir();
|
|
23560
|
+
runtimeState.storeDir = resolvedDir;
|
|
23561
|
+
ensureSafeDirectory(resolvedDir);
|
|
23587
23562
|
withStoreLock(() => {
|
|
23588
|
-
unlinkStaleSubagentModeFile(
|
|
23563
|
+
unlinkStaleSubagentModeFile(resolvedDir);
|
|
23589
23564
|
});
|
|
23590
23565
|
}
|
|
23591
23566
|
function resetStoreForTests() {
|
|
@@ -23597,14 +23572,12 @@ function readRawCarriers() {
|
|
|
23597
23572
|
return readCarriers();
|
|
23598
23573
|
}
|
|
23599
23574
|
function readRawCarriersOrDefaultStore() {
|
|
23600
|
-
return
|
|
23575
|
+
return readCarriers();
|
|
23601
23576
|
}
|
|
23602
23577
|
function getCarriersFilePath() {
|
|
23603
|
-
|
|
23604
|
-
return path25.join(runtimeState.storeDir, FILENAME);
|
|
23578
|
+
return path25.join(resolveStoreDir(), FILENAME);
|
|
23605
23579
|
}
|
|
23606
23580
|
function updateCarriers(mutator) {
|
|
23607
|
-
if (!runtimeState.storeDir) return;
|
|
23608
23581
|
withStoreLock(() => {
|
|
23609
23582
|
const carriers = readCarriers();
|
|
23610
23583
|
const snapshot = structuredClone(carriers);
|
|
@@ -23614,20 +23587,19 @@ function updateCarriers(mutator) {
|
|
|
23614
23587
|
});
|
|
23615
23588
|
}
|
|
23616
23589
|
function withStoreLock(operation) {
|
|
23617
|
-
|
|
23618
|
-
|
|
23619
|
-
const lockDir = path25.join(
|
|
23590
|
+
const dir = resolveStoreDir();
|
|
23591
|
+
ensureSafeDirectory(dir);
|
|
23592
|
+
const lockDir = path25.join(dir, LOCK_DIRNAME);
|
|
23620
23593
|
return withDirectoryLock(
|
|
23621
23594
|
{ lockDir, ownerFileName: LOCK_OWNER_FILENAME },
|
|
23622
23595
|
operation
|
|
23623
23596
|
);
|
|
23624
23597
|
}
|
|
23625
|
-
function
|
|
23626
|
-
|
|
23627
|
-
return readCarriersFromDir(runtimeState.storeDir);
|
|
23598
|
+
function resolveStoreDir() {
|
|
23599
|
+
return runtimeState.storeDir ?? getFleetDataDir();
|
|
23628
23600
|
}
|
|
23629
|
-
function
|
|
23630
|
-
return
|
|
23601
|
+
function readCarriers() {
|
|
23602
|
+
return readCarriersFromDir(resolveStoreDir());
|
|
23631
23603
|
}
|
|
23632
23604
|
function readCarriersFromDir(dir) {
|
|
23633
23605
|
const filePath = path25.join(dir, FILENAME);
|
|
@@ -23658,9 +23630,8 @@ function recordLastLocalWriteFingerprint(filePath, generation) {
|
|
|
23658
23630
|
}
|
|
23659
23631
|
}
|
|
23660
23632
|
function writeCarriers(s) {
|
|
23661
|
-
|
|
23662
|
-
|
|
23663
|
-
const filePath = path25.join(runtimeState.storeDir, FILENAME);
|
|
23633
|
+
const dir = resolveStoreDir();
|
|
23634
|
+
const filePath = path25.join(dir, FILENAME);
|
|
23664
23635
|
const next = serializeFleetCarriers(s);
|
|
23665
23636
|
writeAtomicSync(filePath, `${JSON.stringify(next, null, 2)}
|
|
23666
23637
|
`, { fsync: false, mode: 420 });
|
|
@@ -25567,8 +25538,8 @@ function Create(hidden, enumerable, options = {}) {
|
|
|
25567
25538
|
Metrics.create += 1;
|
|
25568
25539
|
const settings2 = settings_exports.Get();
|
|
25569
25540
|
const withOptions = Merge(enumerable, options);
|
|
25570
|
-
const
|
|
25571
|
-
return settings2.immutableTypes ? Object.freeze(
|
|
25541
|
+
const withHidden2 = settings2.enumerableKind ? Merge(withOptions, hidden) : MergeHidden(withOptions, hidden);
|
|
25542
|
+
return settings2.immutableTypes ? Object.freeze(withHidden2) : withHidden2;
|
|
25572
25543
|
}
|
|
25573
25544
|
|
|
25574
25545
|
// ../../node_modules/.pnpm/typebox@1.1.34/node_modules/typebox/build/system/memory/discard.mjs
|
|
@@ -33764,7 +33735,7 @@ async function directoryExists(dirPath) {
|
|
|
33764
33735
|
|
|
33765
33736
|
// core/host/security-headers.ts
|
|
33766
33737
|
var CONSOLE_SECURITY_HEADERS = {
|
|
33767
|
-
"Content-Security-Policy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data
|
|
33738
|
+
"Content-Security-Policy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://img.shields.io; font-src 'self' data:; connect-src 'self' ws: wss: https://api.github.com; object-src 'none'; base-uri 'none'",
|
|
33768
33739
|
"X-Content-Type-Options": "nosniff",
|
|
33769
33740
|
"Referrer-Policy": "no-referrer",
|
|
33770
33741
|
"Cache-Control": "no-store"
|
|
@@ -35945,7 +35916,7 @@ async function preflightPackageManager(packageRoot, env) {
|
|
|
35945
35916
|
}
|
|
35946
35917
|
function spawnDetachedWorker(spawnWorker, execPath, args, env) {
|
|
35947
35918
|
return new Promise((resolve2, reject) => {
|
|
35948
|
-
const child = spawnWorker(execPath, args, { detached: true, env, stdio: "ignore"
|
|
35919
|
+
const child = spawnWorker(execPath, args, withHidden({ detached: true, env, stdio: "ignore" }));
|
|
35949
35920
|
let settled = false;
|
|
35950
35921
|
child.once("error", (error51) => {
|
|
35951
35922
|
if (settled) return;
|
|
@@ -36149,8 +36120,6 @@ function createConsoleServer(deps = {}) {
|
|
|
36149
36120
|
const operations = createOperationStore();
|
|
36150
36121
|
const folderGrants = createFolderGrantStore();
|
|
36151
36122
|
createInfraServices();
|
|
36152
|
-
const dataDir = deps.dataDir ?? getFleetDataDir();
|
|
36153
|
-
initStore(dataDir);
|
|
36154
36123
|
const durablePaths = createConsoleDataPaths({ fleetDataDir: deps.dataDir });
|
|
36155
36124
|
const durableStateStore = createConsoleDurableStateStore({ paths: durablePaths });
|
|
36156
36125
|
const consoleSettingsStore = createConsoleSettingsStore({ paths: durablePaths });
|
|
@@ -36237,7 +36206,6 @@ function createConsoleServer(deps = {}) {
|
|
|
36237
36206
|
registerSseChannel: () => () => void 0
|
|
36238
36207
|
},
|
|
36239
36208
|
paths: {
|
|
36240
|
-
dataDir: durablePaths.dir,
|
|
36241
36209
|
capturesDir: durablePaths.capturesDir,
|
|
36242
36210
|
pluginDataDir: (pluginId) => path25__default.join(durablePaths.dir, "plugins", pluginId),
|
|
36243
36211
|
resolveTheaterPath: (theaterId) => theaters.get(theaterId)?.path ?? null,
|
|
@@ -37240,7 +37208,7 @@ function createConsoleDaemonLifecycle(deps = {}) {
|
|
|
37240
37208
|
if (typeof probeResult.health?.workspaceCount === "number" && probeResult.health.workspaceCount > 0) return current.endpoint;
|
|
37241
37209
|
}
|
|
37242
37210
|
if (current) await stop();
|
|
37243
|
-
spawnDetached(execPath, [serverModulePath, "serve"], { detached: true, env, stdio: "ignore"
|
|
37211
|
+
spawnDetached(execPath, [serverModulePath, "serve"], withHidden({ detached: true, env, stdio: "ignore" }));
|
|
37244
37212
|
for (let i = 0; i < 30; i += 1) {
|
|
37245
37213
|
await sleep(100);
|
|
37246
37214
|
const next = await probe();
|
|
Binary file
|