@crvouga/dev-fleet 0.8.0 → 0.9.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/README.md +65 -9
- package/agents/audit.md +18 -18
- package/ansible/agents/agents.yml +134 -0
- package/ansible/agents/templates/antigravity.settings.json.j2 +3 -0
- package/ansible/agents/templates/claude.settings.json.j2 +3 -0
- package/ansible/agents/templates/codex.config.toml.j2 +11 -0
- package/ansible/agents/templates/gemini.settings.json.j2 +14 -0
- package/ansible/agents/templates/grok.config.toml.j2 +19 -0
- package/ansible/agents/templates/opencode.json.j2 +24 -0
- package/ansible/agents/templates/receipt.json.j2 +10 -0
- package/ansible/ansible.cfg +14 -0
- package/ansible/github/git.yml +82 -0
- package/ansible/macos/manager-privilege.yml +55 -9
- package/ansible/macos/manager.yml +154 -7
- package/ansible/macos/policy.json +13 -0
- package/ansible/macos/t3-policy.yml +5 -3
- package/ansible/macos/vault-renew.yml +85 -0
- package/ansible/monitoring/agent-darwin.yml +3 -0
- package/ansible/monitoring/agent.yml +2 -0
- package/ansible/monitoring/hub-darwin.yml +3 -0
- package/ansible/monitoring/hub-route-darwin.yml +1 -0
- package/ansible/net/remote-probe.sh +4 -1
- package/ansible/platforms.json +2 -2
- package/ansible/pyproject.toml +5 -0
- package/ansible/ubuntu/baseline.yml +39 -13
- package/ansible/ubuntu/disk-gc.py +6 -2
- package/ansible/ubuntu/firewall.nft.j2 +50 -0
- package/ansible/ubuntu/policy.json +125 -6
- package/ansible/ubuntu/reboot.yml +89 -0
- package/ansible/ubuntu/recovery.yml +13 -3
- package/ansible/ubuntu/t3/policy.json +3 -0
- package/ansible/ubuntu/t3-desktop.yml +23 -2
- package/ansible/ubuntu/t3-headless.yml +128 -0
- package/ansible/ubuntu/t3-health.sh +24 -10
- package/ansible/ubuntu/t3-idle.py +37 -2
- package/ansible/ubuntu/t3-policy.yml +4 -1
- package/ansible/ubuntu/t3-route.yml +1 -0
- package/ansible/ubuntu/tailscale.yml +54 -8
- package/ansible/ubuntu/tasks/browser.yml +3 -0
- package/ansible/ubuntu/tasks/containers-off.yml +43 -0
- package/ansible/ubuntu/tasks/docker.yml +53 -6
- package/ansible/ubuntu/tasks/dpkg.yml +4 -0
- package/ansible/ubuntu/tasks/features.yml +22 -0
- package/ansible/ubuntu/tasks/firewall.yml +172 -0
- package/ansible/ubuntu/tasks/install.yml +2 -1
- package/ansible/ubuntu/tasks/packages.yml +4 -2
- package/ansible/ubuntu/tasks/release.yml +6 -0
- package/ansible/ubuntu/tasks/resources.yml +6 -4
- package/ansible/ubuntu/tasks/services.yml +2 -2
- package/ansible/ubuntu/tasks/sizing.yml +49 -0
- package/ansible/ubuntu/tasks/snapshot.yml +14 -3
- package/ansible/ubuntu/tasks/ssh.yml +61 -4
- package/ansible/ubuntu/tasks/t3-restart.yml +3 -1
- package/ansible/ubuntu/tasks/user.yml +1 -0
- package/ansible/ubuntu/worker-account.yml +117 -0
- package/ansible/uv.lock +265 -0
- package/bin/devfleet.mjs +42 -4
- package/bootstrap/fleet-remote-bootstrap.sh +91 -25
- package/bootstrap/global-config.ts +29 -3
- package/bootstrap/package-smoke.ts +127 -0
- package/bootstrap/pins.ts +189 -0
- package/bootstrap/release-notes.ts +88 -0
- package/bootstrap/release-version.ts +41 -7
- package/bootstrap/worker-features.ts +114 -0
- package/bootstrap/worker-seed.yaml +7 -3
- package/bootstrap/worker-tools.ts +39 -9
- package/cliproxy/README.md +1 -1
- package/cliproxy/docs/health-workflow.example.yml +20 -143
- package/docs/authentication.md +19 -11
- package/docs/consumer-setup.md +42 -27
- package/docs/convergence.md +67 -0
- package/docs/declared.md +61 -34
- package/docs/machine-setup.md +73 -7
- package/docs/manager-setup.md +10 -3
- package/docs/operations.md +64 -9
- package/docs/releasing.md +49 -1
- package/docs/runbooks.md +98 -7
- package/docs/state.md +23 -11
- package/docs/support-matrix.md +40 -4
- package/docs/topology.md +67 -6
- package/docs/ubuntu-sync.md +78 -28
- package/docs/worker-contract.md +122 -19
- package/docs/worker-requirements.md +107 -0
- package/examples/fleet.json +1 -0
- package/examples/workers-from-infrastructure/main.tf +132 -0
- package/examples/workers-from-infrastructure/worker-records.json +14 -0
- package/llms.txt +3 -1
- package/mise.lock +29 -0
- package/mise.toml +5 -3
- package/package.json +2 -2
- package/schema/check-ids.json +1967 -170
- package/schema/check-ids.schema.json +34 -0
- package/schema/fleet.schema.json +327 -8
- package/schema/output/check.schema.json +1 -1
- package/schema/output/fleet-state-definition.schema.json +14 -0
- package/schema/output/fleet-sync-schedule.schema.json +26 -0
- package/schema/output/fleet-sync.schema.json +18 -0
- package/schema/output/report.schema.json +3 -2
- package/schema/worker-record.schema.json +76 -0
- package/src/agents/converge.ts +140 -24
- package/src/agents/files.ts +2 -32
- package/src/agents/native-verify.ts +34 -3
- package/src/agents/secrets.ts +39 -4
- package/src/check-owners.ts +61 -2
- package/src/check-registry.ts +630 -0
- package/src/cli/agent-guide.ts +3 -3
- package/src/cli/cli.ts +4 -0
- package/src/cli/fleet-manifest.ts +9 -3
- package/src/cli/fleet-node-lifecycle.ts +313 -64
- package/src/cli/fleet-node-park.ts +238 -0
- package/src/cli/fleet-node-seed.ts +51 -35
- package/src/cli/fleet-state.ts +2 -1
- package/src/cli/fleet-sync-checkout.ts +7 -0
- package/src/cli/fleet-sync-enrollment.ts +193 -32
- package/src/cli/fleet-sync-forward.ts +4 -0
- package/src/cli/fleet-sync-manager.ts +103 -33
- package/src/cli/fleet-sync-output.ts +31 -11
- package/src/cli/fleet-sync-plan.ts +65 -9
- package/src/cli/fleet-sync-records.ts +113 -0
- package/src/cli/fleet-sync-remote.ts +151 -9
- package/src/cli/fleet-sync-schedule.ts +14 -4
- package/src/cli/fleet-sync-state.ts +28 -16
- package/src/cli/fleet-sync-vault.ts +77 -0
- package/src/cli/fleet-sync-worker.ts +214 -0
- package/src/cli/fleet-sync.ts +150 -19
- package/src/cli/fleet-validate.ts +59 -21
- package/src/cli/fleet-watch.ts +7 -1
- package/src/cli/fleet.ts +87 -5
- package/src/cli/managed-health.ts +25 -1
- package/src/cli/register-fleet.ts +115 -4
- package/src/cli/registry.ts +14 -0
- package/src/cli/tailnet.ts +43 -10
- package/src/cli/ui-data.ts +3 -1
- package/src/cli/worker-usage-report.ts +63 -0
- package/src/clock.ts +42 -0
- package/src/definition.ts +332 -9
- package/src/diagnostics.ts +5 -2
- package/src/exposure.ts +102 -0
- package/src/fleet-config.ts +3 -1
- package/src/git-identity.ts +76 -3
- package/src/ide/ide.ts +5 -0
- package/src/ide/t3.ts +12 -2
- package/src/input-hashes.ts +6 -0
- package/src/machine/machine-class.ts +59 -0
- package/src/machine/platform.ts +90 -11
- package/src/machine/state.ts +37 -15
- package/src/machine/support-matrix.ts +11 -9
- package/src/machine-health.ts +28 -5
- package/src/manager-health.ts +148 -2
- package/src/manager-sync.ts +65 -4
- package/src/monitoring/beszel.ts +8 -8
- package/src/monitoring/enroll.ts +8 -1
- package/src/monitoring/hub-access.ts +10 -4
- package/src/monitoring/systems.ts +138 -0
- package/src/network-definition.ts +13 -4
- package/src/node-converge-linux.ts +136 -27
- package/src/node-converge.ts +13 -3
- package/src/platform/ansible-receipt.ts +17 -2
- package/src/platform/ansible.ts +40 -4
- package/src/platform/manager-host.ts +25 -8
- package/src/platform/resource-verify.ts +18 -7
- package/src/platform/t3-policy.ts +24 -10
- package/src/platform/t3-service.ts +109 -34
- package/src/platform/ubuntu-release.ts +24 -0
- package/src/platform/ubuntu-verify.ts +31 -3
- package/src/platform/ubuntu-worker.ts +58 -0
- package/src/platform/worker-platform.ts +72 -0
- package/src/protocol.ts +1 -1
- package/src/public-reachability.ts +186 -0
- package/src/remote-bootstrap.ts +50 -12
- package/src/remote-operation.ts +18 -4
- package/src/repair-plan.ts +23 -12
- package/src/report.ts +2 -1
- package/src/schedule-check.ts +21 -2
- package/src/secrets.ts +4 -0
- package/src/service-auth.ts +55 -48
- package/src/ssh-connection.ts +141 -0
- package/src/ssh.ts +63 -24
- package/src/state/store.ts +4 -2
- package/src/state/sync.ts +35 -6
- package/src/state/workers.ts +140 -0
- package/src/t3/pairing.ts +59 -0
- package/src/t3/peer-link.ts +333 -0
- package/src/t3/version.ts +1 -1
- package/src/tailnet-cleanup.ts +185 -0
- package/src/tailnet-control.ts +346 -0
- package/src/tailscale.ts +1 -1
- package/src/toolchain-checks.ts +11 -1
- package/src/types.ts +4 -2
- package/src/worker-account.ts +63 -0
- package/src/worker-activity.ts +303 -0
- package/src/worker-capacity.ts +31 -0
- package/src/worker-exposure.ts +388 -0
- package/src/worker-features.ts +201 -0
- package/src/worker-host-checks.ts +71 -20
- package/src/worker-manifest.ts +27 -4
- package/src/worker-parking.ts +70 -0
- package/src/worker-policy.ts +17 -1
- package/src/worker-readiness.ts +15 -3
- package/src/worker-reboot.ts +129 -0
- package/src/worker-records.ts +305 -0
- package/src/worker-seed.ts +16 -11
- package/src/worker-sizing.ts +76 -0
- package/src/cli/schedule-logs.ts +0 -36
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
- name: Inspect effective SSH configuration before changes
|
|
3
3
|
ansible.builtin.command: /usr/sbin/sshd -T
|
|
4
4
|
changed_when: false
|
|
5
|
+
check_mode: false
|
|
5
6
|
register: effective_ssh
|
|
6
7
|
- name: Inspect any existing managed SSH fragment
|
|
7
8
|
ansible.builtin.stat:
|
|
@@ -13,12 +14,27 @@
|
|
|
13
14
|
when: ssh_owned_file.stat.exists
|
|
14
15
|
register: ssh_previous
|
|
15
16
|
no_log: true
|
|
17
|
+
# Authentication is hardened in a fragment of its own that sorts before the
|
|
18
|
+
# image's (cloud-init writes 50-cloud-init.conf): sshd keeps the first value it
|
|
19
|
+
# reads. Listeners stay as they are; the firewall (firewall.yml) decides which
|
|
20
|
+
# interfaces reach them.
|
|
21
|
+
- name: Inspect any existing managed SSH hardening fragment
|
|
22
|
+
ansible.builtin.stat:
|
|
23
|
+
path: /etc/ssh/sshd_config.d/10-personal-fleet-exposure.conf
|
|
24
|
+
register: ssh_hardening_file
|
|
25
|
+
- name: Read previous hardening fragment for validation rollback
|
|
26
|
+
ansible.builtin.slurp:
|
|
27
|
+
src: /etc/ssh/sshd_config.d/10-personal-fleet-exposure.conf
|
|
28
|
+
when: ssh_hardening_file.stat.exists
|
|
29
|
+
register: ssh_hardening_previous
|
|
30
|
+
no_log: true
|
|
16
31
|
- name: Refuse to overwrite unmanaged SSH configuration
|
|
17
32
|
ansible.builtin.assert:
|
|
18
33
|
that:
|
|
19
34
|
- not ssh_owned_file.stat.exists or (ssh_previous.content | b64decode).startswith('# Managed by personal Fleet')
|
|
35
|
+
- not ssh_hardening_file.stat.exists or (ssh_hardening_previous.content | b64decode).startswith('# Managed by personal Fleet')
|
|
20
36
|
fail_msg: SSH_UNMANAGED_CONFIG_CONFLICT; review the existing fragment before adoption.
|
|
21
|
-
- name: Stage and validate forwarding policy while preserving listeners
|
|
37
|
+
- name: Stage and validate forwarding and authentication policy while preserving listeners
|
|
22
38
|
block:
|
|
23
39
|
- name: Write forwarding-only owned fragment
|
|
24
40
|
ansible.builtin.copy:
|
|
@@ -28,14 +44,31 @@
|
|
|
28
44
|
mode: '0644'
|
|
29
45
|
backup: true
|
|
30
46
|
content: |
|
|
31
|
-
# Managed by personal Fleet;
|
|
47
|
+
# Managed by personal Fleet; forwarding and keep-alive. Authentication: 10-personal-fleet-exposure.conf.
|
|
32
48
|
AllowTcpForwarding yes
|
|
33
49
|
ClientAliveInterval 30
|
|
34
50
|
ClientAliveCountMax 3
|
|
35
51
|
register: ssh_fragment
|
|
52
|
+
- name: Write keys-only authentication fragment
|
|
53
|
+
ansible.builtin.copy:
|
|
54
|
+
dest: /etc/ssh/sshd_config.d/10-personal-fleet-exposure.conf
|
|
55
|
+
owner: root
|
|
56
|
+
group: root
|
|
57
|
+
mode: '0644'
|
|
58
|
+
backup: true
|
|
59
|
+
content: |
|
|
60
|
+
# Managed by personal Fleet; keys only and no root login (worker exposure policy).
|
|
61
|
+
PubkeyAuthentication yes
|
|
62
|
+
PasswordAuthentication no
|
|
63
|
+
KbdInteractiveAuthentication no
|
|
64
|
+
PermitEmptyPasswords no
|
|
65
|
+
PermitRootLogin no
|
|
66
|
+
register: ssh_hardening
|
|
36
67
|
- name: Validate complete SSH configuration
|
|
37
68
|
ansible.builtin.command: /usr/sbin/sshd -t
|
|
38
69
|
changed_when: false
|
|
70
|
+
# Verifies a change a dry run does not make.
|
|
71
|
+
when: not ansible_check_mode
|
|
39
72
|
rescue:
|
|
40
73
|
- name: Restore previous fragment after validation failure
|
|
41
74
|
ansible.builtin.copy:
|
|
@@ -51,6 +84,20 @@
|
|
|
51
84
|
path: /etc/ssh/sshd_config.d/60-personal-fleet.conf
|
|
52
85
|
state: absent
|
|
53
86
|
when: not ssh_owned_file.stat.exists
|
|
87
|
+
- name: Restore previous hardening fragment after validation failure
|
|
88
|
+
ansible.builtin.copy:
|
|
89
|
+
content: '{{ ssh_hardening_previous.content | b64decode }}'
|
|
90
|
+
dest: /etc/ssh/sshd_config.d/10-personal-fleet-exposure.conf
|
|
91
|
+
owner: root
|
|
92
|
+
group: root
|
|
93
|
+
mode: '0644'
|
|
94
|
+
when: ssh_hardening_file.stat.exists
|
|
95
|
+
no_log: true
|
|
96
|
+
- name: Remove rejected new hardening fragment
|
|
97
|
+
ansible.builtin.file:
|
|
98
|
+
path: /etc/ssh/sshd_config.d/10-personal-fleet-exposure.conf
|
|
99
|
+
state: absent
|
|
100
|
+
when: not ssh_hardening_file.stat.exists
|
|
54
101
|
- name: Report validation failure without reloading sshd
|
|
55
102
|
ansible.builtin.fail:
|
|
56
103
|
msg: SSH_CONFIG_REJECTED; previous configuration restored, active service untouched.
|
|
@@ -59,13 +106,23 @@
|
|
|
59
106
|
name: ssh.service
|
|
60
107
|
enabled: true
|
|
61
108
|
state: started
|
|
62
|
-
- name: Reload validated forwarding policy without terminating sessions
|
|
109
|
+
- name: Reload validated forwarding and authentication policy without terminating sessions
|
|
63
110
|
ansible.builtin.systemd_service:
|
|
64
111
|
name: ssh.service
|
|
65
112
|
state: reloaded
|
|
66
|
-
when: ssh_fragment.changed
|
|
113
|
+
when: ssh_fragment.changed or ssh_hardening.changed
|
|
67
114
|
- name: Verify effective forwarding after reload
|
|
68
115
|
ansible.builtin.command: /usr/sbin/sshd -T
|
|
69
116
|
changed_when: false
|
|
117
|
+
# Verifies a change a dry run does not make.
|
|
118
|
+
when: not ansible_check_mode
|
|
70
119
|
register: ssh_verified
|
|
71
120
|
failed_when: "'allowtcpforwarding yes' not in ssh_verified.stdout"
|
|
121
|
+
- name: Require keys-only authentication without root login after reload
|
|
122
|
+
ansible.builtin.assert:
|
|
123
|
+
that:
|
|
124
|
+
- "'passwordauthentication no' in ssh_verified.stdout_lines"
|
|
125
|
+
- "'kbdinteractiveauthentication no' in ssh_verified.stdout_lines"
|
|
126
|
+
- "'permitrootlogin no' in ssh_verified.stdout_lines"
|
|
127
|
+
fail_msg: SSH_HARDENING_OVERRIDDEN; an earlier sshd_config fragment or Match block keeps password or root login; remove it, then rerun fleet-sync.
|
|
128
|
+
when: not ansible_check_mode
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# running turn. A busy T3 keeps its turns; the restart is recorded as pending
|
|
4
4
|
# (with the time it was first deferred) and personal-worker-recovery applies it
|
|
5
5
|
# at the first idle check (t3-health.sh). t3_restart_busy restarts it anyway.
|
|
6
|
+
# `t3_unit` names the unit of a headless worker (t3-headless.yml).
|
|
6
7
|
- name: Ask T3 whether an agent turn is running
|
|
7
8
|
become: true
|
|
8
9
|
become_user: '{{ development_user }}'
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
- '{{ development_home }}/.t3/userdata/statev2.sqlite'
|
|
17
18
|
register: t3_idle
|
|
18
19
|
changed_when: false
|
|
20
|
+
check_mode: false
|
|
19
21
|
failed_when: false
|
|
20
22
|
- name: Restart T3 while it runs no agent turn
|
|
21
23
|
become: true
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
|
|
25
27
|
ansible.builtin.systemd_service:
|
|
26
28
|
scope: user
|
|
27
|
-
name: t3code-desktop.service
|
|
29
|
+
name: "{{ t3_unit | default('t3code-desktop.service') }}"
|
|
28
30
|
state: restarted
|
|
29
31
|
when: t3_idle is not skipped and (t3_idle.rc == 0 or t3_restart_busy | default(false) | bool)
|
|
30
32
|
- name: Record the restart as pending while agent turns run
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
# The declared worker account on a machine whose image lets only root in
|
|
3
|
+
# (many rented servers). fleet-sync applies it from the manager, once, over
|
|
4
|
+
# the root login: it creates workers[].network.user with the key root was
|
|
5
|
+
# reached with and the fleet's sudo rule (the path and marker
|
|
6
|
+
# authorize-sudo.sh writes), then continues as that account. The developer
|
|
7
|
+
# toolchain never runs as root. Turning root login off afterwards is the SSH
|
|
8
|
+
# policy's. Variables: target (the SSH alias, or the declared tailnet name),
|
|
9
|
+
# login (root, or empty for the alias's own user, which is root), user (the
|
|
10
|
+
# declared account) and ssh_options (the SSH options every session of the
|
|
11
|
+
# manager takes, src/ssh-connection.ts, with the pinned host key).
|
|
12
|
+
- name: Address the worker over its root login
|
|
13
|
+
hosts: localhost
|
|
14
|
+
connection: local
|
|
15
|
+
gather_facts: false
|
|
16
|
+
tasks:
|
|
17
|
+
- name: Check the request
|
|
18
|
+
ansible.builtin.assert:
|
|
19
|
+
that:
|
|
20
|
+
- "target is match('^[a-zA-Z0-9][a-zA-Z0-9._-]{0,252}$')"
|
|
21
|
+
- ssh_options is string and ssh_options is match('^-o ')
|
|
22
|
+
- "user is match('^[a-z_][a-z0-9_-]{0,31}$')"
|
|
23
|
+
- user != 'root'
|
|
24
|
+
- login | default('') in ['', 'root']
|
|
25
|
+
fail_msg: WORKER_ACCOUNT_UNDECLARED; declare workers[].network.user as a non-root account.
|
|
26
|
+
- name: Address the worker by its alias and its own user, with strict host keys
|
|
27
|
+
ansible.builtin.add_host:
|
|
28
|
+
name: '{{ target }}'
|
|
29
|
+
groups: [fleet_account]
|
|
30
|
+
ansible_python_interpreter: /usr/bin/python3
|
|
31
|
+
ansible_ssh_args: -o ControlMaster=no -o ControlPath=none
|
|
32
|
+
ansible_ssh_common_args: '{{ ssh_options }}'
|
|
33
|
+
account_user: '{{ user }}'
|
|
34
|
+
changed_when: false
|
|
35
|
+
when: login | default('') == ''
|
|
36
|
+
- name: Address the worker by its alias as root, with strict host keys
|
|
37
|
+
ansible.builtin.add_host:
|
|
38
|
+
name: '{{ target }}'
|
|
39
|
+
groups: [fleet_account]
|
|
40
|
+
ansible_python_interpreter: /usr/bin/python3
|
|
41
|
+
ansible_ssh_args: -o ControlMaster=no -o ControlPath=none
|
|
42
|
+
ansible_ssh_common_args: '{{ ssh_options }}'
|
|
43
|
+
ansible_user: root
|
|
44
|
+
account_user: '{{ user }}'
|
|
45
|
+
changed_when: false
|
|
46
|
+
when: login | default('') == 'root'
|
|
47
|
+
|
|
48
|
+
- name: Create the declared worker account
|
|
49
|
+
hosts: fleet_account
|
|
50
|
+
gather_facts: false
|
|
51
|
+
serial: 1
|
|
52
|
+
tasks:
|
|
53
|
+
- name: Ask which account the login is
|
|
54
|
+
ansible.builtin.command:
|
|
55
|
+
argv: [id, -u]
|
|
56
|
+
register: login_uid
|
|
57
|
+
changed_when: false
|
|
58
|
+
check_mode: false
|
|
59
|
+
- name: Require the root login
|
|
60
|
+
ansible.builtin.assert:
|
|
61
|
+
that: login_uid.stdout == '0'
|
|
62
|
+
fail_msg: WORKER_ACCOUNT_ROOT_REQUIRED; only a root login creates the declared account.
|
|
63
|
+
- name: Create the account, its home and its sudo group membership
|
|
64
|
+
ansible.builtin.user:
|
|
65
|
+
name: '{{ account_user }}'
|
|
66
|
+
home: '/home/{{ account_user }}'
|
|
67
|
+
create_home: true
|
|
68
|
+
shell: /bin/bash
|
|
69
|
+
groups: [sudo]
|
|
70
|
+
append: true
|
|
71
|
+
password_lock: true
|
|
72
|
+
- name: Create the account's SSH directory
|
|
73
|
+
ansible.builtin.file:
|
|
74
|
+
path: '/home/{{ account_user }}/.ssh'
|
|
75
|
+
state: directory
|
|
76
|
+
owner: '{{ account_user }}'
|
|
77
|
+
group: '{{ account_user }}'
|
|
78
|
+
mode: '0700'
|
|
79
|
+
# The key the manager reached root with; an account that already holds
|
|
80
|
+
# keys of its own keeps them.
|
|
81
|
+
- name: Authorise the key root was reached with
|
|
82
|
+
ansible.builtin.copy:
|
|
83
|
+
src: /root/.ssh/authorized_keys
|
|
84
|
+
remote_src: true
|
|
85
|
+
dest: '/home/{{ account_user }}/.ssh/authorized_keys'
|
|
86
|
+
owner: '{{ account_user }}'
|
|
87
|
+
group: '{{ account_user }}'
|
|
88
|
+
mode: '0600'
|
|
89
|
+
force: false
|
|
90
|
+
- name: Inspect an existing sudo rule of the account
|
|
91
|
+
ansible.builtin.stat:
|
|
92
|
+
path: '/etc/sudoers.d/personal-fleet-{{ account_user }}'
|
|
93
|
+
register: sudo_rule
|
|
94
|
+
- name: Read the ownership marker of the existing rule
|
|
95
|
+
ansible.builtin.command:
|
|
96
|
+
argv:
|
|
97
|
+
- grep
|
|
98
|
+
- -qxF
|
|
99
|
+
- '# Managed by personal Fleet: designated development user'
|
|
100
|
+
- '/etc/sudoers.d/personal-fleet-{{ account_user }}'
|
|
101
|
+
register: sudo_marker
|
|
102
|
+
changed_when: false
|
|
103
|
+
check_mode: false
|
|
104
|
+
failed_when: false
|
|
105
|
+
when: sudo_rule.stat.exists
|
|
106
|
+
- name: Never take over a rule the fleet did not write
|
|
107
|
+
ansible.builtin.assert:
|
|
108
|
+
that: not sudo_rule.stat.exists or sudo_marker.rc == 0
|
|
109
|
+
fail_msg: SUDOERS_OWNERSHIP_CONFLICT; preserve and review the existing fragment before adoption.
|
|
110
|
+
- name: Grant the account the fleet's sudo rule
|
|
111
|
+
ansible.builtin.copy:
|
|
112
|
+
dest: '/etc/sudoers.d/personal-fleet-{{ account_user }}'
|
|
113
|
+
content: "# Managed by personal Fleet: designated development user\n{{ account_user }} ALL=(ALL:ALL) NOPASSWD: ALL\n"
|
|
114
|
+
owner: root
|
|
115
|
+
group: root
|
|
116
|
+
mode: '0440'
|
|
117
|
+
validate: visudo -cf %s
|
package/ansible/uv.lock
CHANGED
|
@@ -2,6 +2,22 @@ version = 1
|
|
|
2
2
|
revision = 5
|
|
3
3
|
requires-python = "==3.13.16"
|
|
4
4
|
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "ansible-compat"
|
|
7
|
+
version = "26.11.0"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
dependencies = [
|
|
10
|
+
{ name = "ansible-core" },
|
|
11
|
+
{ name = "jsonschema" },
|
|
12
|
+
{ name = "packaging" },
|
|
13
|
+
{ name = "pyyaml" },
|
|
14
|
+
{ name = "subprocess-tee" },
|
|
15
|
+
]
|
|
16
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e8/b3/64652d98718d5a597a15c2eb95e55fd6935b1c4eeee316370e5a498a2a6e/ansible_compat-26.11.0.tar.gz", hash = "sha256:35cbe829e0b5a9acb85d34b8d5da2eb45acaf767f6651f90e0ab2f3a96a060bb", size = 295998, upload-time = "2026-11-05T08:41:46.716Z" }
|
|
17
|
+
wheels = [
|
|
18
|
+
{ url = "https://files.pythonhosted.org/packages/88/3e/696ba32885c888867214baba910cc07c13aa410884a04ff0892f9b744e3c/ansible_compat-26.11.0-py3-none-any.whl", hash = "sha256:c06a486b39ced440f9f6fb9a99583c73d9239c94ece822a27ea2242173462dda", size = 29027, upload-time = "2026-11-05T08:41:45.462Z" },
|
|
19
|
+
]
|
|
20
|
+
|
|
5
21
|
[[package]]
|
|
6
22
|
name = "ansible-core"
|
|
7
23
|
version = "2.19.9"
|
|
@@ -18,6 +34,73 @@ wheels = [
|
|
|
18
34
|
{ url = "https://files.pythonhosted.org/packages/3d/b9/f2c07e6cdba6a5882342d666ceea4eeeb6fea20ec325fac68c27bfcea722/ansible_core-2.19.9-py3-none-any.whl", hash = "sha256:18de80e3ea5a89d2ea84e4d4a3c6d7c121ea62fc3af2877ba083784e3d90b419", size = 2416269, upload-time = "2026-04-21T00:47:06.777Z" },
|
|
19
35
|
]
|
|
20
36
|
|
|
37
|
+
[[package]]
|
|
38
|
+
name = "ansible-lint"
|
|
39
|
+
version = "26.11.0"
|
|
40
|
+
source = { registry = "https://pypi.org/simple" }
|
|
41
|
+
dependencies = [
|
|
42
|
+
{ name = "ansible-compat" },
|
|
43
|
+
{ name = "ansible-core" },
|
|
44
|
+
{ name = "black" },
|
|
45
|
+
{ name = "cffi" },
|
|
46
|
+
{ name = "cryptography" },
|
|
47
|
+
{ name = "distro" },
|
|
48
|
+
{ name = "filelock" },
|
|
49
|
+
{ name = "jsonschema" },
|
|
50
|
+
{ name = "packaging" },
|
|
51
|
+
{ name = "pathspec" },
|
|
52
|
+
{ name = "pyyaml" },
|
|
53
|
+
{ name = "referencing" },
|
|
54
|
+
{ name = "ruamel-yaml" },
|
|
55
|
+
{ name = "subprocess-tee" },
|
|
56
|
+
{ name = "wcmatch" },
|
|
57
|
+
{ name = "yamllint" },
|
|
58
|
+
]
|
|
59
|
+
sdist = { url = "https://files.pythonhosted.org/packages/bc/22/4db7a3ab031c7f08f6b1340df9fbc7b730a2ee25ec21047c1a3da445af0a/ansible_lint-26.11.0.tar.gz", hash = "sha256:74813203aa5450d720c3d86b9d0d4ddc51d30e36ac69e1f0d9f7650f5a3b5dd3", size = 876436, upload-time = "2026-11-05T08:50:53.758Z" }
|
|
60
|
+
wheels = [
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/92/74/15896e47ad1087c1549f7d863631d05d661df0123747622e4cdab88c1428/ansible_lint-26.11.0-py3-none-any.whl", hash = "sha256:b778ea87fa873384b373e8c3fe53a06b3acc7b72ac283e2d2070f76fdf9f8358", size = 349253, upload-time = "2026-11-05T08:50:51.623Z" },
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[[package]]
|
|
65
|
+
name = "attrs"
|
|
66
|
+
version = "26.1.0"
|
|
67
|
+
source = { registry = "https://pypi.org/simple" }
|
|
68
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
|
|
69
|
+
wheels = [
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[[package]]
|
|
74
|
+
name = "black"
|
|
75
|
+
version = "26.10.1"
|
|
76
|
+
source = { registry = "https://pypi.org/simple" }
|
|
77
|
+
dependencies = [
|
|
78
|
+
{ name = "click" },
|
|
79
|
+
{ name = "mypy-extensions" },
|
|
80
|
+
{ name = "packaging" },
|
|
81
|
+
{ name = "pathspec" },
|
|
82
|
+
{ name = "platformdirs" },
|
|
83
|
+
{ name = "pytokens" },
|
|
84
|
+
]
|
|
85
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f8/65/a9611a6ec0a8c88d86e59385da02d68d9533f7e86a05913d20c67be54029/black-26.10.1.tar.gz", hash = "sha256:5f9f83beae62437e060dafd53d7f1fc327e3d3494f74d72ee5c2b73eb90fc4e7", size = 741979, upload-time = "2026-10-10T04:13:40.776Z" }
|
|
86
|
+
wheels = [
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/99/6b/bc0d39990bd7a71457d669639fa06acf6191bec28bafe8b8103f597cce76/black-26.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b6272cfd7e1e8e271f5b0e0207259fe2834687e5cb9b5f620b34a44db9754993", size = 2153812, upload-time = "2026-10-10T04:18:55.041Z" },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/85/0e/cc83b88a6b1a51fc051aa4e24663918f0bf4fa9fca98552ee93a125a6780/black-26.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:978113a40223a6aaefc17364176a809a320e6b288683841427fff04c6d7b4130", size = 1980352, upload-time = "2026-10-10T04:18:56.849Z" },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/7b/0f/4dd24ea0ebadbe05e293c3ea7fbebd4389f50508f714640c9b309a0849d2/black-26.10.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03c0ddd93bb392e71209903a691767eb366fe1a76deb9509ccbaae9e1f14bb52", size = 2055268, upload-time = "2026-10-10T04:18:58.974Z" },
|
|
90
|
+
{ url = "https://files.pythonhosted.org/packages/a2/06/221f8e81891ecf5e4df9c258a11037997f1f218533ffe4fa1dc4db37f19c/black-26.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6dba8138cdc99061ef07b958ac082d2aa057b6961d1936f9717c350f02bab5f", size = 1625938, upload-time = "2026-10-10T04:19:00.65Z" },
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/2b/3b/763d2dd073fc1e2cbf0fe5e584fe96913bf0d14cd725d1116f4b38453adf/black-26.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:d42dd2fac7c342ae67e64ee99c9532e20b2a84e92c79ed3317fa2ef54c801d93", size = 1470752, upload-time = "2026-10-10T04:19:03.009Z" },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/b1/28/9dd29175c1db777e6189e2a0bb5f37101adf19c3e509b236c8920f778d0b/black-26.10.1-py3-none-any.whl", hash = "sha256:28842f9a8207cc1df6eb983a35a14c5a0dfcd603d214fe82d84bef552afd2e3a", size = 239267, upload-time = "2026-10-10T04:13:38.808Z" },
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[[package]]
|
|
96
|
+
name = "bracex"
|
|
97
|
+
version = "3.2.2"
|
|
98
|
+
source = { registry = "https://pypi.org/simple" }
|
|
99
|
+
sdist = { url = "https://files.pythonhosted.org/packages/2d/1f/b7ce2efe406b87bb3693d587b91bcd8fa9030888870ceff29e4a0d233cb2/bracex-3.2.2.tar.gz", hash = "sha256:d18976160b3ba01a57d5c53a5c8c3aa2506efa9e0a3a62467251f82b8f563fde", size = 34980, upload-time = "2026-11-07T21:28:25.433Z" }
|
|
100
|
+
wheels = [
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/21/b5/52a750a062db07ba056cde1c52c802b898a96b70953389a0b2247e18f210/bracex-3.2.2-py3-none-any.whl", hash = "sha256:284d59c4656e5a0421ff691402074bd5847049d50410535764a2c522a47095bc", size = 14151, upload-time = "2026-11-07T21:28:24.028Z" },
|
|
102
|
+
]
|
|
103
|
+
|
|
21
104
|
[[package]]
|
|
22
105
|
name = "cffi"
|
|
23
106
|
version = "2.1.1"
|
|
@@ -43,6 +126,15 @@ wheels = [
|
|
|
43
126
|
{ url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
|
|
44
127
|
]
|
|
45
128
|
|
|
129
|
+
[[package]]
|
|
130
|
+
name = "click"
|
|
131
|
+
version = "8.5.1"
|
|
132
|
+
source = { registry = "https://pypi.org/simple" }
|
|
133
|
+
sdist = { url = "https://files.pythonhosted.org/packages/45/37/edbf1b0707709013c0cf2e8317e03d97ed29bf4cac709fe328904ae92a38/click-8.5.1.tar.gz", hash = "sha256:9bbfada76703fd1d784b83eea012e3e8c6a001a876543ef104fa56aaaeb072b4", size = 399151, upload-time = "2026-11-06T17:37:01.021Z" }
|
|
134
|
+
wheels = [
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/31/7d/164b7ffc8624fe9efb4221bf9af485d5c1e41b9ae118045d2b6dc5457d8b/click-8.5.1-py3-none-any.whl", hash = "sha256:8bb5376d2006950386f8c6427613334a2acadc5f559cb80e638d00b4df957e37", size = 126163, upload-time = "2026-11-06T17:36:59.287Z" },
|
|
136
|
+
]
|
|
137
|
+
|
|
46
138
|
[[package]]
|
|
47
139
|
name = "cryptography"
|
|
48
140
|
version = "50.0.2"
|
|
@@ -101,9 +193,35 @@ dependencies = [
|
|
|
101
193
|
{ name = "ansible-core" },
|
|
102
194
|
]
|
|
103
195
|
|
|
196
|
+
[package.dev-dependencies]
|
|
197
|
+
lint = [
|
|
198
|
+
{ name = "ansible-lint" },
|
|
199
|
+
]
|
|
200
|
+
|
|
104
201
|
[package.metadata]
|
|
105
202
|
requires-dist = [{ name = "ansible-core", specifier = "==2.19.9" }]
|
|
106
203
|
|
|
204
|
+
[package.metadata.requires-dev]
|
|
205
|
+
lint = [{ name = "ansible-lint", specifier = "==26.11.0" }]
|
|
206
|
+
|
|
207
|
+
[[package]]
|
|
208
|
+
name = "distro"
|
|
209
|
+
version = "1.9.0"
|
|
210
|
+
source = { registry = "https://pypi.org/simple" }
|
|
211
|
+
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
|
|
212
|
+
wheels = [
|
|
213
|
+
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
[[package]]
|
|
217
|
+
name = "filelock"
|
|
218
|
+
version = "4.2.10"
|
|
219
|
+
source = { registry = "https://pypi.org/simple" }
|
|
220
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/cf/05bcdacabdc0aec7dc8e19d76281bd82325df62dae3b6430e9ba57397689/filelock-4.2.10.tar.gz", hash = "sha256:fcc3c0cb8bfe7f4b3bb0e231c04b15dbad068cb3c2515f343d8fde4dce93bd5d", size = 670595, upload-time = "2026-11-09T18:58:00.54Z" }
|
|
221
|
+
wheels = [
|
|
222
|
+
{ url = "https://files.pythonhosted.org/packages/ef/90/5d0096a325aa0f83ba7a3968bfb485b67e32c947bdf9cd0fcb4b485be718/filelock-4.2.10-py3-none-any.whl", hash = "sha256:9479aaeb4db20b5a53e3da28493edd24720b66b8a0b84030aa56ccdf243d6857", size = 218040, upload-time = "2026-11-09T18:57:59.088Z" },
|
|
223
|
+
]
|
|
224
|
+
|
|
107
225
|
[[package]]
|
|
108
226
|
name = "jinja2"
|
|
109
227
|
version = "3.1.6"
|
|
@@ -116,6 +234,33 @@ wheels = [
|
|
|
116
234
|
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
|
117
235
|
]
|
|
118
236
|
|
|
237
|
+
[[package]]
|
|
238
|
+
name = "jsonschema"
|
|
239
|
+
version = "4.26.0"
|
|
240
|
+
source = { registry = "https://pypi.org/simple" }
|
|
241
|
+
dependencies = [
|
|
242
|
+
{ name = "attrs" },
|
|
243
|
+
{ name = "jsonschema-specifications" },
|
|
244
|
+
{ name = "referencing" },
|
|
245
|
+
{ name = "rpds-py" },
|
|
246
|
+
]
|
|
247
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" }
|
|
248
|
+
wheels = [
|
|
249
|
+
{ url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" },
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
[[package]]
|
|
253
|
+
name = "jsonschema-specifications"
|
|
254
|
+
version = "2025.9.1"
|
|
255
|
+
source = { registry = "https://pypi.org/simple" }
|
|
256
|
+
dependencies = [
|
|
257
|
+
{ name = "referencing" },
|
|
258
|
+
]
|
|
259
|
+
sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
|
|
260
|
+
wheels = [
|
|
261
|
+
{ url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
|
|
262
|
+
]
|
|
263
|
+
|
|
119
264
|
[[package]]
|
|
120
265
|
name = "markupsafe"
|
|
121
266
|
version = "3.0.4"
|
|
@@ -144,6 +289,15 @@ wheels = [
|
|
|
144
289
|
{ url = "https://files.pythonhosted.org/packages/9f/a8/9b662783ffaa1149221432a923cee562f78b9cbbb8baa3df9b3753e63e1e/markupsafe-3.0.4-cp313-cp313-win_arm64.whl", hash = "sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc", size = 14189, upload-time = "2026-10-02T23:05:29.917Z" },
|
|
145
290
|
]
|
|
146
291
|
|
|
292
|
+
[[package]]
|
|
293
|
+
name = "mypy-extensions"
|
|
294
|
+
version = "1.2.0"
|
|
295
|
+
source = { registry = "https://pypi.org/simple" }
|
|
296
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d0/6f/43edf3be2748d0e40974a5ee046a8bb4c9961a4bdd3e7d5c6dcbf32d1af6/mypy_extensions-1.2.0.tar.gz", hash = "sha256:c6e275a07f1c2fc096b62940452d0eaa3f3af7de45d704787866240a7c6d5a51", size = 6566, upload-time = "2026-11-03T16:26:33.692Z" }
|
|
297
|
+
wheels = [
|
|
298
|
+
{ url = "https://files.pythonhosted.org/packages/b5/99/7b1adac7e97ce7bbda34792dc6e9b2f5cc5cffd1875883f78a818b2a2687/mypy_extensions-1.2.0-py3-none-any.whl", hash = "sha256:993e7a20aa5671eb41a7132cf555af30062da99729601601b9a8382b11cae768", size = 5114, upload-time = "2026-11-03T16:26:32.731Z" },
|
|
299
|
+
]
|
|
300
|
+
|
|
147
301
|
[[package]]
|
|
148
302
|
name = "packaging"
|
|
149
303
|
version = "26.3"
|
|
@@ -153,6 +307,24 @@ wheels = [
|
|
|
153
307
|
{ url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
|
|
154
308
|
]
|
|
155
309
|
|
|
310
|
+
[[package]]
|
|
311
|
+
name = "pathspec"
|
|
312
|
+
version = "1.1.1"
|
|
313
|
+
source = { registry = "https://pypi.org/simple" }
|
|
314
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" }
|
|
315
|
+
wheels = [
|
|
316
|
+
{ url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
|
|
317
|
+
]
|
|
318
|
+
|
|
319
|
+
[[package]]
|
|
320
|
+
name = "platformdirs"
|
|
321
|
+
version = "4.14.5"
|
|
322
|
+
source = { registry = "https://pypi.org/simple" }
|
|
323
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/9a/8475be631fb33ca1466915e70fd594c42a4e84b512db650eaa14f90fe75d/platformdirs-4.14.5.tar.gz", hash = "sha256:32e9921db7385aa8169bee4fe9501923e5715ba9fb7ea3b9c69b0d8e0639f7a1", size = 71464, upload-time = "2026-11-04T16:54:50.358Z" }
|
|
324
|
+
wheels = [
|
|
325
|
+
{ url = "https://files.pythonhosted.org/packages/7c/ae/ecbbbfd0e4343a6a521d49b3c8656fb4643dee2a4f6eaf941cf3280391a8/platformdirs-4.14.5-py3-none-any.whl", hash = "sha256:8e778c6fa421516f4d3369449f8df3b0e1b72c0e0005fe88e3ec772d0a14e750", size = 35402, upload-time = "2026-11-04T16:54:48.875Z" },
|
|
326
|
+
]
|
|
327
|
+
|
|
156
328
|
[[package]]
|
|
157
329
|
name = "pycparser"
|
|
158
330
|
version = "3.12"
|
|
@@ -162,6 +334,20 @@ wheels = [
|
|
|
162
334
|
{ url = "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl", hash = "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", size = 62600, upload-time = "2026-10-22T13:40:02.021Z" },
|
|
163
335
|
]
|
|
164
336
|
|
|
337
|
+
[[package]]
|
|
338
|
+
name = "pytokens"
|
|
339
|
+
version = "0.4.1"
|
|
340
|
+
source = { registry = "https://pypi.org/simple" }
|
|
341
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" }
|
|
342
|
+
wheels = [
|
|
343
|
+
{ url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" },
|
|
344
|
+
{ url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" },
|
|
345
|
+
{ url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" },
|
|
346
|
+
{ url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" },
|
|
347
|
+
{ url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" },
|
|
348
|
+
{ url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" },
|
|
349
|
+
]
|
|
350
|
+
|
|
165
351
|
[[package]]
|
|
166
352
|
name = "pyyaml"
|
|
167
353
|
version = "6.0.3"
|
|
@@ -180,6 +366,19 @@ wheels = [
|
|
|
180
366
|
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
|
181
367
|
]
|
|
182
368
|
|
|
369
|
+
[[package]]
|
|
370
|
+
name = "referencing"
|
|
371
|
+
version = "0.37.0"
|
|
372
|
+
source = { registry = "https://pypi.org/simple" }
|
|
373
|
+
dependencies = [
|
|
374
|
+
{ name = "attrs" },
|
|
375
|
+
{ name = "rpds-py" },
|
|
376
|
+
]
|
|
377
|
+
sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
|
|
378
|
+
wheels = [
|
|
379
|
+
{ url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
|
|
380
|
+
]
|
|
381
|
+
|
|
183
382
|
[[package]]
|
|
184
383
|
name = "resolvelib"
|
|
185
384
|
version = "1.2.1"
|
|
@@ -188,3 +387,69 @@ sdist = { url = "https://files.pythonhosted.org/packages/1d/14/4669927e06631070e
|
|
|
188
387
|
wheels = [
|
|
189
388
|
{ url = "https://files.pythonhosted.org/packages/e2/23/c941a0d0353681ca138489983c4309e0f5095dfd902e1357004f2357ddf2/resolvelib-1.2.1-py3-none-any.whl", hash = "sha256:fb06b66c8da04172d9e72a21d7d06186d8919e32ae5ab5cdf5b9d920be805ac2", size = 18737, upload-time = "2025-10-11T01:07:43.081Z" },
|
|
190
389
|
]
|
|
390
|
+
|
|
391
|
+
[[package]]
|
|
392
|
+
name = "rpds-py"
|
|
393
|
+
version = "2026.9.1"
|
|
394
|
+
source = { registry = "https://pypi.org/simple" }
|
|
395
|
+
sdist = { url = "https://files.pythonhosted.org/packages/42/68/3bd46b8a5e01d3c2ebdf9c5e9497912e3fe0cde02bac21a7130ca866e403/rpds_py-2026.9.1.tar.gz", hash = "sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12", size = 63948, upload-time = "2026-10-04T16:32:36.469Z" }
|
|
396
|
+
wheels = [
|
|
397
|
+
{ url = "https://files.pythonhosted.org/packages/83/ea/ee88fd9e756ff93fb6b1182a47ec09504a242620e33ce1d20679efefe841/rpds_py-2026.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9", size = 346229, upload-time = "2026-10-04T16:29:38.82Z" },
|
|
398
|
+
{ url = "https://files.pythonhosted.org/packages/57/71/a097d6552f837500fc36e6b23d09cfb9890c3cc47531f9ca64e149799615/rpds_py-2026.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858", size = 340514, upload-time = "2026-10-04T16:29:40.405Z" },
|
|
399
|
+
{ url = "https://files.pythonhosted.org/packages/bd/b7/497e85768bf4e0d8ddbaa096a4cac31d1509251dee2728a8490aa367e0b5/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063", size = 372611, upload-time = "2026-10-04T16:29:41.778Z" },
|
|
400
|
+
{ url = "https://files.pythonhosted.org/packages/52/4b/74ab4108916250b6e198e0d3af05bc6835eb046315f22f7a0ceb49667c5a/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025", size = 377712, upload-time = "2026-10-04T16:29:43.242Z" },
|
|
401
|
+
{ url = "https://files.pythonhosted.org/packages/0c/8e/067e77d9d7b3cc793c9d909b7e97e7aadbbb1fb094093b6876902cc96d38/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5", size = 488106, upload-time = "2026-10-04T16:29:44.692Z" },
|
|
402
|
+
{ url = "https://files.pythonhosted.org/packages/3c/b4/c5aae6c2dde269bf955f6b7d35065c655a57e47750d9668052ad67e74dda/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0", size = 390795, upload-time = "2026-10-04T16:29:46.129Z" },
|
|
403
|
+
{ url = "https://files.pythonhosted.org/packages/a0/36/76fab39973ee11e7f9f357c55138197bb01c86f6502cb76487e3b4f42db0/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b", size = 370794, upload-time = "2026-10-04T16:29:47.603Z" },
|
|
404
|
+
{ url = "https://files.pythonhosted.org/packages/3d/fe/cd2a80e6d7b871937a60e935c5d507aa390d143f4ff3636f640b9733d5df/rpds_py-2026.9.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33", size = 375673, upload-time = "2026-10-04T16:29:49.223Z" },
|
|
405
|
+
{ url = "https://files.pythonhosted.org/packages/6c/18/7464a9953724e55a3b3206062fa0ffdeaa519584c6aabf65d3956d94f131/rpds_py-2026.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83", size = 398758, upload-time = "2026-10-04T16:29:50.601Z" },
|
|
406
|
+
{ url = "https://files.pythonhosted.org/packages/c0/86/1534b436700fd49ff411063b7c4d7e938adfabf90895b6cf1622d5a7d1f6/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c", size = 550370, upload-time = "2026-10-04T16:29:52.002Z" },
|
|
407
|
+
{ url = "https://files.pythonhosted.org/packages/57/1c/e1fa82a8a01e3c5820f3ba98a8b2673f642128eb368fa88871b01dd2c909/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36", size = 613106, upload-time = "2026-10-04T16:29:53.655Z" },
|
|
408
|
+
{ url = "https://files.pythonhosted.org/packages/29/55/b20b8c4c3dde8755bfcd5b08492a02d0cd2e26929aedf6199ca2a377d42a/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d", size = 578235, upload-time = "2026-10-04T16:29:55.157Z" },
|
|
409
|
+
{ url = "https://files.pythonhosted.org/packages/55/42/df3f7bbc3f7ab37a8a9db8d6c2ff2c985422899f1f7926afbf7ec3c0b8b4/rpds_py-2026.9.1-cp313-cp313-win32.whl", hash = "sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c", size = 205293, upload-time = "2026-10-04T16:29:56.669Z" },
|
|
410
|
+
{ url = "https://files.pythonhosted.org/packages/31/9c/ba5a9569d719bfdd6ce863df4133ac6a1658cf1b07cc3534c31db729fbbc/rpds_py-2026.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9", size = 222409, upload-time = "2026-10-04T16:29:58.049Z" },
|
|
411
|
+
{ url = "https://files.pythonhosted.org/packages/35/72/f28ca566f6c23c35bbf7445f65eb0364577b25a026305995e24f78b83d94/rpds_py-2026.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f", size = 217681, upload-time = "2026-10-04T16:29:59.449Z" },
|
|
412
|
+
]
|
|
413
|
+
|
|
414
|
+
[[package]]
|
|
415
|
+
name = "ruamel-yaml"
|
|
416
|
+
version = "0.19.1"
|
|
417
|
+
source = { registry = "https://pypi.org/simple" }
|
|
418
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" }
|
|
419
|
+
wheels = [
|
|
420
|
+
{ url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" },
|
|
421
|
+
]
|
|
422
|
+
|
|
423
|
+
[[package]]
|
|
424
|
+
name = "subprocess-tee"
|
|
425
|
+
version = "0.4.2"
|
|
426
|
+
source = { registry = "https://pypi.org/simple" }
|
|
427
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/22/991efbf35bc811dfe7edcd749253f0931d2d4838cf55176132633e1c82a7/subprocess_tee-0.4.2.tar.gz", hash = "sha256:91b2b4da3aae9a7088d84acaf2ea0abee3f4fd9c0d2eae69a9b9122a71476590", size = 14951, upload-time = "2024-06-17T19:51:51.249Z" }
|
|
428
|
+
wheels = [
|
|
429
|
+
{ url = "https://files.pythonhosted.org/packages/4d/ab/e3a3be062cd544b2803760ff707dee38f0b1cb5685b2446de0ec19be28d9/subprocess_tee-0.4.2-py3-none-any.whl", hash = "sha256:21942e976715af4a19a526918adb03a8a27a8edab959f2d075b777e3d78f532d", size = 5249, upload-time = "2024-06-17T19:51:15.949Z" },
|
|
430
|
+
]
|
|
431
|
+
|
|
432
|
+
[[package]]
|
|
433
|
+
name = "wcmatch"
|
|
434
|
+
version = "12.0.1"
|
|
435
|
+
source = { registry = "https://pypi.org/simple" }
|
|
436
|
+
dependencies = [
|
|
437
|
+
{ name = "bracex" },
|
|
438
|
+
]
|
|
439
|
+
sdist = { url = "https://files.pythonhosted.org/packages/2e/17/bf44790f97414e37885830139933b4e96faac71560768ae9f9e458dbd936/wcmatch-12.0.1.tar.gz", hash = "sha256:4ea6acef75b657bf71ac8fa9520165bf5ae55120c4e655895a5b7d4c69896c5e", size = 163680, upload-time = "2026-11-07T21:45:37.072Z" }
|
|
440
|
+
wheels = [
|
|
441
|
+
{ url = "https://files.pythonhosted.org/packages/8a/24/8a5bbb3e73962b5bc901966f926b4fe25cbc9d1170ad9b4d9938441dbdc3/wcmatch-12.0.1-py3-none-any.whl", hash = "sha256:0e11b0bf1700bc8222ad5e7dc79fb326d9e0304458c7e2c25419ab6d26d07d8e", size = 51084, upload-time = "2026-11-07T21:45:35.661Z" },
|
|
442
|
+
]
|
|
443
|
+
|
|
444
|
+
[[package]]
|
|
445
|
+
name = "yamllint"
|
|
446
|
+
version = "1.38.0"
|
|
447
|
+
source = { registry = "https://pypi.org/simple" }
|
|
448
|
+
dependencies = [
|
|
449
|
+
{ name = "pathspec" },
|
|
450
|
+
{ name = "pyyaml" },
|
|
451
|
+
]
|
|
452
|
+
sdist = { url = "https://files.pythonhosted.org/packages/28/a0/8fc2d68e132cf918f18273fdc8a1b8432b60d75ac12fdae4b0ef5c9d2e8d/yamllint-1.38.0.tar.gz", hash = "sha256:09e5f29531daab93366bb061e76019d5e91691ef0a40328f04c927387d1d364d", size = 142446, upload-time = "2026-01-13T07:47:53.276Z" }
|
|
453
|
+
wheels = [
|
|
454
|
+
{ url = "https://files.pythonhosted.org/packages/05/92/aed08e68de6e6a3d7c2328ce7388072cd6affc26e2917197430b646aed02/yamllint-1.38.0-py3-none-any.whl", hash = "sha256:fc394a5b3be980a4062607b8fdddc0843f4fa394152b6da21722f5d59013c220", size = 68940, upload-time = "2026-01-13T07:47:51.343Z" },
|
|
455
|
+
]
|