@harnessforce/cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -28
- package/dist/bin.js +1080 -797
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -23,18 +23,18 @@ harnessforce init
|
|
|
23
23
|
|
|
24
24
|
## Commands
|
|
25
25
|
|
|
26
|
+
`harnessforce --help` lists the commands. `harnessforce <command> --help` explains when to use a command, what it sends and does not send, its exit codes, and examples.
|
|
27
|
+
|
|
26
28
|
| Command | What it does |
|
|
27
29
|
| --- | --- |
|
|
28
|
-
| `harnessforce init [--
|
|
29
|
-
| `harnessforce otel-headers` | Prints the `Authorization` header for Claude Code's `otelHeadersHelper
|
|
30
|
+
| `harnessforce init [--port <port>] [--send-content] [--no-plugin-auto-update]` | Logs in with the browser, stores a user ingest key and an API token in the keychain, and configures Claude Code and Codex |
|
|
31
|
+
| `harnessforce otel-headers` | Prints the `Authorization` header for Claude Code's `otelHeadersHelper`. Exits 3 when there is no key, 4 when the destination differs from the pinned origin, 5 while the CLI version is unsupported, and 1 on other failures. On Linux, after printing a key read from the kernel keyring, it moves that Workspace's items to an unlocked Secret Service (not when the hook starts it), so a reboot does not lose them |
|
|
30
32
|
| `harnessforce run --issue <identifier> -- <agent> [args]` | Launches an agent with its session linked to an Issue |
|
|
31
33
|
| `harnessforce import` | Sends the metadata of past Claude Code sessions in connected repositories |
|
|
32
34
|
| `harnessforce tune [--all] [--no-send] [--show-report] [--json]` | Analyzes past sessions on this machine by public rules and sends the counts (analysis reports) |
|
|
33
35
|
| `harnessforce tune record` / `harnessforce tune --purge` | Records a proposal from stdin / deletes everything under `~/.harnessforce/tune/` |
|
|
34
36
|
| `harnessforce top [--once] [--json] [--ascii] [--theme <auto\|light\|dark\|ansi>]` | Shows the Claude Code and Codex sessions on this machine in the terminal: state, tokens, context and tool failures. Reads only local files and never connects to Harnessforce |
|
|
35
37
|
| `harnessforce hook session-start` | The entry of Codex's `SessionStart` hook: reads `session_id`, `cwd` and `source` from stdin, registers the session, and prints the session ID for the agent's context. Always exits 0 |
|
|
36
|
-
| `harnessforce usage-limits on [--yes]` / `off` / `status` | Shares your usage limits with the workspace after you agree to the consent text (`on`), or withdraws the consent, deletes the stored values and restores your previous statusLine (`off`), or shows the consent, the workspace opt-in and whether the statusLine is wrapped (`status`). Not supported on Windows |
|
|
37
|
-
| `harnessforce usage-limits statusline` | A statusLine command that wraps your existing one: it passes stdin to the original command, returns its output and exit code unchanged, then sends only the usage percentage and reset time of the 5-hour and 7-day windows. It sends nothing until you have consented |
|
|
38
38
|
| `harnessforce --version` | Prints the version |
|
|
39
39
|
|
|
40
40
|
## Watching your sessions (`harnessforce top`)
|
|
@@ -73,23 +73,12 @@ Codex runs this command through the `[[hooks.SessionStart]]` entry that `harness
|
|
|
73
73
|
Claude Code user settings that `harnessforce init` wrote. When `HARNESSFORCE_ISSUE` is a valid identifier
|
|
74
74
|
(`harnessforce run --issue` sets it), the registration claims `source=cli` with that Issue.
|
|
75
75
|
- prints `{"hookSpecificOutput": {...}}` carrying `harnessforce session_id: <id>` so the agent knows its session
|
|
76
|
-
ID. When
|
|
77
|
-
|
|
76
|
+
ID. When it cannot send, it adds one warning as `systemMessage`, which Codex shows in its UI, and writes it to
|
|
77
|
+
stderr: a revoked key (401), sending not set up, an invalid endpoint, no key in the keychain, a destination
|
|
78
|
+
that differs from the pinned origin, a keychain that could not be read within 1 second, or a failed
|
|
79
|
+
registration. Only `startup`, `clear` and a missing `source` show a warning.
|
|
78
80
|
- never fails the session: any error ends with exit code 0, and nothing is sent outside a git repository.
|
|
79
81
|
|
|
80
|
-
## Usage limits in the statusLine (`harnessforce usage-limits statusline`)
|
|
81
|
-
|
|
82
|
-
Claude Code passes the plan limits (`rate_limits`) only to the statusLine command, not to hooks. `harnessforce usage-limits statusline` is meant to sit in front of the statusLine you already use (for example claude-hud):
|
|
83
|
-
|
|
84
|
-
- It reads all of stdin and, if `~/.harnessforce/usage-limits.json` has an `original` command, runs it with `sh -c`, hands it the same stdin and the same environment, and returns its stdout, stderr and exit code as they are. No time limit is added.
|
|
85
|
-
- After that, only if `usage-limits.json` records your consent for the current text version, it takes `rate_limits.five_hour` and `rate_limits.seven_day` (`used_percentage` and `resets_at`) and sends them to `POST <ingest endpoint>/v1/usage-limits` with the user ingest key from the keychain. Nothing else in the JSON (`cwd`, `transcript_path`, `cost`, and so on) is read or sent.
|
|
86
|
-
- The send runs in a detached child process with stdin, stdout and stderr closed, so the statusLine is never delayed. A failure does not change the output or the exit code.
|
|
87
|
-
- It sends at most once every 5 minutes, unless the reset time of a window changed. The time and the reset times of the last send are kept in `~/.harnessforce/usage-limits-state.json` (owner-only).
|
|
88
|
-
|
|
89
|
-
### Turning it on and off
|
|
90
|
-
|
|
91
|
-
`harnessforce usage-limits on` shows the consent text, asks `y` (skipped with `--yes`), records the consent (`PUT /api/v1/cli/usage-limits/consent`), and then wraps the `statusLine` in your Claude Code user settings: only `command` is replaced with `harnessforce usage-limits statusline`, so `type`, `padding`, `refreshInterval` and the other keys stay. The statusLine you had (for example claude-hud) is saved as-is in `~/.harnessforce/usage-limits.json`. If the statusLine is not `type: "command"`, or the settings cannot be read, nothing is recorded or changed. If writing the mark or the settings fails, the consent is withdrawn again. `harnessforce usage-limits off` withdraws the consent first and then puts the saved statusLine back (or removes it if there was none); a statusLine that another tool has changed since is left alone.
|
|
92
|
-
|
|
93
82
|
## What is never sent
|
|
94
83
|
|
|
95
84
|
Harnessforce never turns on prompt or body logging by itself. `harnessforce run` never turns it on, and `harnessforce init`
|
|
@@ -102,7 +91,7 @@ tokens are sent only to the origins pinned by `harnessforce init`.
|
|
|
102
91
|
|
|
103
92
|
## CLI credentials (`harnessforce init`, `harnessforce otel-headers`)
|
|
104
93
|
|
|
105
|
-
`harnessforce init [--
|
|
94
|
+
`harnessforce init [--port <port>] [--send-content] [--no-plugin-auto-update]` connects this machine to a Harnessforce Workspace:
|
|
106
95
|
|
|
107
96
|
1. It checks the OS keychain first: macOS Keychain or the Windows Credential Manager (through
|
|
108
97
|
[`@napi-rs/keyring`](https://github.com/Brooooooklyn/keyring-node)). On Linux it writes and deletes
|
|
@@ -135,6 +124,9 @@ tokens are sent only to the origins pinned by `harnessforce init`.
|
|
|
135
124
|
to content it also adds `OTEL_LOG_USER_PROMPTS=1`
|
|
136
125
|
- `otelHeadersHelper`: `harnessforce otel-headers`
|
|
137
126
|
- `enabledPlugins["harnessforce@harnessforce-agent"]`
|
|
127
|
+
- `extraKnownMarketplaces["harnessforce-agent"]` (or `additionalMarketplaces` when only that alias is
|
|
128
|
+
present): the GitHub source and `autoUpdate: true`, so Claude Code updates the plugin in the
|
|
129
|
+
background. `--no-plugin-auto-update` writes `false`; a value you set yourself is kept.
|
|
138
130
|
6. It updates the Codex `config.toml` (`$CODEX_HOME` when it is an absolute path, otherwise `~/.codex`),
|
|
139
131
|
changing only these three items and keeping every other key and comment:
|
|
140
132
|
- `[otel].exporter`: OTLP/HTTP (`protocol = "binary"`) to the ingest endpoint with
|
|
@@ -169,16 +161,22 @@ authorization server metadata of the base URL, only when that endpoint is on the
|
|
|
169
161
|
origin. The new pair replaces `<workspace_id>:api-token`. A lock older than 60 seconds is treated as
|
|
170
162
|
abandoned, and waiting more than 30 seconds for it counts as a failed refresh. If the refresh fails, or
|
|
171
163
|
the refreshed token is also answered with 401, they stop with
|
|
172
|
-
|
|
164
|
+
"Your login has expired. Run `harnessforce init`".
|
|
165
|
+
|
|
166
|
+
## Updates
|
|
167
|
+
|
|
168
|
+
Every request to Harnessforce carries `User-Agent: harnessforce-cli/<version>` and nothing else about
|
|
169
|
+
the machine. The server answers with the update status, which is kept in
|
|
170
|
+
`~/.harnessforce/update-status.json`:
|
|
173
171
|
|
|
174
|
-
|
|
172
|
+
- A newer version is shown once a day after a command (not with `NO_UPDATE_NOTIFIER` or `CI`).
|
|
173
|
+
- After a security fix, the date this version stops sending is shown after every command.
|
|
174
|
+
- An unsupported version sends nothing: sending commands exit 1, `harnessforce run` starts the agent
|
|
175
|
+
without the Harnessforce environment, `harnessforce otel-headers` exits 5 without a key, and the Codex
|
|
176
|
+
hook registers nothing. After 24 hours it checks again with `GET /v1/client-status`, without
|
|
177
|
+
credentials.
|
|
175
178
|
|
|
176
|
-
|
|
177
|
-
Without it the default is `https://harnessforce.dev`. The value must be `https:`, or `http:` for localhost.
|
|
178
|
-
At run time the base URL is the environment variable `HARNESSFORCE_URL`, then `env.HARNESSFORCE_URL` in the
|
|
179
|
-
user settings (`harnessforce init --url` writes it), then the build default. The plugin build
|
|
180
|
-
uses the same value for its MCP server URL. See
|
|
181
|
-
[Building the local marketplace](https://github.com/lambda-script/harnessforce-agent/blob/main/docs/runbooks/local-marketplace.md).
|
|
179
|
+
Update with `npm install -g @harnessforce/cli@latest`. The CLI never replaces itself.
|
|
182
180
|
|
|
183
181
|
## Launching an agent for an Issue (`harnessforce run`)
|
|
184
182
|
|
|
@@ -236,6 +234,10 @@ work done before the plugin was installed can be linked to issues too. Run `harn
|
|
|
236
234
|
- Sessions are sent 100 at a time to `POST <HARNESSFORCE_ENDPOINT>/v1/imports/sessions` with the user
|
|
237
235
|
ingest key, only when the endpoint origin matches the one pinned by `harnessforce init`. `429` and `503` are
|
|
238
236
|
retried up to 3 times after `Retry-After` (at most 60 seconds).
|
|
237
|
+
- Sessions that are not sent are counted by the first reason that applies (already sent, outside the
|
|
238
|
+
import window, outside a git repository, repository not connected to the Workspace) and printed on one line.
|
|
239
|
+
When nothing was imported because no repository is connected, it says to connect the repository
|
|
240
|
+
with the GitHub App in Harnessforce.
|
|
239
241
|
- Sent sessions are recorded in `~/.harnessforce/import-state.json` per Workspace and ingest endpoint,
|
|
240
242
|
so running it again continues where it stopped and never sends a session twice to the same place.
|
|
241
243
|
Sessions dropped because of the monthly event limit or a read-only Workspace are not recorded and
|