@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,371 @@
|
|
|
1
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import type { Check, Runner } from '../types';
|
|
5
|
+
import { pinnedPath } from './toolchain';
|
|
6
|
+
import { registry } from './registry';
|
|
7
|
+
import {
|
|
8
|
+
cacheKey,
|
|
9
|
+
cachedCheck,
|
|
10
|
+
sourceKey,
|
|
11
|
+
type VerifyCache,
|
|
12
|
+
} from './verify-cache';
|
|
13
|
+
|
|
14
|
+
const PROMPT = 'Reply with exactly: OK';
|
|
15
|
+
|
|
16
|
+
/** Every agent T3 offers on every node, launched exactly as T3 launches it:
|
|
17
|
+
* through the tracked wrapper that injects the shared credential. `pin` names
|
|
18
|
+
* the mise.toml tool whose locked version the installed binary must report.
|
|
19
|
+
*/
|
|
20
|
+
export const NATIVE_AGENTS = [
|
|
21
|
+
{
|
|
22
|
+
name: 'claude',
|
|
23
|
+
binary: 'claude',
|
|
24
|
+
pin: 'claude',
|
|
25
|
+
wrapper: 'claude-proxy.ts',
|
|
26
|
+
// T3's binaryPath: the Claude Agent SDK runs a `.ts` path with node.
|
|
27
|
+
launcher: 'claude-proxy',
|
|
28
|
+
args: ['-p', PROMPT],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'codex',
|
|
32
|
+
binary: 'codex',
|
|
33
|
+
pin: 'codex',
|
|
34
|
+
wrapper: 'codex-proxy.ts',
|
|
35
|
+
launcher: 'codex-proxy.ts',
|
|
36
|
+
args: ['exec', '--skip-git-repo-check', PROMPT],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'grok',
|
|
40
|
+
binary: 'grok',
|
|
41
|
+
pin: 'http:grok',
|
|
42
|
+
wrapper: 'grok-proxy.ts',
|
|
43
|
+
launcher: 'grok-proxy.ts',
|
|
44
|
+
args: ['-p', PROMPT],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'antigravity',
|
|
48
|
+
binary: 'antigravity',
|
|
49
|
+
pin: 'http:antigravity',
|
|
50
|
+
wrapper: 'antigravity-proxy.ts',
|
|
51
|
+
launcher: 'antigravity-proxy.ts',
|
|
52
|
+
args: [
|
|
53
|
+
'-p',
|
|
54
|
+
PROMPT,
|
|
55
|
+
'--model',
|
|
56
|
+
'Gemini 3.8 Flash (High)',
|
|
57
|
+
'--print-timeout',
|
|
58
|
+
'90s',
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'muse',
|
|
63
|
+
binary: 'opencode',
|
|
64
|
+
pin: 'aqua:anomalyco/opencode',
|
|
65
|
+
wrapper: 'opencode-proxy.ts',
|
|
66
|
+
launcher: 'opencode-proxy.ts',
|
|
67
|
+
args: ['run', '--model', 'personal_proxy/devin/muse-wonder-1-0', PROMPT],
|
|
68
|
+
},
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
type Agent = (typeof NATIVE_AGENTS)[number];
|
|
72
|
+
|
|
73
|
+
/** The user service T3 runs as on a worker, and so the environment every
|
|
74
|
+
* agent it starts inherits.
|
|
75
|
+
*/
|
|
76
|
+
export const T3_SERVICE = 't3code-desktop.service';
|
|
77
|
+
|
|
78
|
+
/** How a session is launched: by the sync, through Bun on the pinned PATH,
|
|
79
|
+
* or exactly as the T3 service starts it, with that service's environment
|
|
80
|
+
* and nothing of the sync's.
|
|
81
|
+
*/
|
|
82
|
+
type Launch =
|
|
83
|
+
| { kind: 'sync' }
|
|
84
|
+
| { kind: 'service'; env: Record<string, string> }
|
|
85
|
+
| { kind: 'incomplete' };
|
|
86
|
+
|
|
87
|
+
/** Same versions everywhere (from the shared mise.lock) and a working signed-in
|
|
88
|
+
* session through each T3 launcher. Agents run concurrently and are bounded.
|
|
89
|
+
* With `t3Service` (a Linux worker), sessions run in the environment of the
|
|
90
|
+
* T3 user service, where T3 itself starts them, not in the sync's.
|
|
91
|
+
*/
|
|
92
|
+
export async function verifyNativeAgents(
|
|
93
|
+
node: string,
|
|
94
|
+
workspace: string,
|
|
95
|
+
run: Runner,
|
|
96
|
+
options: {
|
|
97
|
+
/** Proxy routes already found out of quota; their sessions are not run. */
|
|
98
|
+
outOfQuota?: Set<string>;
|
|
99
|
+
cache?: VerifyCache;
|
|
100
|
+
t3Service?: boolean;
|
|
101
|
+
} = {}
|
|
102
|
+
): Promise<Check[]> {
|
|
103
|
+
const { outOfQuota = new Set<string>(), cache = {} } = options;
|
|
104
|
+
const pins = parsePins(await readFile(join(workspace, 'mise.toml'), 'utf8'));
|
|
105
|
+
const environment = options.t3Service
|
|
106
|
+
? await serviceLaunch(run)
|
|
107
|
+
: { launch: { kind: 'sync' } as Launch, checks: [] };
|
|
108
|
+
const { launch } = environment;
|
|
109
|
+
const cwd = await mkdtemp(join(tmpdir(), 'fleet-agents-'));
|
|
110
|
+
try {
|
|
111
|
+
const results = await Promise.all(
|
|
112
|
+
NATIVE_AGENTS.map(async (agent) => [
|
|
113
|
+
await versionCheck(agent, pins[agent.pin], run),
|
|
114
|
+
outOfQuota.has(agent.name)
|
|
115
|
+
? quotaPass(agent)
|
|
116
|
+
: launch.kind === 'incomplete'
|
|
117
|
+
? incompleteSession(agent)
|
|
118
|
+
: await cachedCheck(
|
|
119
|
+
`agent-${node}-${agent.name}`,
|
|
120
|
+
cacheKey(
|
|
121
|
+
node,
|
|
122
|
+
agent,
|
|
123
|
+
pins[agent.pin] ?? '',
|
|
124
|
+
registry,
|
|
125
|
+
await sourceKey(launcherSources(workspace, agent))
|
|
126
|
+
),
|
|
127
|
+
() =>
|
|
128
|
+
sessionCheck(agent, { node, workspace, cwd, launch }, run),
|
|
129
|
+
cache
|
|
130
|
+
),
|
|
131
|
+
])
|
|
132
|
+
);
|
|
133
|
+
return [...environment.checks, ...results.flat()];
|
|
134
|
+
} finally {
|
|
135
|
+
await rm(cwd, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Reads `tool = "x"` and `"tool" = { version = "x", ... }` pins. */
|
|
140
|
+
export function parsePins(source: string): Record<string, string> {
|
|
141
|
+
const pins: Record<string, string> = {};
|
|
142
|
+
const pattern =
|
|
143
|
+
/^"?([A-Za-z0-9:@/._-]+)"?\s*=\s*(?:"([^"]+)"|\{[^\n]*?\bversion\s*=\s*"([^"]+)")/gm;
|
|
144
|
+
for (const match of source.matchAll(pattern))
|
|
145
|
+
pins[match[1]!] = (match[2] ?? match[3])!;
|
|
146
|
+
return pins;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Self-healing: a binary that drifted from the lock (a vendor self-updater
|
|
150
|
+
* rewrote it) is reinstalled from the locked artifact, then checked again.
|
|
151
|
+
*/
|
|
152
|
+
async function versionCheck(
|
|
153
|
+
agent: Agent,
|
|
154
|
+
pin: string | undefined,
|
|
155
|
+
run: Runner
|
|
156
|
+
): Promise<Check> {
|
|
157
|
+
const matches = async () => {
|
|
158
|
+
const result = await run([agent.binary, '--version'], {
|
|
159
|
+
env: { PATH: pinnedPath() },
|
|
160
|
+
timeoutMs: 30_000,
|
|
161
|
+
});
|
|
162
|
+
return Boolean(pin) && result.ok && result.stdout.includes(pin!);
|
|
163
|
+
};
|
|
164
|
+
let ok = await matches();
|
|
165
|
+
if (!ok && pin) {
|
|
166
|
+
await run(
|
|
167
|
+
['mise', 'install', '--locked', '--force', `${agent.pin}@${pin}`],
|
|
168
|
+
{
|
|
169
|
+
timeoutMs: 600_000,
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
ok = await matches();
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
id: `agent:${agent.name}:version`,
|
|
176
|
+
status: ok ? 'pass' : 'fail',
|
|
177
|
+
summary: ok
|
|
178
|
+
? `${agent.binary} ${pin} matches the shared toolchain lock.`
|
|
179
|
+
: 'AGENT_VERSION_MISMATCH',
|
|
180
|
+
...(ok
|
|
181
|
+
? {}
|
|
182
|
+
: {
|
|
183
|
+
remedy: {
|
|
184
|
+
action: `Every node runs the ${agent.binary} pinned in mise.toml/mise.lock; reinstalling it from the lock failed. Inspect mise install --locked ${agent.pin} on this node; never install it with a vendor self-updater.`,
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Quota is the account's, not the node's: no warning (as for providers). */
|
|
191
|
+
function quotaPass(agent: Agent): Check {
|
|
192
|
+
return {
|
|
193
|
+
id: `agent:${agent.name}:session`,
|
|
194
|
+
status: 'pass',
|
|
195
|
+
summary: `The shared ${agent.name} account is rate limited or out of quota; its session is checked again once it recovers.`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function sessionCheck(
|
|
200
|
+
agent: Agent,
|
|
201
|
+
context: { node: string; workspace: string; cwd: string; launch: Launch },
|
|
202
|
+
run: Runner
|
|
203
|
+
): Promise<Check> {
|
|
204
|
+
const agents = join(context.workspace, 'agents');
|
|
205
|
+
const command =
|
|
206
|
+
context.launch.kind === 'service'
|
|
207
|
+
? [join(agents, agent.launcher), ...agent.args]
|
|
208
|
+
: ['bun', join(agents, agent.wrapper), ...agent.args];
|
|
209
|
+
const env =
|
|
210
|
+
context.launch.kind === 'service'
|
|
211
|
+
? { ...context.launch.env, WS_AGENT_NODE: context.node }
|
|
212
|
+
: { WS_AGENT_NODE: context.node, PATH: pinnedPath() };
|
|
213
|
+
const launch = () =>
|
|
214
|
+
run(command, {
|
|
215
|
+
cwd: context.cwd,
|
|
216
|
+
env,
|
|
217
|
+
...(context.launch.kind === 'service' ? { isolatedEnv: true } : {}),
|
|
218
|
+
input: '',
|
|
219
|
+
timeoutMs: 150_000,
|
|
220
|
+
});
|
|
221
|
+
const expected = /\bOK\b/;
|
|
222
|
+
let result = await launch();
|
|
223
|
+
// One retry: a CLI's first launch on a node may still be initializing.
|
|
224
|
+
if (!(result.ok && expected.test(result.stdout))) result = await launch();
|
|
225
|
+
const ok = result.ok && expected.test(result.stdout);
|
|
226
|
+
return {
|
|
227
|
+
id: `agent:${agent.name}:session`,
|
|
228
|
+
status: ok ? 'pass' : 'fail',
|
|
229
|
+
summary: ok
|
|
230
|
+
? `${agent.name} runs signed in through its T3 launcher with the shared account.`
|
|
231
|
+
: sessionFailure(result.stderr + result.stdout),
|
|
232
|
+
...(ok ? {} : { remedy: { action: sessionRemedy(agent) } }),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The environment of the T3 user service: what its user manager holds
|
|
237
|
+
* (PATH included) and what the unit adds. Every launcher starts with
|
|
238
|
+
* `#!/usr/bin/env bun` or runs `bun`, so that PATH must find Bun.
|
|
239
|
+
*/
|
|
240
|
+
async function serviceLaunch(
|
|
241
|
+
run: Runner
|
|
242
|
+
): Promise<{ launch: Launch; checks: Check[] }> {
|
|
243
|
+
const manager = await run(['systemctl', '--user', 'show-environment'], {
|
|
244
|
+
timeoutMs: 15_000,
|
|
245
|
+
});
|
|
246
|
+
const unit = await run(
|
|
247
|
+
[
|
|
248
|
+
'systemctl',
|
|
249
|
+
'--user',
|
|
250
|
+
'show',
|
|
251
|
+
T3_SERVICE,
|
|
252
|
+
'--property=Environment',
|
|
253
|
+
'--value',
|
|
254
|
+
],
|
|
255
|
+
{ timeoutMs: 15_000 }
|
|
256
|
+
);
|
|
257
|
+
if (!manager.ok || !unit.ok)
|
|
258
|
+
return {
|
|
259
|
+
launch: { kind: 'sync' },
|
|
260
|
+
checks: [
|
|
261
|
+
environmentCheck(
|
|
262
|
+
'warn',
|
|
263
|
+
'AGENT_SERVICE_ENVIRONMENT_UNVERIFIED',
|
|
264
|
+
'The environment of the T3 service could not be read, so sessions were launched from the sync instead. Inspect the user service manager of this worker: systemctl --user show-environment.'
|
|
265
|
+
),
|
|
266
|
+
],
|
|
267
|
+
};
|
|
268
|
+
const env = {
|
|
269
|
+
...parseAssignments(manager.stdout.split('\n')),
|
|
270
|
+
...parseAssignments(words(unit.stdout)),
|
|
271
|
+
};
|
|
272
|
+
const bun = await run(['/bin/sh', '-c', 'command -v bun'], {
|
|
273
|
+
env,
|
|
274
|
+
isolatedEnv: true,
|
|
275
|
+
timeoutMs: 15_000,
|
|
276
|
+
});
|
|
277
|
+
if (!bun.ok)
|
|
278
|
+
return {
|
|
279
|
+
launch: { kind: 'incomplete' },
|
|
280
|
+
checks: [
|
|
281
|
+
environmentCheck(
|
|
282
|
+
'fail',
|
|
283
|
+
'AGENT_SERVICE_ENVIRONMENT_INCOMPLETE',
|
|
284
|
+
INCOMPLETE_REMEDY
|
|
285
|
+
),
|
|
286
|
+
],
|
|
287
|
+
};
|
|
288
|
+
return {
|
|
289
|
+
launch: { kind: 'service', env },
|
|
290
|
+
checks: [
|
|
291
|
+
environmentCheck(
|
|
292
|
+
'pass',
|
|
293
|
+
'The T3 service environment finds Bun; every session was launched with it alone.'
|
|
294
|
+
),
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const INCOMPLETE_REMEDY = `The PATH of ${T3_SERVICE} does not find Bun, so T3 cannot start any agent launcher even though they work from a shell. Put the pinned shims on the PATH of the user service manager through the baseline (never by hand), then run fleet-sync.`;
|
|
300
|
+
|
|
301
|
+
function environmentCheck(
|
|
302
|
+
status: Check['status'],
|
|
303
|
+
summary: string,
|
|
304
|
+
remedy?: string
|
|
305
|
+
): Check {
|
|
306
|
+
return {
|
|
307
|
+
id: 'agent:service-environment',
|
|
308
|
+
status,
|
|
309
|
+
summary,
|
|
310
|
+
...(remedy ? { remedy: { action: remedy } } : {}),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function incompleteSession(agent: Agent): Check {
|
|
315
|
+
return {
|
|
316
|
+
id: `agent:${agent.name}:session`,
|
|
317
|
+
status: 'fail',
|
|
318
|
+
summary: 'AGENT_SERVICE_ENVIRONMENT_INCOMPLETE',
|
|
319
|
+
remedy: { action: INCOMPLETE_REMEDY },
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** `KEY=value` lines or words; a value systemd quoted loses its quotes. */
|
|
324
|
+
function parseAssignments(entries: string[]): Record<string, string> {
|
|
325
|
+
const env: Record<string, string> = {};
|
|
326
|
+
for (const entry of entries) {
|
|
327
|
+
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s.exec(entry.trim());
|
|
328
|
+
if (match) env[match[1]!] = unquote(match[2]!);
|
|
329
|
+
}
|
|
330
|
+
return env;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** `systemctl show -p Environment` separates assignments with spaces and
|
|
334
|
+
* double-quotes one that holds a space.
|
|
335
|
+
*/
|
|
336
|
+
function words(line: string): string[] {
|
|
337
|
+
return line.trim().match(/(?:[^\s"]+|"[^"]*")+/g) ?? [];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function unquote(value: string): string {
|
|
341
|
+
return value.replace(/"([^"]*)"/g, '$1').replace(/^\$'(.*)'$/s, '$1');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** A stable code only; raw CLI output is never reported. */
|
|
345
|
+
function sessionFailure(output: string): string {
|
|
346
|
+
if (/missing or out of date/.test(output)) return 'AGENT_PROFILE_STALE';
|
|
347
|
+
if (/Required secret unavailable/.test(output))
|
|
348
|
+
return 'AGENT_VAULT_UNAVAILABLE';
|
|
349
|
+
if (/model_not_found|unknown provider for model/.test(output))
|
|
350
|
+
return 'AGENT_MODEL_UNSERVED';
|
|
351
|
+
return 'AGENT_SESSION_UNVERIFIED';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function sessionRemedy(agent: Agent): string {
|
|
355
|
+
if (agent.name === 'antigravity')
|
|
356
|
+
return 'Antigravity sends its own catalog model ids; the proxy serves them through declared aliases that fleet-sync applies on the manager (see proxy:config-pending). Pick Gemini 3.8 Flash in T3: the default Pro model is not served by the shared accounts.';
|
|
357
|
+
return `Run the ${agent.name} launcher on this node to inspect it: WS_AGENT_NODE=<node> bun agents/${agent.wrapper}. Check this node's Vault session and the shared proxy.`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** The launcher and profile code a session depends on. */
|
|
361
|
+
function launcherSources(workspace: string, agent: Agent): string[] {
|
|
362
|
+
const agents = join(workspace, 'agents');
|
|
363
|
+
const lib = join(workspace, 'src/agents');
|
|
364
|
+
return [
|
|
365
|
+
join(agents, agent.wrapper),
|
|
366
|
+
join(agents, 'managed-proxy.ts'),
|
|
367
|
+
...['launch.ts', 'render.ts', 'secrets.ts', 'toolchain.ts'].map((name) =>
|
|
368
|
+
join(lib, name)
|
|
369
|
+
),
|
|
370
|
+
];
|
|
371
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AgentConfigurationError } from './errors';
|
|
2
|
+
|
|
3
|
+
export class ProviderVerificationError extends AgentConfigurationError {
|
|
4
|
+
constructor(
|
|
5
|
+
readonly code: string,
|
|
6
|
+
message: string
|
|
7
|
+
) {
|
|
8
|
+
super(message);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** The gateway has no such route, method or media type for this protocol:
|
|
12
|
+
* the route as declared cannot work, whatever the account.
|
|
13
|
+
*/
|
|
14
|
+
const INCOMPATIBLE = new Set([404, 405, 415, 501]);
|
|
15
|
+
|
|
16
|
+
export function providerHttpFailure(status: number): ProviderVerificationError {
|
|
17
|
+
const code = INCOMPATIBLE.has(status)
|
|
18
|
+
? 'PROVIDER_PROTOCOL_INCOMPATIBLE'
|
|
19
|
+
: status === 401 || status === 403
|
|
20
|
+
? 'PROVIDER_AUTH_REJECTED'
|
|
21
|
+
: status === 429
|
|
22
|
+
? 'PROVIDER_RATE_LIMITED'
|
|
23
|
+
: status >= 500
|
|
24
|
+
? 'PROVIDER_UPSTREAM_UNAVAILABLE'
|
|
25
|
+
: 'PROVIDER_PROTOCOL_REJECTED';
|
|
26
|
+
return new ProviderVerificationError(
|
|
27
|
+
code,
|
|
28
|
+
'The gateway rejected a bounded verification request; response content is withheld.'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const SAFE_CODES = new Set([
|
|
32
|
+
'PROVIDER_AUTH_REJECTED',
|
|
33
|
+
'PROVIDER_RATE_LIMITED',
|
|
34
|
+
'PROVIDER_UPSTREAM_UNAVAILABLE',
|
|
35
|
+
'PROVIDER_PROTOCOL_REJECTED',
|
|
36
|
+
'PROVIDER_PROTOCOL_INCOMPATIBLE',
|
|
37
|
+
'PROVIDER_MODEL_UNAVAILABLE',
|
|
38
|
+
'PROVIDER_OUTPUT_UNVERIFIED',
|
|
39
|
+
'PROVIDER_TOOL_RESULT_UNVERIFIED',
|
|
40
|
+
'PROVIDER_EMPTY_RESPONSE',
|
|
41
|
+
'PROVIDER_RESPONSE_LIMIT',
|
|
42
|
+
'PROVIDER_TOOL_MISSING',
|
|
43
|
+
'PROVIDER_TOOL_INVALID',
|
|
44
|
+
'PROVIDER_TOOL_ARGUMENTS_INVALID',
|
|
45
|
+
'PROVIDER_STREAM_INCOMPLETE',
|
|
46
|
+
]);
|
|
47
|
+
export function providerFailureCode(error: unknown): string {
|
|
48
|
+
if (error instanceof ProviderVerificationError && SAFE_CODES.has(error.code))
|
|
49
|
+
return error.code;
|
|
50
|
+
if (error instanceof Error && SAFE_CODES.has(error.message))
|
|
51
|
+
return error.message;
|
|
52
|
+
if (error instanceof Error && error.name === 'TimeoutError')
|
|
53
|
+
return 'PROVIDER_TIMEOUT';
|
|
54
|
+
if (error instanceof TypeError) return 'PROVIDER_NETWORK_UNVERIFIED';
|
|
55
|
+
if (error instanceof SyntaxError) return 'PROVIDER_RESPONSE_INVALID';
|
|
56
|
+
return 'PROVIDER_ROUTE_UNVERIFIED';
|
|
57
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AgentConfigurationError } from './errors';
|
|
2
|
+
import source from '../../agents/registry.json';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { fleet } from '../fleet-config';
|
|
5
|
+
|
|
6
|
+
export type Tool = keyof typeof source.defaults;
|
|
7
|
+
export type Profile = 'direct' | 'proxy';
|
|
8
|
+
export type Selection = {
|
|
9
|
+
node: string;
|
|
10
|
+
tool: Tool;
|
|
11
|
+
profile: Profile;
|
|
12
|
+
model: string;
|
|
13
|
+
};
|
|
14
|
+
/** The tracked registry plus the proxy endpoint from this fleet's config. */
|
|
15
|
+
export const registry = {
|
|
16
|
+
...source,
|
|
17
|
+
proxy: {
|
|
18
|
+
// Read on use: a command that reaches no proxy needs no configuration.
|
|
19
|
+
get endpoint(): string {
|
|
20
|
+
return fleet.services.proxy.endpoint;
|
|
21
|
+
},
|
|
22
|
+
...source.proxy,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function select(node: string, tool: string, profile: string): Selection {
|
|
27
|
+
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(node))
|
|
28
|
+
throw new AgentConfigurationError('Invalid node ID.');
|
|
29
|
+
if (!(tool in source.defaults) || !['direct', 'proxy'].includes(profile))
|
|
30
|
+
throw new AgentConfigurationError(
|
|
31
|
+
'Unsupported tool/profile; run bun src/agents/cli.ts agents inventory.'
|
|
32
|
+
);
|
|
33
|
+
const id = tool as Tool;
|
|
34
|
+
const model = resolveModel(node, id, profile as Profile);
|
|
35
|
+
if (!model || !/^[a-zA-Z0-9_./:+-]+$/.test(model))
|
|
36
|
+
throw new AgentConfigurationError(
|
|
37
|
+
'No approved model. Add an exact model ID to agents/registry.json and review it.'
|
|
38
|
+
);
|
|
39
|
+
return { node, tool: id, profile: profile as Profile, model };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveModel(
|
|
43
|
+
node: string,
|
|
44
|
+
tool: Tool,
|
|
45
|
+
profile: Profile,
|
|
46
|
+
nodes = source.nodes as Record<
|
|
47
|
+
string,
|
|
48
|
+
Partial<Record<Tool, { direct?: string; proxy?: string }>>
|
|
49
|
+
>
|
|
50
|
+
) {
|
|
51
|
+
const override = nodes[node]?.[tool]?.[profile];
|
|
52
|
+
const models = source.proxy.models as Partial<Record<Tool, string | null>>;
|
|
53
|
+
return (
|
|
54
|
+
override ??
|
|
55
|
+
(profile === 'direct' ? source.defaults[tool].model : models[tool])
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function digest(value: string): string {
|
|
60
|
+
return createHash('sha256').update(value).digest('hex');
|
|
61
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { join, win32 } from 'node:path';
|
|
2
|
+
import type { PlatformName } from '../platform/types';
|
|
3
|
+
import { registry, type Selection } from './registry';
|
|
4
|
+
|
|
5
|
+
/** Dedicated outputs; native login/state directories are never copied. */
|
|
6
|
+
export function agentRoot(home: string, platform: PlatformName): string {
|
|
7
|
+
return platform === 'windows'
|
|
8
|
+
? win32.join(home, 'AppData', 'Local', 'personal', 'agents')
|
|
9
|
+
: join(home, '.config', 'personal', 'agents');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function render(s: Selection): Record<string, string> {
|
|
13
|
+
const model = JSON.stringify(s.model);
|
|
14
|
+
if (s.tool === 'codex') {
|
|
15
|
+
const provider =
|
|
16
|
+
s.profile === 'proxy'
|
|
17
|
+
? '\nmodel_provider = "personal_proxy"\n[model_providers.personal_proxy]\nname = "Personal CLI Proxy"\nbase_url = ' +
|
|
18
|
+
JSON.stringify(registry.proxy.endpoint + '/v1') +
|
|
19
|
+
'\nenv_key = "CLIPROXY_API_KEY"\nwire_api = "responses"\nrequires_openai_auth = false\nsupports_websockets = false\n'
|
|
20
|
+
: '\n';
|
|
21
|
+
return { 'config.toml': `model = ${model}${provider}` };
|
|
22
|
+
}
|
|
23
|
+
if (s.tool === 'claude')
|
|
24
|
+
return {
|
|
25
|
+
'settings.json': JSON.stringify({ model: s.model }, null, 2) + '\n',
|
|
26
|
+
};
|
|
27
|
+
if (s.tool === 'grok') return { 'config.toml': grokConfig(s) };
|
|
28
|
+
if (s.tool === 'antigravity')
|
|
29
|
+
return {
|
|
30
|
+
// The fleet profile home for direct and one-shot runs (see launchArgs);
|
|
31
|
+
// never the human's ~/.gemini. T3 keeps its own private GEMINI_HOME.
|
|
32
|
+
'.gemini/antigravity-cli/settings.json':
|
|
33
|
+
JSON.stringify({ modelProvider: 'gemini' }, null, 2) + '\n',
|
|
34
|
+
};
|
|
35
|
+
if (s.tool === 'gemini')
|
|
36
|
+
return {
|
|
37
|
+
'.gemini/settings.json':
|
|
38
|
+
JSON.stringify(
|
|
39
|
+
{
|
|
40
|
+
model: { name: s.model },
|
|
41
|
+
...(s.profile === 'proxy'
|
|
42
|
+
? { security: { auth: { selectedType: 'gateway' } } }
|
|
43
|
+
: {}),
|
|
44
|
+
},
|
|
45
|
+
null,
|
|
46
|
+
2
|
|
47
|
+
) + '\n',
|
|
48
|
+
};
|
|
49
|
+
const config =
|
|
50
|
+
s.profile === 'proxy'
|
|
51
|
+
? {
|
|
52
|
+
$schema: 'https://opencode.ai/config.json',
|
|
53
|
+
model: `personal_proxy/${s.model}`,
|
|
54
|
+
provider: {
|
|
55
|
+
personal_proxy: {
|
|
56
|
+
npm: '@ai-sdk/openai',
|
|
57
|
+
options: {
|
|
58
|
+
baseURL: registry.proxy.endpoint + '/v1',
|
|
59
|
+
apiKey: '{env:CLIPROXY_API_KEY}',
|
|
60
|
+
},
|
|
61
|
+
models: Object.fromEntries(
|
|
62
|
+
[
|
|
63
|
+
...Object.values(registry.proxy.routes).map(
|
|
64
|
+
(route) => route.model
|
|
65
|
+
),
|
|
66
|
+
s.model,
|
|
67
|
+
].map((id) => [id, { name: id }])
|
|
68
|
+
),
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
: { $schema: 'https://opencode.ai/config.json', model: s.model };
|
|
73
|
+
return { 'opencode.json': JSON.stringify(config, null, 2) + '\n' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Grok Build reads custom models only from GROK_HOME/config.toml. The key
|
|
77
|
+
* comes from the environment (env_key), never the file; no grok login.
|
|
78
|
+
*/
|
|
79
|
+
function grokConfig(s: Selection): string {
|
|
80
|
+
// Grok records a one-time skills migration in config.toml; declaring it up
|
|
81
|
+
// front keeps the rendered file unchanged by the CLI (no profile drift).
|
|
82
|
+
const lines = [
|
|
83
|
+
'[cli]',
|
|
84
|
+
'auto_update = false',
|
|
85
|
+
'',
|
|
86
|
+
'[marketplace]',
|
|
87
|
+
'default_skills_installs_purged = true',
|
|
88
|
+
'',
|
|
89
|
+
];
|
|
90
|
+
if (s.profile !== 'proxy')
|
|
91
|
+
return [
|
|
92
|
+
...lines,
|
|
93
|
+
'[models]',
|
|
94
|
+
`default = ${JSON.stringify(s.model)}`,
|
|
95
|
+
'',
|
|
96
|
+
].join('\n');
|
|
97
|
+
return [
|
|
98
|
+
...lines,
|
|
99
|
+
'[models]',
|
|
100
|
+
'default = "personal-proxy"',
|
|
101
|
+
'',
|
|
102
|
+
'[model.personal-proxy]',
|
|
103
|
+
`model = ${JSON.stringify(s.model)}`,
|
|
104
|
+
`name = ${JSON.stringify(`${s.model} (Personal CLI Proxy)`)}`,
|
|
105
|
+
`base_url = ${JSON.stringify(registry.proxy.endpoint + '/v1')}`,
|
|
106
|
+
'env_key = "CLIPROXY_API_KEY"',
|
|
107
|
+
'api_backend = "chat_completions"',
|
|
108
|
+
'',
|
|
109
|
+
].join('\n');
|
|
110
|
+
}
|