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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions server/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ The barrel `server/lib/index.js` is a machine-checkable enumeration of every pub
| `fleetQuotas.js` | Unifies subscription-quota readings across federated instances — one plan, several machines, each able to read only its own local CLI. `sanitizeQuotaCards` bounds a peer-supplied payload to the wire shape; `mergeFleetQuotaCards(localCards, peerEntries)` folds every peer's reading into this install's cards, taking the FRESHEST reading per limit key (the meters are account-wide, so summing them would multiply one allowance) and SUMMING activity counts (those are per-machine, which is why the provider captions them "does not include other devices"); `fleetNote` writes the caption naming what was combined. `metrics[]` is left local — its values are prose, not addends. Fed by `services/providerQuotaShare.js` (this machine's readings, persisted) and `services/peerUsage.js` (the `usage` sync category that carries them). |
| `harnessOutput.js` | Parsers for what a coding-agent HARNESS prints about itself: `parseHarnessVersion(stdout)` (the one semver run in a `--version` banner, `null` when unparseable), `compareHarnessVersions(a, b)` (the null-guarding wrapper around `versionUtils.js#compareSemver` — `null` when either side is unparseable, so a version that did not parse never reads as "out of date"), `parseHarnessModels(harnessId, stdout)` + `HARNESS_MODEL_PARSER_IDS` (OpenCode's `provider/model` lines and Grok's bulleted list are parsed here; Antigravity and Cursor DELEGATE to `antigravity.js#parseAntigravityModelList` / `aiToolkit/internal/cursor.js#parseCursorModelList`, which the provider-card refresh has used for far longer), `MAX_MODELS`, and `parseNpmLatestVersion`. Pure: the service layer runs the child and hands the captured stdout here, so the vendor output shapes are pinned by table-driven tests instead of by running six real binaries in CI. Model ids come back in the exact spelling `--model` takes — namespaces kept where the vendor keeps them. Consumed by `services/providerRuntimeInstaller.js` and `services/harnesses.js`. |
| `providerGateways.js` | `PROVIDER_GATEWAYS` — one row per hosted OpenAI-compatible gateway an OpenCode CLI/TUI wrapper can front-end (`orcarouter`, `openrouter`), plus `PROVIDER_GATEWAY_IDS`, `gatewayById`, `isGatewayNamespace(ns)` and `gatewayForProvider(config)` → row or null. Each row's `id` is simultaneously the OpenCode provider namespace, the `gatewayBacked` marker value, and the id of the sibling `api` record that owns the key — so the sibling lookup is `providers[gateway.id]` and an OrcaRouter key can never satisfy an OpenRouter wrapper. Replaces the `orcarouterBacked` boolean + literal `'orcarouter'` that had been hand-copied across ~15 server and client files (namespace resolution, the OpenCode config builder, both zod schemas, the model-fetcher table, the sibling-key attach, the prerequisite check, and the two "not a local runtime" carve-outs in `cliChildEnv.js`/`localProviderRuntime.js`). Reads the legacy per-gateway boolean FOREVER, so stored records are never rewritten. Distinct from a local runtime (`ollamaBacked`, `vllmBacked`, …): remote, always authenticating, and no thinking toggle. Deliberately mirrored in `aiToolkit/internal/gateways.js` (the vendored toolkit may not import out) and `client/src/utils/providerGateways.js` (the browser cannot import server code) — `providerGateways.parity.test.js` fails when the first two drift. Dependency-light: imports nothing. |
| `providerHarnesses.js` | Provider HARNESS identity for the connection graph (#6366) — `PROVIDER_HARNESSES` (one row per agent program PortOS drives: claude/opencode/codex/antigravity/cursor/grok/kimi/pi, each with its supported `modes`, wire `protocol` and the existing `is*Provider` predicate as its matcher), `PROVIDER_HARNESS_IDS`, `harnessById`, `harnessForProvider` (null for an `api` record AND for an unknown harness — use `providerRouteMode` to tell them apart), `harnessSupportsMode`, `ROUTE_MODES`, plus the model-name adapters `toExecutableModelName` / `toCanonicalModelName` (the verified inverse of `prefixOpencodeModel`: a stored alias is canonicalized only when it maps back to the exact stored string, so import never rewrites a saved model by heuristically stripping a prefix). Distinct from `providerVendors.js` (argv-shaped), `providerFamilies.js` (subscription quota) and `providerGateways.js` (hosted backend). |
| `providerConnections.js` | Provider CONNECTION identity for the connection graph (#6366) — `providerConnectionProfile(provider)` reads one record as the backend it describes (`kind`, `protocol`, `transports`, RAW server-only `credentials`, the `owned` field/env split, and `reasons` from `CONNECTION_ISOLATION_REASONS` when the config is dynamic, external, unparsable or endpoint-less). `sameConnectionIdentity` compares real credential values and refuses once any is the `REDACTED_CREDENTIAL` sentinel, so a sanitized record can never merge two connections; `compareBackendEndpoints` is the looser same-daemon test that only ever produces a link SUGGESTION, applying the recognized OpenAI-compatible `/v1` conversion (never DNS resolution, loopback/remote equivalence or arbitrary path trimming). `withoutConnectionOwnedFields` / `withConnectionOwnedFields` are the lossless split/materialize pair that keeps a downgraded install runnable from `data/providers.json` alone. Pure: no I/O. |
| `providerGraphPreview.js` | READ-ONLY import preview for the provider connection graph (#6366) — `buildProviderGraphPreview({providers, activeProvider})` projects the records an install already runs into proposed connections, harness bindings and executable routes, grouping ONLY the proven same-harness CLI/TUI siblings from `providerModeGroups` and surfacing a cross-harness daemon match as a `suggestedLinks` entry requiring an explicit link. `toManagementPreviewDto` sanitizes and validates the version-1 DTOs (`managementPreviewSchema`, `PROVIDER_GRAPH_SCHEMA_VERSION`) that `GET /api/providers/management/preview` returns; `projectPreviewToProviders` re-materializes every original record (pins, secrets, consent flags and unknown custom fields included) as the import-fidelity and downgrade proof; `providerGraphUniquenessViolations` checks the UNIQUE constraints #6367 will enforce; `routeModeEligibility` is the pure declarative half of mode eligibility (harness support + enabled + caller-allowed mode + text-transport consent), with the prerequisite/fallback policy left to #6368. Persists nothing and makes no generation call or runtime launch. |
| `providerTranscriptUsage.js` | Parsers for the session files the coding CLIs write to disk (0 tokens to read) — `parseClaudeTranscript` (`~/.claude/projects/<cwd-slug>/*.jsonl`), `parseCodexRollout` (`~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`), `parseGrokTurns`/`parseGrokChatHistory`/`decodeGrokSessionDir` (`~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/`), `parseAgyTranscript`/`parseAgyHistory` (`~/.gemini/antigravity-cli/`), `claudeProjectSlug`, `totalTranscriptTokens`. Each de-duplicates a format hazard that otherwise inflates counts badly: Claude repeats one response across several lines sharing a `message.id`, Codex's `total_token_usage` is cumulative and repeated, grok's `turn_completed.usage` has shipped in both per-prompt and cumulative shapes (detected and delta'd, never summed raw) while its `_meta.totalTokens` is context occupancy and never billed. Antigravity writes no token fields at all, so its parser returns chars for the caller to estimate from. Each parser returns per-model buckets (`byModel`) plus the message keys it counted (`countedKeys`), and accepts an `exclude` set — that is what stops two overlapping PortOS runs from both billing the same messages. Tolerant of truncated (mid-write) files; consumed by `services/usageReconciler.js`. |
| `opencodeCatalogCache.js` | Primes the on-disk catalog `opencode models` prints from — `primeOpencodeCatalogCache()` fetches OpenCode's `api.json` with Node's fetch and atomically writes `$XDG_CACHE_HOME/opencode/models.json` (`~/.cache` when unset). OpenCode refreshes that file from a forked task whose failures it swallows (`opencode models --refresh` still prints `Models cache refreshed`) and its HTTP client has no Happy Eyeballs, so a host advertising an unreachable IPv6 default route freezes the catalog indefinitely while other machines on the same account list newer models. Refuses to fetch or write when `OPENCODE_MODELS_PATH` / a custom `OPENCODE_MODELS_URL` / `OPENCODE_DISABLE_MODELS_FETCH` means PortOS cannot be sure which file OpenCode reads, when the file is under five minutes old, or when the body did not parse as a catalog — a stale list beats an empty picker. Never throws; the caller probes either way. |
| `opencodeConfig.js` | OpenCode config builder — `buildOpencodeEnvVars(provider, model)` builds dynamic `OPENCODE_CONFIG_CONTENT` declaring model ids under the namespace the provider's marker selects: a local runtime (`ollama` / `mtplx` / `llama` / `vllm` / `sglang`, bare ids) or a hosted gateway from `providerGateways.js` (`vendor/model` ids kept whole). Fixes --model rejection. Also attaches the key for a key-bearing namespace, and pins `small_model` to the run model for a gateway so OpenCode's own side calls (titles, summarization) can't land on its built-in default — a billed model the operator never chose. Under a `no-tool` public-review profile it also applies `hardenOpencodeConfigForNoTool` — root `permission: deny`, an emptied tool map on every agent, `tool_call: false` on every declared model, and no MCP/plugins/share/autoupdate — which IS OpenCode's enforced tool-free recipe, since it ships no read-only argv flag (`providerVendors.js` pairs it with `run --agent` + `OPENCODE_PUBLIC_REVIEW_AGENT`). The harden step also copies `agent.build`'s generation settings onto that agent, so the stage's configured thinking effort reaches the model that actually runs. |
Expand Down
12 changes: 10 additions & 2 deletions server/lib/apiRouteCatalog.generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -13984,6 +13984,14 @@
"server/routes/providers.js"
]
},
{
"method": "GET",
"path": "/api/providers/management/preview",
"mountPath": "/api/providers",
"sources": [
"server/routes/providers.js"
]
},
{
"method": "POST",
"path": "/api/providers/opencode/install",
Expand Down Expand Up @@ -17788,8 +17796,8 @@
],
"stats": {
"mounts": 150,
"operations": 2203,
"declarations": 2211,
"operations": 2204,
"declarations": 2212,
"sourceFiles": 233
}
}
3 changes: 3 additions & 0 deletions server/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,9 @@ export * from './providerFamilies.js';
export * from './fleetQuotas.js';
export * from './harnessOutput.js';
export * from './providerGateways.js';
export * from './providerHarnesses.js';
export * from './providerConnections.js';
export * from './providerGraphPreview.js';
export * from './personaTraitBlend.js';
export * from './pipelineIssueOrder.js';
export * from './postAdaptive.js';
Expand Down
Loading