fp-cloud-cli 0.0.1__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.
Files changed (128) hide show
  1. fp_cloud_cli-0.0.1/CHANGELOG.md +509 -0
  2. fp_cloud_cli-0.0.1/LICENSE +42 -0
  3. fp_cloud_cli-0.0.1/MANIFEST.in +10 -0
  4. fp_cloud_cli-0.0.1/PKG-INFO +335 -0
  5. fp_cloud_cli-0.0.1/README.md +259 -0
  6. fp_cloud_cli-0.0.1/fp_cli/__init__.py +10 -0
  7. fp_cloud_cli-0.0.1/fp_cli/__main__.py +4 -0
  8. fp_cloud_cli-0.0.1/fp_cli/_click_compat.py +87 -0
  9. fp_cloud_cli-0.0.1/fp_cli/_context.py +332 -0
  10. fp_cloud_cli-0.0.1/fp_cli/_version.py +1 -0
  11. fp_cloud_cli-0.0.1/fp_cli/analytics.py +432 -0
  12. fp_cloud_cli-0.0.1/fp_cli/analytics_config.py +77 -0
  13. fp_cloud_cli-0.0.1/fp_cli/analytics_registry.py +83 -0
  14. fp_cloud_cli-0.0.1/fp_cli/app.py +492 -0
  15. fp_cloud_cli-0.0.1/fp_cli/auth.py +160 -0
  16. fp_cloud_cli-0.0.1/fp_cli/client.py +1738 -0
  17. fp_cloud_cli-0.0.1/fp_cli/commands/__init__.py +0 -0
  18. fp_cloud_cli-0.0.1/fp_cli/commands/_write.py +214 -0
  19. fp_cloud_cli-0.0.1/fp_cli/commands/agent_cmds.py +407 -0
  20. fp_cloud_cli-0.0.1/fp_cli/commands/alerts_cmds.py +445 -0
  21. fp_cloud_cli-0.0.1/fp_cli/commands/audits_cmds.py +1054 -0
  22. fp_cloud_cli-0.0.1/fp_cli/commands/auth_cmds.py +512 -0
  23. fp_cloud_cli-0.0.1/fp_cli/commands/errors_cmds.py +190 -0
  24. fp_cloud_cli-0.0.1/fp_cli/commands/evals_cmds.py +161 -0
  25. fp_cloud_cli-0.0.1/fp_cli/commands/events_cmds.py +159 -0
  26. fp_cloud_cli-0.0.1/fp_cli/commands/fleet_cmds.py +416 -0
  27. fp_cloud_cli-0.0.1/fp_cli/commands/guardrails_cmds.py +148 -0
  28. fp_cloud_cli-0.0.1/fp_cli/commands/incidents_cmds.py +693 -0
  29. fp_cloud_cli-0.0.1/fp_cli/commands/keys_cmds.py +407 -0
  30. fp_cloud_cli-0.0.1/fp_cli/commands/list_cmds.py +63 -0
  31. fp_cloud_cli-0.0.1/fp_cli/commands/orgs_cmds.py +319 -0
  32. fp_cloud_cli-0.0.1/fp_cli/commands/policies_cmds.py +499 -0
  33. fp_cloud_cli-0.0.1/fp_cli/commands/queries_cmds.py +378 -0
  34. fp_cloud_cli-0.0.1/fp_cli/commands/sessions_cmds.py +151 -0
  35. fp_cloud_cli-0.0.1/fp_cli/commands/settings_cmds.py +150 -0
  36. fp_cloud_cli-0.0.1/fp_cli/commands/usage_cmds.py +35 -0
  37. fp_cloud_cli-0.0.1/fp_cli/commands/users_cmds.py +404 -0
  38. fp_cloud_cli-0.0.1/fp_cli/config.py +330 -0
  39. fp_cloud_cli-0.0.1/fp_cli/dates.py +78 -0
  40. fp_cloud_cli-0.0.1/fp_cli/enforcement.py +345 -0
  41. fp_cloud_cli-0.0.1/fp_cli/errors.py +98 -0
  42. fp_cloud_cli-0.0.1/fp_cli/models.py +902 -0
  43. fp_cloud_cli-0.0.1/fp_cli/orgs.py +30 -0
  44. fp_cloud_cli-0.0.1/fp_cli/output.py +6660 -0
  45. fp_cloud_cli-0.0.1/fp_cli/permissions.py +209 -0
  46. fp_cloud_cli-0.0.1/fp_cli/policy_check.py +290 -0
  47. fp_cloud_cli-0.0.1/fp_cli/py.typed +0 -0
  48. fp_cloud_cli-0.0.1/fp_cli/select.py +322 -0
  49. fp_cloud_cli-0.0.1/fp_cli/theme.py +53 -0
  50. fp_cloud_cli-0.0.1/fp_cloud_cli.egg-info/PKG-INFO +335 -0
  51. fp_cloud_cli-0.0.1/fp_cloud_cli.egg-info/SOURCES.txt +126 -0
  52. fp_cloud_cli-0.0.1/fp_cloud_cli.egg-info/dependency_links.txt +1 -0
  53. fp_cloud_cli-0.0.1/fp_cloud_cli.egg-info/entry_points.txt +2 -0
  54. fp_cloud_cli-0.0.1/fp_cloud_cli.egg-info/requires.txt +10 -0
  55. fp_cloud_cli-0.0.1/fp_cloud_cli.egg-info/top_level.txt +1 -0
  56. fp_cloud_cli-0.0.1/pyproject.toml +113 -0
  57. fp_cloud_cli-0.0.1/setup.cfg +4 -0
  58. fp_cloud_cli-0.0.1/tests/__init__.py +0 -0
  59. fp_cloud_cli-0.0.1/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  60. fp_cloud_cli-0.0.1/tests/__pycache__/conftest.cpython-312-pytest-9.1.1.pyc +0 -0
  61. fp_cloud_cli-0.0.1/tests/__pycache__/test_alerting.cpython-312-pytest-9.1.1.pyc +0 -0
  62. fp_cloud_cli-0.0.1/tests/__pycache__/test_analytics.cpython-312-pytest-9.1.1.pyc +0 -0
  63. fp_cloud_cli-0.0.1/tests/__pycache__/test_audits.cpython-312-pytest-9.1.1.pyc +0 -0
  64. fp_cloud_cli-0.0.1/tests/__pycache__/test_auth.cpython-312-pytest-9.1.1.pyc +0 -0
  65. fp_cloud_cli-0.0.1/tests/__pycache__/test_auth_mode.cpython-312-pytest-9.1.1.pyc +0 -0
  66. fp_cloud_cli-0.0.1/tests/__pycache__/test_click_compat.cpython-312-pytest-9.1.1.pyc +0 -0
  67. fp_cloud_cli-0.0.1/tests/__pycache__/test_client.cpython-312-pytest-9.1.1.pyc +0 -0
  68. fp_cloud_cli-0.0.1/tests/__pycache__/test_commands.cpython-312-pytest-9.1.1.pyc +0 -0
  69. fp_cloud_cli-0.0.1/tests/__pycache__/test_config.cpython-312-pytest-9.1.1.pyc +0 -0
  70. fp_cloud_cli-0.0.1/tests/__pycache__/test_dashboards_agent.cpython-312-pytest-9.1.1.pyc +0 -0
  71. fp_cloud_cli-0.0.1/tests/__pycache__/test_dates.cpython-312-pytest-9.1.1.pyc +0 -0
  72. fp_cloud_cli-0.0.1/tests/__pycache__/test_enforcement_logic.cpython-312-pytest-9.1.1.pyc +0 -0
  73. fp_cloud_cli-0.0.1/tests/__pycache__/test_facets.cpython-312-pytest-9.1.1.pyc +0 -0
  74. fp_cloud_cli-0.0.1/tests/__pycache__/test_failproofai_home.cpython-312-pytest-9.1.1.pyc +0 -0
  75. fp_cloud_cli-0.0.1/tests/__pycache__/test_failproofai_home.cpython-312.pyc +0 -0
  76. fp_cloud_cli-0.0.1/tests/__pycache__/test_fp_home_contract.cpython-312-pytest-9.1.1.pyc +0 -0
  77. fp_cloud_cli-0.0.1/tests/__pycache__/test_hardening.cpython-312-pytest-9.1.1.pyc +0 -0
  78. fp_cloud_cli-0.0.1/tests/__pycache__/test_help_table_coverage.cpython-312-pytest-9.1.1.pyc +0 -0
  79. fp_cloud_cli-0.0.1/tests/__pycache__/test_keys_queries.cpython-312-pytest-9.1.1.pyc +0 -0
  80. fp_cloud_cli-0.0.1/tests/__pycache__/test_list.cpython-312-pytest-9.1.1.pyc +0 -0
  81. fp_cloud_cli-0.0.1/tests/__pycache__/test_multivalue.cpython-312-pytest-9.1.1.pyc +0 -0
  82. fp_cloud_cli-0.0.1/tests/__pycache__/test_no_customer_identifiers.cpython-312-pytest-9.1.1.pyc +0 -0
  83. fp_cloud_cli-0.0.1/tests/__pycache__/test_operator.cpython-312-pytest-9.1.1.pyc +0 -0
  84. fp_cloud_cli-0.0.1/tests/__pycache__/test_orgs.cpython-312-pytest-9.1.1.pyc +0 -0
  85. fp_cloud_cli-0.0.1/tests/__pycache__/test_output.cpython-312-pytest-9.1.1.pyc +0 -0
  86. fp_cloud_cli-0.0.1/tests/__pycache__/test_pagination.cpython-312-pytest-9.1.1.pyc +0 -0
  87. fp_cloud_cli-0.0.1/tests/__pycache__/test_policy_check.cpython-312-pytest-9.1.1.pyc +0 -0
  88. fp_cloud_cli-0.0.1/tests/__pycache__/test_readme_matches_reality.cpython-312-pytest-9.1.1.pyc +0 -0
  89. fp_cloud_cli-0.0.1/tests/__pycache__/test_review_fixes.cpython-312-pytest-9.1.1.pyc +0 -0
  90. fp_cloud_cli-0.0.1/tests/__pycache__/test_telemetry_completeness.cpython-312-pytest-9.1.1.pyc +0 -0
  91. fp_cloud_cli-0.0.1/tests/__pycache__/test_usage.cpython-312-pytest-9.1.1.pyc +0 -0
  92. fp_cloud_cli-0.0.1/tests/__pycache__/test_v1_origin_diagnostic.cpython-312-pytest-9.1.1.pyc +0 -0
  93. fp_cloud_cli-0.0.1/tests/__pycache__/test_v1_routing.cpython-312-pytest-9.1.1.pyc +0 -0
  94. fp_cloud_cli-0.0.1/tests/__pycache__/test_whoami.cpython-312-pytest-9.1.1.pyc +0 -0
  95. fp_cloud_cli-0.0.1/tests/conftest.py +92 -0
  96. fp_cloud_cli-0.0.1/tests/test_alerting.py +828 -0
  97. fp_cloud_cli-0.0.1/tests/test_analytics.py +385 -0
  98. fp_cloud_cli-0.0.1/tests/test_audits.py +950 -0
  99. fp_cloud_cli-0.0.1/tests/test_auth.py +98 -0
  100. fp_cloud_cli-0.0.1/tests/test_auth_mode.py +272 -0
  101. fp_cloud_cli-0.0.1/tests/test_click_compat.py +172 -0
  102. fp_cloud_cli-0.0.1/tests/test_client.py +299 -0
  103. fp_cloud_cli-0.0.1/tests/test_commands.py +814 -0
  104. fp_cloud_cli-0.0.1/tests/test_config.py +99 -0
  105. fp_cloud_cli-0.0.1/tests/test_dashboards_agent.py +340 -0
  106. fp_cloud_cli-0.0.1/tests/test_dates.py +76 -0
  107. fp_cloud_cli-0.0.1/tests/test_enforcement_logic.py +481 -0
  108. fp_cloud_cli-0.0.1/tests/test_facets.py +170 -0
  109. fp_cloud_cli-0.0.1/tests/test_failproofai_home.py +722 -0
  110. fp_cloud_cli-0.0.1/tests/test_fp_home_contract.py +198 -0
  111. fp_cloud_cli-0.0.1/tests/test_hardening.py +186 -0
  112. fp_cloud_cli-0.0.1/tests/test_help_table_coverage.py +127 -0
  113. fp_cloud_cli-0.0.1/tests/test_keys_queries.py +496 -0
  114. fp_cloud_cli-0.0.1/tests/test_list.py +59 -0
  115. fp_cloud_cli-0.0.1/tests/test_multivalue.py +183 -0
  116. fp_cloud_cli-0.0.1/tests/test_no_customer_identifiers.py +168 -0
  117. fp_cloud_cli-0.0.1/tests/test_operator.py +492 -0
  118. fp_cloud_cli-0.0.1/tests/test_orgs.py +692 -0
  119. fp_cloud_cli-0.0.1/tests/test_output.py +1732 -0
  120. fp_cloud_cli-0.0.1/tests/test_pagination.py +204 -0
  121. fp_cloud_cli-0.0.1/tests/test_policy_check.py +191 -0
  122. fp_cloud_cli-0.0.1/tests/test_readme_matches_reality.py +125 -0
  123. fp_cloud_cli-0.0.1/tests/test_review_fixes.py +161 -0
  124. fp_cloud_cli-0.0.1/tests/test_telemetry_completeness.py +90 -0
  125. fp_cloud_cli-0.0.1/tests/test_usage.py +115 -0
  126. fp_cloud_cli-0.0.1/tests/test_v1_origin_diagnostic.py +70 -0
  127. fp_cloud_cli-0.0.1/tests/test_v1_routing.py +214 -0
  128. fp_cloud_cli-0.0.1/tests/test_whoami.py +104 -0
