@crvouga/dev-fleet 0.0.0-stage → 0.2.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/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
- package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
- package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
- package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
- package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
- package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
- package/LICENSE +21 -0
- package/README.md +357 -2
- package/agents/antigravity-proxy.ts +3 -0
- package/agents/audit.md +89 -0
- package/agents/claude-proxy +5 -0
- package/agents/claude-proxy.ts +3 -0
- package/agents/codex-proxy.ts +3 -0
- package/agents/grok-proxy.ts +3 -0
- package/agents/managed-proxy.ts +55 -0
- package/agents/opencode-proxy.ts +3 -0
- package/agents/registry.json +209 -0
- package/agents/registry.schema.json +149 -0
- package/agents/setup.md +158 -0
- package/agents/t3.json +40 -0
- package/ansible/macos/manager.yml +30 -0
- package/ansible/macos/policy.json +9 -0
- package/ansible/monitoring/README.md +215 -0
- package/ansible/monitoring/agent-darwin.yml +198 -0
- package/ansible/monitoring/agent.yml +172 -0
- package/ansible/monitoring/beszel-agent.service +42 -0
- package/ansible/monitoring/compose.yml +74 -0
- package/ansible/monitoring/hub-darwin.yml +102 -0
- package/ansible/monitoring/hub-route-darwin.yml +51 -0
- package/ansible/monitoring/lock.json +16 -0
- package/ansible/net/remote-probe.sh +13 -0
- package/ansible/pyproject.toml +12 -0
- package/ansible/ubuntu/authorize-sudo.sh +51 -0
- package/ansible/ubuntu/baseline.yml +89 -0
- package/ansible/ubuntu/disk-gc.py +336 -0
- package/ansible/ubuntu/disk-guard.py +104 -0
- package/ansible/ubuntu/policy.json +52 -0
- package/ansible/ubuntu/recovery.yml +15 -0
- package/ansible/ubuntu/t3/policy.json +7 -0
- package/ansible/ubuntu/t3-desktop.yml +220 -0
- package/ansible/ubuntu/t3-health.sh +33 -0
- package/ansible/ubuntu/t3-policy.yml +76 -0
- package/ansible/ubuntu/t3-route.yml +28 -0
- package/ansible/ubuntu/tailscale.yml +78 -0
- package/ansible/ubuntu/tasks/credentials.yml +47 -0
- package/ansible/ubuntu/tasks/docker.yml +31 -0
- package/ansible/ubuntu/tasks/packages.yml +56 -0
- package/ansible/ubuntu/tasks/power.yml +51 -0
- package/ansible/ubuntu/tasks/recovery.yml +39 -0
- package/ansible/ubuntu/tasks/resources.yml +312 -0
- package/ansible/ubuntu/tasks/services.yml +31 -0
- package/ansible/ubuntu/tasks/ssh.yml +71 -0
- package/ansible/ubuntu/tasks/user.yml +120 -0
- package/ansible/uv.lock +190 -0
- package/bin/devfleet.mjs +36 -0
- package/bootstrap/bootstrap.ps1 +50 -0
- package/bootstrap/bootstrap.sh +80 -0
- package/bootstrap/devfleet +7 -0
- package/bootstrap/docker-desktop.sh +33 -0
- package/bootstrap/doctor.ts +234 -0
- package/bootstrap/fleet-manager-run.sh +103 -0
- package/bootstrap/fleet-remote-bootstrap.sh +258 -0
- package/bootstrap/global-config.ts +171 -0
- package/bootstrap/install-launcher.sh +10 -0
- package/bootstrap/native-aws.ts +86 -0
- package/bootstrap/native-t3.ts +154 -0
- package/bootstrap/npm.ts +358 -0
- package/bootstrap/release-version.ts +157 -0
- package/bootstrap/setup-status.ts +51 -0
- package/bootstrap/setup.ts +88 -0
- package/bootstrap/shell-path.ts +105 -0
- package/bootstrap/verify-t3-archive.py +49 -0
- package/bootstrap/worker-auth.ts +65 -0
- package/bootstrap/worker-github.ts +47 -0
- package/bootstrap/worker-tools.ts +24 -0
- package/cliproxy/AGENTS.md +11 -0
- package/cliproxy/README.md +269 -0
- package/cliproxy/clients/bootstrap.py +143 -0
- package/cliproxy/clients/launcher.py +57 -0
- package/cliproxy/compose.yaml +90 -0
- package/cliproxy/config.template.yaml +70 -0
- package/cliproxy/docs/cloud-adoption.md +16 -0
- package/cliproxy/docs/deployment.png +0 -0
- package/cliproxy/docs/deployment.svg +109 -0
- package/cliproxy/docs/health-workflow.example.yml +185 -0
- package/cliproxy/docs/migration.md +131 -0
- package/cliproxy/docs/model-health-report.schema.json +364 -0
- package/cliproxy/docs/model-health.md +393 -0
- package/cliproxy/image.lock.json +5 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
- package/cliproxy/models.json +1618 -0
- package/cliproxy/requirements.lock +78 -0
- package/cliproxy/scripts/accounts.py +38 -0
- package/cliproxy/scripts/audit.py +123 -0
- package/cliproxy/scripts/backup +234 -0
- package/cliproxy/scripts/check.py +34 -0
- package/cliproxy/scripts/checkpoint.py +226 -0
- package/cliproxy/scripts/cli.py +64 -0
- package/cliproxy/scripts/cliproxy_host.py +257 -0
- package/cliproxy/scripts/cloud_state.py +150 -0
- package/cliproxy/scripts/container-health +6 -0
- package/cliproxy/scripts/docker +24 -0
- package/cliproxy/scripts/external-health.py +79 -0
- package/cliproxy/scripts/fleet_config.py +43 -0
- package/cliproxy/scripts/fleet_sync.py +274 -0
- package/cliproxy/scripts/initialize +84 -0
- package/cliproxy/scripts/install-dependencies +48 -0
- package/cliproxy/scripts/install.py +406 -0
- package/cliproxy/scripts/model_health.py +16 -0
- package/cliproxy/scripts/modelhealth/__init__.py +5 -0
- package/cliproxy/scripts/modelhealth/checks.py +429 -0
- package/cliproxy/scripts/modelhealth/cli.py +117 -0
- package/cliproxy/scripts/modelhealth/config.py +186 -0
- package/cliproxy/scripts/modelhealth/emulator.py +536 -0
- package/cliproxy/scripts/modelhealth/expect.py +88 -0
- package/cliproxy/scripts/modelhealth/findings.py +183 -0
- package/cliproxy/scripts/modelhealth/protocol.py +330 -0
- package/cliproxy/scripts/modelhealth/redact.py +28 -0
- package/cliproxy/scripts/modelhealth/report.py +150 -0
- package/cliproxy/scripts/modelhealth/runner.py +390 -0
- package/cliproxy/scripts/modelhealth/scenario.py +131 -0
- package/cliproxy/scripts/modelhealth/schema.py +63 -0
- package/cliproxy/scripts/modelhealth/simulate.py +111 -0
- package/cliproxy/scripts/modelhealth/transport.py +213 -0
- package/cliproxy/scripts/monitor +68 -0
- package/cliproxy/scripts/ops +527 -0
- package/cliproxy/scripts/public-endpoint +127 -0
- package/cliproxy/scripts/recover_node.py +177 -0
- package/cliproxy/scripts/restore-verify +60 -0
- package/cliproxy/scripts/retention.py +144 -0
- package/cliproxy/scripts/service-start +7 -0
- package/cliproxy/scripts/validate-config +56 -0
- package/cliproxy/scripts/vault-access.py +57 -0
- package/cliproxy/scripts/vault-backup-login +55 -0
- package/cliproxy/scripts/vault-recovery +56 -0
- package/cliproxy/scripts/vault-renew +30 -0
- package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
- package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
- package/cliproxy/systemd/cliproxy-backup.service +13 -0
- package/cliproxy/systemd/cliproxy-backup.timer +8 -0
- package/cliproxy/systemd/cliproxy-fleet.service +17 -0
- package/cliproxy/systemd/cliproxy-gc.service +13 -0
- package/cliproxy/systemd/cliproxy-gc.timer +8 -0
- package/cliproxy/systemd/cliproxy-monitor.service +8 -0
- package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
- package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
- package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
- package/cliproxy/vendor/LICENSE +22 -0
- package/cliproxy/vendor/config.upstream.yaml +1349 -0
- package/docs/consumer-setup.md +408 -0
- package/docs/declared.md +89 -0
- package/docs/machine-setup.md +399 -0
- package/docs/operations.md +276 -0
- package/docs/releasing.md +103 -0
- package/docs/state.md +254 -0
- package/docs/topology.md +67 -0
- package/docs/ubuntu-sync.md +351 -0
- package/docs/worker-contract.md +67 -0
- package/examples/fleet.json +100 -0
- package/examples/roster.json +13 -0
- package/llms.txt +35 -0
- package/mise.lock +1521 -0
- package/mise.toml +110 -0
- package/package.json +74 -4
- package/schema/fleet.schema.json +420 -0
- package/schema/roster.schema.json +77 -0
- package/src/agents/account-relay.ts +111 -0
- package/src/agents/accounts.ts +85 -0
- package/src/agents/audit.ts +77 -0
- package/src/agents/cli.ts +13 -0
- package/src/agents/command.ts +204 -0
- package/src/agents/converge.ts +123 -0
- package/src/agents/doctor.ts +107 -0
- package/src/agents/errors.ts +2 -0
- package/src/agents/files.ts +73 -0
- package/src/agents/fleet-verify.ts +165 -0
- package/src/agents/launch.ts +290 -0
- package/src/agents/native-verify.ts +371 -0
- package/src/agents/provider-errors.ts +57 -0
- package/src/agents/registry.ts +61 -0
- package/src/agents/render.ts +110 -0
- package/src/agents/round-trip.ts +192 -0
- package/src/agents/secrets.ts +13 -0
- package/src/agents/toolchain.ts +34 -0
- package/src/agents/verify-cache.ts +103 -0
- package/src/agents/verify.ts +127 -0
- package/src/auth-checks.ts +163 -0
- package/src/cli/agent-guide.ts +129 -0
- package/src/cli/app.ts +122 -0
- package/src/cli/cli.ts +28 -0
- package/src/cli/command-index.ts +57 -0
- package/src/cli/contracts.ts +25 -0
- package/src/cli/fleet-reference.ts +17 -0
- package/src/cli/fleet-state.ts +499 -0
- package/src/cli/fleet-sync-checkout.ts +130 -0
- package/src/cli/fleet-sync-enrollment.ts +140 -0
- package/src/cli/fleet-sync-forward.ts +134 -0
- package/src/cli/fleet-sync-manager.ts +249 -0
- package/src/cli/fleet-sync-monitoring.ts +185 -0
- package/src/cli/fleet-sync-output.ts +317 -0
- package/src/cli/fleet-sync-privilege.ts +27 -0
- package/src/cli/fleet-sync-progress.ts +109 -0
- package/src/cli/fleet-sync-remote.ts +242 -0
- package/src/cli/fleet-sync-report.ts +26 -0
- package/src/cli/fleet-sync-schedule.ts +138 -0
- package/src/cli/fleet-sync-state.ts +86 -0
- package/src/cli/fleet-sync-vault.ts +166 -0
- package/src/cli/fleet-sync.ts +375 -0
- package/src/cli/fleet.ts +165 -0
- package/src/cli/managed-health.ts +219 -0
- package/src/cli/node-deployment.ts +34 -0
- package/src/cli/node-sync.ts +121 -0
- package/src/cli/node-t3.ts +31 -0
- package/src/cli/options.ts +141 -0
- package/src/cli/probe-source.ts +7 -0
- package/src/cli/program.ts +185 -0
- package/src/cli/project.ts +51 -0
- package/src/cli/register-fleet.ts +296 -0
- package/src/cli/registry.ts +185 -0
- package/src/cli/repair-apply.ts +67 -0
- package/src/cli/repair-guard.ts +11 -0
- package/src/cli/repair-inspect.ts +150 -0
- package/src/cli/repair-script.ts +76 -0
- package/src/cli/repair.ts +209 -0
- package/src/cli/roster.ts +105 -0
- package/src/cli/search.ts +35 -0
- package/src/cli/settings.ts +67 -0
- package/src/cli/source-operations.ts +102 -0
- package/src/cli/t3/command.ts +90 -0
- package/src/cli/t3/register.ts +55 -0
- package/src/cli/tailnet.ts +127 -0
- package/src/cli/ui-actions.ts +133 -0
- package/src/cli/ui-controller.ts +113 -0
- package/src/cli/ui-data.ts +106 -0
- package/src/cli/ui-execute.ts +163 -0
- package/src/cli/ui-repairs.ts +34 -0
- package/src/cli/ui-reports.ts +211 -0
- package/src/cli/ui-targets.ts +100 -0
- package/src/cli/ui-terminal.ts +109 -0
- package/src/cli/ui-types.ts +49 -0
- package/src/cli/ui-wizard.ts +101 -0
- package/src/cli/ui.ts +9 -0
- package/src/cli/worker-health-output.ts +37 -0
- package/src/config.ts +186 -0
- package/src/configure.ts +255 -0
- package/src/definition.ts +290 -0
- package/src/diagnostics.ts +92 -0
- package/src/doctor-check.ts +10 -0
- package/src/errors.ts +1 -0
- package/src/fingerprint.ts +120 -0
- package/src/fleet-config.ts +174 -0
- package/src/input-hashes.ts +73 -0
- package/src/machine/local-source.ts +52 -0
- package/src/machine/lock-error.ts +2 -0
- package/src/machine/platform.ts +44 -0
- package/src/machine/source.ts +68 -0
- package/src/machine/state.ts +173 -0
- package/src/machine-health.ts +95 -0
- package/src/managed-source.ts +47 -0
- package/src/manager-failure.ts +48 -0
- package/src/manager-sync.ts +405 -0
- package/src/manager.ts +62 -0
- package/src/monitoring/agent-evidence.ts +146 -0
- package/src/monitoring/apply.ts +183 -0
- package/src/monitoring/beszel.ts +198 -0
- package/src/monitoring/enroll.ts +111 -0
- package/src/monitoring/hub-access.ts +114 -0
- package/src/monitoring/hub-backup.ts +121 -0
- package/src/monitoring/hub-viewer.ts +142 -0
- package/src/monitoring/hub.ts +93 -0
- package/src/monitoring/manager-agent.ts +73 -0
- package/src/monitoring/manager-hub.ts +248 -0
- package/src/monitoring/remote.ts +65 -0
- package/src/network-definition.ts +83 -0
- package/src/node-converge-linux.ts +374 -0
- package/src/node-converge-shared.ts +83 -0
- package/src/node-converge.ts +40 -0
- package/src/onboard.ts +35 -0
- package/src/package-info.ts +45 -0
- package/src/platform/ansible-receipt.ts +128 -0
- package/src/platform/ansible.ts +66 -0
- package/src/platform/darwin.ts +203 -0
- package/src/platform/detect.ts +11 -0
- package/src/platform/fallback.ts +110 -0
- package/src/platform/index.ts +38 -0
- package/src/platform/launchd.ts +98 -0
- package/src/platform/linux.ts +139 -0
- package/src/platform/ps.ts +45 -0
- package/src/platform/resource-verify.ts +132 -0
- package/src/platform/systemd-user.ts +95 -0
- package/src/platform/t3-policy.ts +137 -0
- package/src/platform/t3-route.ts +68 -0
- package/src/platform/t3-service.ts +75 -0
- package/src/platform/types.ts +123 -0
- package/src/platform/ubuntu-audit.ts +66 -0
- package/src/platform/ubuntu-verify.ts +105 -0
- package/src/platform/windows.ts +148 -0
- package/src/power-recovery.ts +55 -0
- package/src/probe.ts +51 -0
- package/src/protocol.ts +119 -0
- package/src/proxy-converge.ts +85 -0
- package/src/remote-bootstrap.ts +210 -0
- package/src/remote-entry.ts +89 -0
- package/src/remote-lock.ts +12 -0
- package/src/repair-catalog.ts +206 -0
- package/src/repair-plan.ts +78 -0
- package/src/repair-runner.ts +84 -0
- package/src/report.ts +71 -0
- package/src/runner.ts +104 -0
- package/src/secrets.ts +169 -0
- package/src/service-auth.ts +115 -0
- package/src/ssh.ts +135 -0
- package/src/state/bucket.ts +208 -0
- package/src/state/check.ts +74 -0
- package/src/state/connection.ts +92 -0
- package/src/state/enrollment.ts +45 -0
- package/src/state/legacy.ts +89 -0
- package/src/state/litestream.ts +143 -0
- package/src/state/operator.ts +20 -0
- package/src/state/paths.ts +40 -0
- package/src/state/roster.ts +62 -0
- package/src/state/store.ts +169 -0
- package/src/state/sync.ts +352 -0
- package/src/sync.ts +104 -0
- package/src/t3/accounts.ts +81 -0
- package/src/t3/desired.ts +165 -0
- package/src/t3/files.ts +81 -0
- package/src/t3/names-check.ts +53 -0
- package/src/t3/names.ts +155 -0
- package/src/t3/plan.ts +82 -0
- package/src/t3/providers.ts +74 -0
- package/src/t3/reconcile.ts +123 -0
- package/src/t3-endpoint.ts +104 -0
- package/src/t3-linked-check.ts +123 -0
- package/src/tailnet-health.ts +231 -0
- package/src/tailnet-matching.ts +37 -0
- package/src/tailscale.ts +141 -0
- package/src/toolchain-checks.ts +108 -0
- package/src/topology.ts +24 -0
- package/src/transport.ts +122 -0
- package/src/types.ts +99 -0
- package/src/vault-config.ts +38 -0
- package/src/worker-access-checks.ts +168 -0
- package/src/worker-capacity.ts +34 -0
- package/src/worker-host-checks.ts +181 -0
- package/src/worker-policy.ts +64 -0
- package/src/worker-readiness.ts +64 -0
- package/src/workstation-checks.ts +29 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
name: CLI proxy health
|
|
2
|
+
# One workflow, five entry points, deliberately cheap by default:
|
|
3
|
+
# every 15 min readiness: free (no inference) + cloud checkpoint freshness
|
|
4
|
+
# hourly light: 2 tiny requests per model (about 200 requests a day)
|
|
5
|
+
# 03:41 nightly comprehensive suite (bounded by models.json budgets)
|
|
6
|
+
# pull request contract: offline, no credentials, no inference
|
|
7
|
+
# manual any profile, one model or one check, optional force-probe
|
|
8
|
+
on:
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: '7,22,37,52 * * * *'
|
|
11
|
+
- cron: '17 * * * *'
|
|
12
|
+
- cron: '41 3 * * *'
|
|
13
|
+
pull_request:
|
|
14
|
+
paths:
|
|
15
|
+
- cliproxy/**
|
|
16
|
+
- .github/workflows/cliproxy-health.yml
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
inputs:
|
|
19
|
+
profile:
|
|
20
|
+
description: readiness is free; light is the hourly check; nightly is comprehensive; manual adds wrong-key probes and fault simulations.
|
|
21
|
+
type: choice
|
|
22
|
+
default: light
|
|
23
|
+
options: [liveness, readiness, light, nightly, manual]
|
|
24
|
+
suite:
|
|
25
|
+
description: Which declared checks to run.
|
|
26
|
+
type: choice
|
|
27
|
+
default: all
|
|
28
|
+
options: [all, core, cursor]
|
|
29
|
+
model:
|
|
30
|
+
description: Optional single model id from cliproxy/models.json (partial runs never open or close the tracking issue).
|
|
31
|
+
type: string
|
|
32
|
+
default: ''
|
|
33
|
+
check:
|
|
34
|
+
description: Optional capability glob such as chat.tools or responses.* (cheap reproduction of one finding).
|
|
35
|
+
type: string
|
|
36
|
+
default: ''
|
|
37
|
+
max_requests:
|
|
38
|
+
description: Optional lower request budget for this run.
|
|
39
|
+
type: string
|
|
40
|
+
default: ''
|
|
41
|
+
force_probe:
|
|
42
|
+
description: Probe models even if they were rate limited recently (normally paused to protect quota).
|
|
43
|
+
type: boolean
|
|
44
|
+
default: false
|
|
45
|
+
permissions:
|
|
46
|
+
contents: read
|
|
47
|
+
concurrency:
|
|
48
|
+
group: cliproxy-model-health-${{ github.event_name == 'pull_request' && format('pr-{0}', github.ref) || github.event_name == 'workflow_dispatch' && inputs.profile || github.event.schedule }}
|
|
49
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
50
|
+
env:
|
|
51
|
+
PROFILE: ${{ github.event_name == 'workflow_dispatch' && inputs.profile || github.event.schedule == '41 3 * * *' && 'nightly' || github.event.schedule == '17 * * * *' && 'light' || 'readiness' }}
|
|
52
|
+
jobs:
|
|
53
|
+
contract:
|
|
54
|
+
# Pull requests: deterministic protocol, routing and report contracts. No credentials, no inference.
|
|
55
|
+
if: github.event_name == 'pull_request'
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
timeout-minutes: 10
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
60
|
+
- name: Install PyYAML for the validate-config contract
|
|
61
|
+
run: |
|
|
62
|
+
sudo apt-get update
|
|
63
|
+
sudo apt-get install -y --no-install-recommends python3-yaml
|
|
64
|
+
- name: Lint models.json and run the offline contract tests
|
|
65
|
+
run: |
|
|
66
|
+
python3 -B cliproxy/scripts/check.py
|
|
67
|
+
python3 -B -m unittest discover -s cliproxy/tests -p "test_model_*.py"
|
|
68
|
+
- name: Run the full declarative suite against the local emulator
|
|
69
|
+
run: |
|
|
70
|
+
set +e
|
|
71
|
+
python3 -B cliproxy/scripts/model_health.py model-suite --target mock --profile manual \
|
|
72
|
+
--fail-on degraded --output model-health-report --seed 1 > /dev/null
|
|
73
|
+
code=$?
|
|
74
|
+
if [ -f model-health-report/model-health.md ]; then cat model-health-report/model-health.md >> "$GITHUB_STEP_SUMMARY"; fi
|
|
75
|
+
exit $code
|
|
76
|
+
- name: Keep structured report
|
|
77
|
+
if: ${{ always() && hashFiles('model-health-report/model-health.json') != '' }}
|
|
78
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
79
|
+
with:
|
|
80
|
+
name: model-health-contract-${{ github.run_id }}
|
|
81
|
+
path: model-health-report
|
|
82
|
+
retention-days: 14
|
|
83
|
+
health:
|
|
84
|
+
if: github.event_name != 'pull_request' && github.repository == 'crvouga/dev-fleet' && github.ref == 'refs/heads/main'
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
timeout-minutes: 60
|
|
87
|
+
permissions:
|
|
88
|
+
contents: read
|
|
89
|
+
id-token: write
|
|
90
|
+
issues: write
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
93
|
+
- name: Install read-only backup client
|
|
94
|
+
if: env.PROFILE == 'readiness'
|
|
95
|
+
run: |
|
|
96
|
+
sudo apt-get update
|
|
97
|
+
sudo apt-get install -y --no-install-recommends restic
|
|
98
|
+
- name: Read-only Vault login using GitHub OIDC
|
|
99
|
+
uses: hashicorp/vault-action@892a26828f195e65540a40b4768ae4571f51ebfc # v4.0.0
|
|
100
|
+
with:
|
|
101
|
+
url: https://vault.example.com
|
|
102
|
+
method: jwt
|
|
103
|
+
path: jwt
|
|
104
|
+
role: github-actions
|
|
105
|
+
jwtGithubAudience: https://vault.example.com
|
|
106
|
+
exportToken: true
|
|
107
|
+
- name: Verify endpoint and cloud recovery freshness
|
|
108
|
+
if: env.PROFILE == 'readiness'
|
|
109
|
+
run: python3 -B cliproxy/scripts/external-health.py
|
|
110
|
+
- name: Restore last-success and rate-limit state
|
|
111
|
+
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
112
|
+
with:
|
|
113
|
+
path: model-health-state.json
|
|
114
|
+
key: cliproxy-model-health-state-${{ github.run_id }}
|
|
115
|
+
restore-keys: cliproxy-model-health-state-
|
|
116
|
+
- name: Validate every required model
|
|
117
|
+
# Bounded spend: the profile's request/token budget, concurrency and timeouts come from models.json;
|
|
118
|
+
# inference is never retried; a model rate limited recently is not probed again until its pause ends.
|
|
119
|
+
# The client key is read in-process from Vault and never printed.
|
|
120
|
+
if: ${{ !cancelled() }}
|
|
121
|
+
env:
|
|
122
|
+
MODEL: ${{ inputs.model }}
|
|
123
|
+
CHECK: ${{ inputs.check }}
|
|
124
|
+
MAX_REQUESTS: ${{ inputs.max_requests }}
|
|
125
|
+
SUITE: ${{ inputs.suite || 'all' }}
|
|
126
|
+
FORCE_PROBE: ${{ inputs.force_probe }}
|
|
127
|
+
run: |
|
|
128
|
+
command=model-health
|
|
129
|
+
fail_on=unhealthy
|
|
130
|
+
case "$PROFILE" in nightly | manual) command=model-suite; fail_on=degraded ;; esac
|
|
131
|
+
args=(--profile "$PROFILE" --suite "$SUITE" --fail-on "$fail_on"
|
|
132
|
+
--output model-health-report --state model-health-state.json)
|
|
133
|
+
if [ -n "$MODEL" ]; then args+=(--model "$MODEL"); fi
|
|
134
|
+
if [ -n "$CHECK" ]; then args+=(--check "$CHECK"); fi
|
|
135
|
+
if [ -n "$MAX_REQUESTS" ]; then args+=(--max-requests "$MAX_REQUESTS"); fi
|
|
136
|
+
if [ "$FORCE_PROBE" = "true" ]; then args+=(--force-probe); fi
|
|
137
|
+
set +e
|
|
138
|
+
python3 -B cliproxy/scripts/model_health.py "$command" "${args[@]}" > /dev/null
|
|
139
|
+
code=$?
|
|
140
|
+
if [ -f model-health-report/agent-brief.md ]; then cat model-health-report/agent-brief.md >> "$GITHUB_STEP_SUMMARY"; fi
|
|
141
|
+
if [ -f model-health-report/model-health.md ]; then cat model-health-report/model-health.md >> "$GITHUB_STEP_SUMMARY"; fi
|
|
142
|
+
exit $code
|
|
143
|
+
- name: Save last-success and rate-limit state
|
|
144
|
+
if: ${{ always() && hashFiles('model-health-state.json') != '' }}
|
|
145
|
+
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
146
|
+
with:
|
|
147
|
+
path: model-health-state.json
|
|
148
|
+
key: cliproxy-model-health-state-${{ github.run_id }}
|
|
149
|
+
- name: Keep structured report
|
|
150
|
+
if: ${{ always() && hashFiles('model-health-report/model-health.json') != '' }}
|
|
151
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
152
|
+
with:
|
|
153
|
+
name: model-health-${{ env.PROFILE }}-${{ github.run_id }}
|
|
154
|
+
path: model-health-report
|
|
155
|
+
retention-days: 30
|
|
156
|
+
- name: Track findings in one issue per scope
|
|
157
|
+
# Opens or updates a single tracking issue when the findings change, and closes it when a full
|
|
158
|
+
# run is healthy. The body is the redacted agent brief: structure and numbers, no prompts or keys.
|
|
159
|
+
if: ${{ always() && hashFiles('model-health-report/model-health.json') != '' && inputs.model == '' && inputs.check == '' }}
|
|
160
|
+
env:
|
|
161
|
+
GH_TOKEN: ${{ github.token }}
|
|
162
|
+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
163
|
+
run: |
|
|
164
|
+
scope=live
|
|
165
|
+
case "$PROFILE" in nightly | manual) scope=nightly ;; esac
|
|
166
|
+
title="[ops] CLI proxy model health ($scope)"
|
|
167
|
+
report=model-health-report/model-health.json
|
|
168
|
+
status=$(jq -r .summary.status "$report")
|
|
169
|
+
fingerprint=$(jq -r .summary.findings.fingerprint "$report")
|
|
170
|
+
number=$(gh issue list --state open --search "\"$title\" in:title" --json number,title \
|
|
171
|
+
--jq "map(select(.title==\"$title\"))[0].number // empty")
|
|
172
|
+
if [ "$status" = unhealthy ] || [ "$status" = degraded ]; then
|
|
173
|
+
{
|
|
174
|
+
head -c 58000 model-health-report/agent-brief.md
|
|
175
|
+
printf '\n\nRun: %s (artifact model-health-%s-%s)\n<!-- fingerprint: %s -->\n' "$RUN_URL" "$PROFILE" "$GITHUB_RUN_ID" "$fingerprint"
|
|
176
|
+
} > model-health-report/issue.md
|
|
177
|
+
if [ -z "$number" ]; then
|
|
178
|
+
gh issue create --title "$title" --body-file model-health-report/issue.md
|
|
179
|
+
elif ! gh issue view "$number" --json body --jq .body | grep -q "fingerprint: $fingerprint"; then
|
|
180
|
+
gh issue edit "$number" --body-file model-health-report/issue.md
|
|
181
|
+
gh issue comment "$number" --body "Findings changed: $RUN_URL"
|
|
182
|
+
fi
|
|
183
|
+
elif [ "$status" = healthy ] && [ -n "$number" ]; then
|
|
184
|
+
gh issue close "$number" --comment "Healthy again: $RUN_URL"
|
|
185
|
+
fi
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Restore and migration
|
|
2
|
+
|
|
3
|
+
Exactly one proxy instance runs, on the **manager** (`manager.id` in `fleet.json`). Workers are clients and never host it. Never activate duplicated OAuth or Tailscale identities concurrently. Source code comes from this package at a reviewed Git revision; secrets and state come from Vault and encrypted R2 snapshots.
|
|
4
|
+
|
|
5
|
+
`INSTALL_ROOT` below is `~/Library/Application Support/cliproxy-fleet` on the manager and `/opt/cliproxy-fleet` on the legacy Linux host. Run every `devfleet cliproxy …` command from a repository checkout on the host named in the step.
|
|
6
|
+
|
|
7
|
+
## Move the proxy from a Linux worker to the manager
|
|
8
|
+
|
|
9
|
+
Today the proxy still runs on the worker `worker-one` (legacy systemd, `/opt/cliproxy-fleet`). This one-time, operator-approved move fences that host and recovers its latest checkpoint on the manager `manager-mac`. The acknowledgement flags below are operator fencing, not a distributed lock. Plan a short outage: workers have no proxy between the source stop and the manager health check.
|
|
10
|
+
|
|
11
|
+
### 1. Prepare the manager (no outage)
|
|
12
|
+
|
|
13
|
+
On the manager, logged in, on AC power, with Docker Desktop running (enable **Start Docker Desktop when you sign in**):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cliproxy/scripts/install-dependencies
|
|
17
|
+
devfleet cliproxy install --prepare-only --revision HEAD
|
|
18
|
+
devfleet cliproxy vault-login
|
|
19
|
+
devfleet cliproxy recover
|
|
20
|
+
devfleet cliproxy snapshots
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`install-dependencies` builds the pinned tools directory (mise Python + hash-locked PyYAML, mise `restic`) that the LaunchAgents run with; it never uses sudo or Homebrew. `--prepare-only` creates the worktree and private directories without starting anything. `recover` reads the existing Restic password from Vault and confirms it; it never generates or writes cloud secrets. Do not run `initialize` before recovery.
|
|
24
|
+
|
|
25
|
+
### 2. Fence the source (outage starts)
|
|
26
|
+
|
|
27
|
+
On `worker-one`, stop scheduling, drain the dedicated Tailscale Service advertisement, stop and disable the stack, then take the final consistent checkpoint:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
sudo systemctl disable --now cliproxy-backup.timer cliproxy-backup-check.timer cliproxy-gc.timer
|
|
31
|
+
devfleet cliproxy drain
|
|
32
|
+
devfleet cliproxy stop
|
|
33
|
+
sudo systemctl disable cliproxy-fleet.service
|
|
34
|
+
devfleet cliproxy backup
|
|
35
|
+
devfleet cliproxy snapshots
|
|
36
|
+
sudo systemctl disable --now cliproxy-monitor.timer cliproxy-vault-renew.timer
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
An already stopped stack remains stopped after backup. Record the **latest successful snapshot ID** from `/opt/cliproxy-fleet/state/status/backup-success.json`; do not choose an incomplete snapshot from a failed backup. Confirm the fence before continuing:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
systemctl is-active cliproxy-fleet.service # inactive
|
|
43
|
+
systemctl is-enabled cliproxy-fleet.service # disabled
|
|
44
|
+
python3 -B cliproxy/scripts/fleet_sync.py --definition fleet.json \
|
|
45
|
+
--node worker-one --revision "$(git rev-parse HEAD)" # PROXY_DECOMMISSION_PENDING, not PROXY_DUPLICATE_OWNER
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Keep the source powered off or fenced until the move is verified or rolled back.
|
|
49
|
+
|
|
50
|
+
### 3. Recover and start on the manager
|
|
51
|
+
|
|
52
|
+
On the manager:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
devfleet cliproxy restore-cache --previous-node-stopped
|
|
56
|
+
devfleet cliproxy install --revision HEAD
|
|
57
|
+
devfleet cliproxy audit
|
|
58
|
+
devfleet cliproxy health
|
|
59
|
+
devfleet cliproxy activate
|
|
60
|
+
devfleet cliproxy public-endpoint verify
|
|
61
|
+
python3 -B cliproxy/tests/live.py --endpoint https://cli-proxy.example.com --codex
|
|
62
|
+
devfleet cliproxy backup
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`restore-cache` downloads the latest modern checkpoint, validates it and populates the empty cache without starting services. `install` (never sudo on macOS) regenerates secret caches from Vault, records the user's UID/GID for Compose, links and loads the `dev.devfleet.cliproxy-*` LaunchAgents in `gui/$UID`, and starts the recovered stack; it refuses missing runtime config and stops with `DOCKER_DESKTOP_NOT_RUNNING` if Docker Desktop is down. `audit` must show clean provenance and loaded agents. `activate` resumes the existing `svc:ai-proxy` advertisement from the restored dedicated identity. A restored `state/public-endpoint-enabled` marker starts the public connector automatically; otherwise run `devfleet cliproxy public-endpoint enable` first.
|
|
66
|
+
|
|
67
|
+
Then confirm the fleet view and clients:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
python3 -B cliproxy/scripts/fleet_sync.py --definition fleet.json \
|
|
71
|
+
--node manager-mac --revision "$(git rev-parse HEAD)" # ok: true, role manager, backend launchd
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
From each worker, run the client bootstrap's `--validate` and an actual client/tool request through `https://cli-proxy.example.com` and `https://ai-proxy.tail0a1b2c.ts.net`. Remove temporary plaintext restore copies after verification.
|
|
75
|
+
|
|
76
|
+
### 4. Decommission the source
|
|
77
|
+
|
|
78
|
+
After the manager has run cleanly through several scheduled backups, retire the old host's installation. It contains private provider/Tailscale state, so remove it rather than leaving it enabled:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
sudo docker compose --project-directory /opt/cliproxy-fleet \
|
|
82
|
+
--profile portable-endpoint --profile public-endpoint down
|
|
83
|
+
sudo rm /etc/systemd/system/cliproxy-*.service /etc/systemd/system/cliproxy-*.timer
|
|
84
|
+
sudo systemctl daemon-reload
|
|
85
|
+
sudo rm -rf /opt/cliproxy-fleet
|
|
86
|
+
git worktree prune
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`fleet_sync.py --node worker-one` then reports `PROXY_CLIENT_ROLE`. Fleet-sync never performs these steps itself.
|
|
90
|
+
|
|
91
|
+
### Rollback a failed move
|
|
92
|
+
|
|
93
|
+
1. On the manager, stop and unload it, keeping any newer state:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
devfleet cliproxy drain
|
|
97
|
+
devfleet cliproxy stop
|
|
98
|
+
devfleet cliproxy backup # only if the manager refreshed credentials or changed config
|
|
99
|
+
for label in fleet monitor vault-renew backup backup-check gc; do
|
|
100
|
+
launchctl bootout "gui/$(id -u)/dev.devfleet.cliproxy-$label"
|
|
101
|
+
launchctl disable "gui/$(id -u)/dev.devfleet.cliproxy-$label"
|
|
102
|
+
done
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Move `INSTALL_ROOT/state/runtime`, `state/tailscale` and `state/endpoint.json` into a private directory under `INSTALL_ROOT/recovery/`. With the runtime cache gone, `fleet-sync` reports `PROXY_RECOVERY_REQUIRED` and can never restart the manager copy.
|
|
106
|
+
|
|
107
|
+
2. On the source, if the manager took a newer backup, move the source's `state/runtime`, `state/tailscale` and `state/endpoint.json` aside the same way and run `devfleet cliproxy restore-cache --previous-node-stopped`; otherwise keep the original source state (old OAuth refresh tokens may be invalid after the manager refreshed them).
|
|
108
|
+
3. Restart the source with `sudo systemctl enable cliproxy-fleet.service`, `devfleet cliproxy install --revision REV` (the revision it ran before), `devfleet cliproxy activate`, then re-enable its timers with `sudo systemctl enable --now cliproxy-monitor.timer cliproxy-vault-renew.timer cliproxy-backup.timer cliproxy-backup-check.timer cliproxy-gc.timer`.
|
|
109
|
+
4. Verify the stable endpoints from fleet clients. Until the move is retried, fleet-sync reports `PROXY_DUPLICATE_OWNER` on the source; that is expected and is never auto-remediated.
|
|
110
|
+
|
|
111
|
+
## One-command offline recovery
|
|
112
|
+
|
|
113
|
+
On a host with the tracked package installed and a valid private Vault token, `devfleet cliproxy recover-node NEW_DIRECTORY` builds a complete new cache using fresh Vault secrets and a verified R2 checkpoint. Supply `--vault-token-file FILE` to use an explicit existing login. It refuses existing destinations and never changes or stops the running stack. This is also a safe recovery rehearsal while the manager remains online: the new identity files are never activated. Remove rehearsal caches after inspection, since they contain private credentials.
|
|
114
|
+
|
|
115
|
+
On a replacement manager without installed tooling, invoke `python3 -B cliproxy/scripts/recover_node.py "$HOME/Library/Application Support/cliproxy-fleet" --vault-token-file FILE` from a full Git clone after `install-dependencies` passes. The destination must not yet exist. After explicitly fencing the old host, `devfleet cliproxy install --revision REV` installs tracked code and activates the recovered cache. This route replaces the separate prepare/login/password/restore steps above when an existing Vault token is already available. It does not obtain a new machine identity or bypass old-node fencing.
|
|
116
|
+
|
|
117
|
+
## Recovery details
|
|
118
|
+
|
|
119
|
+
Recovery uses the current trusted Git checkout, validates decrypted data, and preserves the new Vault login and independently recovered Restic password. It never executes backed-up code. To reproduce the original software version, read the recovered checkpoint's `INSTALLATION.json` in an offline restore first and choose that reviewed Git revision instead of HEAD.
|
|
120
|
+
|
|
121
|
+
`restore-cache` intentionally refuses an existing `runtime`, `tailscale` or endpoint cache. An existing repository pin must match the restored destination. For recovery on the same host, stop the service/agents and move these cache entries to a private rollback directory first. Never move `state/status/operation.lock` while an operation is active. An interrupted recovery leaves `state/recovery-incomplete`; inspect/remove the partial cache before retrying. Keep `state/secrets` (new Vault login/password) and the lock directory intact.
|
|
122
|
+
|
|
123
|
+
For offline inspection or historical recovery, use `devfleet cliproxy restore SNAPSHOT_ID EMPTY_DIRECTORY`, then `python3 -B INSTALL_ROOT/scripts/restore-verify EMPTY_DIRECTORY`. Modern checkpoints are located automatically despite different source paths. Old snapshots without `CHECKPOINT.json` require the original explicit restored deployment path and manual state recovery. No automatic fallback to an older snapshot occurs when validation fails.
|
|
124
|
+
|
|
125
|
+
The existing Tailscale Service remains in the cloud. Reuse the restored dedicated identity only while the original is stopped. If a drained identity requires re-advertising, use the pinned Tailscale CLI's `serve advertise` command for `svc:ai-proxy`, then inspect `serve status`. Do not change the manager's ordinary Tailscale identity or assume hostnames transfer. A new tagged identity/service definition requires the platform's OpenTofu-managed enrollment/approval workflow; no cloud provisioning helper exists in this package.
|
|
126
|
+
|
|
127
|
+
A normal code/image rollback uses `devfleet cliproxy install --rollback`. That restores a recorded Git revision, not application data. For incompatible state migrations, restore a matching pre-upgrade snapshot while stopped.
|
|
128
|
+
|
|
129
|
+
## Public hostname cutover
|
|
130
|
+
|
|
131
|
+
The public DNS/tunnel resources remain owned by OpenTofu. A restored `state/public-endpoint-enabled` marker makes service startup fetch the current isolated tunnel credential from Vault and start the connector automatically. The tunnel token itself is excluded from snapshots. If the marker is absent, run `devfleet cliproxy public-endpoint enable` after restoring and stopping the old host. Verify with `devfleet cliproxy public-endpoint verify` and `tests/live.py --endpoint https://cli-proxy.example.com --codex`. Client API and management keys do not change; keep the old host stopped during recovery.
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Subset of JSON Schema checked by scripts/modelhealth/schema.py in tests/test_model_agent_report.py. Report format: cliproxy-model-health/1.",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"required": [
|
|
5
|
+
"schema",
|
|
6
|
+
"generated_at",
|
|
7
|
+
"profile",
|
|
8
|
+
"suite",
|
|
9
|
+
"target",
|
|
10
|
+
"seed",
|
|
11
|
+
"levels",
|
|
12
|
+
"models",
|
|
13
|
+
"simulations",
|
|
14
|
+
"summary",
|
|
15
|
+
"findings",
|
|
16
|
+
"routing",
|
|
17
|
+
"cursor"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"schema": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"const": "cliproxy-model-health/1"
|
|
23
|
+
},
|
|
24
|
+
"generated_at": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"profile": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": ["liveness", "readiness", "light", "nightly", "manual"]
|
|
30
|
+
},
|
|
31
|
+
"suite": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"enum": ["core", "cursor", "all"]
|
|
34
|
+
},
|
|
35
|
+
"target": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"required": ["kind", "base_url"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"kind": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"enum": ["live", "mock"]
|
|
42
|
+
},
|
|
43
|
+
"base_url": {
|
|
44
|
+
"type": "string"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"seed": {
|
|
49
|
+
"type": "integer"
|
|
50
|
+
},
|
|
51
|
+
"levels": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["liveness"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"liveness": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"required": ["status", "category", "details"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"status": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"enum": ["healthy", "degraded", "unhealthy", "unknown"]
|
|
62
|
+
},
|
|
63
|
+
"category": {
|
|
64
|
+
"type": ["string", "null"]
|
|
65
|
+
},
|
|
66
|
+
"details": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "string"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"readiness": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["status", "category", "details"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"status": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"enum": ["healthy", "degraded", "unhealthy", "unknown"]
|
|
81
|
+
},
|
|
82
|
+
"category": {
|
|
83
|
+
"type": ["string", "null"]
|
|
84
|
+
},
|
|
85
|
+
"details": {
|
|
86
|
+
"type": "array",
|
|
87
|
+
"items": {
|
|
88
|
+
"type": "string"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"models": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"items": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"required": [
|
|
100
|
+
"id",
|
|
101
|
+
"provider",
|
|
102
|
+
"status",
|
|
103
|
+
"reasons",
|
|
104
|
+
"coverage",
|
|
105
|
+
"capabilities",
|
|
106
|
+
"checks",
|
|
107
|
+
"metrics",
|
|
108
|
+
"rate_limited",
|
|
109
|
+
"paused",
|
|
110
|
+
"last_success"
|
|
111
|
+
],
|
|
112
|
+
"properties": {
|
|
113
|
+
"id": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
},
|
|
116
|
+
"provider": {
|
|
117
|
+
"type": "string"
|
|
118
|
+
},
|
|
119
|
+
"status": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"enum": ["healthy", "degraded", "unhealthy", "unknown"]
|
|
122
|
+
},
|
|
123
|
+
"reasons": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"items": {
|
|
126
|
+
"type": "string"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"coverage": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"required": ["capabilities", "exercised", "by_result"]
|
|
132
|
+
},
|
|
133
|
+
"capabilities": {
|
|
134
|
+
"type": "object"
|
|
135
|
+
},
|
|
136
|
+
"checks": {
|
|
137
|
+
"type": "array",
|
|
138
|
+
"items": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"required": [
|
|
141
|
+
"id",
|
|
142
|
+
"capability",
|
|
143
|
+
"status",
|
|
144
|
+
"category",
|
|
145
|
+
"severity",
|
|
146
|
+
"endpoint",
|
|
147
|
+
"details",
|
|
148
|
+
"duration_ms"
|
|
149
|
+
],
|
|
150
|
+
"properties": {
|
|
151
|
+
"status": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"enum": ["pass", "fail", "untested"]
|
|
154
|
+
},
|
|
155
|
+
"severity": {
|
|
156
|
+
"type": "string",
|
|
157
|
+
"enum": ["critical", "major", "minor"]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"metrics": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"required": [
|
|
165
|
+
"requests",
|
|
166
|
+
"availability",
|
|
167
|
+
"success_rate",
|
|
168
|
+
"ttft_ms",
|
|
169
|
+
"latency_ms",
|
|
170
|
+
"streaming_completion_rate",
|
|
171
|
+
"tool_call_success_rate",
|
|
172
|
+
"error_categories",
|
|
173
|
+
"upstream_failures",
|
|
174
|
+
"resolved_models"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
"rate_limited": {
|
|
178
|
+
"type": "boolean"
|
|
179
|
+
},
|
|
180
|
+
"paused": {
|
|
181
|
+
"type": "boolean"
|
|
182
|
+
},
|
|
183
|
+
"last_success": {
|
|
184
|
+
"type": "object"
|
|
185
|
+
},
|
|
186
|
+
"rate_limit_detail": {
|
|
187
|
+
"type": ["string", "null"]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"simulations": {
|
|
193
|
+
"type": "array",
|
|
194
|
+
"items": {
|
|
195
|
+
"type": "object",
|
|
196
|
+
"required": ["id", "capability", "status", "evidence", "details"],
|
|
197
|
+
"properties": {
|
|
198
|
+
"status": {
|
|
199
|
+
"type": "string",
|
|
200
|
+
"enum": ["pass", "fail"]
|
|
201
|
+
},
|
|
202
|
+
"evidence": {
|
|
203
|
+
"type": "string",
|
|
204
|
+
"const": "simulated"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"summary": {
|
|
210
|
+
"type": "object",
|
|
211
|
+
"required": [
|
|
212
|
+
"status",
|
|
213
|
+
"levels",
|
|
214
|
+
"models",
|
|
215
|
+
"rate_limited",
|
|
216
|
+
"simulation_failures",
|
|
217
|
+
"findings"
|
|
218
|
+
],
|
|
219
|
+
"properties": {
|
|
220
|
+
"rate_limited": {
|
|
221
|
+
"type": "array",
|
|
222
|
+
"items": {
|
|
223
|
+
"type": "string"
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"status": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"enum": ["healthy", "degraded", "unhealthy", "unknown"]
|
|
229
|
+
},
|
|
230
|
+
"findings": {
|
|
231
|
+
"type": "object",
|
|
232
|
+
"required": ["total", "operator", "agent", "fingerprint"],
|
|
233
|
+
"properties": {
|
|
234
|
+
"total": {
|
|
235
|
+
"type": "integer",
|
|
236
|
+
"minimum": 0
|
|
237
|
+
},
|
|
238
|
+
"operator": {
|
|
239
|
+
"type": "integer",
|
|
240
|
+
"minimum": 0
|
|
241
|
+
},
|
|
242
|
+
"agent": {
|
|
243
|
+
"type": "integer",
|
|
244
|
+
"minimum": 0
|
|
245
|
+
},
|
|
246
|
+
"fingerprint": {
|
|
247
|
+
"type": "string",
|
|
248
|
+
"minLength": 8
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"findings": {
|
|
255
|
+
"type": "array",
|
|
256
|
+
"items": {
|
|
257
|
+
"type": "object",
|
|
258
|
+
"required": [
|
|
259
|
+
"id",
|
|
260
|
+
"status",
|
|
261
|
+
"severity",
|
|
262
|
+
"category",
|
|
263
|
+
"model",
|
|
264
|
+
"check",
|
|
265
|
+
"endpoint",
|
|
266
|
+
"summary",
|
|
267
|
+
"owner",
|
|
268
|
+
"resolver",
|
|
269
|
+
"agent_fixable",
|
|
270
|
+
"evidence",
|
|
271
|
+
"reproduce",
|
|
272
|
+
"fix_hints"
|
|
273
|
+
],
|
|
274
|
+
"properties": {
|
|
275
|
+
"id": {
|
|
276
|
+
"type": "string",
|
|
277
|
+
"minLength": 3
|
|
278
|
+
},
|
|
279
|
+
"status": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"enum": ["fail", "paused", "untested"]
|
|
282
|
+
},
|
|
283
|
+
"severity": {
|
|
284
|
+
"type": "string",
|
|
285
|
+
"enum": ["critical", "major", "minor", "info"]
|
|
286
|
+
},
|
|
287
|
+
"category": {
|
|
288
|
+
"type": "string"
|
|
289
|
+
},
|
|
290
|
+
"model": {
|
|
291
|
+
"type": ["string", "null"]
|
|
292
|
+
},
|
|
293
|
+
"check": {
|
|
294
|
+
"type": ["string", "null"]
|
|
295
|
+
},
|
|
296
|
+
"endpoint": {
|
|
297
|
+
"type": ["string", "null"]
|
|
298
|
+
},
|
|
299
|
+
"summary": {
|
|
300
|
+
"type": "string",
|
|
301
|
+
"minLength": 1
|
|
302
|
+
},
|
|
303
|
+
"owner": {
|
|
304
|
+
"type": "string"
|
|
305
|
+
},
|
|
306
|
+
"resolver": {
|
|
307
|
+
"type": "string",
|
|
308
|
+
"enum": ["operator", "agent"]
|
|
309
|
+
},
|
|
310
|
+
"agent_fixable": {
|
|
311
|
+
"type": "boolean"
|
|
312
|
+
},
|
|
313
|
+
"evidence": {
|
|
314
|
+
"type": "object",
|
|
315
|
+
"required": ["details", "trace"],
|
|
316
|
+
"properties": {
|
|
317
|
+
"details": {
|
|
318
|
+
"type": "array",
|
|
319
|
+
"items": {
|
|
320
|
+
"type": "string"
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
"trace": {
|
|
324
|
+
"type": "array",
|
|
325
|
+
"items": {
|
|
326
|
+
"type": "object"
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
"reproduce": {
|
|
332
|
+
"type": "string",
|
|
333
|
+
"minLength": 10
|
|
334
|
+
},
|
|
335
|
+
"fix_hints": {
|
|
336
|
+
"type": "array",
|
|
337
|
+
"items": {
|
|
338
|
+
"type": "string"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"routing": {
|
|
345
|
+
"type": "object",
|
|
346
|
+
"required": ["declared", "live_verified"],
|
|
347
|
+
"properties": {
|
|
348
|
+
"live_verified": {
|
|
349
|
+
"type": "boolean"
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
"cursor": {
|
|
354
|
+
"type": "object",
|
|
355
|
+
"required": ["application_verified"],
|
|
356
|
+
"properties": {
|
|
357
|
+
"application_verified": {
|
|
358
|
+
"type": "boolean",
|
|
359
|
+
"const": false
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|