@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
|
+
import { Command, CommanderError } from 'commander';
|
|
2
|
+
import { FleetUsageError } from '../errors';
|
|
3
|
+
import { indexedCommands } from './command-index';
|
|
4
|
+
import { registerT3 } from './t3/register';
|
|
5
|
+
import { registerSourceOperations } from './source-operations';
|
|
6
|
+
import { registerFleet } from './register-fleet';
|
|
7
|
+
import { agentGuide } from './agent-guide';
|
|
8
|
+
import type { Settings } from './settings';
|
|
9
|
+
import type { CliOptions } from './options';
|
|
10
|
+
import type { CommandOutcome } from './contracts';
|
|
11
|
+
|
|
12
|
+
export interface Release {
|
|
13
|
+
version: string;
|
|
14
|
+
/** ISO timestamp recorded when this distribution was built; null for source runs. */
|
|
15
|
+
releasedAt: string | null;
|
|
16
|
+
/** The commit a published package was built from: what fleet-sync run from
|
|
17
|
+
* it converges every machine to. Absent for source runs.
|
|
18
|
+
*/
|
|
19
|
+
revision?: string | null;
|
|
20
|
+
/** The issue tracker of the package: where its users report a defect or
|
|
21
|
+
* ask for a feature.
|
|
22
|
+
*/
|
|
23
|
+
issues?: string | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const GLOBAL_OPTIONS = [
|
|
27
|
+
{
|
|
28
|
+
flag: '--json',
|
|
29
|
+
description: 'One versioned JSON document on stdout; never prompt',
|
|
30
|
+
},
|
|
31
|
+
{ flag: '--non-interactive', description: 'Never open interactive prompts' },
|
|
32
|
+
{
|
|
33
|
+
flag: '--workspace <path>',
|
|
34
|
+
description:
|
|
35
|
+
'A dev-fleet checkout to run from instead of this package (development); does not clone or change it',
|
|
36
|
+
},
|
|
37
|
+
{ flag: '--config <path>', description: 'Local, secret-free fleet roster' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/** The version a checkout carries; a published package has a real one. */
|
|
41
|
+
export const SOURCE_VERSION = '0.0.0-development';
|
|
42
|
+
|
|
43
|
+
export function releaseLabel(release: Release): string {
|
|
44
|
+
if (release.releasedAt)
|
|
45
|
+
return `${release.version} (released ${release.releasedAt})`;
|
|
46
|
+
if (release.version === SOURCE_VERSION)
|
|
47
|
+
return `${release.version} (unreleased source build)`;
|
|
48
|
+
return release.revision
|
|
49
|
+
? `${release.version} (commit ${release.revision})`
|
|
50
|
+
: release.version;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createProgram(
|
|
54
|
+
release: Release,
|
|
55
|
+
output: (text: string) => void
|
|
56
|
+
): Command {
|
|
57
|
+
const program = new Command()
|
|
58
|
+
.name('devfleet')
|
|
59
|
+
.description('Developer fleet, T3 and platform operations')
|
|
60
|
+
.version(releaseLabel(release))
|
|
61
|
+
.exitOverride()
|
|
62
|
+
.configureOutput({ writeOut: output, writeErr: () => undefined })
|
|
63
|
+
.showSuggestionAfterError(false)
|
|
64
|
+
.addHelpCommand('help [command]', 'Show help for a command')
|
|
65
|
+
.option(
|
|
66
|
+
'--interactive',
|
|
67
|
+
'Open the interactive terminal application (TTY required)'
|
|
68
|
+
);
|
|
69
|
+
for (const option of GLOBAL_OPTIONS)
|
|
70
|
+
program.option(option.flag, option.description);
|
|
71
|
+
program.addHelpText(
|
|
72
|
+
'after',
|
|
73
|
+
[
|
|
74
|
+
'',
|
|
75
|
+
`Release: ${releaseLabel(release)}`,
|
|
76
|
+
'Groups: fleet-* developer computers · t3-* T3 provider instances · cliproxy the CLI proxy service.',
|
|
77
|
+
'Apply on invocation (--dry-run previews): fleet-node-sync, t3-apply, t3-cleanup. fleet-sync applies on invocation too; --apply --yes is still accepted.',
|
|
78
|
+
'Preview until --apply --yes: fleet-repair, fleet-node-add. --json does not make a command read-only.',
|
|
79
|
+
'Agents: devfleet commands --json. Menu: devfleet --interactive.',
|
|
80
|
+
...(release.issues
|
|
81
|
+
? [
|
|
82
|
+
`Bugs and feature requests: ${release.issues}. Report them there; never patch the installed package.`,
|
|
83
|
+
]
|
|
84
|
+
: []),
|
|
85
|
+
].join('\n')
|
|
86
|
+
);
|
|
87
|
+
return program;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function catalog(options: CliOptions, release: Release): CommandOutcome {
|
|
91
|
+
const commands = indexedCommands(options.search, options.group);
|
|
92
|
+
return {
|
|
93
|
+
exitCode: 0,
|
|
94
|
+
data: {
|
|
95
|
+
version: 1,
|
|
96
|
+
cliVersion: release.version,
|
|
97
|
+
releasedAt: release.releasedAt,
|
|
98
|
+
command: 'commands',
|
|
99
|
+
ok: true,
|
|
100
|
+
commands,
|
|
101
|
+
globalOptions: GLOBAL_OPTIONS,
|
|
102
|
+
defaultCommand: 'help',
|
|
103
|
+
schema: 'devfleet fleet-nodes --schema --json',
|
|
104
|
+
agent: agentGuide(release),
|
|
105
|
+
},
|
|
106
|
+
text: [
|
|
107
|
+
`Personal command catalog · ${releaseLabel(release)}`,
|
|
108
|
+
...commands.map((entry) => `\n ${entry.id}\n ${entry.description}`),
|
|
109
|
+
'\nUse personal <command> --help for options; personal --interactive opens the menu.',
|
|
110
|
+
].join('\n'),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function execute(
|
|
115
|
+
args: string[],
|
|
116
|
+
defaults: Settings,
|
|
117
|
+
version: string | Release
|
|
118
|
+
): Promise<CommandOutcome> {
|
|
119
|
+
const release: Release =
|
|
120
|
+
typeof version === 'string' ? { version, releasedAt: null } : version;
|
|
121
|
+
let outcome: CommandOutcome | undefined;
|
|
122
|
+
let output = '';
|
|
123
|
+
const program = createProgram(release, (text) => {
|
|
124
|
+
output += text;
|
|
125
|
+
});
|
|
126
|
+
const complete = (result: CommandOutcome) => {
|
|
127
|
+
outcome = result;
|
|
128
|
+
};
|
|
129
|
+
registerFleet(program, defaults, complete);
|
|
130
|
+
registerT3(program, defaults, complete);
|
|
131
|
+
registerSourceOperations(program, defaults, complete);
|
|
132
|
+
program
|
|
133
|
+
.command('commands')
|
|
134
|
+
.description('Search the command catalog and agent contract')
|
|
135
|
+
.option('--search <text>', 'Filter names, descriptions and keywords')
|
|
136
|
+
.option(
|
|
137
|
+
'--group <name>',
|
|
138
|
+
'Filter group: Fleet, T3, Infra, Workstation, Reference'
|
|
139
|
+
)
|
|
140
|
+
.action((_options, command: Command) => {
|
|
141
|
+
outcome = catalog(command.optsWithGlobals<CliOptions>(), release);
|
|
142
|
+
});
|
|
143
|
+
program.action(() => {
|
|
144
|
+
if (program.opts<CliOptions>().json)
|
|
145
|
+
outcome = catalog(program.opts<CliOptions>(), release);
|
|
146
|
+
else program.help();
|
|
147
|
+
});
|
|
148
|
+
try {
|
|
149
|
+
await program.parseAsync(args, { from: 'user' });
|
|
150
|
+
return (
|
|
151
|
+
outcome ?? {
|
|
152
|
+
exitCode: 0,
|
|
153
|
+
data: { version: 1, help: output.trim() },
|
|
154
|
+
text: output.trim(),
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return commandError(error, output);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function commandError(error: unknown, output: string): CommandOutcome {
|
|
163
|
+
if (error instanceof CommanderError && error.exitCode === 0)
|
|
164
|
+
return {
|
|
165
|
+
exitCode: 0,
|
|
166
|
+
data: { version: 1, help: output.trim() },
|
|
167
|
+
text: output.trim(),
|
|
168
|
+
};
|
|
169
|
+
const usage =
|
|
170
|
+
error instanceof CommanderError || error instanceof FleetUsageError;
|
|
171
|
+
return {
|
|
172
|
+
exitCode: usage ? 2 : 1,
|
|
173
|
+
data: {
|
|
174
|
+
version: 1,
|
|
175
|
+
ok: false,
|
|
176
|
+
code: usage ? 'invalid_usage' : 'operation_failed',
|
|
177
|
+
error:
|
|
178
|
+
error instanceof FleetUsageError
|
|
179
|
+
? error.message
|
|
180
|
+
: usage
|
|
181
|
+
? 'Invalid command or option. Run personal --help or personal commands --json.'
|
|
182
|
+
: 'Operation failed. Inspect configuration and permissions locally; raw errors are withheld to protect credentials.',
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { record } from '../config';
|
|
4
|
+
|
|
5
|
+
/** The file a project keeps its fleet configuration in. */
|
|
6
|
+
export const PROJECT_CONFIG = 'fleet.json';
|
|
7
|
+
|
|
8
|
+
/** Another tool's fleet.json is not this one: a fleet configuration is a JSON
|
|
9
|
+
* object that names a manager. Whether it is a valid one is said later, with
|
|
10
|
+
* the file's path, by whatever reads it.
|
|
11
|
+
*/
|
|
12
|
+
function fleetConfiguration(path: string): boolean {
|
|
13
|
+
try {
|
|
14
|
+
const value: unknown = JSON.parse(readFileSync(path, 'utf8'));
|
|
15
|
+
return record(value) && record(value['manager']);
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The fleet.json of the project a command is run from: the closest one in
|
|
22
|
+
* the working directory or above it, or null.
|
|
23
|
+
*/
|
|
24
|
+
export function projectConfig(cwd: string): string | null {
|
|
25
|
+
let current = resolve(cwd);
|
|
26
|
+
while (true) {
|
|
27
|
+
const candidate = join(current, PROJECT_CONFIG);
|
|
28
|
+
if (fleetConfiguration(candidate)) return candidate;
|
|
29
|
+
const parent = dirname(current);
|
|
30
|
+
if (parent === current) return null;
|
|
31
|
+
current = parent;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A fleet is a project of your own: a fleet.json in Git, and the devfleet
|
|
36
|
+
* CLI run from inside it. This names that file for the whole command, and
|
|
37
|
+
* for everything the command starts in another directory (fleet-sync re-runs
|
|
38
|
+
* itself from a checkout of the revision). `DEVFLEET_CONFIG` names a file
|
|
39
|
+
* elsewhere and wins. Only the CLI does this: launchers and scheduled jobs
|
|
40
|
+
* run in directories that belong to other projects and read the copy
|
|
41
|
+
* fleet-sync installed.
|
|
42
|
+
*/
|
|
43
|
+
export function useProjectConfig(
|
|
44
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
45
|
+
cwd = process.cwd()
|
|
46
|
+
): string | null {
|
|
47
|
+
const named = env['DEVFLEET_CONFIG'];
|
|
48
|
+
const path = named ? resolve(cwd, named) : projectConfig(cwd);
|
|
49
|
+
if (path) env['DEVFLEET_CONFIG'] = path;
|
|
50
|
+
return path;
|
|
51
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CliOptions } from './options';
|
|
3
|
+
import type { CommandOutcome } from './contracts';
|
|
4
|
+
import type { Settings } from './settings';
|
|
5
|
+
import { resolveSettings } from './settings';
|
|
6
|
+
import { executeFleet } from './fleet';
|
|
7
|
+
import { fleetSync, type FleetSyncOptions } from './fleet-sync';
|
|
8
|
+
import { syncNode, type NodeSyncOptions } from './node-sync';
|
|
9
|
+
import { fleetState, type FleetStateOptions } from './fleet-state';
|
|
10
|
+
|
|
11
|
+
type Complete = (outcome: CommandOutcome) => void;
|
|
12
|
+
|
|
13
|
+
interface Registration {
|
|
14
|
+
program: Command;
|
|
15
|
+
defaults: Settings;
|
|
16
|
+
complete: Complete;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function collect(value: string, previous: string[]): string[] {
|
|
20
|
+
return [...previous, value];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function targetOptions(command: Command): Command {
|
|
24
|
+
return command
|
|
25
|
+
.option('--node <id>', 'Select a registered node; repeatable', collect, [])
|
|
26
|
+
.option('--local', 'This manager only; no roster or SSH required')
|
|
27
|
+
.option('--timeout <ms>', 'Per-node deadline, 1000–1800000 milliseconds');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function withSettings(defaults: Settings, options: CliOptions) {
|
|
31
|
+
return resolveSettings({
|
|
32
|
+
...defaults,
|
|
33
|
+
...(options.workspace ? { workspace: options.workspace } : {}),
|
|
34
|
+
...(options.config ? { config: options.config } : {}),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Internal fleet actions are selected by flags; the public surface stays flat. */
|
|
39
|
+
function fleetAction(name: string, options: CliOptions): string {
|
|
40
|
+
if (name === 'fleet-status')
|
|
41
|
+
return options.tailnet || options.workers ? 'health' : 'doctor';
|
|
42
|
+
if (name === 'fleet-repair') return options.list ? 'repairs' : 'repair';
|
|
43
|
+
if (name === 'fleet-nodes')
|
|
44
|
+
return options.onboard ? 'onboard' : options.schema ? 'schema' : 'nodes';
|
|
45
|
+
if (name === 'fleet-node-add') return 'add';
|
|
46
|
+
return 'discover';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function register(
|
|
50
|
+
{ program, defaults, complete }: Registration,
|
|
51
|
+
name: string,
|
|
52
|
+
description: string
|
|
53
|
+
): Command {
|
|
54
|
+
return program
|
|
55
|
+
.command(name)
|
|
56
|
+
.description(description)
|
|
57
|
+
.action(async (_options, command: Command) => {
|
|
58
|
+
const options = command.optsWithGlobals<CliOptions>();
|
|
59
|
+
const settings = await withSettings(defaults, options);
|
|
60
|
+
complete(
|
|
61
|
+
await executeFleet(
|
|
62
|
+
fleetAction(name, options),
|
|
63
|
+
{ ...options, ...(options.workers ? { managed: true } : {}) },
|
|
64
|
+
settings
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function registerStatus(registration: Registration) {
|
|
71
|
+
targetOptions(
|
|
72
|
+
register(
|
|
73
|
+
registration,
|
|
74
|
+
'fleet-status',
|
|
75
|
+
'Check this computer and registered nodes without changing them'
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
.option('--all', 'Explicit spelling of the default roster scope')
|
|
79
|
+
.option(
|
|
80
|
+
'--tailnet',
|
|
81
|
+
'Check every visible Tailscale computer instead of the roster'
|
|
82
|
+
)
|
|
83
|
+
.option(
|
|
84
|
+
'--workers',
|
|
85
|
+
'Audit the canonical worker fleet against the published reference'
|
|
86
|
+
)
|
|
87
|
+
.option(
|
|
88
|
+
'--require <capabilities>',
|
|
89
|
+
'Manager capabilities, comma separated, or none'
|
|
90
|
+
)
|
|
91
|
+
.option('--strict', 'Warnings and skipped checks also produce exit 1')
|
|
92
|
+
.addHelpText(
|
|
93
|
+
'after',
|
|
94
|
+
'\nRead-only. --local, --node and --all select roster scope; --tailnet and --workers check Tailscale computers and never take a roster scope.'
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function registerRepair(registration: Registration) {
|
|
99
|
+
targetOptions(
|
|
100
|
+
register(
|
|
101
|
+
registration,
|
|
102
|
+
'fleet-repair',
|
|
103
|
+
'Preview owner-managed repairs; --apply --yes executes them'
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
.option('--list', 'Print the static repair and login catalog instead')
|
|
107
|
+
.option('--all', 'Every visible Tailscale computer (default)')
|
|
108
|
+
.option(
|
|
109
|
+
'--require <capabilities>',
|
|
110
|
+
'Local only: manager capabilities, comma separated, or none'
|
|
111
|
+
)
|
|
112
|
+
.option(
|
|
113
|
+
'--repair <ids>',
|
|
114
|
+
'workspace,workstation,providers; comma separated'
|
|
115
|
+
)
|
|
116
|
+
.option(
|
|
117
|
+
'--plan-hash <sha256>',
|
|
118
|
+
'Require the reviewed plan to match current diagnostics'
|
|
119
|
+
)
|
|
120
|
+
.option(
|
|
121
|
+
'--script',
|
|
122
|
+
'Generate a reviewable POSIX script without executing it'
|
|
123
|
+
)
|
|
124
|
+
.option('--strict', 'Warnings and skipped checks also produce exit 1')
|
|
125
|
+
.option('--apply', 'Authorize the displayed repair plan; requires --yes')
|
|
126
|
+
.option('--yes', 'Confirm an explicitly requested --apply operation')
|
|
127
|
+
.addHelpText(
|
|
128
|
+
'after',
|
|
129
|
+
'\nExample: devfleet fleet-repair --local --repair workspace --plan-hash REVIEWED_SHA256 --apply --yes --json'
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function registerNodes(registration: Registration) {
|
|
134
|
+
register(
|
|
135
|
+
registration,
|
|
136
|
+
'fleet-nodes',
|
|
137
|
+
'List registered developer computers; --onboard and --schema add guidance'
|
|
138
|
+
)
|
|
139
|
+
.option('--node <id>', 'Select a registered node; repeatable', collect, [])
|
|
140
|
+
.option(
|
|
141
|
+
'--onboard',
|
|
142
|
+
'Per-node SSH, VS Code and authentication instructions'
|
|
143
|
+
)
|
|
144
|
+
.option('--schema', 'Print the versioned roster JSON schema');
|
|
145
|
+
register(
|
|
146
|
+
registration,
|
|
147
|
+
'fleet-node-add',
|
|
148
|
+
'Register an existing computer in the local roster; writes need --apply --yes'
|
|
149
|
+
)
|
|
150
|
+
.requiredOption('--id <name>', 'Unique lowercase node ID')
|
|
151
|
+
.requiredOption(
|
|
152
|
+
'--ssh <alias>',
|
|
153
|
+
'Existing OpenSSH alias; never writes SSH configuration'
|
|
154
|
+
)
|
|
155
|
+
.requiredOption('--path <path>', 'Absolute POSIX checkout path on the node')
|
|
156
|
+
.option(
|
|
157
|
+
'--runtime <path>',
|
|
158
|
+
'Optional absolute Node/Bun runtime; defaults to node on SSH PATH'
|
|
159
|
+
)
|
|
160
|
+
.option(
|
|
161
|
+
'--require <capabilities>',
|
|
162
|
+
'Required capabilities, comma separated, or none'
|
|
163
|
+
)
|
|
164
|
+
.option('--apply', 'Write the roster; requires --yes')
|
|
165
|
+
.option('--yes', 'Confirm an explicitly requested --apply operation');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function registerSync({ program, defaults, complete }: Registration) {
|
|
169
|
+
program
|
|
170
|
+
.command('fleet-sync')
|
|
171
|
+
.description(
|
|
172
|
+
'Synchronize the enrolled Ubuntu fleet from this manager over verified SSH'
|
|
173
|
+
)
|
|
174
|
+
.option(
|
|
175
|
+
'--revision <sha>',
|
|
176
|
+
'Converge to an exact source commit; defaults to the commit the dev-fleet package you run was published from'
|
|
177
|
+
)
|
|
178
|
+
.option(
|
|
179
|
+
'--node <id>',
|
|
180
|
+
'Explicit enrolled remote target; repeatable',
|
|
181
|
+
collect,
|
|
182
|
+
[]
|
|
183
|
+
)
|
|
184
|
+
.option(
|
|
185
|
+
'--all',
|
|
186
|
+
'Explicit spelling of the default enrolled fleet scope; never discovered peers'
|
|
187
|
+
)
|
|
188
|
+
.option(
|
|
189
|
+
'--dry-run',
|
|
190
|
+
'Read-only plan; no authentication, enrollment, installation or scheduling'
|
|
191
|
+
)
|
|
192
|
+
.option(
|
|
193
|
+
'--apply',
|
|
194
|
+
'Accepted for compatibility; fleet-sync applies by default (schedule writes still need --apply --yes)'
|
|
195
|
+
)
|
|
196
|
+
.option('--yes', 'Accepted for compatibility with --apply')
|
|
197
|
+
.option(
|
|
198
|
+
'--schedule <action>',
|
|
199
|
+
'Manager-only schedule: enable, pause, resume, disable, status'
|
|
200
|
+
)
|
|
201
|
+
.action(async (_options, command: Command) => {
|
|
202
|
+
const options = command.optsWithGlobals<
|
|
203
|
+
FleetSyncOptions & Partial<Settings>
|
|
204
|
+
>();
|
|
205
|
+
complete(
|
|
206
|
+
await fleetSync(options, {
|
|
207
|
+
...defaults,
|
|
208
|
+
...(options.config ? { config: options.config } : {}),
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
program
|
|
213
|
+
.command('fleet-node-sync')
|
|
214
|
+
.description(
|
|
215
|
+
'Reconcile this node’s locked tools, launcher, Codex proxy profile and T3 instances; applies by default'
|
|
216
|
+
)
|
|
217
|
+
.option(
|
|
218
|
+
'--id <name>',
|
|
219
|
+
'Stable node name; defaults to this machine hostname'
|
|
220
|
+
)
|
|
221
|
+
.option('--revision <sha>', 'Reviewed commit; defaults to checkout HEAD')
|
|
222
|
+
.option('--dry-run', 'Preview without changing this node')
|
|
223
|
+
.option('--t3-home <path>', 'T3 base directory')
|
|
224
|
+
.action(async (_options, command: Command) => {
|
|
225
|
+
const options = command.optsWithGlobals<
|
|
226
|
+
NodeSyncOptions & Partial<Settings>
|
|
227
|
+
>();
|
|
228
|
+
complete(await syncNode(options, await withSettings(defaults, options)));
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function registerState({ program, complete }: Registration) {
|
|
233
|
+
program
|
|
234
|
+
.command('fleet-state')
|
|
235
|
+
.description(
|
|
236
|
+
'Show where the fleet state is kept and whether the bucket holds it; flags connect, upload and restore'
|
|
237
|
+
)
|
|
238
|
+
.option(
|
|
239
|
+
'--init',
|
|
240
|
+
'Connect the bucket: restore the fleet state from it, or seed it from this machine, and save its credentials for runs this machine starts by itself'
|
|
241
|
+
)
|
|
242
|
+
.option(
|
|
243
|
+
'--bucket <name>',
|
|
244
|
+
'With --init, when no fleet.json names the bucket: S3-compatible bucket name'
|
|
245
|
+
)
|
|
246
|
+
.option(
|
|
247
|
+
'--endpoint <url>',
|
|
248
|
+
'With --init: https origin of the S3 service; omit for AWS S3'
|
|
249
|
+
)
|
|
250
|
+
.option('--region <name>', 'With --init: bucket region, such as auto')
|
|
251
|
+
.option('--prefix <prefix>', 'With --init: key prefix; default dev-fleet')
|
|
252
|
+
.option(
|
|
253
|
+
'--directory <path>',
|
|
254
|
+
'With --init: replicate to a directory instead of a bucket'
|
|
255
|
+
)
|
|
256
|
+
.option(
|
|
257
|
+
'--import <path>',
|
|
258
|
+
'Store a fleet configuration without running fleet-sync'
|
|
259
|
+
)
|
|
260
|
+
.option(
|
|
261
|
+
'--export',
|
|
262
|
+
'Print the stored fleet configuration: the one fleet-sync last applied'
|
|
263
|
+
)
|
|
264
|
+
.option('--example', 'Print an example fleet.json to start a project from')
|
|
265
|
+
.option('--push', 'Upload this machine’s changes to the bucket')
|
|
266
|
+
.option(
|
|
267
|
+
'--restore',
|
|
268
|
+
'Replace this machine’s copy with the bucket’s version'
|
|
269
|
+
)
|
|
270
|
+
.option('--yes', 'With --restore: discard changes the bucket does not hold')
|
|
271
|
+
.addHelpText(
|
|
272
|
+
'after',
|
|
273
|
+
'\nRead-only without a flag. The bucket is the one named under "state" in fleet.json. Every command reads its credentials from DEVFLEET_BUCKET_ACCESS_KEY_ID and DEVFLEET_BUCKET_SECRET_ACCESS_KEY; --init also asks on the terminal. They are never taken from arguments.'
|
|
274
|
+
)
|
|
275
|
+
.action(async (_options, command: Command) => {
|
|
276
|
+
complete(await fleetState(command.optsWithGlobals<FleetStateOptions>()));
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function registerFleet(
|
|
281
|
+
program: Command,
|
|
282
|
+
defaults: Settings,
|
|
283
|
+
complete: Complete
|
|
284
|
+
): void {
|
|
285
|
+
const registration = { program, defaults, complete };
|
|
286
|
+
registerStatus(registration);
|
|
287
|
+
register(
|
|
288
|
+
registration,
|
|
289
|
+
'fleet-discover',
|
|
290
|
+
'List computers visible through Tailscale, including offline devices'
|
|
291
|
+
);
|
|
292
|
+
registerSync(registration);
|
|
293
|
+
registerState(registration);
|
|
294
|
+
registerRepair(registration);
|
|
295
|
+
registerNodes(registration);
|
|
296
|
+
}
|