@@ -0,0 +1,509 @@
1
+ # Changelog — `fp` CLI
2
+
3
+ ## 0.0.1 — 2026-09-22
4
+
5
+ **First stable release.** `0.0.1b1` and `0.0.1b2` are the two betas behind it; the
6
+ `0.0.1b3` line the `bump` job opened is cut stable here rather than published, because
7
+ the server endpoints these commands drive are deployed to FailproofAI Cloud and the
8
+ CLI half should not stay a pre-release behind them. Until now `pipx install
9
+ fp-cloud-cli` resolved a beta only because no stable existed — a default that would
10
+ have silently changed the day one did. `Development Status` moves to
11
+ `5 - Production/Stable` with the version, so the classifier and the one string pip
12
+ reads say the same thing.
13
+
14
+ Nothing about the command surface changes at the cut: the entries below are the
15
+ entire diff from `0.0.1b2`, and `0.0.2b0` opens the next beta line.
16
+
17
+ ### Added
18
+
19
+ - `fp issues close <id>` — end an issue as won't-fix. Distinct from `resolve`: a
20
+ recurring audit finding reopens a **resolved** issue and leaves a **closed** one
21
+ alone. On an audit issue it marks the finding `dismissed`, without writing the
22
+ org-wide fingerprint suppression that `fp audits dismiss` writes. Exit 9 if the
23
+ issue already ended. (#815)
24
+ - `fp issues archive <id>` / `unarchive <id>` — take an issue off the board and put
25
+ it back. Orthogonal to state, allowed in any state, no confirmation prompt. (#815)
26
+ - `fp issues clear (--audit <id> | --all-audits | --everything) [--dry-run]` — resolve
27
+ every open issue in a scope plus the audit findings behind them, in one server-side
28
+ transaction. Exactly one scope flag is required. Needs `issues:close` **and**
29
+ `audits:write`. Writes no suppression, so anything still broken reopens its issue
30
+ on the next run. The confirm names the count from a dry run that shares its
31
+ scope predicate with the write. (#815)
32
+ - `issues list --state closed` is accepted, and `Incident` carries `closed_at` and
33
+ `archived_at`. (#815)
34
+
35
+ ### Fixes
36
+
37
+ - The recurrence sentence said two different things in one release. `audits resolve`'s
38
+ confirm line (`a genuine recurrence re-opens as new`), the skill and the command
39
+ reference implied a recurrence opens a **different** issue; `issues close --help`, the
40
+ audits guide and the CLI reference said the resolved issue **reopens**. The second is
41
+ the one the rest of the model needs — a `closed` issue has to have something to stay
42
+ closed through — so `reopens` is now what every surface says. (#815)
43
+ - `issues clear --help` claimed the `--dry-run` count "cannot disagree" with the write.
44
+ The two requests share a scope predicate, not a row set, so an issue entering the scope
45
+ between them is cleared without being in the confirmed number — correct for a scoped
46
+ clear, but not the guarantee that was written. The help now states the real one, and
47
+ names the closing line as the count of what changed. (#815)
48
+ - The skill answered "clear all our issues" with `--all-audits`, which leaves alert-born
49
+ and hand-opened issues untouched. Settling the scope is now the first step. (#815)
50
+
51
+ ## 0.0.1b2 — 2026-08-25
52
+
53
+ Open for the next release. `0.0.1b1` published on 2026-08-24 and the `bump` job
54
+ moved the version here automatically. Add entries as changes merge — this section
55
+ becomes the GitHub Release body when it ships.
56
+
57
+ ### Fixes
58
+
59
+ - **Typed errors keep their exit codes under typer 0.27.2.** That release moved
60
+ `Abort` out of its vendored `typer._click.exceptions`, and `_click_compat`
61
+ imported all six of its symbols under a single
62
+ `try: … except ImportError: from click import …`. One missing name was enough
63
+ to send the whole block to the fallback, binding `ClickException`,
64
+ `UsageError`, `BadParameter`, `Command` and `Parameter` to the pip `click`
65
+ distribution — which is *not* the Click Typer runs, and Typer catches only its
66
+ own. So every typed error escaped its handler: `fp alerts show ghost` exited
67
+ **1 with an empty stderr** instead of **6** with `no alert named "ghost"`, and
68
+ likewise for exits 2, 3, 4 and 5. Nothing warned; the CLI imported and every
69
+ happy path passed. The Click is now decided **once**, on whether `typer._click`
70
+ exists at all, and every symbol imported from that decision, so a future move
71
+ fails at import — a CLI that refuses to start — instead of silently flattening
72
+ the exit-code contract. `Abort` is resolved from `typer.Abort`, which is by
73
+ construction the class `typer.prompt` raises and typer's own `_main` catches on
74
+ every version in range. Suite verified green against typer 0.25.1, 0.27.0,
75
+ 0.27.1 and 0.27.2 (#771)
76
+
77
+ ### Dependencies
78
+
79
+ - typer 0.27.1 → 0.27.2, click 8.4.2 → 8.5.0, posthog 7.42.0 → 7.44.2 (#771)
80
+
81
+ ## 0.0.1b1 — 2026-08-24
82
+
83
+ ### The package is `fp-cloud-cli`; the command is still `fp`
84
+
85
+ - **What you type to install it changed. What you type to use it did not.**
86
+
87
+ ```bash
88
+ pipx install fp-cloud-cli # or: uv tool install fp-cloud-cli
89
+ fp --version
90
+ ```
91
+
92
+ - **Why not `fp-cli`.** PyPI refuses a name "too similar to an existing project",
93
+ and its similarity check strips separators — so `fp-cli` collides with `fpcli`,
94
+ a real package with 42 releases and uploads as recent as April 2025. It is not
95
+ a squat, so there is no PEP 541 claim to make: the name simply is not available.
96
+ (`fp` itself was taken long before that, which is why the command and the
97
+ distribution never matched in the first place.)
98
+ - **`fp-cloud-cli` also says what this is.** It talks to the FailproofAI Cloud
99
+ dashboard's `/api` surface and nothing else — never the Rust server directly —
100
+ and it is not the `failproofai` npm CLI, which enforces inside the agent loop
101
+ rather than reading back what the loop did.
102
+ - **Nothing else moved.** `~/.failproofai/fpcli/cli-auth.json` keeps its name, so
103
+ nobody is signed out — it is a contract with the Enforcement CLI's
104
+ `src/hooks/fp-home.ts`, not a label. `FP_HOME` still works and still wins. The
105
+ import package stays `fp_cli`, which nobody types: this is an application, and
106
+ the `fp` command is its entire public surface.
107
+
108
+ ### Versioning restarts at `0.0.1b1`
109
+
110
+ - **The number was inherited from a distribution this is not.** `0.1.22` was where the
111
+ CLI had got to under the shared `agenteye` name in the private monorepo. It came over
112
+ verbatim when the CLI was open-sourced as `fp-cloud-cli` and nothing has moved it since — but
113
+ `fp-cloud-cli` is a 404 on PyPI, so that number would have opened the project's first release
114
+ claiming a history that lives entirely on another name.
115
+ - **`0.1.22` also said "stable" where everything else says beta.** It is a plain release
116
+ version, so `pip install fp-cloud-cli` would resolve it by default while the classifier
117
+ (`Development Status :: 4 - Beta`) and every doc page call the CLI beta. `0.0.1b1` is a
118
+ PEP 440 pre-release, which is the only mechanism PyPI has for that distinction — there
119
+ are no dist-tags here the way there are on npm.
120
+ - **Nothing is stranded and nobody is downgraded.** There is no published `fp-cloud-cli` version
121
+ to reuse or to fall behind, and pip, pipx and uv all install a pre-release when it is the
122
+ only release available, so `pipx install fp-cloud-cli` keeps working with no `--pre`.
123
+ - **The `## 0.1.7` and older sections below predate the rename.** They are the `agenteye`
124
+ CLI's history, kept because the behaviour they describe is this CLI's behaviour. They are
125
+ not versions of `fp-cloud-cli`, and no `fp-cloud-cli` release will ever carry those numbers.
126
+
127
+ ### The session moved to `~/.failproofai/fpcli/cli-auth.json`
128
+
129
+ - **Nothing to do. Nobody is signed out.** The session was at `~/.fp/cli.json`; it is now
130
+ `~/.failproofai/fpcli/cli-auth.json`, still mode `0600`. A session at the old path is
131
+ adopted on the next command, so an upgrade is invisible.
132
+ - **Adoption is a COPY.** The old file is left exactly where it is, which keeps a
133
+ downgrade working — an older `fp` still finds its session where it left it — and means
134
+ nothing irreversible happens on a machine mid-rollout. Remove `~/.fp/` yourself once
135
+ you are happy.
136
+ - **It is best-effort on purpose.** If the new location cannot be written (read-only
137
+ home, full disk, a symlink we refuse) the session found is still returned, so a machine
138
+ that cannot be migrated keeps working rather than being logged out by our own
139
+ housekeeping.
140
+ - **Why:** one product owned three top-level dotfiles — `~/.fp` (this CLI),
141
+ `~/.failproofai` (the Enforcement CLI) and `~/.agenteye` (the SDK/collector spool).
142
+ This collapses the first into the second. `~/.agenteye` stays: it is a wire contract
143
+ with the collector, and renaming it from the SDK's side would write events into a
144
+ directory nothing watches, with no error on either side.
145
+ - **`FP_HOME` still works and still wins.** Resolution is `FP_HOME` >
146
+ `$FAILPROOFAI_HOME/fpcli` > `~/.failproofai/fpcli`. `FP_HOME` names the CLI's own
147
+ directory and is used as-is, so an existing export addresses the same place it always
148
+ did; `FAILPROOFAI_HOME` names the shared home root, so `fpcli/` is appended. The
149
+ filename changed too (`cli.json` → `cli-auth.json`), so an `FP_HOME` user's old session
150
+ sits beside the new path and is adopted from there. A redirected config is never
151
+ reached past: with `FP_HOME` set the CLI does not look in `~/.fp` at all, because that
152
+ would adopt a session from a context the user explicitly moved away from.
153
+ - **Nothing that authenticates without the config file changed.** `--token` / `FP_TOKEN`
154
+ and `--api-key` / `FP_API_KEY` never touched disk and still do not, so CI that
155
+ authenticates by environment never enters any of this. Read-only commands still create
156
+ no file at all.
157
+ - **The CLI only ever creates.** It will bring `~/.failproofai` into existence on a
158
+ machine that has never run the Enforcement CLI, and leaves a populated one untouched.
159
+ The new path is registered in that home's layout (`src/hooks/fp-home.ts`) and
160
+ classified `user-typed`, so a layout migration cannot drop it — `resettablePaths()` is
161
+ a filter over that table, and only `derived` / `refetchable` entries are deleted. No
162
+ `LAYOUT_VERSION` bump: nothing moved, and a bump would mark every existing home stale
163
+ and run a reset on machines that have nothing to migrate.
164
+
165
+ ### Two hardenings the shared directory made necessary
166
+
167
+ Both are consequences of writing next to another product's secrets rather than
168
+ into a directory the CLI owned outright.
169
+
170
+ - **A symlinked config no longer writes through to whatever it points at.**
171
+ `O_TRUNC` follows symlinks, so a link at `cli-auth.json` pointing at
172
+ `../credentials.json` made `fp login` silently truncate the Enforcement CLI's
173
+ token and write the session over it — no error, nothing in either product's
174
+ logs. The final component is now opened `O_NOFOLLOW` and a link is refused
175
+ with a message naming the file. The link is not deleted: a symlink is
176
+ something a person put there.
177
+ - **`fpcli/` is created `0700` rather than inheriting the umask.** A common
178
+ `0002` umask made it `0775`. The session file itself was always `0600`, so it
179
+ was never readable — but a group-writable directory lets anyone in the group
180
+ replace the file, which is a session swap. The shared parent is untouched: if
181
+ the CLI is the first to create `~/.failproofai` it leaves it to the umask, as
182
+ the Enforcement CLI would, and an existing directory is never re-permissioned.
183
+ - **The session is written atomically, to a temp file that is renamed into place.**
184
+ `O_NOFOLLOW` closed the symlink hole but says nothing about a **hard link** —
185
+ that is not a link, it is a second name for one inode, so an in-place write
186
+ went straight through it into the neighbour's file exactly as before. `rename`
187
+ swaps the directory entry instead, so the other name keeps the old inode. The
188
+ same change buys three more things: a reader never observes a half-written
189
+ credential, two racing `fp` processes end with one whole session rather than a
190
+ splice, and a FIFO left in the config position can no longer hang the CLI
191
+ forever (`open` on a FIFO blocks for a reader; the previous code sat there
192
+ indefinitely with no output). The temp file is removed on every failure path.
193
+
194
+ ## 0.1.7
195
+
196
+ ### Packaging fix: declare `click` and `pygments` explicitly
197
+ - **Fixes `ModuleNotFoundError: No module named 'click'` on a fresh install.** The CLI imports
198
+ `click` directly (and `pygments` for SQL highlighting) but only ever got them transitively via
199
+ `typer` / `rich`. Modern `typer` (>=0.13) **no longer installs `click`**, so a clean
200
+ `pipx install fp-cloud-cli` left `click` absent and the CLI crashed on startup. Both are now listed
201
+ as direct dependencies (`click>=8.1`, `pygments>=2.13`) so installs are self-contained.
202
+ - No behaviour change — same code, correct dependency metadata.
203
+
204
+ ### `keys create` / `keys update`: compact permission format + shared ending
205
+ - **New permission input**: the compact `slug:action.action` token format, space-separated and/or
206
+ repeated `-p` (both compose) — `keys create "ci-bot" -p events:read.add keys:read` expands to
207
+ `events:read, events:add, keys:read` (de-duped). A malformed token (`events`, `events:`) or an
208
+ unknown permission → a red `✗ bad permission …` box **before** any mutation (exit 2). Replaces
209
+ the old one-permission-per-`-p` form.
210
+ - **`keys update` now takes the unique key NAME** (resolved to the id), not a UUID — consistent
211
+ with `regenerate`/`disable`. Permissions REPLACE the current grants; a calm confirm (`replace
212
+ permissions on key <name>? the key keeps working …`, default no, `--yes` skips); decline → faint
213
+ `cancelled — permissions unchanged` box.
214
+ - **Shared ending** (both): a green `✓ created/updated key <name> · {n} permissions` line + the
215
+ **same grouped permissions box** as `whoami` / `orgs perms`. `create` shows a `secret · shown
216
+ once` green box first (piping still captures just the bare secret); name-collision → red box.
217
+ - `--json`: create `{id,name,permissions,created_at,key}`, update `{id,name,permissions,created_at,
218
+ revoked_at}` — `permissions` is the expanded flat list.
219
+
220
+ ### `keys`: boxed `list` (active-first), and name-based `regenerate` / `disable` flows
221
+ - **`keys list`** → a boxed `api keys · {n} · active first` table (`created · name · permissions ·
222
+ status`) — **active keys sort to the top** (then revoked), each group newest-first. Status is
223
+ colour-coded (`● active` green / `○ revoked` red; filled = live, hollow = dead) with a
224
+ `{total} keys · {n} active · {m} revoked` footer. The raw UUID is hidden by default (`--show-id`
225
+ for a short id, `--json` for the full id); created shows compact `MM-DD HH:MM`. `--json` unchanged.
226
+ - **`keys regenerate <name>` / `keys disable <name>`** now take the **unique key name** (resolved
227
+ to the id), not a UUID. Every flow state is a **rounded notice box** (one consistent boxed
228
+ family): an amber `confirm` box (`⚠ … <name>?` + dim consequence) with a `[y/N]` prompt below
229
+ defaulting to **no**; a faint `cancelled` box (`○ nothing changed`) on decline (exit 0, no more
230
+ red "Aborted"); a red `error` box (`✗ no key named <name>` + hint) when not found (exit 6); a
231
+ green `secret rotated` box (regenerate — piping still captures just the bare secret on stdout)
232
+ or `disabled` box (disable); a faint `no change` box when already disabled. `--json`: regenerate
233
+ → `{name, secret}`, disable → `{name, status:"disabled"}`, decline → `{cancelled:true}`.
234
+ - Shared helpers `confirm_destructive` + `print_cancelled` + `_notice_box` (reused by the
235
+ destructive key actions). `keys create` / `keys update` are unchanged (separate pass).
236
+
237
+ ### `list <kind>`: one boxed, column-flowing view
238
+ All nine `list` subcommands (envs, agents, event_types, score_filters, models, hooks, triggers,
239
+ tools, error_types) now render through one shared boxed renderer (`render_value_list`):
240
+ - An ACCENT panel titled `{kind} · {count} {description}` with the (sorted) values in
241
+ **column-major flow** — a column fills to 8 rows then overflows to the next; the column count
242
+ is capped to the terminal width (prefers taller over wider-than-screen; never overflows; min
243
+ one column). Empty → `none found`.
244
+ - A dim **filter-hint footer** for kinds that map to a real filter flag: `envs`/`agents`/
245
+ `event_types` → `fp events --env/--agent-id/--event-type`, `error_types` →
246
+ `fp errors --error-type`. Kinds with no matching value filter (models/hooks/triggers/
247
+ tools/score_filters) get no footer.
248
+ - `--json` is unchanged (`{"kind", "values"}`).
249
+
250
+ ### `orgs`: boxed `list`, a `current` identity card, and a new `orgs perms`
251
+ - **`orgs list`** now renders the same boxed `your orgs · N` panel as `whoami` (marker / org /
252
+ name / role / perms + a `switch with …` line) instead of a plain table.
253
+ - **`orgs current`** is a compact `current org` identity card (slug + name · role + permission
254
+ count · signed-in email) with a footer cross-linking `orgs perms` / `orgs switch`. The
255
+ 28-permission comma wall moved out. `--json`: `{slug, name, role, permission_count, user_email}`.
256
+ - **New `orgs perms`** — your grants in the active org as the grouped, risk-coloured permissions
257
+ panel (the **same shared renderer** as `whoami`: read=green, create/modify=pink, invoke=amber,
258
+ destructive=red; NO_COLOR `*`). `--json`: `{slug, role, permissions, permission_count}`.
259
+ - The permissions panel and the orgs panel are now single shared helpers
260
+ (`render_permissions_panel` / `render_orgs_panel`) used by `whoami`, `orgs perms`, and
261
+ `orgs list` so they can't drift. Resource order aligned (dashboards · keys · queries · users · …).
262
+
263
+ ### `errors` now lists the errored events (with `--aggregate` for the old summary)
264
+ `errors` was a summary-only command; it now **fetches and lists the errored events** (the
265
+ dashboard `/errors` view), with the rollup moved behind a flag — mirroring `evals`.
266
+ - **`errors`** (default) — one row per errored event in an error-themed (muted red-purple)
267
+ boxed table: `time · event · env · agent · session · summary`. The `event` cell is `● {type}`,
268
+ **red only when the type names an error** (`error`/`fail`); the `summary` is derived from the
269
+ payload (tool name, `error_type: message`, hook name, …) and truncates with `…`. Session ids
270
+ truncate (`--full-ids` for whole). Same filter/paging options as `events`/`sessions` plus the
271
+ errors-specific `--error-type`/`--search-exclude`.
272
+ - **`errors --aggregate`** — the old summary, redesigned as an errors-themed card: a large red
273
+ hero count + `across N sessions · N agents · last <relative>`; zero errors → a calm green
274
+ `✓ no errors found` in a neutral panel.
275
+ - `--json`: list → `{"errors": [...], "next_cursor": …}` (full event rows); aggregate → `{total,
276
+ sessions, agents, last_ts, bins}`. Re-added `errored`/`error_type`/`search_exclude` to the
277
+ `list_events` client. `analytics_cmds.py` renamed → `errors_cmds.py`.
278
+
279
+ ### Score bar → braille + unified colour bands
280
+ - The `evals --aggregate` score bar now uses **braille** (`⣿` fill / `⣀` track) on a **zoomed
281
+ `.40–1.0` scale** (so the typical .7–.9 range shows visible variation), with a band-tinted
282
+ track. Falls back to solid blocks (`█`/`░`) under `NO_COLOR` (clearer in mono; failing avgs
283
+ get a trailing `!`).
284
+ - **Unified score colour bands** (`.80`/`.50`, was `.85`/`.70`) used **everywhere** a score is
285
+ coloured — the evals score cells, the aggregate avg, and the bar: **≥.80** cyan-green
286
+ `#3ddbb8`, **.50–.80** amber, **<.50** red. So a given score reads the same colour CLI-wide.
287
+ - Aggregate gains a one-line legend under the panel: `scale .40–1.0 · ⣿ ≥.80 ⣿ .50–.80 ⣿ <.50`;
288
+ the evals list footer legend updates to the new bands. Presentation only.
289
+
290
+ ### Split scores back out: `sessions` (runs) vs `evals` (scores + `--aggregate`)
291
+ Refined the previous merge into a clearer operational-vs-quality split:
292
+ - **`sessions`** is now a pure run list — columns `time · env · agent · session · status`
293
+ (the **scores column is gone**, and so are `--score`/`--scores-full`). Footer has no score
294
+ legend. `--json` still includes `scores` per row.
295
+ - **`evals`** (renamed from `eval-aggregate`, now a full command) has **two modes, same filters**:
296
+ - bare → the eval **list** with the scores column (`time · env · agent · session · status · scores`).
297
+ - **`--aggregate`** → a redesigned two-panel view: a **totals card** (hero count + colour-coded
298
+ status dots + a derived success-rate line) and a **score-stats table** (per metric: n, avg +
299
+ a 10-cell threshold-coloured bar, min/max/p50), sorted worst-average first, all metrics shown.
300
+ - `--json`: list → `{"evaluations": [...], "next_cursor": …}`; aggregate → `{total,
301
+ status_counts, score_stats[], timeline}`.
302
+ - The standalone `eval-aggregate` command is removed (use `evals --aggregate`). Aggregate numbers
303
+ are rounded to 2 decimals for display (full precision stays in `--json`); under `NO_COLOR` a
304
+ failing avg (<.70) gets a trailing `!`. No change to what's computed or fetched.
305
+
306
+ ### Merged `evals` into `sessions` (one command, not two)
307
+ `evals` and `sessions` both listed evaluation results from the same endpoint, so they're now
308
+ **one command: `sessions`**. The richer `evals` implementation (boxed output, full option set,
309
+ width-aware scores) was kept and renamed; the old `sessions` command was removed.
310
+ - `sessions` lists evaluation results newest-first with the boxed renderer (status colours,
311
+ threshold-coloured scores, session-id truncation, `--full-ids`/`--scores-full`, score legend).
312
+ - `--json` returns `{"sessions": [...], "next_cursor": ...}`.
313
+ - The dead `list_sessions` client helper was removed. (`eval-aggregate` is unchanged.)
314
+ - Help/schema/README scrubbed of `evals`; top-level examples fixed to put the global `--json`
315
+ before the command.
316
+
317
+ ### Validation hardening (`--score` / `--from` / `--to`)
318
+ - **`--score KEY:..` (both bounds empty) is now a clean usage error (exit 2).** It used to pass
319
+ validation and the server silently dropped it → the **unfiltered** set came back.
320
+ - **`--from`/`--to` now require a full RFC3339 UTC timestamp** (a `T` separator + a `Z`/offset).
321
+ A timezone-less (`2026-05-01T00:00:00`) or space-separated value used to slip through and hit
322
+ a server 400 (exit 1); it's now caught client-side as a usage error (exit 2).
323
+
324
+ ### Redesigned the `evals` table
325
+ - **New default output**: the same accent-bordered rounded panel as `events`, built on a shared
326
+ `render_list_panel` helper. Columns `time · env · agent · session · status · scores` (agent
327
+ bright, the rest contextual-dim). **status** is colour-coded by state (done green / running
328
+ amber / failed-error-timeout red); **scores** render as `metric value` with the value coloured
329
+ by threshold (**≥.85** green / **.70–.85** amber / **<.70** red) and compacted (`.94`, `1.0`).
330
+ - **Scores are width-aware**: as many pairs as fit, then `+N`; an eval always stays one row and
331
+ the `time`/`status` columns never get squeezed. `--scores-full` shows every pair (may wrap).
332
+ - **Session ids truncate** by default (`sess-…fcf97e01`); `--full-ids` keeps them whole. `--json`
333
+ always has full ids + structured scores.
334
+ - **Footer** carries a compact score-colour legend (`score: ≥.85 .70–.85 <.70`), dropped on a
335
+ narrow terminal. Under `NO_COLOR`, failing scores (<.70) get a trailing `!` so they stay visible.
336
+
337
+ ### `evals`: fix docs, reorder options, drop `--latest-per-session`
338
+ - **Fixed the broken `--json` examples** in the `evals` help — `--json` is a **global** option and
339
+ goes before the command (`fp --json evals …`), not after it (`fp evals … --json`,
340
+ which exits 2). The docstring examples now show the correct form.
341
+ - **Reordered the options** to match the `events -h` layout: limit, since, from, to, session-id,
342
+ env, agent-id, status, score, all, cursor, page-size, fields.
343
+ - **Removed `--latest-per-session`** — use `fp sessions` for the newest evaluation per
344
+ session (it's the same query, deduped). The flag's client param is retained internally (the
345
+ `sessions` command still forces it).
346
+
347
+ ### Redesigned the `events` table + trimmed its filters
348
+ - **New default output**: an accent-bordered rounded box whose title carries the row count,
349
+ sort direction and date (`events · 10 · newest first · 2026-06-22`), with columns
350
+ `time · type · env · agent · session` (now sharing the `render_list_panel` helper with `evals`:
351
+ a dim LABEL header + a thin rule). Rows show clock time (the date lives in the title); a
352
+ window spanning more than one UTC day adds the date back into each row. The summary line below
353
+ reads `<n> shown · more available · fp events --all`. `--json` is unchanged; `--fields`
354
+ still prints the plain projected table.
355
+ - **Removed filters**: `--tool-name`, `--error-type`, `--errored`, `--search-exclude` (the
356
+ `errors` command keeps the error/exclude filters). The now-orphaned `list_events` client params
357
+ were dropped too.
358
+ - **`--environment` dropped in favour of `--env`** (one spelling). `evals`/`sessions`/`errors`
359
+ still accept both.
360
+
361
+ ### Replaced `facets` with `fp list <thing>`
362
+ The single `fp facets --kind <kind>` command is now a `list` group with one named
363
+ subcommand per dropdown, matching the dashboard's filters:
364
+ `list envs | agents | event_types | score_filters | models | hooks | triggers | tools | error_types`.
365
+ Each prints a flat list of the distinct values (or `--json {"kind", "values"}`) — the same
366
+ per-org cached data behind the dashboard dropdowns. **New: `list score_filters`** surfaces the
367
+ evaluation score keys/metrics (needs `evaluations:read`; the others need `events:read`).
368
+
369
+ ### Slimmed the command surface (removed 7 commands)
370
+ Trimmed the CLI to a focused, maintainable core — each of these is still available in the
371
+ dashboard:
372
+ - **`environments`**, **`latency`**, **`score-keys`** — niche analytics one-offs; use `facets`
373
+ (value discovery), `errors`, and `eval-aggregate` instead.
374
+ - **`session`** (the singular `session show` / `session export` group) — inspect a single session
375
+ with `sessions`, `events --session-id <id>`, and `evals --session-id <id>`.
376
+ - **`re-evaluate`** — redundant; trigger a fresh evaluation from the dashboard.
377
+ - **`permission-sets`** (list/show/create/update/delete) — manage sets in the dashboard;
378
+ `users`/`keys` still accept a set by name via `--permission-set`.
379
+ - **`dashboards`** (list/show/create/update/delete/tiles) — manage dashboards in the dashboard UI.
380
+
381
+ The orphaned client/model plumbing and their unit tests were removed alongside the commands.
382
+
383
+ ### Reworked the `agent` command surface (chats + models; `compose-sql` removed)
384
+ - Dropped the `conversation-` prefix and renamed the list: **`conversations` → `chats`**,
385
+ `conversation-show` → **`show`**, `conversation-rename` → **`rename`**, `conversation-delete`
386
+ → **`delete`**. (`agent chats --json` now returns `{"chats": [...]}`.)
387
+ - **Removed `agent conversation-create`** — `ask` manages the chat lifecycle itself now.
388
+ - **`agent ask` is chat-first.** With **`--chat <id>`** it continues that chat (prior thread
389
+ sent for context, new turn appended); **without it** it starts a **new** chat, answers,
390
+ persists, and prints the new `chat_id` (the first question auto-titles it). The chat is created
391
+ only once an answer lands, so a failed/aborted ask leaves no empty chat. (`--conversation` →
392
+ **`--chat`**; `--json` now includes `chat_id`.)
393
+ - **New `agent models`** — lists the deployment's model allowlist (default marked), read from the
394
+ agent health endpoint. **`agent ask --model`** is now validated against that allowlist
395
+ client-side (an unknown model exits 2 with the valid choices) instead of silently falling back
396
+ to the default.
397
+ - **Removed `agent compose-sql`** from the CLI.
398
+
399
+ ### Fixed: `alerts update` single-field edits (were HTTP 500)
400
+ - A flag-only update such as `fp alerts update <id> --disabled` (the documented
401
+ example) used to send a **partial** body to a server endpoint that does a **full
402
+ replace**, which failed with `HTTP 500`. Only a complete `--file` AlertInput worked.
403
+ - `alerts update` now does a **read-merge** (like `users update`): with override flags and
404
+ no `--file`, it fetches the current alert and re-sends it with just the named fields
405
+ changed — so `--disabled`, `--severity`, `--name`, etc. work on their own. This path now
406
+ needs `alerts:read` **and** `alerts:write`, and a missing alert returns a clean not-found
407
+ (exit 6) instead of a 500. The `--file` path is unchanged (a straight full replace).
408
+
409
+ ### Single `orgs` group (merged `org` + `orgs`)
410
+ - All tenant functionality now lives under one group, **`orgs`** — the separate singular
411
+ `org` group was removed.
412
+ - **`orgs list`** — list the orgs you belong to with your role (permission set) and permission
413
+ count in each; the active org is marked. `--json` adds `is_instance_admin` and a per-org
414
+ `active` flag.
415
+ - **`orgs switch [slug]`** — switch the active tenant. Pass a slug (`orgs switch acme`) for a
416
+ direct switch, or omit it (`orgs switch`) to choose from a list in a terminal — the current
417
+ org is the Enter-to-keep default, a sole org auto-selects, and a non-interactive run requires
418
+ a slug. Same access validation as `orgs use` (a non-existent/unauthorised slug is rejected).
419
+ - **`orgs use <slug>`** — set the default active tenant (unchanged behaviour, now under `orgs`).
420
+ - The interactive picker is shared between `login` and `orgs switch` (one implementation).
421
+
422
+ ### Removed the `logs` alias
423
+ - Dropped the `logs` command (it was a duplicate alias of `events`). Use `fp events`.
424
+
425
+ ### Consistent visual language across commands
426
+ - Unified the icon/colour vocabulary used everywhere: `◆` brand/identity (login header,
427
+ `whoami`, `orgs list`), `›` step, `✓` success (green), `○` neutral status (dim), `✗` error
428
+ (red). `whoami` when signed out is now a neutral `○ not signed in` line (not plain text), and
429
+ when signed in leads with the `◆ <email>` banner + a clean key/value + orgs table.
430
+ - Errors render as a clean one-line `✗ <message>` (with a `try '… -h'` nudge for usage errors)
431
+ instead of Typer's heavy red panel, so failures match the success/status lines. A doubled
432
+ "Did you mean …?" suggestion is collapsed.
433
+
434
+ ### Cleaner login output
435
+ - The sign-in flow now shows a single line — **`sending a 6-digit OTP to <email> …`** (the
436
+ email highlighted) — and drops the dashboard-URL line and the "code is in your email / dev
437
+ SMTP" hint. It states only that an OTP was sent, never whether the address is valid.
438
+
439
+ ### Logout clears the whole session
440
+ - `logout` now clears the **active org**, email, and user id from `~/.fp/cli.json`
441
+ (not just the token) — previously the file kept `org`/`email`/`user_id`, so the CLI
442
+ "remembered" the last tenant after logout. `base_url`, the `insecure` preference, and the
443
+ machine-stable `anonymous_id` are kept. The next `login` therefore starts the org picker
444
+ fresh, with no remembered default.
445
+ - `logout` when you are **not** signed in is now a no-op that reports `○ already signed out`
446
+ (and `"already_signed_out": true` in `--json`) instead of falsely confirming a `✓ signed out`.
447
+
448
+ ### Nicer login/logout experience (presentation only)
449
+ - Restyled the sign-in flow — a compact `◆ fp · sign in` header, `›` step lines, a
450
+ cleaner numbered org picker, and a `✓ signed in as … · <org>` confirmation (and `✓ signed
451
+ out`). No behaviour change; all of it is stderr chrome, so `--json` stdout is unchanged.
452
+
453
+ ### Org validation at login / `orgs use`
454
+ - `login --org <slug>`, `FP_ORG`, and `orgs use <slug>` now **validate the org
455
+ against the server before saving it** — it must exist and be accessible to you. This
456
+ fixes a hole where an instance admin could activate (and persist) a non-existent or
457
+ unauthorised slug (e.g. a typo like `--org fp`), which then broke every later
458
+ command. A member's own orgs are accepted with no extra round-trip; a non-member org is
459
+ verified via a cheap org-scoped probe (HTTP 200 → ok, otherwise rejected with
460
+ "Org '<slug>' does not exist or you do not have access to it").
461
+
462
+ ### Login org picker
463
+ - `login` now shows an **interactive org picker** after your email is verified when you
464
+ belong to more than one org, so you choose the active tenant each login instead of
465
+ silently re-entering a previously-saved one. A still-valid saved org is offered as the
466
+ Enter-to-keep default (marked `(current)`). An explicit `--org`/`FP_ORG` still skips
467
+ the picker; single-org users still auto-select. In a non-interactive run (`--json` / piped
468
+ stdin) a still-valid saved org is reused, else login reports `needs_org_selection`.
469
+
470
+ ## 0.1.6
471
+
472
+ Full parity with the dashboard API, multi-tenant support, and exhaustive telemetry.
473
+
474
+ ### Multi-tenancy (correctness fix)
475
+ - The CLI is now org-aware. The active tenant is chosen **at login** (`login --org <slug>`)
476
+ and persisted; override per command with the global `--org` / `FP_ORG`, or change the
477
+ default with `orgs list` / `org use <slug>`.
478
+ - Every request now sends the `X-AgentEye-Org` header, so **multi-org accounts work** (previously
479
+ they were rejected). Permissions are resolved per org.
480
+ - `whoami` reports the active org, instance-admin status, per-org permissions, and all memberships
481
+ (the session model dropped the old flat `permissions` list).
482
+
483
+ ### New command groups
484
+ - **keys** — create/list/update/disable/regenerate API keys (secret shown once).
485
+ - **query** — saved SQL + ad-hoc runner (`query run`), schema introspection.
486
+ - **users**, **permission-sets**, **settings** — org administration.
487
+ - **alerts**, **incidents** — alert definitions and incident triage.
488
+ - **dashboards** (+ tiles), **agent** (`ask`, `compose-sql`, conversations) — JSON/streaming.
489
+ - **facets**, **errors**, **latency**, **score-keys**, **eval-aggregate** — discovery & analytics.
490
+ - `events` gains `--tool-name`, `--error-type`, `--errored`, `--order`, `--search`, `--search-exclude`.
491
+ - **schema** — emits the entire CLI surface as JSON for agents/tooling.
492
+
493
+ ### Safety & ergonomics
494
+ - Mutations confirm in a terminal but auto-skip under `--json` / non-TTY; `--yes`/`-y` to skip.
495
+ - Complex bodies accepted via `--file payload.json` (or `--file -` for stdin).
496
+ - One-time API-key secrets print to stdout (capturable) with the warning on stderr.
497
+
498
+ ### Telemetry
499
+ - Every command/subcommand and flag is tracked (the catalog is derived from the app, with an
500
+ anti-drift test that fails if anything goes untracked). Per-action events for all mutations.
501
+ Privacy unchanged: only static names/enums/coarse counts — never ids, emails, SQL, or values.
502
+
503
+ ### Notes
504
+ - Exit code `6` (resource not found) is now documented.
505
+ - New global option `--org`/`FP_ORG`.
506
+
507
+ ## 0.1.5 and earlier
508
+
509
+ See the repository changelog.
@@ -0,0 +1,42 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ExosphereHost Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ Commons Clause License Condition v1.0
24
+
25
+ The Software is provided to you by the Licensor under the License, as defined
26
+ below, subject to the following condition.
27
+
28
+ Without limiting other conditions in the License, the grant of rights under
29
+ the License will not include, and the License does not grant to you, the right
30
+ to Sell the Software.
31
+
32
+ For purposes of the foregoing, "Sell" means practicing any or all of the
33
+ rights granted to you under the License to provide to third parties, for a
34
+ fee or other consideration (including without limitation fees for hosting or
35
+ consulting/support services related to the Software), a product or service
36
+ whose value derives, entirely or substantially, from the functionality of the
37
+ Software. Any license notice or attribution required by the License must also
38
+ include this Commons Clause License Condition notice.
39
+
40
+ Software: failproofai
41
+ License: MIT
42
+ Licensor: ExosphereHost Inc
@@ -0,0 +1,10 @@
1
+ # Same reason as the SDK's: setuptools' default sdist heuristic picks up only
2
+ # top-level `test*.py`, so the suite shipped without `tests/conftest.py` (the
3
+ # `home`/`runner`/`logged_in` fixtures) and was simply unrunnable — every test
4
+ # erroring on a missing fixture. Loud rather than destructive here, unlike the
5
+ # SDK, but a suite that cannot run is not worth shipping either.
6
+ graft tests
7
+ include LICENSE
8
+ include README.md
9
+ include CHANGELOG.md
10
+ recursive-include fp_cli py.typed