@crvouga/dev-fleet 0.0.0-stage → 0.1.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 +157 -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 +128 -0
- package/agents/t3.json +40 -0
- package/ansible/macos/manager.yml +30 -0
- package/ansible/macos/policy.json +17 -0
- package/ansible/monitoring/README.md +154 -0
- package/ansible/monitoring/agent-darwin.yml +198 -0
- package/ansible/monitoring/agent.yml +134 -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/lock.json +11 -0
- package/ansible/net/remote-probe.sh +13 -0
- package/ansible/ubuntu/authorize-sudo.sh +51 -0
- package/ansible/ubuntu/baseline.yml +89 -0
- package/ansible/ubuntu/disk-guard.py +104 -0
- package/ansible/ubuntu/policy.json +43 -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 +75 -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 +252 -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/bin/devfleet.mjs +36 -0
- package/bootstrap/bootstrap.ps1 +50 -0
- package/bootstrap/bootstrap.sh +73 -0
- package/bootstrap/devfleet +7 -0
- package/bootstrap/docker-desktop.sh +33 -0
- package/bootstrap/doctor.ts +189 -0
- package/bootstrap/fleet-manager-run.sh +76 -0
- package/bootstrap/fleet-remote-bootstrap.sh +185 -0
- package/bootstrap/global-config.ts +113 -0
- package/bootstrap/install-launcher.sh +7 -0
- package/bootstrap/native-aws.ts +86 -0
- package/bootstrap/native-t3.ts +154 -0
- package/bootstrap/npm.ts +350 -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/examples/fleet.json +95 -0
- package/examples/roster.json +13 -0
- package/mise.lock +1492 -0
- package/mise.toml +99 -0
- package/package.json +65 -4
- package/schema/fleet.schema.json +364 -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 +156 -0
- package/src/agents/launch.ts +290 -0
- package/src/agents/native-verify.ts +226 -0
- package/src/agents/provider-errors.ts +50 -0
- package/src/agents/registry.ts +55 -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 +95 -0
- package/src/cli/app.ts +115 -0
- package/src/cli/cli.ts +21 -0
- package/src/cli/command-index.ts +50 -0
- package/src/cli/contracts.ts +25 -0
- package/src/cli/fleet-reference.ts +17 -0
- package/src/cli/fleet-sync-checkout.ts +91 -0
- package/src/cli/fleet-sync-enrollment.ts +144 -0
- package/src/cli/fleet-sync-forward.ts +134 -0
- package/src/cli/fleet-sync-manager.ts +193 -0
- package/src/cli/fleet-sync-monitoring.ts +185 -0
- package/src/cli/fleet-sync-output.ts +314 -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 +236 -0
- package/src/cli/fleet-sync-report.ts +26 -0
- package/src/cli/fleet-sync-schedule.ts +138 -0
- package/src/cli/fleet-sync-vault.ts +166 -0
- package/src/cli/fleet-sync.ts +309 -0
- package/src/cli/fleet.ts +163 -0
- package/src/cli/managed-health.ts +219 -0
- package/src/cli/node-deployment.ts +34 -0
- package/src/cli/node-sync.ts +114 -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 +169 -0
- package/src/cli/register-fleet.ts +246 -0
- package/src/cli/registry.ts +158 -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 +89 -0
- package/src/cli/search.ts +35 -0
- package/src/cli/settings.ts +64 -0
- package/src/cli/source-operations.ts +78 -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 +260 -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 +88 -0
- package/src/index.ts +6 -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 +185 -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 +400 -0
- package/src/manager.ts +62 -0
- package/src/monitoring/apply.ts +171 -0
- package/src/monitoring/beszel.ts +191 -0
- package/src/monitoring/enroll.ts +110 -0
- package/src/monitoring/hub-access.ts +113 -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 +232 -0
- package/src/monitoring/remote.ts +65 -0
- package/src/network-definition.ts +83 -0
- package/src/node-converge-linux.ts +372 -0
- package/src/node-converge-shared.ts +83 -0
- package/src/node-converge.ts +40 -0
- package/src/onboard.ts +35 -0
- package/src/platform/ansible-receipt.ts +128 -0
- package/src/platform/ansible.ts +62 -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 +128 -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 +63 -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 +212 -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/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 +48 -0
- package/src/t3-linked-check.ts +101 -0
- package/src/tailnet-health.ts +231 -0
- package/src/tailnet-matching.ts +37 -0
- package/src/tailscale.ts +141 -0
- package/src/test-fixtures.ts +36 -0
- package/src/toolchain-checks.ts +83 -0
- package/src/topology.ts +24 -0
- package/src/transport.ts +122 -0
- package/src/types.ts +95 -0
- package/src/vault-config.ts +38 -0
- package/src/worker-access-checks.ts +115 -0
- package/src/worker-capacity.ts +34 -0
- package/src/worker-host-checks.ts +176 -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,176 @@
|
|
|
1
|
+
import { powerRecoveryCheck } from './power-recovery';
|
|
2
|
+
import { auditUbuntu } from './platform/ubuntu-audit';
|
|
3
|
+
import { verifyUbuntuBoot } from './platform/ubuntu-verify';
|
|
4
|
+
import { verifyResourceSafety } from './platform/resource-verify';
|
|
5
|
+
import policy from '../ansible/ubuntu/policy.json';
|
|
6
|
+
import { userInfo } from 'node:os';
|
|
7
|
+
import type { Check, Runner } from './types';
|
|
8
|
+
|
|
9
|
+
function check(id: string, ok: boolean, summary: string): Check {
|
|
10
|
+
return {
|
|
11
|
+
id: `worker:${id}`,
|
|
12
|
+
status: ok ? 'pass' : 'fail',
|
|
13
|
+
summary,
|
|
14
|
+
...(!ok
|
|
15
|
+
? {
|
|
16
|
+
remedy: {
|
|
17
|
+
action:
|
|
18
|
+
'Apply the approved fleet-sync revision over verified SSH, then rerun fleet-status --workers. No password prompt or state reset is attempted by health.',
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
: {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Observed unit state for a failed service check, e.g.
|
|
25
|
+
* `SERVICE_NOT_INSTALLED beszel-agent.service (inactive)`. */
|
|
26
|
+
export function serviceState(name: string, show: string): string {
|
|
27
|
+
const field = (key: string) =>
|
|
28
|
+
new RegExp(`^${key}=(.*)$`, 'm').exec(show)?.[1] ?? '';
|
|
29
|
+
const file = field('UnitFileState');
|
|
30
|
+
const state = field('ActiveState') || 'unknown';
|
|
31
|
+
const code = !file
|
|
32
|
+
? 'SERVICE_NOT_INSTALLED'
|
|
33
|
+
: state !== 'active'
|
|
34
|
+
? 'SERVICE_NOT_RUNNING'
|
|
35
|
+
: file !== 'enabled'
|
|
36
|
+
? 'SERVICE_NOT_ENABLED'
|
|
37
|
+
: 'SERVICE_RESTART_POLICY';
|
|
38
|
+
return `${code} ${name}.service (${state}, unit file ${file || 'missing'}, restart ${field('Restart') || 'unknown'})`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Measured runtime evidence, independent of installer receipts. */
|
|
42
|
+
export async function workerHostChecks(
|
|
43
|
+
user: string,
|
|
44
|
+
run: Runner
|
|
45
|
+
): Promise<Check[]> {
|
|
46
|
+
const audit = await auditUbuntu(run);
|
|
47
|
+
const checks = [
|
|
48
|
+
check(
|
|
49
|
+
'ubuntu',
|
|
50
|
+
audit.supported,
|
|
51
|
+
'Required Ubuntu release and architecture.'
|
|
52
|
+
),
|
|
53
|
+
check(
|
|
54
|
+
'sudo',
|
|
55
|
+
audit.passwordlessSudo,
|
|
56
|
+
'Designated worker user has noninteractive sudo.'
|
|
57
|
+
),
|
|
58
|
+
...Object.entries(policy.packages).map(([name, version]) =>
|
|
59
|
+
check(
|
|
60
|
+
`package:${name}`,
|
|
61
|
+
audit.packages[name] === version,
|
|
62
|
+
'Exact declarative Ubuntu package version.'
|
|
63
|
+
)
|
|
64
|
+
),
|
|
65
|
+
];
|
|
66
|
+
for (const name of policy.alwaysOnServices) {
|
|
67
|
+
const unit = await run([
|
|
68
|
+
'systemctl',
|
|
69
|
+
'show',
|
|
70
|
+
`${name}.service`,
|
|
71
|
+
'--property=UnitFileState,ActiveState,Restart',
|
|
72
|
+
]);
|
|
73
|
+
const active =
|
|
74
|
+
name === 'beszel-agent'
|
|
75
|
+
? /^ActiveState=active$/m.test(unit.stdout)
|
|
76
|
+
: audit.services[name] === true;
|
|
77
|
+
const ok =
|
|
78
|
+
unit.ok &&
|
|
79
|
+
active &&
|
|
80
|
+
/^UnitFileState=enabled$/m.test(unit.stdout) &&
|
|
81
|
+
/^Restart=always$/m.test(unit.stdout);
|
|
82
|
+
checks.push(
|
|
83
|
+
check(
|
|
84
|
+
`service:${name}`,
|
|
85
|
+
ok,
|
|
86
|
+
ok
|
|
87
|
+
? 'Required system service is active, enabled for boot and restarts unconditionally.'
|
|
88
|
+
: serviceState(name, unit.stdout)
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
checks.push(...(await recoveryChecks(run)));
|
|
93
|
+
checks.push(...(await verifyUbuntuBoot(user, run)));
|
|
94
|
+
checks.push(...(await verifyResourceSafety(userInfo().homedir, run)));
|
|
95
|
+
return checks;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function recoveryChecks(run: Runner): Promise<Check[]> {
|
|
99
|
+
const checks: Check[] = [];
|
|
100
|
+
const t3 = await run([
|
|
101
|
+
'systemctl',
|
|
102
|
+
'--user',
|
|
103
|
+
'show',
|
|
104
|
+
't3code-desktop.service',
|
|
105
|
+
'--property=ActiveState,UnitFileState,Restart',
|
|
106
|
+
]);
|
|
107
|
+
checks.push(
|
|
108
|
+
check(
|
|
109
|
+
't3-recovery',
|
|
110
|
+
t3.ok &&
|
|
111
|
+
/^ActiveState=active$/m.test(t3.stdout) &&
|
|
112
|
+
/^UnitFileState=enabled$/m.test(t3.stdout) &&
|
|
113
|
+
/^Restart=(always|on-failure)$/m.test(t3.stdout),
|
|
114
|
+
'The T3 desktop app runs as an enabled session service with automatic crash recovery.'
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
checks.push(await t3HealthCheck(run));
|
|
118
|
+
const renewal = await run([
|
|
119
|
+
'systemctl',
|
|
120
|
+
'--user',
|
|
121
|
+
'is-active',
|
|
122
|
+
'personal-worker-auth.timer',
|
|
123
|
+
]);
|
|
124
|
+
const recovery = await run([
|
|
125
|
+
'systemctl',
|
|
126
|
+
'--user',
|
|
127
|
+
'is-active',
|
|
128
|
+
'personal-worker-recovery.timer',
|
|
129
|
+
]);
|
|
130
|
+
checks.push(
|
|
131
|
+
check(
|
|
132
|
+
'credential-renewal',
|
|
133
|
+
renewal.ok,
|
|
134
|
+
'Worker credential renewal timer is active.'
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
checks.push(
|
|
138
|
+
check(
|
|
139
|
+
'service-recovery',
|
|
140
|
+
recovery.ok,
|
|
141
|
+
'Worker service recovery timer is active.'
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
checks.push(await powerRecoveryCheck(run));
|
|
145
|
+
return checks;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The server answers its local API now, not merely that a process exists. */
|
|
149
|
+
async function t3HealthCheck(run: Runner): Promise<Check> {
|
|
150
|
+
const probe = await run(
|
|
151
|
+
[
|
|
152
|
+
'curl',
|
|
153
|
+
'--fail',
|
|
154
|
+
'--silent',
|
|
155
|
+
'--max-time',
|
|
156
|
+
'10',
|
|
157
|
+
'http://127.0.0.1:3773/.well-known/t3/environment',
|
|
158
|
+
],
|
|
159
|
+
{ timeoutMs: 15_000 }
|
|
160
|
+
);
|
|
161
|
+
let identified = false;
|
|
162
|
+
try {
|
|
163
|
+
const value: unknown = JSON.parse(probe.stdout);
|
|
164
|
+
identified =
|
|
165
|
+
typeof value === 'object' &&
|
|
166
|
+
value !== null &&
|
|
167
|
+
typeof (value as Record<string, unknown>)['environmentId'] === 'string';
|
|
168
|
+
} catch {
|
|
169
|
+
/* Raw responses are never reported. */
|
|
170
|
+
}
|
|
171
|
+
return check(
|
|
172
|
+
't3-health',
|
|
173
|
+
probe.ok && identified,
|
|
174
|
+
'T3 local API answers with its environment identity within 10 seconds.'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CAPABILITIES, record } from './config';
|
|
2
|
+
import { FleetUsageError } from './errors';
|
|
3
|
+
import type { Capability } from './types';
|
|
4
|
+
|
|
5
|
+
export interface WorkerPolicy {
|
|
6
|
+
requiredCapabilities: Capability[];
|
|
7
|
+
githubLogin: string;
|
|
8
|
+
/** Vault userpass login each node uses for its own periodic token. */
|
|
9
|
+
vaultUser: string;
|
|
10
|
+
toolchain: 'mise.lock';
|
|
11
|
+
agentProfile: 'proxy';
|
|
12
|
+
unattendedSudo: true;
|
|
13
|
+
persistentT3: true;
|
|
14
|
+
neverSleep: true;
|
|
15
|
+
}
|
|
16
|
+
const REQUIRED = [
|
|
17
|
+
'github',
|
|
18
|
+
'vault',
|
|
19
|
+
't3',
|
|
20
|
+
'toolchain',
|
|
21
|
+
'workstation',
|
|
22
|
+
'worker',
|
|
23
|
+
];
|
|
24
|
+
function validCapabilities(value: unknown): boolean {
|
|
25
|
+
return (
|
|
26
|
+
Array.isArray(value) &&
|
|
27
|
+
new Set(value).size === value.length &&
|
|
28
|
+
value.every((item) => CAPABILITIES.includes(item)) &&
|
|
29
|
+
REQUIRED.every((item) => value.includes(item))
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
function validPolicy(value: Record<string, unknown>): boolean {
|
|
33
|
+
return (
|
|
34
|
+
Object.keys(value).every((key) =>
|
|
35
|
+
[
|
|
36
|
+
'requiredCapabilities',
|
|
37
|
+
'githubLogin',
|
|
38
|
+
'vaultUser',
|
|
39
|
+
'toolchain',
|
|
40
|
+
'agentProfile',
|
|
41
|
+
'unattendedSudo',
|
|
42
|
+
'persistentT3',
|
|
43
|
+
'neverSleep',
|
|
44
|
+
].includes(key)
|
|
45
|
+
) &&
|
|
46
|
+
validCapabilities(value['requiredCapabilities']) &&
|
|
47
|
+
typeof value['githubLogin'] === 'string' &&
|
|
48
|
+
/^[a-zA-Z0-9-]{1,39}$/.test(value['githubLogin']) &&
|
|
49
|
+
typeof value['vaultUser'] === 'string' &&
|
|
50
|
+
/^[a-zA-Z0-9_.-]{1,64}$/.test(value['vaultUser']) &&
|
|
51
|
+
value['toolchain'] === 'mise.lock' &&
|
|
52
|
+
value['agentProfile'] === 'proxy' &&
|
|
53
|
+
['unattendedSudo', 'persistentT3', 'neverSleep'].every(
|
|
54
|
+
(key) => value[key] === true
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
export function parseWorkerPolicy(value: unknown): WorkerPolicy {
|
|
59
|
+
if (!record(value) || !validPolicy(value))
|
|
60
|
+
throw new FleetUsageError(
|
|
61
|
+
'Invalid mandatory worker policy. Workers require the locked toolchain, common services, unattended sudo, persistent T3 and no sleep.'
|
|
62
|
+
);
|
|
63
|
+
return value as unknown as WorkerPolicy;
|
|
64
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { workerCapacity } from './worker-capacity';
|
|
2
|
+
import { userInfo, hostname } from 'node:os';
|
|
3
|
+
import { hostId } from './topology';
|
|
4
|
+
import { configPath } from './fleet-config';
|
|
5
|
+
import type { Check, Runner } from './types';
|
|
6
|
+
import { workerHostChecks } from './worker-host-checks';
|
|
7
|
+
import { workerAccessChecks } from './worker-access-checks';
|
|
8
|
+
import { record } from './config';
|
|
9
|
+
|
|
10
|
+
export async function workerReadiness(
|
|
11
|
+
run: Runner,
|
|
12
|
+
nodeId = hostId(hostname()),
|
|
13
|
+
root = process.cwd()
|
|
14
|
+
): Promise<Check[]> {
|
|
15
|
+
const host = await workerHostChecks(userInfo().username, run);
|
|
16
|
+
const access = await workerAccessChecks(run);
|
|
17
|
+
const revision = await run(['git', 'rev-parse', 'HEAD']);
|
|
18
|
+
const singleton = await run(
|
|
19
|
+
[
|
|
20
|
+
'python3',
|
|
21
|
+
'-B',
|
|
22
|
+
'cliproxy/scripts/fleet_sync.py',
|
|
23
|
+
'--definition',
|
|
24
|
+
configPath(),
|
|
25
|
+
'--node',
|
|
26
|
+
nodeId,
|
|
27
|
+
'--revision',
|
|
28
|
+
revision.stdout.trim(),
|
|
29
|
+
],
|
|
30
|
+
{ timeoutMs: 180_000 }
|
|
31
|
+
);
|
|
32
|
+
let proxy: Check = {
|
|
33
|
+
id: 'worker:proxy-singleton',
|
|
34
|
+
status: 'fail',
|
|
35
|
+
summary:
|
|
36
|
+
'Singleton gateway ownership or client-only role could not be verified.',
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
const value: unknown = JSON.parse(singleton.stdout);
|
|
40
|
+
if (
|
|
41
|
+
singleton.ok &&
|
|
42
|
+
record(value) &&
|
|
43
|
+
Array.isArray(value['phases']) &&
|
|
44
|
+
value['phases'].length &&
|
|
45
|
+
value['phases'].every(
|
|
46
|
+
(item) => record(item) && item['status'] === 'unchanged'
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
proxy = {
|
|
50
|
+
...proxy,
|
|
51
|
+
status: 'pass',
|
|
52
|
+
summary:
|
|
53
|
+
'Declared gateway host/client-only role verified without changing runtime identity.',
|
|
54
|
+
};
|
|
55
|
+
} catch {
|
|
56
|
+
/* Raw proxy output is never exposed. */
|
|
57
|
+
}
|
|
58
|
+
return [
|
|
59
|
+
...host,
|
|
60
|
+
...access,
|
|
61
|
+
proxy,
|
|
62
|
+
...(await workerCapacity(root, nodeId, run)),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import type { Check, Runner } from './types';
|
|
4
|
+
|
|
5
|
+
/** This machine has the `devfleet` launcher, linked into a checkout. */
|
|
6
|
+
export async function workstationChecks(
|
|
7
|
+
_root: string,
|
|
8
|
+
run: Runner
|
|
9
|
+
): Promise<Check[]> {
|
|
10
|
+
const launcher = join(homedir(), '.local/bin/devfleet');
|
|
11
|
+
const result = await run(['test', '-x', launcher], { timeoutMs: 5_000 });
|
|
12
|
+
return [
|
|
13
|
+
result.ok
|
|
14
|
+
? {
|
|
15
|
+
id: 'workstation:configuration',
|
|
16
|
+
status: 'pass',
|
|
17
|
+
summary: 'The devfleet launcher is installed.',
|
|
18
|
+
}
|
|
19
|
+
: {
|
|
20
|
+
id: 'workstation:configuration',
|
|
21
|
+
status: 'fail',
|
|
22
|
+
summary: 'The devfleet launcher is missing.',
|
|
23
|
+
remedy: {
|
|
24
|
+
action:
|
|
25
|
+
'Run fleet-sync from the manager, or sh bootstrap/install-launcher.sh in the dev-fleet checkout on this computer.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
}
|