@50heads/cli 2026.925.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/cli.js +222 -0
- package/package.json +52 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Changes to `@50heads/cli`, the `50heads` command. It has its own calendar versions, separate from `@50heads/mcp`.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions are calendar versions, `YYYY.MDD.N`: the year, the month and two-digit day, then the release number for that day (`2026.924.1` is the first release on 24 September 2026). Breaking changes are called out in the notes.
|
|
6
|
+
|
|
7
|
+
Add what changed under Unreleased as you go, written for the people using it. The release workflow moves it under the new version and uses it for the GitHub Release.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [2026.925.1] - 2026-09-25
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- First public release: `ask` (from flags, a JSON file, JSON Lines or CSV on stdin, with `--wait`), `estimate`, `wait`, `results`, `answers`, `list`, `cancel`, `balance`, `templates`, `schema`, `login`, `logout` and `status`.
|
|
16
|
+
- `--json` on every command, and exit codes scripts can branch on.
|
|
17
|
+
- Asks are keyed by a hash of the question, so re-running a script never asks or charges twice. `--new` asks again on purpose.
|
|
18
|
+
- One sign-in for the CLI and `npx @50heads/mcp`.
|
|
19
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 50heads
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# 50heads CLI
|
|
2
|
+
|
|
3
|
+
Ask verified real people from a script, a Makefile or CI, wait for the answers and read the split. 20 credits an answer at Tier 1, priced in your account's currency; fifty answers take about ten minutes.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx -y @50heads/cli --help
|
|
7
|
+
# or
|
|
8
|
+
npm install -g @50heads/cli
|
|
9
|
+
50heads --help
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Needs Node 20 or later.
|
|
13
|
+
|
|
14
|
+
## Sign in
|
|
15
|
+
|
|
16
|
+
For CI and scripts, make an API key in the portal (Developers, API keys) with a daily spend cap, and set it:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
export FIFTYHEADS_API_KEY=fh_live_…
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
On your own machine you can sign in once in the browser instead:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
50heads login # kept in the macOS Keychain, the Secret Service, or ~/.config/50heads (mode 600)
|
|
26
|
+
50heads status
|
|
27
|
+
50heads logout
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The same sign-in works for `npx @50heads/mcp`. The API key wins when both are there.
|
|
31
|
+
|
|
32
|
+
## Ask
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
# Price and time first. Never spends.
|
|
36
|
+
50heads estimate --text "Which name sounds friendlier?" --option Pip --option Moss
|
|
37
|
+
# 50 answers, about 10 minutes, $12.70. 20 credits an answer.
|
|
38
|
+
|
|
39
|
+
# Ask and wait for the answers.
|
|
40
|
+
50heads ask --text "Which name sounds friendlier?" --option Pip --option Moss --wait
|
|
41
|
+
|
|
42
|
+
# Two images: an A or B question.
|
|
43
|
+
50heads ask --text "Which cover would you pick up?" \
|
|
44
|
+
--image https://example.com/a.png --image https://example.com/b.png --n 100
|
|
45
|
+
|
|
46
|
+
# From a file: the question draft that POST /v2/questions takes (50heads schema prints its JSON Schema).
|
|
47
|
+
50heads ask --file question.json --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`question.json`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"type": "single_choice",
|
|
55
|
+
"text": "Which name sounds friendlier?",
|
|
56
|
+
"language": "en",
|
|
57
|
+
"options": [{ "label": "Pip" }, { "label": "Moss" }],
|
|
58
|
+
"n": 50,
|
|
59
|
+
"tier": 1,
|
|
60
|
+
"targeting": { "countries": ["GB", "IE"], "tags": [] }
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Wrap it as `{"draft": {…}, "then": [{"text": "Why did you pick {winner}?"}]}` to add follow-ups.
|
|
65
|
+
|
|
66
|
+
### Many at once
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
50heads ask --csv asks.csv --json > asked.jsonl
|
|
70
|
+
cat asks.jsonl | 50heads ask - --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
CSV columns (a header row, any order): `text` (required), `type`, `options` (`A|B|C`) or `option1` to `option8`, `images` (`|`, in option order), `context`, `language`, `n`, `tier`, `rush`, `neither`, `countries` (`|`), `tags` (`|`), `stimulus_image`, `stimulus_text`. Every row is checked before anything is asked; a row that fails is reported and the rest go ahead (exit 8).
|
|
74
|
+
|
|
75
|
+
### Never twice by accident
|
|
76
|
+
|
|
77
|
+
Every ask carries an idempotency key made from a hash of the question. Running the same command or file again, or retrying after a dropped connection, returns the same question within 24 hours and never charges twice. To ask the same question again on purpose, pass `--new` (or `--salt something` for a whole file). `--key` sets the key yourself.
|
|
78
|
+
|
|
79
|
+
## Read
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
50heads wait q_123 --timeout 900 # waits on the server, prints the result
|
|
83
|
+
50heads results q_123 # the result so far
|
|
84
|
+
50heads answers q_123 --option 0 --limit 100
|
|
85
|
+
50heads answers q_123 --q price --all --json # one answer per line
|
|
86
|
+
50heads list --status live
|
|
87
|
+
50heads cancel q_123 # stops it; unanswered heads are refunded
|
|
88
|
+
50heads balance
|
|
89
|
+
50heads templates
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`answers` filters by `--option` (0 is the first), `--tier`, `--country`, `--age-band` and `--q` (a word in written answers). Country and age filters show nothing when fewer than 5 answers match. Answers never identify a head.
|
|
93
|
+
|
|
94
|
+
## Output and exit codes
|
|
95
|
+
|
|
96
|
+
Human-readable by default. With `--json`, each result is one JSON object on stdout (JSON Lines for many), progress stays on stderr (`--quiet` turns it off), and errors are JSON on stderr: `{"error": {"code", "message", "exit"}}`.
|
|
97
|
+
|
|
98
|
+
| Exit | Meaning |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| 0 | Done |
|
|
101
|
+
| 1 | Something went wrong |
|
|
102
|
+
| 2 | The command line is wrong |
|
|
103
|
+
| 3 | Not signed in, or the key is not allowed to do that |
|
|
104
|
+
| 4 | Not enough credits, or over the key's daily cap |
|
|
105
|
+
| 5 | The question needs fixing (estimate prints the fixes) |
|
|
106
|
+
| 6 | No question with that id |
|
|
107
|
+
| 7 | 50heads is unavailable or rate limited; try again |
|
|
108
|
+
| 8 | Some asks in a bulk run failed; the rest were asked |
|
|
109
|
+
| 124 | `wait` ran out of time and the question is still live |
|
|
110
|
+
|
|
111
|
+
## In CI
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
- run: npx -y @50heads/cli ask --file .50heads/headline.json --wait --timeout 1200 --json > result.json
|
|
115
|
+
env:
|
|
116
|
+
FIFTYHEADS_API_KEY: ${{ secrets.FIFTYHEADS_API_KEY }}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The key's daily cap is the limit on what a runaway job can spend.
|
|
120
|
+
|
|
121
|
+
## Environment
|
|
122
|
+
|
|
123
|
+
| Variable | Meaning |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `FIFTYHEADS_API_KEY` | API key for CI and scripts |
|
|
126
|
+
| `FIFTYHEADS_API_URL` | API base, default `https://api.50heads.com` |
|
|
127
|
+
| `FIFTYHEADS_AUTH_URL` | Sign-in server for `login` |
|
|
128
|
+
| `FIFTYHEADS_CREDENTIALS_STORE=file` | Keep the sign-in in a file, not the keychain |
|
|
129
|
+
| `FIFTYHEADS_NO_BROWSER=1` | `login` prints the link without opening a browser |
|
|
130
|
+
|
|
131
|
+
## Versions
|
|
132
|
+
|
|
133
|
+
Calendar versions, `YYYY.MDD.N`, released from the monorepo by `.github/workflows/release-cli.yml`. See `CHANGELOG.md`.
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
pnpm --filter @50heads/cli test
|
|
139
|
+
pnpm --filter @50heads/cli build && node packages/cli/dist/cli.js --help
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The sign-in and credential store are in `packages/login`, shared with `@50heads/mcp`. Commands are in `src/run.ts`; `run()` takes its I/O as an argument, so the tests drive it in-process against the fake API from `@50heads/mcp/testing`.
|