insightfactory-cli 1.0.2.dev13__py3-none-any.whl → 1.0.2.dev16__py3-none-any.whl
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.
- insightfactory_cli-1.0.2.dev16.dist-info/METADATA +314 -0
- {insightfactory_cli-1.0.2.dev13.dist-info → insightfactory_cli-1.0.2.dev16.dist-info}/RECORD +5 -5
- insightfactory_cli-1.0.2.dev13.dist-info/METADATA +0 -317
- {insightfactory_cli-1.0.2.dev13.dist-info → insightfactory_cli-1.0.2.dev16.dist-info}/WHEEL +0 -0
- {insightfactory_cli-1.0.2.dev13.dist-info → insightfactory_cli-1.0.2.dev16.dist-info}/entry_points.txt +0 -0
- {insightfactory_cli-1.0.2.dev13.dist-info → insightfactory_cli-1.0.2.dev16.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: insightfactory-cli
|
|
3
|
+
Version: 1.0.2.dev16
|
|
4
|
+
Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
|
|
5
|
+
Project-URL: Homepage, https://insightfactory.ai
|
|
6
|
+
Author-email: "insightfactory.ai Support" <support@insightfactory.ai>
|
|
7
|
+
License-Expression: LicenseRef-Proprietary
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: cli,insightfactory,oauth,pkce
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: Other/Proprietary License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# insightfactory-cli
|
|
20
|
+
|
|
21
|
+
Profile-based authentication CLI for the InsightFactory Interfaces API. It
|
|
22
|
+
installs the `if-cli` command, which logs in through the browser with OAuth
|
|
23
|
+
authorization code and PKCE, refreshes tokens, and makes authenticated API
|
|
24
|
+
calls. Profiles live in `~/.insightfactory` in the same layout the Node
|
|
25
|
+
`@insightfactory-ai/if-cli` uses, so both implementations can share one config
|
|
26
|
+
directory.
|
|
27
|
+
|
|
28
|
+
This is the production Python CLI, published to public PyPI. It needs Python
|
|
29
|
+
3.10 or newer.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv tool install insightfactory-cli # persistent install, on PATH
|
|
35
|
+
uvx insightfactory-cli profiles # or zero-install, run-once
|
|
36
|
+
uv tool upgrade insightfactory-cli # updates
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`uv` fetches a managed Python if the machine lacks one. Fallbacks:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pipx install insightfactory-cli
|
|
43
|
+
pip install insightfactory-cli
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Develop-branch builds are published as PEP 440 `.devN` pre-releases. Install
|
|
47
|
+
the latest one with:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install --pre insightfactory-cli
|
|
51
|
+
uv tool install --prerelease allow insightfactory-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
PEP 440 sorts `1.0.0.devN` before `1.0.0`, so a dependency spec of
|
|
55
|
+
`insightfactory-cli>=1.0.0` does not match a develop-channel dev build. An
|
|
56
|
+
in-process consumer that wants those builds needs a spec that admits
|
|
57
|
+
pre-releases, such as `insightfactory-cli>=1.0.0.dev0`. That spec matches both
|
|
58
|
+
the `.devN` builds and the final `1.0.0` release.
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Create a profile and authenticate through the browser.
|
|
64
|
+
if-cli login -p example-dev --host https://factory.example
|
|
65
|
+
|
|
66
|
+
# Inspect profiles and cached-token status. Statuses are colour-coded on a TTY
|
|
67
|
+
# (green valid, yellow refreshable-but-expired, red missing or broken); set
|
|
68
|
+
# NO_COLOR=1 to disable or FORCE_COLOR=1 to keep colour when piping.
|
|
69
|
+
if-cli profiles
|
|
70
|
+
if-cli profiles --json
|
|
71
|
+
|
|
72
|
+
# Read a single non-secret profile value for scripting.
|
|
73
|
+
if-cli config get host -p example-dev
|
|
74
|
+
|
|
75
|
+
# Print a valid token, refreshing it when a refresh token is available.
|
|
76
|
+
if-cli token -p example-dev
|
|
77
|
+
if-cli token -p example-dev --env
|
|
78
|
+
if-cli token -p example-dev --env-name INSIGHTFACTORY_ACCESS_TOKEN_DEV
|
|
79
|
+
|
|
80
|
+
# Make an authenticated API request.
|
|
81
|
+
if-cli api -p example-dev /api/agent-projects
|
|
82
|
+
if-cli api -p example-dev -X POST -d '{"name":"example"}' /api/example
|
|
83
|
+
|
|
84
|
+
# Wait longer than the 30-second default for a long-running endpoint.
|
|
85
|
+
if-cli api -p example-dev --timeout 300 -X PUT -d '{"productionLineCodes":["PL001"]}' \
|
|
86
|
+
/api/orchestration/run
|
|
87
|
+
|
|
88
|
+
# Discover API routes from the factory's OpenAPI document.
|
|
89
|
+
if-cli api routes -p example-dev
|
|
90
|
+
if-cli api routes -p example-dev agent-projects
|
|
91
|
+
if-cli api describe -p example-dev GET /api/agent-projects/{id}
|
|
92
|
+
|
|
93
|
+
# Remove the cached credential for a profile.
|
|
94
|
+
if-cli logout -p example-dev
|
|
95
|
+
|
|
96
|
+
# Fallback for factories where browser OAuth is not available.
|
|
97
|
+
if-cli set-token -p example-dev
|
|
98
|
+
|
|
99
|
+
if-cli --version
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Profile selection tries `-p` first, then `INSIGHTFACTORY_CONFIG_PROFILE`, then
|
|
103
|
+
`[DEFAULT]`. Route discovery reads `{host}/swagger/v1/swagger.json` and does
|
|
104
|
+
not need a login. Authenticated requests only go to the selected factory's
|
|
105
|
+
origin, so a profile token cannot be forwarded to another host. A profile host
|
|
106
|
+
must be a bare origin, not a URL with an application path prefix.
|
|
107
|
+
|
|
108
|
+
Requests time out after 30 seconds by default. Raise the deadline for one
|
|
109
|
+
invocation with `--timeout <seconds>`, or for a whole session with
|
|
110
|
+
`INSIGHTFACTORY_REQUEST_TIMEOUT`. The flag wins over the environment variable,
|
|
111
|
+
and the CLI validates both before it touches the network. There is no upper
|
|
112
|
+
bound, because the right ceiling depends on the endpoint.
|
|
113
|
+
|
|
114
|
+
Both settings apply to the API request itself. OAuth discovery and token
|
|
115
|
+
refresh keep the 30-second default, so `if-cli token` and in-process callers of
|
|
116
|
+
`if_cli.oauth.get_valid_token` always wait a bounded time that no ambient
|
|
117
|
+
environment value can change.
|
|
118
|
+
|
|
119
|
+
A timeout is a client-side deadline, not a rejection. The factory may have
|
|
120
|
+
accepted and completed the request after `if-cli` gave up, so re-issuing a
|
|
121
|
+
`POST`, `PUT`, `PATCH`, or `DELETE` can apply it twice. For those methods the
|
|
122
|
+
timeout message tells you to confirm the current state before retrying rather
|
|
123
|
+
than repeat the call.
|
|
124
|
+
|
|
125
|
+
Override the config directory with `INSIGHTFACTORY_CONFIG_DIR`, which is useful
|
|
126
|
+
in tests and CI. The default is `~/.insightfactory`.
|
|
127
|
+
|
|
128
|
+
The request method goes in `-X`. `if-cli api` takes exactly one positional
|
|
129
|
+
argument and reads it as the path, so the `curl`-shaped
|
|
130
|
+
`if-cli api GET /api/schedules` parses `GET` as the path. Write
|
|
131
|
+
`if-cli api -X GET /api/schedules` instead. The error names the misplaced verb
|
|
132
|
+
rather than reporting a problem with `/api/schedules`.
|
|
133
|
+
|
|
134
|
+
On Git Bash (MSYS2) on Windows, path conversion rewrites a leading-slash
|
|
135
|
+
argument such as `/api/agent-projects` into a Windows path. Prefix the command
|
|
136
|
+
with `MSYS_NO_PATHCONV=1`:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
MSYS_NO_PATHCONV=1 if-cli api -p example-dev /api/agent-projects
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Scripting against profiles
|
|
143
|
+
|
|
144
|
+
`profiles --json` and `config get` are the supported machine-readable
|
|
145
|
+
interfaces. The padded `profiles` columns are for humans and are not a
|
|
146
|
+
contract, and neither is the layout of `~/.insightfactory/config`.
|
|
147
|
+
|
|
148
|
+
`if-cli profiles --json` prints an array with one entry per configured profile,
|
|
149
|
+
in file order, each holding `name`, `host`, and `token`:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
[
|
|
153
|
+
{
|
|
154
|
+
"name": "example-dev",
|
|
155
|
+
"host": "https://factory.example",
|
|
156
|
+
"token": { "status": "valid", "expires_at": 1785918336, "refreshable": true }
|
|
157
|
+
},
|
|
158
|
+
{ "name": "other", "host": "https://other.factory.example", "token": { "status": "none" } }
|
|
159
|
+
]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`token.status` is `valid`, `expired`, `none`, or `unknown`. `expires_at` and
|
|
163
|
+
`refreshable` appear only when a cached token exists. `valid` means
|
|
164
|
+
`if-cli token` will hand back the cached token as-is. A token inside the
|
|
165
|
+
60-second slack window that command refreshes within is therefore reported as
|
|
166
|
+
`expired`, so read the raw `expires_at` if you need the literal expiry instant.
|
|
167
|
+
A profile that cannot be resolved has a `host` of `null`, or its raw configured
|
|
168
|
+
value, plus a top-level `error` describing the problem. It does not stop the
|
|
169
|
+
other profiles from being listed. `token.error` is set only when the token
|
|
170
|
+
cache itself could not be read.
|
|
171
|
+
|
|
172
|
+
`if-cli config get <key> -p <profile>` prints one newline-terminated value and
|
|
173
|
+
exits non-zero if the profile cannot be resolved or the key is not configured
|
|
174
|
+
in it. A profile with no host cannot be resolved at all, so every key on it
|
|
175
|
+
fails, not just `host`. A key that is present but empty, such as `client_id =`,
|
|
176
|
+
counts as not configured:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
curl -H "Authorization: Bearer $(if-cli token -p example-dev)" \
|
|
180
|
+
"$(if-cli config get host -p example-dev)/api/agent-projects"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The readable keys are `host`, `audience`, `callback_port`, `client_id`, and
|
|
184
|
+
`organization`. All are non-secret and resolved the same way the CLI resolves
|
|
185
|
+
them, so `audience` and `callback_port` return their defaults when the profile
|
|
186
|
+
omits them. Tokens are deliberately not readable this way. Use `if-cli token`.
|
|
187
|
+
|
|
188
|
+
## Programmatic API
|
|
189
|
+
|
|
190
|
+
You can import `insightfactory-cli` in-process to resolve a profile to a
|
|
191
|
+
factory host and a fresh access token. These four symbols are supported for
|
|
192
|
+
in-process consumers:
|
|
193
|
+
|
|
194
|
+
- `if_cli.config.load_config`
|
|
195
|
+
- `if_cli.config.get_profile`
|
|
196
|
+
- `if_cli.oauth.get_valid_token`
|
|
197
|
+
- `if_cli.runtime.CliError`
|
|
198
|
+
|
|
199
|
+
They raise `CliError` on failure, never `SystemExit` or `sys.exit`. They never
|
|
200
|
+
write to stdout, never launch a browser or block on interactive input, and only
|
|
201
|
+
use bounded waits. Changing or removing any of them is a breaking change and
|
|
202
|
+
needs a major version bump.
|
|
203
|
+
|
|
204
|
+
## How authentication works
|
|
205
|
+
|
|
206
|
+
- Profiles live in `~/.insightfactory/config`, one per customer and environment.
|
|
207
|
+
- The CLI manages the profile file and regenerates it on updates. Comments and
|
|
208
|
+
empty sections are not preserved.
|
|
209
|
+
- Tokens are cached by host in `~/.insightfactory/token-cache.json` with file
|
|
210
|
+
mode `0600`.
|
|
211
|
+
- The configuration directory is `0700`. Configuration and cache updates are
|
|
212
|
+
atomic.
|
|
213
|
+
- Remote factory, authorization, and token endpoints must use HTTPS. HTTP is
|
|
214
|
+
accepted only for loopback development.
|
|
215
|
+
- Login discovers OAuth metadata from
|
|
216
|
+
`{host}/.well-known/oauth-authorization-server`.
|
|
217
|
+
- Authorization uses PKCE through the factory `/authorize` proxy with a
|
|
218
|
+
loopback-only `127.0.0.1` callback. The CLI is a public OAuth client. It reads
|
|
219
|
+
the client ID from the factory's discovery document at runtime and holds no
|
|
220
|
+
client secret.
|
|
221
|
+
|
|
222
|
+
## Development
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
uv sync
|
|
226
|
+
uv run pytest
|
|
227
|
+
uv run ruff check .
|
|
228
|
+
uv run ty check
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Smoke-test against a throwaway config directory, never `~/.insightfactory`:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
uv run if-cli --help
|
|
235
|
+
uv run if-cli --version
|
|
236
|
+
INSIGHTFACTORY_CONFIG_DIR=$(mktemp -d) uv run if-cli profiles
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Python 3.10 or newer is required. Runtime code is stdlib-only.
|
|
240
|
+
|
|
241
|
+
## Release
|
|
242
|
+
|
|
243
|
+
A release is cut by pushing a tag, not by merging. Merging to `main` runs CI
|
|
244
|
+
and publishes nothing.
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
git tag v1.0.1 && git push origin v1.0.1
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Verify and build live in the reusable workflow
|
|
251
|
+
[`if_s_insightfactory_cli.yml`](https://github.com/insightfactory-ai/if_sre_github_actions/blob/main/.github/workflows/if_s_insightfactory_cli.yml)
|
|
252
|
+
in `insightfactory-ai/if_sre_github_actions`. This repository's
|
|
253
|
+
`.github/workflows/release.yml` keeps only the OIDC publish job, because PyPI
|
|
254
|
+
Trusted Publishing is registered for this repo, workflow `release.yml`, and
|
|
255
|
+
environment `pypi`. A reusable workflow cannot be registered.
|
|
256
|
+
|
|
257
|
+
The PyPI project needs a one-time trusted-publisher registration for that
|
|
258
|
+
triple. The publish job downloads the `dist-release` artifact staged by the
|
|
259
|
+
reusable workflow and uploads it with `pypa/gh-action-pypi-publish`.
|
|
260
|
+
|
|
261
|
+
| Trigger | Published version | PyPI role |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| Push / merge to `develop` | `1.0.1.dev{N}` | pre-release (`pip install --pre`) |
|
|
264
|
+
| Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest |
|
|
265
|
+
| Merge to `main` | nothing, CI only | none |
|
|
266
|
+
|
|
267
|
+
`N` is `github.run_number` of the reusable workflow. It is a per-workflow
|
|
268
|
+
integer that goes up on every run and stays the same across re-runs of that
|
|
269
|
+
run, so a failed publish can be retried under the same version. It does not
|
|
270
|
+
depend on git history or clone depth. After a release, bump `version` in
|
|
271
|
+
`pyproject.toml` on `develop` so later `.devN` builds sort after what just
|
|
272
|
+
landed. PEP 440 puts `1.0.1.devN` before `1.0.1`, which is also why a spec of
|
|
273
|
+
`insightfactory-cli>=1.0.0` skips develop-channel builds and an in-process
|
|
274
|
+
consumer that wants them must ask for `insightfactory-cli>=1.0.0.dev0`.
|
|
275
|
+
|
|
276
|
+
A `v*` tag whose name does not match `pyproject.toml` fails before publishing.
|
|
277
|
+
|
|
278
|
+
The two channels treat an already-published version differently, on purpose:
|
|
279
|
+
|
|
280
|
+
- **Release channel, a tag.** Publishing fails and names the fix. A tag is a
|
|
281
|
+
request to release that version. If it cannot be honoured, the run must say
|
|
282
|
+
so rather than report success having published nothing. Git also refuses to
|
|
283
|
+
push a tag that already exists, so this is hard to reach.
|
|
284
|
+
- **Dev channel, `develop`.** Publishing is skipped quietly. `.dev{N}` uses
|
|
285
|
+
`github.run_number`, which is stable across re-runs of one run, so an
|
|
286
|
+
already-present version there can only mean a re-run of a run that already
|
|
287
|
+
published. That is idempotency, not a swallowed mistake.
|
|
288
|
+
|
|
289
|
+
This split is why releasing moved off `main`. While a merge was the trigger, an
|
|
290
|
+
already-published version had to be tolerated as a no-op for merges to stay
|
|
291
|
+
green. A merge that released nothing then looked the same as one that
|
|
292
|
+
released, and a forgotten version bump shipped nothing, silently.
|
|
293
|
+
|
|
294
|
+
## Differences from the Node CLI
|
|
295
|
+
|
|
296
|
+
Behaviour matches `@insightfactory-ai/if-cli` except where the platforms
|
|
297
|
+
differ:
|
|
298
|
+
|
|
299
|
+
- **Unknown-option wording** is aligned to `Unknown option '--flag'`. Other
|
|
300
|
+
argparse messages, such as a missing option value, still differ from Node's
|
|
301
|
+
`util.parseArgs`.
|
|
302
|
+
- **Expiry timestamps** in human-readable output use the C-locale `%c` format
|
|
303
|
+
rather than JavaScript `Date#toLocaleString()`.
|
|
304
|
+
- **`--version`** reads the installed package metadata from `1.0.0` onward
|
|
305
|
+
rather than an npm `package.json`.
|
|
306
|
+
|
|
307
|
+
On-disk profile and token-cache paths, JSON shapes, and file permissions stay
|
|
308
|
+
compatible with the Node CLI, so you can switch implementations without
|
|
309
|
+
re-authenticating.
|
|
310
|
+
|
|
311
|
+
## Licence
|
|
312
|
+
|
|
313
|
+
Copyright 2026 insightfactory.ai. All rights reserved. This package is proprietary
|
|
314
|
+
and may be used only under a separate written agreement with insightfactory.ai.
|
{insightfactory_cli-1.0.2.dev13.dist-info → insightfactory_cli-1.0.2.dev16.dist-info}/RECORD
RENAMED
|
@@ -17,8 +17,8 @@ if_cli/commands/logout.py,sha256=TW0P4_tXcTtiHHR73xVPHyhlbOWD1jBnkZsMImCiCBw,668
|
|
|
17
17
|
if_cli/commands/profiles.py,sha256=JNBrYWdHQ9MHauwZxH33IAOYgRYRniX7bwd7KBT6Vyk,3869
|
|
18
18
|
if_cli/commands/set_token.py,sha256=BVyv_QmByvllr04pE2sz8JFEscHbIgOC-7ZPg61PRoc,1886
|
|
19
19
|
if_cli/commands/token.py,sha256=CyiXkyH7vxgH91-n-XJzeHnLI6mZJwO9Js5AHvfHaeM,1120
|
|
20
|
-
insightfactory_cli-1.0.2.
|
|
21
|
-
insightfactory_cli-1.0.2.
|
|
22
|
-
insightfactory_cli-1.0.2.
|
|
23
|
-
insightfactory_cli-1.0.2.
|
|
24
|
-
insightfactory_cli-1.0.2.
|
|
20
|
+
insightfactory_cli-1.0.2.dev16.dist-info/METADATA,sha256=Iq2H3FJKYrsSgrIHMnaXt1Nl4FuJUGv53lXQ0iTPTb0,13101
|
|
21
|
+
insightfactory_cli-1.0.2.dev16.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
22
|
+
insightfactory_cli-1.0.2.dev16.dist-info/entry_points.txt,sha256=UlX854D7f-7dBj5F0wGdakVy6vopvP9hA1BN-gv739w,44
|
|
23
|
+
insightfactory_cli-1.0.2.dev16.dist-info/licenses/LICENSE,sha256=8eZ1YAABL398qESVJc_FlK1voYQ0GAh-R_rNBQK6QH8,234
|
|
24
|
+
insightfactory_cli-1.0.2.dev16.dist-info/RECORD,,
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: insightfactory-cli
|
|
3
|
-
Version: 1.0.2.dev13
|
|
4
|
-
Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
|
|
5
|
-
Project-URL: Homepage, https://insightfactory.ai
|
|
6
|
-
Author-email: "insightfactory.ai Support" <support@insightfactory.ai>
|
|
7
|
-
License-Expression: LicenseRef-Proprietary
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Keywords: cli,insightfactory,oauth,pkce
|
|
10
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: Environment :: Console
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: Other/Proprietary License
|
|
14
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Typing :: Typed
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
|
|
19
|
-
# insightfactory-cli
|
|
20
|
-
|
|
21
|
-
Profile-based authentication CLI for the InsightFactory Interfaces API. It
|
|
22
|
-
installs the `if-cli` command: browser OAuth login (authorization-code + PKCE),
|
|
23
|
-
token refresh, and authenticated API calls, with profiles stored in
|
|
24
|
-
`~/.insightfactory` in the same layout as the Node `@insightfactory-ai/if-cli`
|
|
25
|
-
so the two implementations can share a config directory.
|
|
26
|
-
|
|
27
|
-
This is the production Python CLI, published to public PyPI. Python 3.10 or
|
|
28
|
-
newer is required.
|
|
29
|
-
|
|
30
|
-
## Install
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
uv tool install insightfactory-cli # persistent install, on PATH
|
|
34
|
-
uvx insightfactory-cli profiles # or zero-install, run-once
|
|
35
|
-
uv tool upgrade insightfactory-cli # updates
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
`uv` fetches a managed Python automatically if the machine lacks one. Fallbacks:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
pipx install insightfactory-cli
|
|
42
|
-
pip install insightfactory-cli
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Develop-branch builds are published as PEP 440 `.devN` pre-releases. Install the
|
|
46
|
-
bleeding edge with:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
pip install --pre insightfactory-cli
|
|
50
|
-
uv tool install --prerelease allow insightfactory-cli
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
PEP 440 orders `1.0.0.devN` *before* `1.0.0`, so a dependency spec of
|
|
54
|
-
`insightfactory-cli>=1.0.0` does **not** match a develop-channel dev build.
|
|
55
|
-
An in-process consumer that wants those builds must use a pre-release-bearing
|
|
56
|
-
spec such as `insightfactory-cli>=1.0.0.dev0`, which matches both `.devN`
|
|
57
|
-
builds and the final `1.0.0` release.
|
|
58
|
-
|
|
59
|
-
## Usage
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
# Create a profile and authenticate through the browser.
|
|
63
|
-
if-cli login -p example-dev --host https://factory.example
|
|
64
|
-
|
|
65
|
-
# Inspect profiles and cached-token status. Statuses are colour-coded on a TTY
|
|
66
|
-
# (green valid, yellow refreshable-but-expired, red missing or broken); set
|
|
67
|
-
# NO_COLOR=1 to disable or FORCE_COLOR=1 to keep colour when piping.
|
|
68
|
-
if-cli profiles
|
|
69
|
-
if-cli profiles --json
|
|
70
|
-
|
|
71
|
-
# Read a single non-secret profile value for scripting.
|
|
72
|
-
if-cli config get host -p example-dev
|
|
73
|
-
|
|
74
|
-
# Print a valid token, refreshing it when a refresh token is available.
|
|
75
|
-
if-cli token -p example-dev
|
|
76
|
-
if-cli token -p example-dev --env
|
|
77
|
-
if-cli token -p example-dev --env-name INSIGHTFACTORY_ACCESS_TOKEN_DEV
|
|
78
|
-
|
|
79
|
-
# Make an authenticated API request.
|
|
80
|
-
if-cli api -p example-dev /api/agent-projects
|
|
81
|
-
if-cli api -p example-dev -X POST -d '{"name":"example"}' /api/example
|
|
82
|
-
|
|
83
|
-
# Wait longer than the 30-second default for a long-running endpoint.
|
|
84
|
-
if-cli api -p example-dev --timeout 300 -X PUT -d '{"productionLineCodes":["PL001"]}' \
|
|
85
|
-
/api/orchestration/run
|
|
86
|
-
|
|
87
|
-
# Discover API routes from the factory's OpenAPI document.
|
|
88
|
-
if-cli api routes -p example-dev
|
|
89
|
-
if-cli api routes -p example-dev agent-projects
|
|
90
|
-
if-cli api describe -p example-dev GET /api/agent-projects/{id}
|
|
91
|
-
|
|
92
|
-
# Remove the cached credential for a profile.
|
|
93
|
-
if-cli logout -p example-dev
|
|
94
|
-
|
|
95
|
-
# Fallback for factories where browser OAuth is not available.
|
|
96
|
-
if-cli set-token -p example-dev
|
|
97
|
-
|
|
98
|
-
if-cli --version
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Profile selection uses `-p`, then `INSIGHTFACTORY_CONFIG_PROFILE`, then
|
|
102
|
-
`[DEFAULT]`. Route discovery reads `{host}/swagger/v1/swagger.json` and does
|
|
103
|
-
not require a login. Authenticated API requests are restricted to the selected
|
|
104
|
-
factory origin so a profile token cannot be forwarded to another host. Profile
|
|
105
|
-
hosts must be bare origins rather than URLs containing application path
|
|
106
|
-
prefixes.
|
|
107
|
-
|
|
108
|
-
Requests time out after 30 seconds by default. Raise the deadline per
|
|
109
|
-
invocation with `--timeout <seconds>`, or for a whole session with
|
|
110
|
-
`INSIGHTFACTORY_REQUEST_TIMEOUT`; the flag wins over the environment variable,
|
|
111
|
-
and both are validated before the command touches the network. There is no
|
|
112
|
-
upper bound, because the right ceiling depends on the endpoint.
|
|
113
|
-
|
|
114
|
-
Both apply to the API request itself. The OAuth discovery and token-refresh
|
|
115
|
-
round trips keep the 30-second default, so `if-cli token` and in-process callers
|
|
116
|
-
of `if_cli.oauth.get_valid_token` always have a bounded wait that no ambient
|
|
117
|
-
environment value can redefine.
|
|
118
|
-
|
|
119
|
-
A timeout is a client-side deadline, not a rejection: the factory may have
|
|
120
|
-
accepted and completed the request after `if-cli` gave up. Re-issuing a
|
|
121
|
-
non-idempotent call (`POST`, `PUT`, `PATCH`, `DELETE`) can therefore apply it
|
|
122
|
-
twice, so the timeout message for those methods says to confirm the current
|
|
123
|
-
state before retrying rather than repeating the call.
|
|
124
|
-
|
|
125
|
-
Override the config directory with `INSIGHTFACTORY_CONFIG_DIR` (useful in tests
|
|
126
|
-
and CI). The default is `~/.insightfactory`.
|
|
127
|
-
|
|
128
|
-
The request method goes in `-X`; `if-cli api` takes exactly one positional
|
|
129
|
-
argument and reads it as the path. A `curl`-shaped `if-cli api GET /api/schedules`
|
|
130
|
-
therefore parses `GET` as the path — write `if-cli api -X GET /api/schedules`
|
|
131
|
-
instead. The CLI names the misplaced verb rather than reporting a problem with
|
|
132
|
-
`/api/schedules`.
|
|
133
|
-
|
|
134
|
-
On Git Bash (MSYS2) on Windows, path conversion rewrites a leading-slash
|
|
135
|
-
argument such as `/api/agent-projects` into a Windows path. Prefix the command
|
|
136
|
-
with `MSYS_NO_PATHCONV=1`:
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
MSYS_NO_PATHCONV=1 if-cli api -p example-dev /api/agent-projects
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Scripting against profiles
|
|
143
|
-
|
|
144
|
-
`profiles --json` and `config get` are the supported machine-readable
|
|
145
|
-
interfaces; the padded `profiles` columns are for humans and are not a
|
|
146
|
-
contract, and neither is the layout of `~/.insightfactory/config`.
|
|
147
|
-
|
|
148
|
-
`if-cli profiles --json` prints an array — one entry per configured profile, in
|
|
149
|
-
file order — of `name`, `host`, and `token`:
|
|
150
|
-
|
|
151
|
-
```json
|
|
152
|
-
[
|
|
153
|
-
{
|
|
154
|
-
"name": "example-dev",
|
|
155
|
-
"host": "https://factory.example",
|
|
156
|
-
"token": { "status": "valid", "expires_at": 1785918336, "refreshable": true }
|
|
157
|
-
},
|
|
158
|
-
{ "name": "other", "host": "https://other.factory.example", "token": { "status": "none" } }
|
|
159
|
-
]
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
`token.status` is `valid`, `expired`, `none`, or `unknown`; `expires_at` and
|
|
163
|
-
`refreshable` are present only when a cached token exists. `valid` means
|
|
164
|
-
`if-cli token` will hand back the cached token as-is, so a token inside the
|
|
165
|
-
60-second slack window that command refreshes within is reported as `expired` —
|
|
166
|
-
use the raw `expires_at` if you need the literal expiry instant instead. A
|
|
167
|
-
profile that cannot be resolved has a `host` of `null` (or its raw configured
|
|
168
|
-
value) and a top-level `error` describing the problem, and does not stop the
|
|
169
|
-
other profiles from being listed; `token.error` is set only when the token
|
|
170
|
-
cache itself could not be read.
|
|
171
|
-
|
|
172
|
-
`if-cli config get <key> -p <profile>` prints one newline-terminated value and
|
|
173
|
-
exits non-zero if the profile cannot be resolved, or the key is not configured
|
|
174
|
-
within it. A profile with no host cannot be resolved at all, so every key on it
|
|
175
|
-
fails, not just `host`. A key present but empty (`client_id =`) counts as not
|
|
176
|
-
configured:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
curl -H "Authorization: Bearer $(if-cli token -p example-dev)" \
|
|
180
|
-
"$(if-cli config get host -p example-dev)/api/agent-projects"
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
The readable keys are `host`, `audience`, `callback_port`, `client_id`, and
|
|
184
|
-
`organization` — all non-secret, and resolved the same way the CLI itself
|
|
185
|
-
resolves them, so `audience` and `callback_port` return their defaults when the
|
|
186
|
-
profile omits them. Tokens are deliberately not readable this way; use
|
|
187
|
-
`if-cli token`.
|
|
188
|
-
|
|
189
|
-
## Programmatic API
|
|
190
|
-
|
|
191
|
-
`insightfactory-cli` may be imported in-process as a library to resolve a
|
|
192
|
-
profile to a factory host and a fresh access token. These four symbols are a
|
|
193
|
-
supported programmatic surface for in-process consumers:
|
|
194
|
-
|
|
195
|
-
- `if_cli.config.load_config`
|
|
196
|
-
- `if_cli.config.get_profile`
|
|
197
|
-
- `if_cli.oauth.get_valid_token`
|
|
198
|
-
- `if_cli.runtime.CliError`
|
|
199
|
-
|
|
200
|
-
They raise `CliError` on failure (never `SystemExit` or `sys.exit`), never
|
|
201
|
-
write to stdout, never launch a browser or block on interactive input, and use
|
|
202
|
-
only bounded waits. Changing or removing any of them is a breaking change and
|
|
203
|
-
requires a major version bump.
|
|
204
|
-
|
|
205
|
-
## How authentication works
|
|
206
|
-
|
|
207
|
-
- Profiles are stored in `~/.insightfactory/config`, one per customer/environment.
|
|
208
|
-
- The profile file is managed by the CLI and is regenerated on updates; comments
|
|
209
|
-
and empty sections are not preserved.
|
|
210
|
-
- Tokens are cached by host in `~/.insightfactory/token-cache.json` with file
|
|
211
|
-
mode `0600`.
|
|
212
|
-
- The configuration directory is `0700`; configuration and cache updates are
|
|
213
|
-
atomic.
|
|
214
|
-
- Remote factory, authorization, and token endpoints must use HTTPS. HTTP is
|
|
215
|
-
accepted only for loopback development.
|
|
216
|
-
- Login discovers OAuth metadata from
|
|
217
|
-
`{host}/.well-known/oauth-authorization-server`.
|
|
218
|
-
- Authorization uses PKCE through the factory `/authorize` proxy with a
|
|
219
|
-
loopback-only (`127.0.0.1`) callback. The CLI is a public OAuth client: the
|
|
220
|
-
client ID is read from the factory's discovery document at runtime (no client
|
|
221
|
-
secret).
|
|
222
|
-
|
|
223
|
-
## Development
|
|
224
|
-
|
|
225
|
-
```bash
|
|
226
|
-
uv sync
|
|
227
|
-
uv run pytest
|
|
228
|
-
uv run ruff check .
|
|
229
|
-
uv run ty check
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Smoke-test against a throwaway config directory, never `~/.insightfactory`:
|
|
233
|
-
|
|
234
|
-
```bash
|
|
235
|
-
uv run if-cli --help
|
|
236
|
-
uv run if-cli --version
|
|
237
|
-
INSIGHTFACTORY_CONFIG_DIR=$(mktemp -d) uv run if-cli profiles
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Python 3.10 or newer is required. Runtime code is stdlib-only.
|
|
241
|
-
|
|
242
|
-
## Release
|
|
243
|
-
|
|
244
|
-
**A release is cut by pushing a tag, not by merging.** Merging to `main` runs CI and
|
|
245
|
-
publishes nothing.
|
|
246
|
-
|
|
247
|
-
```bash
|
|
248
|
-
git tag v1.0.1 && git push origin v1.0.1
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
Verify and build live in the reusable workflow
|
|
252
|
-
[`if_s_insightfactory_cli.yml`](https://github.com/insightfactory-ai/if_sre_github_actions/blob/main/.github/workflows/if_s_insightfactory_cli.yml)
|
|
253
|
-
in `insightfactory-ai/if_sre_github_actions`. This repository's
|
|
254
|
-
`.github/workflows/release.yml` keeps only the OIDC publish job, because PyPI
|
|
255
|
-
Trusted Publishing is registered for **this** repo, workflow `release.yml`, and
|
|
256
|
-
environment `pypi` — a reusable workflow cannot be registered.
|
|
257
|
-
|
|
258
|
-
A one-time trusted-publisher registration is required on the PyPI project for
|
|
259
|
-
that triple. The publish job downloads the `dist-release` artifact staged by
|
|
260
|
-
the reusable workflow and uploads it with `pypa/gh-action-pypi-publish`.
|
|
261
|
-
|
|
262
|
-
| Trigger | Published version | PyPI role |
|
|
263
|
-
|---|---|---|
|
|
264
|
-
| Push / merge to `develop` | `1.0.1.dev{N}` | pre-release (`pip install --pre`) |
|
|
265
|
-
| Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest |
|
|
266
|
-
| Merge to `main` | nothing — CI only | — |
|
|
267
|
-
|
|
268
|
-
`N` is `github.run_number` of the reusable workflow: a per-workflow integer
|
|
269
|
-
that goes up on every run and stays the same across re-runs of that run (so a
|
|
270
|
-
failed publish can be retried under the same version). It does not depend on
|
|
271
|
-
git history or clone depth. After a successful release, bump `version` in
|
|
272
|
-
`pyproject.toml` on `develop` so later `.devN` builds sort *after* what just
|
|
273
|
-
landed; PEP 440 puts `1.0.1.devN` before `1.0.1`. Because of that ordering, a
|
|
274
|
-
dependency spec of `insightfactory-cli>=1.0.0` does not match develop-channel
|
|
275
|
-
`.devN` builds; an in-process consumer that wants them must specify a
|
|
276
|
-
pre-release-bearing spec such as `insightfactory-cli>=1.0.0.dev0` (which also
|
|
277
|
-
matches the final `1.0.0` release).
|
|
278
|
-
|
|
279
|
-
A `v*` tag whose name does not match `pyproject.toml` fails before publishing.
|
|
280
|
-
|
|
281
|
-
The two channels treat an already-published version differently, on purpose:
|
|
282
|
-
|
|
283
|
-
- **Release channel (a tag):** publishing fails, naming the fix. A tag is a request to
|
|
284
|
-
release that version; if it cannot be honoured, the run must say so rather than report
|
|
285
|
-
success having published nothing. (git also refuses to push a tag that already exists,
|
|
286
|
-
so this is hard to reach.)
|
|
287
|
-
- **Dev channel (`develop`):** publishing is skipped quietly. `.dev{N}` uses
|
|
288
|
-
`github.run_number`, which is stable across re-runs of one run, so an already-present
|
|
289
|
-
version there can only mean a re-run of a run that already published — real idempotency,
|
|
290
|
-
not a swallowed mistake.
|
|
291
|
-
|
|
292
|
-
This split is why releasing moved off `main`. While a merge was the trigger, an
|
|
293
|
-
already-published version *had* to be tolerated as a no-op for merges to stay green — so a
|
|
294
|
-
merge that released nothing looked exactly like one that released, and a forgotten version
|
|
295
|
-
bump shipped nothing silently.
|
|
296
|
-
|
|
297
|
-
## Differences from the Node CLI
|
|
298
|
-
|
|
299
|
-
Behaviour matches `@insightfactory-ai/if-cli` except where the platforms
|
|
300
|
-
genuinely diverge:
|
|
301
|
-
|
|
302
|
-
- **Unknown-option wording** is aligned (`Unknown option '--flag'`). Other
|
|
303
|
-
argparse messages (for example a missing option value) still differ from
|
|
304
|
-
Node's `util.parseArgs`.
|
|
305
|
-
- **Expiry timestamps** in human-readable output use the C-locale `%c` format
|
|
306
|
-
rather than JavaScript `Date#toLocaleString()`.
|
|
307
|
-
- **`--version`** reads the installed package metadata (`1.0.0` and later)
|
|
308
|
-
rather than an npm `package.json`.
|
|
309
|
-
|
|
310
|
-
On-disk profile and token-cache paths, JSON shapes, and file permissions stay
|
|
311
|
-
compatible with the Node CLI, so you can switch implementations without
|
|
312
|
-
re-authenticating.
|
|
313
|
-
|
|
314
|
-
## Licence
|
|
315
|
-
|
|
316
|
-
Copyright 2026 insightfactory.ai. All rights reserved. This package is proprietary
|
|
317
|
-
and may be used only under a separate written agreement with insightfactory.ai.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|