fcloud-sdk 0.1.2__tar.gz → 0.2.0__tar.gz
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.
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/CHANGELOG.md +76 -0
- {fcloud_sdk-0.1.2/src/fcloud_sdk.egg-info → fcloud_sdk-0.2.0}/PKG-INFO +8 -4
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/README.md +7 -3
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/SKILL.md +122 -12
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/pyproject.toml +2 -2
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/SKILL.md +122 -12
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/__init__.py +4 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/__main__.py +1 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/_direct_bridge.py +18 -1
- fcloud_sdk-0.2.0/src/fcloud/autoresearch/SETUP.md +67 -0
- fcloud_sdk-0.2.0/src/fcloud/autoresearch/program.template.md +159 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/__init__.py +2 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/attach.py +43 -9
- fcloud_sdk-0.2.0/src/fcloud/cli/autoresearch.py +36 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/common.py +63 -7
- fcloud_sdk-0.2.0/src/fcloud/cli/config_cmd.py +130 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/context.py +50 -9
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/exec_cmd.py +42 -9
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/help.py +6 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/interactive.py +30 -7
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/job.py +29 -6
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/main.py +52 -3
- fcloud_sdk-0.2.0/src/fcloud/cli/output.py +45 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/processes.py +189 -25
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/run.py +42 -11
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/sessions.py +124 -31
- fcloud_sdk-0.2.0/src/fcloud/cli/setup.py +101 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/sweep.py +432 -25
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/sweep_harvest.py +3 -2
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/sweep_watch.py +81 -20
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli_args.py +15 -3
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/client.py +34 -13
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/config.py +90 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/errors.py +35 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/session.py +15 -3
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/setup_cmd.py +8 -2
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/shell.py +9 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/sweeps.py +54 -3
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/types.py +6 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/v2_connect.py +94 -10
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/volumes.py +23 -3
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0/src/fcloud_sdk.egg-info}/PKG-INFO +8 -4
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud_sdk.egg-info/SOURCES.txt +18 -0
- fcloud_sdk-0.2.0/tests/test_autoresearch.py +21 -0
- fcloud_sdk-0.2.0/tests/test_checkpoint_option.py +208 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_client.py +10 -9
- fcloud_sdk-0.2.0/tests/test_config_cmd.py +75 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_exec_attach_retry.py +24 -2
- fcloud_sdk-0.2.0/tests/test_first_run_login.py +77 -0
- fcloud_sdk-0.2.0/tests/test_kill_records_first.py +236 -0
- fcloud_sdk-0.2.0/tests/test_map_dag.py +40 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_map_default_image.py +3 -0
- fcloud_sdk-0.2.0/tests/test_map_window_flags.py +40 -0
- fcloud_sdk-0.2.0/tests/test_no_resume_gate.py +51 -0
- fcloud_sdk-0.2.0/tests/test_resume_volume_validation.py +86 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_run_wait_flags.py +2 -2
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_secret_resolution.py +2 -2
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_setup_cmd.py +6 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_shell_volumes.py +1 -1
- fcloud_sdk-0.2.0/tests/test_stop_refused_classification.py +95 -0
- fcloud_sdk-0.2.0/tests/test_sweep_boost.py +53 -0
- fcloud_sdk-0.2.0/tests/test_sweep_harvest.py +184 -0
- fcloud_sdk-0.2.0/tests/test_sweep_lifecycle_ux.py +523 -0
- fcloud_sdk-0.2.0/tests/test_sweep_retry_and_kill_truth.py +90 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_v2_connect.py +86 -2
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_volume_download_delete.py +16 -1
- fcloud_sdk-0.2.0/tests/test_volume_preflight.py +61 -0
- fcloud_sdk-0.1.2/src/fcloud/cli/output.py +0 -17
- fcloud_sdk-0.1.2/src/fcloud/cli/setup.py +0 -44
- fcloud_sdk-0.1.2/tests/test_sweep_lifecycle_ux.py +0 -145
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/CONTRIBUTING.md +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/LICENSE +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/MANIFEST.in +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/NOTICE +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/setup.cfg +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/_legacy_env.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/console.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/files.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/hardware.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/migration.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/mount.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/registry.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/ssh.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/volume.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/cli/wait.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/client_projects.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/client_sessions.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/client_volumes.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/direct.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/fileset.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/image.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/job.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/providers/__init__.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/providers/requests_http.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/py.typed +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/tunnel.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/version.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud/volume_wait.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud_sdk.egg-info/dependency_links.txt +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud_sdk.egg-info/entry_points.txt +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud_sdk.egg-info/requires.txt +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/fcloud_sdk.egg-info/top_level.txt +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/src/foom/__init__.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/conftest.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_cli_args.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_cli_dispatch.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_cli_guards.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_cli_help.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_client_contracts_c.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_client_host_frames_contract.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_client_http_contract.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_client_telemetry.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_config.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_default_image.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_direct_bridge.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_direct_fake_host.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_dotenv_precedence.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_download_volume_hint.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_emit_pid.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_fileset.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_job.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_legacy_shim.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_progress_narration.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_provider_env_default.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_queued_reason_render.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_rate_limit_ride.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_rebuild_respawn.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_ride_cap.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_run_migration.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_run_volume_collision.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_session_truth.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_sessions_live_statuses.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_sessions_point_lookup.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_shell_env.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_shell_interrupt.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_skus.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_sweep_cost_optin.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_sweep_status_view.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_sweeps_binding.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_tunnel.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_types.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_upload_s3_path.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_volume_cli_hardening.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_volume_resolution.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_volume_wait.py +0 -0
- {fcloud_sdk-0.1.2 → fcloud_sdk-0.2.0}/tests/test_wait.py +0 -0
|
@@ -4,6 +4,82 @@ All notable changes to the `fcloud` Python SDK and CLI are recorded here.
|
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
5
5
|
versions follow [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.2.0] - 2026-09-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Per-session checkpoint/restore opt-out: `--no-checkpoint` on
|
|
11
|
+
`create`/`exec`/`run`/`shell` (SDK: `checkpoint=False` on
|
|
12
|
+
`Project.session`/`cold_session`, `Client.create_session`/
|
|
13
|
+
`create_cold_session`). A preempted opt-out session is rebuilt cold on any
|
|
14
|
+
available host (`/workspace` kept, running processes lost) instead of being
|
|
15
|
+
checkpointed and restored pinned to the region its checkpoint lives in.
|
|
16
|
+
Defaults resolve `--checkpoint`/`--no-checkpoint` → `FCLOUD_CHECKPOINT` →
|
|
17
|
+
`fcloud.json` `"checkpoint"` → the new `fcloud config set checkpoint on|off`
|
|
18
|
+
user default (`~/.fcloud/config.json`) → server default (on).
|
|
19
|
+
`SessionInfo.checkpoint` and `fcloud sessions --json` expose the policy; the
|
|
20
|
+
create fails loudly against a dispatcher too old to honour the opt-out.
|
|
21
|
+
- `--no-resume` on `exec`/`run`/`shell`: refuse to wake a cold (stopped)
|
|
22
|
+
session instead of transparently re-provisioning its hardware. The refusal
|
|
23
|
+
exits 1 with a typed `session_cold` error naming the session and the SKU a
|
|
24
|
+
resume would have provisioned. SDK: `Client.attach_session(...,
|
|
25
|
+
resume=False)` raises the new `fcloud.SessionColdError`.
|
|
26
|
+
- Pipelines: `fcloud map --after SWEEP[,SWEEP]` (SDK: `client.map(...,
|
|
27
|
+
after=handle_or_name, after_strict=…)`) holds a sweep in the new
|
|
28
|
+
`waiting` state until the named sweeps finish, then launches it — the
|
|
29
|
+
barrier is durable server-side, so a generate → train → eval chain
|
|
30
|
+
submits up front and the client can disconnect. An upstream failure
|
|
31
|
+
fails the downstream sweep (`upstream_failed:<name>`; re-arm with
|
|
32
|
+
`fcloud sweep retry <name> --all`); a partially-succeeded upstream
|
|
33
|
+
counts as finished unless `--after-strict`.
|
|
34
|
+
- `--volume NAME[:MOUNT][:ro]` (also fcloud.json `"read_only": true` or
|
|
35
|
+
the `name:/mount:ro` string form) attaches a volume read-only: the
|
|
36
|
+
session sees it, but its writes are discarded at close (no commit).
|
|
37
|
+
- `fcloud autoresearch init`: scaffolds a karpathy/autoresearch-style keep/discard
|
|
38
|
+
research loop (agent setup guide + `program.md` template) that runs experiments
|
|
39
|
+
on a persistent fcloud session.
|
|
40
|
+
The loop uploads `results.tsv` to the session each experiment; the dashboard's
|
|
41
|
+
session page then shows an Autoresearch tab (metric chart + results table).
|
|
42
|
+
- Skip-the-line boost: `fcloud sweep boost <name>` / pressing `s` in
|
|
43
|
+
`fcloud sweep status --watch` (offered while blocked on capacity) moves a
|
|
44
|
+
sweep to the fast claim lane; queued tasks re-lane immediately
|
|
45
|
+
(`SweepHandle.boost()` in the SDK). Freed machines alternate fast/free.
|
|
46
|
+
- `fcloud sweep status` explains WHY a sweep is waiting: a `why:` line under
|
|
47
|
+
`blocked on capacity` names the line (`3 session(s) ahead in the gpu_1x_l4
|
|
48
|
+
queue (1 fast-lane)`) and/or the platform (`a new machine is being
|
|
49
|
+
provisioned`, `no capacity ... (stockout)`, `cloud quota exhausted`, ...),
|
|
50
|
+
from the new `blocked.sku/queued_reason/queued_ahead/fast_ahead` status
|
|
51
|
+
fields; a `state: waiting` (pipeline) sweep names the upstream sweeps it
|
|
52
|
+
holds for. Older dispatchers omit the fields and status reads as before.
|
|
53
|
+
- Async sweep scheduling: `fcloud map --not-before WHEN` (park until), `--by WHEN`
|
|
54
|
+
(deadline; opportunistic idle-fill until `deadline - --runtime - pad`, then
|
|
55
|
+
normal launch) and `--runtime DUR` (per-task estimate, default 1h).
|
|
56
|
+
`fcloud sweep status` shows the window phase; a missed deadline marks the
|
|
57
|
+
sweep `deadline_missed` (webhook `job.deadline_missed`) but never cancels it.
|
|
58
|
+
- First-run login prompt: with no API key configured, the CLI offers to open
|
|
59
|
+
the dashboard login and store the key instead of failing with a bare error.
|
|
60
|
+
- CLI errors carry typed error codes and the request id; the client never
|
|
61
|
+
provisions hardware for a request the server would reject.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
- Resuming a cold session now always prints one stderr line (`session <sid>
|
|
65
|
+
is cold — resuming (provisions <sku>); pass --no-resume to refuse`), even
|
|
66
|
+
when stderr is not a TTY or `FCLOUD_QUIET` is set — waking billed hardware
|
|
67
|
+
is a spend event, and non-TTY runs used to do it silently.
|
|
68
|
+
- Failed map task attempts now retry with a growing delay (45s × attempt,
|
|
69
|
+
capped at 10m) instead of relaunching immediately; preemption requeues
|
|
70
|
+
remain immediate and still don't consume the retry budget.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
- `fcloud map --volume name:/path` sent the custom mount path under a key
|
|
74
|
+
the server ignores, silently mounting the volume at its default path.
|
|
75
|
+
Custom mount paths now apply to sweep task sessions.
|
|
76
|
+
|
|
77
|
+
## [0.1.3] - 2026-08-27
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
- `fcloud setup` installs the agent skill for all agents by default (Enter or
|
|
81
|
+
non-interactive stdin = `all`); `--agents none` opts out.
|
|
82
|
+
|
|
7
83
|
## [0.1.2] - 2026-08-27
|
|
8
84
|
|
|
9
85
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fcloud-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python SDK and CLI for the fcloud GPU compute platform
|
|
5
5
|
Author: fcloud
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -51,13 +51,15 @@ pip install fcloud-sdk
|
|
|
51
51
|
|
|
52
52
|
Create an account on https://fcloud-home.vercel.app.
|
|
53
53
|
Enter a credit card, and copy your API key.
|
|
54
|
+
Or just run any `fcloud` command (or `fcloud login`): a fresh install
|
|
55
|
+
walks you through sign-up and saves the key you paste.
|
|
54
56
|
|
|
55
57
|
```bash
|
|
56
58
|
fcloud setup # prompts for the key; optionally installs the agent skill
|
|
57
59
|
fcloud health # verify connectivity
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
`fcloud setup
|
|
62
|
+
`fcloud setup` also installs (by default) the fcloud skill file for supported
|
|
61
63
|
coding agents (Cursor, Claude Code, Codex) so an agent can drive fcloud for you.
|
|
62
64
|
Use `--agents none` to skip that.
|
|
63
65
|
|
|
@@ -201,16 +203,18 @@ Other environment switches:
|
|
|
201
203
|
| `FCLOUD_QUIET=1` | Suppress "still waiting" progress lines while a host is provisioned |
|
|
202
204
|
| `FCLOUD_QUEUE_TIMEOUT=<seconds>` | How long to wait for capacity before giving up (default 1200) |
|
|
203
205
|
| `FCLOUD_MIGRATE_RESTART=never` | Don't automatically re-run a command after a host rebuild (default `auto`) |
|
|
206
|
+
| `FCLOUD_CHECKPOINT=off` | Default checkpoint/restore policy for new sessions. `off`: a preempted session rebuilds cold on any available host (`/workspace` kept, processes lost) instead of restoring pinned to its checkpoint's region. Per-session: `--checkpoint`/`--no-checkpoint`; per-user: `fcloud config set checkpoint off`; per-project: `fcloud.json` `"checkpoint": false` |
|
|
204
207
|
| `FCLOUD_INSECURE_HTTP=1` | Allow a plaintext `http://` API URL to a non-loopback host (refused by default — the API key would travel unencrypted). Loopback URLs never need this |
|
|
205
208
|
| `FCLOUD_TELEMETRY=0` | Disable all client telemetry. When enabled (the default), the client reports failures the backend cannot otherwise see — an uncaught CLI error, a queue-wait timeout, exhausted connect retries — as a fixed-allowlist payload (session id, event type, error class, truncated message, SKU/timing fields); never file contents, paths from OS errors, or credentials |
|
|
206
209
|
|
|
207
210
|
A `fcloud.json` at the project root can set defaults (image build steps, default
|
|
208
|
-
volumes
|
|
211
|
+
volumes, checkpoint policy); `fcloud config` stores per-user defaults in
|
|
212
|
+
`~/.fcloud/config.json`. Note that fcloud will run the build steps it finds there, so treat a
|
|
209
213
|
cloned repo's `fcloud.json` the way you would its Dockerfile.
|
|
210
214
|
|
|
211
215
|
## Agent skill
|
|
212
216
|
|
|
213
|
-
`fcloud setup
|
|
217
|
+
`fcloud setup` installs `SKILL.md` by default (`--agents none` to skip) for supported coding agents. It
|
|
214
218
|
is the long-form, agent-oriented guide: workflow patterns, monitoring loops,
|
|
215
219
|
and anti-patterns.
|
|
216
220
|
|
|
@@ -17,13 +17,15 @@ pip install fcloud-sdk
|
|
|
17
17
|
|
|
18
18
|
Create an account on https://fcloud-home.vercel.app.
|
|
19
19
|
Enter a credit card, and copy your API key.
|
|
20
|
+
Or just run any `fcloud` command (or `fcloud login`): a fresh install
|
|
21
|
+
walks you through sign-up and saves the key you paste.
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
24
|
fcloud setup # prompts for the key; optionally installs the agent skill
|
|
23
25
|
fcloud health # verify connectivity
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
`fcloud setup
|
|
28
|
+
`fcloud setup` also installs (by default) the fcloud skill file for supported
|
|
27
29
|
coding agents (Cursor, Claude Code, Codex) so an agent can drive fcloud for you.
|
|
28
30
|
Use `--agents none` to skip that.
|
|
29
31
|
|
|
@@ -167,16 +169,18 @@ Other environment switches:
|
|
|
167
169
|
| `FCLOUD_QUIET=1` | Suppress "still waiting" progress lines while a host is provisioned |
|
|
168
170
|
| `FCLOUD_QUEUE_TIMEOUT=<seconds>` | How long to wait for capacity before giving up (default 1200) |
|
|
169
171
|
| `FCLOUD_MIGRATE_RESTART=never` | Don't automatically re-run a command after a host rebuild (default `auto`) |
|
|
172
|
+
| `FCLOUD_CHECKPOINT=off` | Default checkpoint/restore policy for new sessions. `off`: a preempted session rebuilds cold on any available host (`/workspace` kept, processes lost) instead of restoring pinned to its checkpoint's region. Per-session: `--checkpoint`/`--no-checkpoint`; per-user: `fcloud config set checkpoint off`; per-project: `fcloud.json` `"checkpoint": false` |
|
|
170
173
|
| `FCLOUD_INSECURE_HTTP=1` | Allow a plaintext `http://` API URL to a non-loopback host (refused by default — the API key would travel unencrypted). Loopback URLs never need this |
|
|
171
174
|
| `FCLOUD_TELEMETRY=0` | Disable all client telemetry. When enabled (the default), the client reports failures the backend cannot otherwise see — an uncaught CLI error, a queue-wait timeout, exhausted connect retries — as a fixed-allowlist payload (session id, event type, error class, truncated message, SKU/timing fields); never file contents, paths from OS errors, or credentials |
|
|
172
175
|
|
|
173
176
|
A `fcloud.json` at the project root can set defaults (image build steps, default
|
|
174
|
-
volumes
|
|
177
|
+
volumes, checkpoint policy); `fcloud config` stores per-user defaults in
|
|
178
|
+
`~/.fcloud/config.json`. Note that fcloud will run the build steps it finds there, so treat a
|
|
175
179
|
cloned repo's `fcloud.json` the way you would its Dockerfile.
|
|
176
180
|
|
|
177
181
|
## Agent skill
|
|
178
182
|
|
|
179
|
-
`fcloud setup
|
|
183
|
+
`fcloud setup` installs `SKILL.md` by default (`--agents none` to skip) for supported coding agents. It
|
|
180
184
|
is the long-form, agent-oriented guide: workflow patterns, monitoring loops,
|
|
181
185
|
and anti-patterns.
|
|
182
186
|
|
|
@@ -336,6 +336,39 @@ once**, keep one client attached per box for its whole run — e.g. N parallel
|
|
|
336
336
|
creating N sessions and attending to them one at a time, which lets the idle
|
|
337
337
|
ones lose their hosts.
|
|
338
338
|
|
|
339
|
+
### Checkpoint/restore on preemption (and how to turn it off)
|
|
340
|
+
|
|
341
|
+
Sessions run on spot capacity. By default, when a host is reclaimed fcloud
|
|
342
|
+
**checkpoints** the live sandbox (GPU state included) and **restores** it on a
|
|
343
|
+
fresh box, so running processes continue. The restore is pinned to the region
|
|
344
|
+
the checkpoint lives in (cross-region transfer of a multi-GB image is not free),
|
|
345
|
+
so a restore can wait in `awaiting_restore_capacity` until that region has spot
|
|
346
|
+
capacity again.
|
|
347
|
+
|
|
348
|
+
If you would rather get **any** available host quickly and re-run your command
|
|
349
|
+
yourself, turn checkpoint/restore off for the session. A preempted
|
|
350
|
+
`--no-checkpoint` session is rebuilt cold on any host in any region:
|
|
351
|
+
`/workspace` is preserved (synced before the box dies), running processes are
|
|
352
|
+
lost, and the session carries `state_loss_reason=spot_preempt_checkpoint_disabled`
|
|
353
|
+
in `fcloud sessions --json`. `fcloud run`/`exec` re-run the command after a
|
|
354
|
+
rebuild (`FCLOUD_MIGRATE_RESTART`), so a resumable script (one that reloads its
|
|
355
|
+
own checkpoints from `/workspace` or a volume) is the natural pairing.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
fcloud run train.py --sku gpu_1x_h100 --no-checkpoint # this session only
|
|
359
|
+
fcloud create --sku gpu_1x_l4 --no-checkpoint # for the session's life
|
|
360
|
+
fcloud config set checkpoint off # my default, every project
|
|
361
|
+
fcloud config get checkpoint # -> off
|
|
362
|
+
fcloud config unset checkpoint # back to on
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Precedence, strongest first: `--checkpoint`/`--no-checkpoint` (or SDK
|
|
366
|
+
`checkpoint=True/False`) → `FCLOUD_CHECKPOINT=on|off` → `fcloud.json`
|
|
367
|
+
`"checkpoint": false` → `fcloud config set checkpoint` → server default (on).
|
|
368
|
+
The policy is fixed when the session is **created**; `--on <sid>` keeps the
|
|
369
|
+
session's existing policy. Jobs (`fcloud job`, `fcloud map`) never checkpoint.
|
|
370
|
+
`fcloud sessions` marks opted-out sessions `[no-checkpoint]`.
|
|
371
|
+
|
|
339
372
|
### File Transfer
|
|
340
373
|
|
|
341
374
|
| Command | Description | Needs active session? |
|
|
@@ -470,9 +503,12 @@ detach or session close.
|
|
|
470
503
|
Volumes are attached
|
|
471
504
|
**after** the session is online, so a volume-mounted command queues and
|
|
472
505
|
provisions a host like any other session, and `--volume` also works against an
|
|
473
|
-
already-running session (`--on <sid>`).
|
|
474
|
-
|
|
475
|
-
|
|
506
|
+
already-running session (`--on <sid>`). Any number of sessions may attach the
|
|
507
|
+
same volume concurrently — commits merge per path, last writer wins per file
|
|
508
|
+
(so key parallel writers' outputs by distinct paths). Only `delete` and
|
|
509
|
+
`import` conflict while a volume is attached; browsing works any time. Append
|
|
510
|
+
`:ro` (`--volume data:/vol:ro`) to attach read-only: the session sees the
|
|
511
|
+
volume but its writes are discarded at close (no commit).
|
|
476
512
|
|
|
477
513
|
Declare volumes once in `fcloud.json` to skip `--volume` on every command (see
|
|
478
514
|
[Project Config](#project-config-fcloudjson)):
|
|
@@ -678,6 +714,18 @@ without it the cap is **8**, and submitting more tasks than that runs them
|
|
|
678
714
|
in waves. Submit says so out loud, and the cap is changeable on a live
|
|
679
715
|
sweep: `fcloud sweep set <name> --max-parallel N`.
|
|
680
716
|
|
|
717
|
+
**Scheduling (async).** `--not-before WHEN` parks the sweep until then;
|
|
718
|
+
`--by WHEN` sets a deadline: the platform runs tasks opportunistically on
|
|
719
|
+
already-idle hosts (no capacity is provisioned early) and switches to
|
|
720
|
+
normal launching at `deadline - --runtime - a provisioning pad`, so the
|
|
721
|
+
sweep finishes by the deadline without paying for eager scale-up.
|
|
722
|
+
`--runtime DUR` is the per-task runtime estimate (default 1h). WHEN is a
|
|
723
|
+
duration from now (`30m`, `8h`, `2d`) or an ISO time (`2026-09-01T02:00`,
|
|
724
|
+
local). A missed deadline never cancels the sweep — status shows
|
|
725
|
+
`deadline_missed` and the webhook fires `job.deadline_missed`. While
|
|
726
|
+
waiting, `fcloud sweep status` prints the window phase and when launches
|
|
727
|
+
begin.
|
|
728
|
+
|
|
681
729
|
**Canary.** Task 0 runs first and gates the rest: a broken sweep costs one
|
|
682
730
|
task, not N. `fcloud map` blocks until the canary passes, then detaches (Ctrl-C
|
|
683
731
|
detaches the watch — it does NOT cancel). `--no-canary` / `--no-wait` opt out.
|
|
@@ -687,11 +735,53 @@ the rest — cheaper than `--no-canary`, which risks N bad tasks to save one.
|
|
|
687
735
|
Failures don't consume the retry budget when caused by spot preemption — the
|
|
688
736
|
task just requeues.
|
|
689
737
|
|
|
738
|
+
**Skip the line.** When tasks are blocked on capacity, `fcloud sweep boost
|
|
739
|
+
<name>` (or pressing `s` in `sweep status --watch`, offered only while a
|
|
740
|
+
line exists) moves the sweep to the fast claim lane: queued tasks skip
|
|
741
|
+
ahead of free-lane work, and freed machines alternate fast/free so nobody
|
|
742
|
+
is starved. SDK: `handle.boost()`. Unpriced for now.
|
|
743
|
+
|
|
690
744
|
**Waiting for capacity is not failure.** A task with no host yet keeps its
|
|
691
745
|
queued session, charges no retry, and is reported as blocked:
|
|
692
|
-
`fcloud sweep status` prints `blocked on capacity: N task(s), oldest 6m12s
|
|
693
|
-
|
|
694
|
-
|
|
746
|
+
`fcloud sweep status` prints `blocked on capacity: N task(s), oldest 6m12s`
|
|
747
|
+
followed by a `why:` line saying what the wait actually is — a line
|
|
748
|
+
(`3 session(s) ahead in the gpu_1x_l4 queue (1 fast-lane)`), the platform
|
|
749
|
+
(`a new machine is being provisioned`, `waiting for a busy machine to free
|
|
750
|
+
up`), or bad news worth acting on (`no capacity ... (stockout) — consider
|
|
751
|
+
another SKU`, `cloud quota exhausted`). Use it to decide: a line or a
|
|
752
|
+
provision resolves itself (or `boost` past the free lane); a stockout or
|
|
753
|
+
quota wall means pick another SKU or cancel. A `state: waiting` sweep is a
|
|
754
|
+
pipeline hold, and status names the upstream sweeps it waits on.
|
|
755
|
+
|
|
756
|
+
**Retry backoff.** A charged failure re-queues with a growing delay (45s ×
|
|
757
|
+
attempt, capped at 10m) so a deterministic crasher doesn't burn its budget
|
|
758
|
+
in seconds; preemption/lost-host requeues re-run immediately and never
|
|
759
|
+
consume the budget.
|
|
760
|
+
|
|
761
|
+
**Pipelines (`--after`).** `--after SWEEP[,SWEEP]` holds a sweep in
|
|
762
|
+
`waiting` until the named sweeps finish, then launches it — a durable
|
|
763
|
+
server-side barrier, so a whole generate → train → eval pipeline submits up
|
|
764
|
+
front and the client can disconnect:
|
|
765
|
+
|
|
766
|
+
```bash
|
|
767
|
+
fcloud map --name gen --volume run42:/vol \
|
|
768
|
+
-- python3 gen.py --out /vol/data/shard-{i}.jsonl ::: {0..63}
|
|
769
|
+
fcloud map --name train --sku gpu_8x_h100 --volume run42:/vol --after gen \
|
|
770
|
+
-- python3 train.py --data /vol/data --ckpt /vol/ckpt
|
|
771
|
+
fcloud map --name eval --volume run42:/vol --after train \
|
|
772
|
+
-- python3 eval.py --ckpt /vol/ckpt --suite {} ::: mmlu,gsm8k,mbpp
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
Data flows between stages through the shared volume: map tasks write
|
|
776
|
+
disjoint task-keyed paths, the next stage mounts the same volume and sees
|
|
777
|
+
the union (the gate waits for the upstream's volume commits to land before
|
|
778
|
+
releasing). An upstream failure fails the downstream sweep with
|
|
779
|
+
`upstream_failed:<name>` instead of running it on missing inputs; after
|
|
780
|
+
fixing and re-running the upstream, `fcloud sweep retry <name> --all`
|
|
781
|
+
re-arms the downstream. A `succeeded_partial` upstream counts as finished
|
|
782
|
+
unless `--after-strict`. Edges bind at submit, so upstreams must be
|
|
783
|
+
submitted first (which also makes cycles impossible). SDK:
|
|
784
|
+
`client.map(..., after=gen_handle)` (or a name, or a list; `after_strict=True`).
|
|
695
785
|
|
|
696
786
|
**Tracking & recovery:**
|
|
697
787
|
```bash
|
|
@@ -699,15 +789,16 @@ fcloud sweeps # all sweeps: state + done/run/fail count
|
|
|
699
789
|
fcloud sweep status <name> # counts + failures clustered by error, with exemplar task
|
|
700
790
|
fcloud sweep status <name> --watch # same, repainted until terminal (--interval S)
|
|
701
791
|
fcloud sweep harvest <name> '*.json' ./out # every task's outputs → out/task-<i>/…
|
|
702
|
-
fcloud sweep logs <name> [--task N]
|
|
792
|
+
fcloud sweep logs <name> [--task N] [--attempt K] # durable output (defaults to the exemplar failure, falling back to earlier attempts; --attempt 1 = most recent attempt, explicitly)
|
|
703
793
|
fcloud sweep set <name> --max-parallel N # change the fan-out cap on a live sweep
|
|
704
|
-
fcloud sweep retry <name> [--all] # re-run
|
|
794
|
+
fcloud sweep retry <name> [--all] # re-run failed tasks (fix code first: resubmit is idempotent); --all adds canceled + WEDGED in-flight tasks (lost host / dead placement) and never restarts healthy running work
|
|
705
795
|
fcloud sweep cancel <name> [--remaining] # stop; --remaining keeps running tasks (partial success)
|
|
706
|
-
fcloud sweep wait <name>
|
|
796
|
+
fcloud sweep wait <name> [--poll S] [--timeout S] # block until terminal; exit 0 = success, 124 = still running at timeout
|
|
707
797
|
```
|
|
708
|
-
`--webhook URL`
|
|
709
|
-
|
|
710
|
-
|
|
798
|
+
`--webhook URL` POSTs the status document on canary pass/fail and
|
|
799
|
+
completion — agents should submit with `--no-wait --json` and wake on the
|
|
800
|
+
webhook instead of polling. There is no account-default webhook: without
|
|
801
|
+
`--webhook`, nothing is POSTed.
|
|
711
802
|
|
|
712
803
|
**The status JSON is a versioned public contract** (`schema_version: 1`),
|
|
713
804
|
specified in `docs/sweep-status-schema.md`: `counts` is an open map keyed by
|
|
@@ -1086,6 +1177,7 @@ flags merge with (and re-map by name) the `volumes` defaults.
|
|
|
1086
1177
|
| ------------ | ------------------------------------------ |
|
|
1087
1178
|
| `sku` | Default hardware SKU for `exec` and `run` |
|
|
1088
1179
|
| `volumes` | Volumes auto-attached to every `exec`/`run` session. Each entry is `"name"`, `"name:/mount"`, or `{"name": ..., "mount": ...}` |
|
|
1180
|
+
| `checkpoint` | `false` turns checkpoint/restore off for sessions created from this project (preemption rebuilds cold on any host). Default `true`. See *Checkpoint/restore on preemption* |
|
|
1089
1181
|
| `image.base` | Base image (default: `python:3.11-slim`) |
|
|
1090
1182
|
| `image.apt` | Packages to `apt-get install` |
|
|
1091
1183
|
| `image.pip` | Packages to `pip install` |
|
|
@@ -1166,6 +1258,24 @@ CLI commands or explicitly via `client.project("name", image=...)`.
|
|
|
1166
1258
|
remains pinned after a hard kill, recover with `fcloud stop <sid>` and then use the
|
|
1167
1259
|
session again.
|
|
1168
1260
|
|
|
1261
|
+
## Autonomous research loops (autoresearch)
|
|
1262
|
+
|
|
1263
|
+
To run an overnight keep/discard experiment loop on the user's code — after
|
|
1264
|
+
[karpathy/autoresearch](https://github.com/karpathy/autoresearch), with the GPU on
|
|
1265
|
+
fcloud instead of under the desk:
|
|
1266
|
+
|
|
1267
|
+
```bash
|
|
1268
|
+
fcloud autoresearch init # writes autoresearch/SETUP.md + program.template.md
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
Then read `autoresearch/SETUP.md` and follow it. It walks you through deciding the
|
|
1272
|
+
metric, the one mutable file vs the frozen eval, and the fixed time budget with the
|
|
1273
|
+
user; you write `program.md`, run the baseline on a persistent session, and hand
|
|
1274
|
+
off to the loop, which only ever re-uploads the mutable file and greps the metric.
|
|
1275
|
+
Progress is visible on the session's page in the fcloud dashboard (Autoresearch
|
|
1276
|
+
tab: metric chart + results table) once the loop's `results.tsv` upload syncs;
|
|
1277
|
+
point the user there when the loop starts.
|
|
1278
|
+
|
|
1169
1279
|
## Anti-Patterns
|
|
1170
1280
|
|
|
1171
1281
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fcloud-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Python SDK and CLI for the fcloud GPU compute platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -45,7 +45,7 @@ where = ["src"]
|
|
|
45
45
|
# editable copy at the package root) so `fcloud setup` can find it via
|
|
46
46
|
# importlib.resources under any install scheme, including `pip install --user`.
|
|
47
47
|
[tool.setuptools.package-data]
|
|
48
|
-
fcloud = ["py.typed", "SKILL.md"]
|
|
48
|
+
fcloud = ["py.typed", "SKILL.md", "autoresearch/*.md"]
|
|
49
49
|
|
|
50
50
|
[project.optional-dependencies]
|
|
51
51
|
dev = [
|
|
@@ -336,6 +336,39 @@ once**, keep one client attached per box for its whole run — e.g. N parallel
|
|
|
336
336
|
creating N sessions and attending to them one at a time, which lets the idle
|
|
337
337
|
ones lose their hosts.
|
|
338
338
|
|
|
339
|
+
### Checkpoint/restore on preemption (and how to turn it off)
|
|
340
|
+
|
|
341
|
+
Sessions run on spot capacity. By default, when a host is reclaimed fcloud
|
|
342
|
+
**checkpoints** the live sandbox (GPU state included) and **restores** it on a
|
|
343
|
+
fresh box, so running processes continue. The restore is pinned to the region
|
|
344
|
+
the checkpoint lives in (cross-region transfer of a multi-GB image is not free),
|
|
345
|
+
so a restore can wait in `awaiting_restore_capacity` until that region has spot
|
|
346
|
+
capacity again.
|
|
347
|
+
|
|
348
|
+
If you would rather get **any** available host quickly and re-run your command
|
|
349
|
+
yourself, turn checkpoint/restore off for the session. A preempted
|
|
350
|
+
`--no-checkpoint` session is rebuilt cold on any host in any region:
|
|
351
|
+
`/workspace` is preserved (synced before the box dies), running processes are
|
|
352
|
+
lost, and the session carries `state_loss_reason=spot_preempt_checkpoint_disabled`
|
|
353
|
+
in `fcloud sessions --json`. `fcloud run`/`exec` re-run the command after a
|
|
354
|
+
rebuild (`FCLOUD_MIGRATE_RESTART`), so a resumable script (one that reloads its
|
|
355
|
+
own checkpoints from `/workspace` or a volume) is the natural pairing.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
fcloud run train.py --sku gpu_1x_h100 --no-checkpoint # this session only
|
|
359
|
+
fcloud create --sku gpu_1x_l4 --no-checkpoint # for the session's life
|
|
360
|
+
fcloud config set checkpoint off # my default, every project
|
|
361
|
+
fcloud config get checkpoint # -> off
|
|
362
|
+
fcloud config unset checkpoint # back to on
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Precedence, strongest first: `--checkpoint`/`--no-checkpoint` (or SDK
|
|
366
|
+
`checkpoint=True/False`) → `FCLOUD_CHECKPOINT=on|off` → `fcloud.json`
|
|
367
|
+
`"checkpoint": false` → `fcloud config set checkpoint` → server default (on).
|
|
368
|
+
The policy is fixed when the session is **created**; `--on <sid>` keeps the
|
|
369
|
+
session's existing policy. Jobs (`fcloud job`, `fcloud map`) never checkpoint.
|
|
370
|
+
`fcloud sessions` marks opted-out sessions `[no-checkpoint]`.
|
|
371
|
+
|
|
339
372
|
### File Transfer
|
|
340
373
|
|
|
341
374
|
| Command | Description | Needs active session? |
|
|
@@ -470,9 +503,12 @@ detach or session close.
|
|
|
470
503
|
Volumes are attached
|
|
471
504
|
**after** the session is online, so a volume-mounted command queues and
|
|
472
505
|
provisions a host like any other session, and `--volume` also works against an
|
|
473
|
-
already-running session (`--on <sid>`).
|
|
474
|
-
|
|
475
|
-
|
|
506
|
+
already-running session (`--on <sid>`). Any number of sessions may attach the
|
|
507
|
+
same volume concurrently — commits merge per path, last writer wins per file
|
|
508
|
+
(so key parallel writers' outputs by distinct paths). Only `delete` and
|
|
509
|
+
`import` conflict while a volume is attached; browsing works any time. Append
|
|
510
|
+
`:ro` (`--volume data:/vol:ro`) to attach read-only: the session sees the
|
|
511
|
+
volume but its writes are discarded at close (no commit).
|
|
476
512
|
|
|
477
513
|
Declare volumes once in `fcloud.json` to skip `--volume` on every command (see
|
|
478
514
|
[Project Config](#project-config-fcloudjson)):
|
|
@@ -678,6 +714,18 @@ without it the cap is **8**, and submitting more tasks than that runs them
|
|
|
678
714
|
in waves. Submit says so out loud, and the cap is changeable on a live
|
|
679
715
|
sweep: `fcloud sweep set <name> --max-parallel N`.
|
|
680
716
|
|
|
717
|
+
**Scheduling (async).** `--not-before WHEN` parks the sweep until then;
|
|
718
|
+
`--by WHEN` sets a deadline: the platform runs tasks opportunistically on
|
|
719
|
+
already-idle hosts (no capacity is provisioned early) and switches to
|
|
720
|
+
normal launching at `deadline - --runtime - a provisioning pad`, so the
|
|
721
|
+
sweep finishes by the deadline without paying for eager scale-up.
|
|
722
|
+
`--runtime DUR` is the per-task runtime estimate (default 1h). WHEN is a
|
|
723
|
+
duration from now (`30m`, `8h`, `2d`) or an ISO time (`2026-09-01T02:00`,
|
|
724
|
+
local). A missed deadline never cancels the sweep — status shows
|
|
725
|
+
`deadline_missed` and the webhook fires `job.deadline_missed`. While
|
|
726
|
+
waiting, `fcloud sweep status` prints the window phase and when launches
|
|
727
|
+
begin.
|
|
728
|
+
|
|
681
729
|
**Canary.** Task 0 runs first and gates the rest: a broken sweep costs one
|
|
682
730
|
task, not N. `fcloud map` blocks until the canary passes, then detaches (Ctrl-C
|
|
683
731
|
detaches the watch — it does NOT cancel). `--no-canary` / `--no-wait` opt out.
|
|
@@ -687,11 +735,53 @@ the rest — cheaper than `--no-canary`, which risks N bad tasks to save one.
|
|
|
687
735
|
Failures don't consume the retry budget when caused by spot preemption — the
|
|
688
736
|
task just requeues.
|
|
689
737
|
|
|
738
|
+
**Skip the line.** When tasks are blocked on capacity, `fcloud sweep boost
|
|
739
|
+
<name>` (or pressing `s` in `sweep status --watch`, offered only while a
|
|
740
|
+
line exists) moves the sweep to the fast claim lane: queued tasks skip
|
|
741
|
+
ahead of free-lane work, and freed machines alternate fast/free so nobody
|
|
742
|
+
is starved. SDK: `handle.boost()`. Unpriced for now.
|
|
743
|
+
|
|
690
744
|
**Waiting for capacity is not failure.** A task with no host yet keeps its
|
|
691
745
|
queued session, charges no retry, and is reported as blocked:
|
|
692
|
-
`fcloud sweep status` prints `blocked on capacity: N task(s), oldest 6m12s
|
|
693
|
-
|
|
694
|
-
|
|
746
|
+
`fcloud sweep status` prints `blocked on capacity: N task(s), oldest 6m12s`
|
|
747
|
+
followed by a `why:` line saying what the wait actually is — a line
|
|
748
|
+
(`3 session(s) ahead in the gpu_1x_l4 queue (1 fast-lane)`), the platform
|
|
749
|
+
(`a new machine is being provisioned`, `waiting for a busy machine to free
|
|
750
|
+
up`), or bad news worth acting on (`no capacity ... (stockout) — consider
|
|
751
|
+
another SKU`, `cloud quota exhausted`). Use it to decide: a line or a
|
|
752
|
+
provision resolves itself (or `boost` past the free lane); a stockout or
|
|
753
|
+
quota wall means pick another SKU or cancel. A `state: waiting` sweep is a
|
|
754
|
+
pipeline hold, and status names the upstream sweeps it waits on.
|
|
755
|
+
|
|
756
|
+
**Retry backoff.** A charged failure re-queues with a growing delay (45s ×
|
|
757
|
+
attempt, capped at 10m) so a deterministic crasher doesn't burn its budget
|
|
758
|
+
in seconds; preemption/lost-host requeues re-run immediately and never
|
|
759
|
+
consume the budget.
|
|
760
|
+
|
|
761
|
+
**Pipelines (`--after`).** `--after SWEEP[,SWEEP]` holds a sweep in
|
|
762
|
+
`waiting` until the named sweeps finish, then launches it — a durable
|
|
763
|
+
server-side barrier, so a whole generate → train → eval pipeline submits up
|
|
764
|
+
front and the client can disconnect:
|
|
765
|
+
|
|
766
|
+
```bash
|
|
767
|
+
fcloud map --name gen --volume run42:/vol \
|
|
768
|
+
-- python3 gen.py --out /vol/data/shard-{i}.jsonl ::: {0..63}
|
|
769
|
+
fcloud map --name train --sku gpu_8x_h100 --volume run42:/vol --after gen \
|
|
770
|
+
-- python3 train.py --data /vol/data --ckpt /vol/ckpt
|
|
771
|
+
fcloud map --name eval --volume run42:/vol --after train \
|
|
772
|
+
-- python3 eval.py --ckpt /vol/ckpt --suite {} ::: mmlu,gsm8k,mbpp
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
Data flows between stages through the shared volume: map tasks write
|
|
776
|
+
disjoint task-keyed paths, the next stage mounts the same volume and sees
|
|
777
|
+
the union (the gate waits for the upstream's volume commits to land before
|
|
778
|
+
releasing). An upstream failure fails the downstream sweep with
|
|
779
|
+
`upstream_failed:<name>` instead of running it on missing inputs; after
|
|
780
|
+
fixing and re-running the upstream, `fcloud sweep retry <name> --all`
|
|
781
|
+
re-arms the downstream. A `succeeded_partial` upstream counts as finished
|
|
782
|
+
unless `--after-strict`. Edges bind at submit, so upstreams must be
|
|
783
|
+
submitted first (which also makes cycles impossible). SDK:
|
|
784
|
+
`client.map(..., after=gen_handle)` (or a name, or a list; `after_strict=True`).
|
|
695
785
|
|
|
696
786
|
**Tracking & recovery:**
|
|
697
787
|
```bash
|
|
@@ -699,15 +789,16 @@ fcloud sweeps # all sweeps: state + done/run/fail count
|
|
|
699
789
|
fcloud sweep status <name> # counts + failures clustered by error, with exemplar task
|
|
700
790
|
fcloud sweep status <name> --watch # same, repainted until terminal (--interval S)
|
|
701
791
|
fcloud sweep harvest <name> '*.json' ./out # every task's outputs → out/task-<i>/…
|
|
702
|
-
fcloud sweep logs <name> [--task N]
|
|
792
|
+
fcloud sweep logs <name> [--task N] [--attempt K] # durable output (defaults to the exemplar failure, falling back to earlier attempts; --attempt 1 = most recent attempt, explicitly)
|
|
703
793
|
fcloud sweep set <name> --max-parallel N # change the fan-out cap on a live sweep
|
|
704
|
-
fcloud sweep retry <name> [--all] # re-run
|
|
794
|
+
fcloud sweep retry <name> [--all] # re-run failed tasks (fix code first: resubmit is idempotent); --all adds canceled + WEDGED in-flight tasks (lost host / dead placement) and never restarts healthy running work
|
|
705
795
|
fcloud sweep cancel <name> [--remaining] # stop; --remaining keeps running tasks (partial success)
|
|
706
|
-
fcloud sweep wait <name>
|
|
796
|
+
fcloud sweep wait <name> [--poll S] [--timeout S] # block until terminal; exit 0 = success, 124 = still running at timeout
|
|
707
797
|
```
|
|
708
|
-
`--webhook URL`
|
|
709
|
-
|
|
710
|
-
|
|
798
|
+
`--webhook URL` POSTs the status document on canary pass/fail and
|
|
799
|
+
completion — agents should submit with `--no-wait --json` and wake on the
|
|
800
|
+
webhook instead of polling. There is no account-default webhook: without
|
|
801
|
+
`--webhook`, nothing is POSTed.
|
|
711
802
|
|
|
712
803
|
**The status JSON is a versioned public contract** (`schema_version: 1`),
|
|
713
804
|
specified in `docs/sweep-status-schema.md`: `counts` is an open map keyed by
|
|
@@ -1086,6 +1177,7 @@ flags merge with (and re-map by name) the `volumes` defaults.
|
|
|
1086
1177
|
| ------------ | ------------------------------------------ |
|
|
1087
1178
|
| `sku` | Default hardware SKU for `exec` and `run` |
|
|
1088
1179
|
| `volumes` | Volumes auto-attached to every `exec`/`run` session. Each entry is `"name"`, `"name:/mount"`, or `{"name": ..., "mount": ...}` |
|
|
1180
|
+
| `checkpoint` | `false` turns checkpoint/restore off for sessions created from this project (preemption rebuilds cold on any host). Default `true`. See *Checkpoint/restore on preemption* |
|
|
1089
1181
|
| `image.base` | Base image (default: `python:3.11-slim`) |
|
|
1090
1182
|
| `image.apt` | Packages to `apt-get install` |
|
|
1091
1183
|
| `image.pip` | Packages to `pip install` |
|
|
@@ -1166,6 +1258,24 @@ CLI commands or explicitly via `client.project("name", image=...)`.
|
|
|
1166
1258
|
remains pinned after a hard kill, recover with `fcloud stop <sid>` and then use the
|
|
1167
1259
|
session again.
|
|
1168
1260
|
|
|
1261
|
+
## Autonomous research loops (autoresearch)
|
|
1262
|
+
|
|
1263
|
+
To run an overnight keep/discard experiment loop on the user's code — after
|
|
1264
|
+
[karpathy/autoresearch](https://github.com/karpathy/autoresearch), with the GPU on
|
|
1265
|
+
fcloud instead of under the desk:
|
|
1266
|
+
|
|
1267
|
+
```bash
|
|
1268
|
+
fcloud autoresearch init # writes autoresearch/SETUP.md + program.template.md
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
Then read `autoresearch/SETUP.md` and follow it. It walks you through deciding the
|
|
1272
|
+
metric, the one mutable file vs the frozen eval, and the fixed time budget with the
|
|
1273
|
+
user; you write `program.md`, run the baseline on a persistent session, and hand
|
|
1274
|
+
off to the loop, which only ever re-uploads the mutable file and greps the metric.
|
|
1275
|
+
Progress is visible on the session's page in the fcloud dashboard (Autoresearch
|
|
1276
|
+
tab: metric chart + results table) once the loop's `results.tsv` upload syncs;
|
|
1277
|
+
point the user there when the loop starts.
|
|
1278
|
+
|
|
1169
1279
|
## Anti-Patterns
|
|
1170
1280
|
|
|
1171
1281
|
|
|
@@ -22,6 +22,7 @@ import fcloud._legacy_env # noqa: F401 — must run before any env read below
|
|
|
22
22
|
from fcloud.client import (
|
|
23
23
|
Client,
|
|
24
24
|
FcloudError,
|
|
25
|
+
NoApiKeyError,
|
|
25
26
|
PaymentOverdueError,
|
|
26
27
|
SessionNotFoundError,
|
|
27
28
|
)
|
|
@@ -31,6 +32,7 @@ from fcloud.errors import (
|
|
|
31
32
|
FcloudRuntimeError,
|
|
32
33
|
FoomError,
|
|
33
34
|
RateLimitedError,
|
|
35
|
+
SessionColdError,
|
|
34
36
|
VolumeHydrationError,
|
|
35
37
|
)
|
|
36
38
|
from fcloud.image import Image
|
|
@@ -75,8 +77,10 @@ __all__ = [
|
|
|
75
77
|
"FcloudError",
|
|
76
78
|
"FcloudRuntimeError",
|
|
77
79
|
"MapSpecError",
|
|
80
|
+
"NoApiKeyError",
|
|
78
81
|
"PaymentOverdueError",
|
|
79
82
|
"RateLimitedError",
|
|
83
|
+
"SessionColdError",
|
|
80
84
|
"SessionNotFoundError",
|
|
81
85
|
"VolumeConflictError",
|
|
82
86
|
"VolumeHydrationError",
|