Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

insightfactory-cli

Profile-based authentication CLI for the InsightFactory Interfaces API. It installs the if-cli command, which logs in through the browser with OAuth authorization code and PKCE, refreshes tokens, and makes authenticated API calls. Profiles live in ~/.insightfactory in the same layout the Node @insightfactory-ai/if-cli uses, so both implementations can share one config directory.

This is the production Python CLI, published to public PyPI. It needs Python 3.10 or newer.

Install

uv tool install insightfactory-cli   # persistent install, on PATH
uvx insightfactory-cli profiles      # or zero-install, run-once
uv tool upgrade insightfactory-cli   # updates

uv fetches a managed Python if the machine lacks one. Fallbacks:

pipx install insightfactory-cli
pip install insightfactory-cli

Develop-branch builds are published as PEP 440 .devN pre-releases. Install the latest one with:

pip install --pre insightfactory-cli
uv tool install --prerelease allow insightfactory-cli

PEP 440 sorts 1.0.0.devN before 1.0.0, so a dependency spec of insightfactory-cli>=1.0.0 does not match a develop-channel dev build. An in-process consumer that wants those builds needs a spec that admits pre-releases, such as insightfactory-cli>=1.0.0.dev0. That spec matches both the .devN builds and the final 1.0.0 release.

Usage

# Create a profile and authenticate through the browser.
if-cli login -p example-dev --host https://factory.example

# Inspect profiles and cached-token status. Statuses are colour-coded on a TTY
# (green valid, yellow refreshable-but-expired, red missing or broken); set
# NO_COLOR=1 to disable or FORCE_COLOR=1 to keep colour when piping.
if-cli profiles
if-cli profiles --json

# Read a single non-secret profile value for scripting.
if-cli config get host -p example-dev

# Print a valid token, refreshing it when a refresh token is available.
if-cli token -p example-dev
if-cli token -p example-dev --env
if-cli token -p example-dev --env-name INSIGHTFACTORY_ACCESS_TOKEN_DEV

# Make an authenticated API request.
if-cli api -p example-dev /api/agent-projects
if-cli api -p example-dev -X POST -d '{"name":"example"}' /api/example

# Wait longer than the 30-second default for a long-running endpoint.
if-cli api -p example-dev --timeout 300 -X PUT -d '{"productionLineCodes":["PL001"]}' \
  /api/orchestration/run

# Discover API routes from the factory's OpenAPI document.
if-cli api routes -p example-dev
if-cli api routes -p example-dev agent-projects
if-cli api describe -p example-dev GET /api/agent-projects/{id}

# Remove the cached credential for a profile.
if-cli logout -p example-dev

# Fallback for factories where browser OAuth is not available.
if-cli set-token -p example-dev

if-cli --version

Profile selection tries -p first, then INSIGHTFACTORY_CONFIG_PROFILE, then [DEFAULT]. Route discovery reads {host}/swagger/v1/swagger.json and does not need a login. Authenticated requests only go to the selected factory's origin, so a profile token cannot be forwarded to another host. A profile host must be a bare origin, not a URL with an application path prefix.

Requests time out after 30 seconds by default. Raise the deadline for one invocation with --timeout <seconds>, or for a whole session with INSIGHTFACTORY_REQUEST_TIMEOUT. The flag wins over the environment variable, and the CLI validates both before it touches the network. There is no upper bound, because the right ceiling depends on the endpoint.

Both settings apply to the API request itself. OAuth discovery and token refresh keep the 30-second default, so if-cli token and in-process callers of if_cli.oauth.get_valid_token always wait a bounded time that no ambient environment value can change.

A timeout is a client-side deadline, not a rejection. The factory may have accepted and completed the request after if-cli gave up, so re-issuing a POST, PUT, PATCH, or DELETE can apply it twice. For those methods the timeout message tells you to confirm the current state before retrying rather than repeat the call.

Override the config directory with INSIGHTFACTORY_CONFIG_DIR, which is useful in tests and CI. The default is ~/.insightfactory.

The request method goes in -X. if-cli api takes exactly one positional argument and reads it as the path, so the curl-shaped if-cli api GET /api/schedules parses GET as the path. Write if-cli api -X GET /api/schedules instead. The error names the misplaced verb rather than reporting a problem with /api/schedules.

On Git Bash (MSYS2) on Windows, path conversion rewrites a leading-slash argument such as /api/agent-projects into a Windows path. Prefix the command with MSYS_NO_PATHCONV=1:

MSYS_NO_PATHCONV=1 if-cli api -p example-dev /api/agent-projects

Scripting against profiles

profiles --json and config get are the supported machine-readable interfaces. The padded profiles columns are for humans and are not a contract, and neither is the layout of ~/.insightfactory/config.

if-cli profiles --json prints an array with one entry per configured profile, in file order, each holding name, host, and token:

[
  {
    "name": "example-dev",
    "host": "https://factory.example",
    "token": { "status": "valid", "expires_at": 1785918336, "refreshable": true }
  },
  { "name": "other", "host": "https://other.factory.example", "token": { "status": "none" } }
]

token.status is valid, expired, none, or unknown. expires_at and refreshable appear only when a cached token exists. valid means if-cli token will hand back the cached token as-is. A token inside the 60-second slack window that command refreshes within is therefore reported as expired, so read the raw expires_at if you need the literal expiry instant. A profile that cannot be resolved has a host of null, or its raw configured value, plus a top-level error describing the problem. It does not stop the other profiles from being listed. token.error is set only when the token cache itself could not be read.

if-cli config get <key> -p <profile> prints one newline-terminated value and exits non-zero if the profile cannot be resolved or the key is not configured in it. A profile with no host cannot be resolved at all, so every key on it fails, not just host. A key that is present but empty, such as client_id =, counts as not configured:

curl -H "Authorization: Bearer $(if-cli token -p example-dev)" \
  "$(if-cli config get host -p example-dev)/api/agent-projects"

The readable keys are host, audience, callback_port, client_id, and organization. All are non-secret and resolved the same way the CLI resolves them, so audience and callback_port return their defaults when the profile omits them. Tokens are deliberately not readable this way. Use if-cli token.

Programmatic API

You can import insightfactory-cli in-process to resolve a profile to a factory host and a fresh access token. These four symbols are supported for in-process consumers:

  • if_cli.config.load_config
  • if_cli.config.get_profile
  • if_cli.oauth.get_valid_token
  • if_cli.runtime.CliError

They raise CliError on failure, never SystemExit or sys.exit. They never write to stdout, never launch a browser or block on interactive input, and only use bounded waits. Changing or removing any of them is a breaking change and needs a major version bump.

get_valid_token takes a keyword-only force_refresh (default False). When True, it refreshes the cached token even if it is still fresh, which a caller that just received a 401 from the factory needs: the cached token was fresh a moment ago but the factory has since rejected it. With a refresh token cached, this re-mints regardless of freshness; without one — a token pasted through if-cli set-token — it raises CliError with the same if-cli login -p <name> hint rather than serving the token that was just rejected.

How authentication works

  • Profiles live in ~/.insightfactory/config, one per customer and environment.
  • The CLI manages the profile file and regenerates it on updates. Comments and empty sections are not preserved.
  • Tokens are cached by host in ~/.insightfactory/token-cache.json with file mode 0600.
  • The configuration directory is 0700. Configuration and cache updates are atomic.
  • Remote factory, authorization, and token endpoints must use HTTPS. HTTP is accepted only for loopback development.
  • Login discovers OAuth metadata from {host}/.well-known/oauth-authorization-server.
  • Authorization uses PKCE through the factory /authorize proxy with a loopback-only 127.0.0.1 callback. The CLI is a public OAuth client. It reads the client ID from the factory's discovery document at runtime and holds no client secret.

MCP router (if-cli mcp)

Runs one MCP server over stdio that fronts several factory environments as a single tool catalogue, instead of one MCP connection per environment. Every tool gets a required environment argument and each call is forwarded to that environment's own factory /mcp endpoint, authenticated with that environment's if-cli profile — a profile's bearer token never reaches a different environment's host. A tool is only offered on the environments whose input schema matches the published one, so a factory on an older release cannot be handed argument shapes it does not accept.

It needs the mcp extra, which is not part of the base install. Name its environments once in a [factory <name>] section of ~/.insightfactory/config, alongside profiles:

[factory foundry]
dev = example-dev
tst = example-tst
writable = dev

Every key except writable and catalog is <environment code> = <profile name>; file order is the environment order. writable is a comma-separated list of codes (default none); catalog is one code (default the first environment). Then:

uv tool install 'insightfactory-cli[mcp]'
uvx --from 'insightfactory-cli[mcp]' if-cli mcp foundry

Add it to .mcp.json:

{
  "mcpServers": {
    "if-factory": {
      "command": "uvx",
      "args": ["--from", "insightfactory-cli[mcp]", "if-cli", "mcp", "foundry"]
    }
  }
}

--env, --writable, --catalog and --allow-tool still work without a factory section (--env is required in that case), and each one overrides the section when both are given: a repeated --env CODE=PROFILE replaces that code's profile, --writable/--catalog, when passed at all, replace the section's value outright, and --allow-tool is always additive.

Flag Meaning
FACTORY (optional, first positional) Name of a [factory <name>] config section supplying the flags below.
--env CODE=PROFILE (repeatable, required without FACTORY) The environment code the model passes, mapped to the if-cli profile that reaches it.
--writable CODE (repeatable) Environments where write tools are allowed. Everything else is read-only.
--read-only Forces every environment read-only, overriding both the section's writable and any --writable. Cannot be combined with --writable.
--catalog CODE (default: the first environment) Whose tool list is published; its schemas are the reference every environment is checked against.
--allow-tool NAME (repeatable) Extra tool names treated as read-only, beyond the shipped list.
--timeout SECONDS (default 120) Per upstream request; higher than the api command's default because some tools are slow.

mcp's --timeout does not read INSIGHTFACTORY_REQUEST_TIMEOUT: it parses the flag with parse_timeout_seconds rather than resolve_request_timeout, because a long-running stdio server is not the one-shot request that environment variable is scoped to.

Only environments passed to --writable accept mutating tools; every other environment allows only tools it knows to read (an allowlist, not a blocklist — a tool the model reaches through invoke_tool is never in the published catalogue, so a blocklist could never cover everything callable). Passing dev-only writes to production means changing an argument value, not connecting to a different server, so keep --writable scoped to the environments meant to be written by hand.

Development

uv sync
uv run pytest
uv run ruff check .
uv run ty check

Smoke-test against a throwaway config directory, never ~/.insightfactory:

uv run if-cli --help
uv run if-cli --version
INSIGHTFACTORY_CONFIG_DIR=$(mktemp -d) uv run if-cli profiles

Python 3.10 or newer is required. The base install has no runtime dependencies; optional features, such as if-cli mcp, ship as extras and are imported lazily inside their command module.

Release

A release is cut by pushing a tag, not by merging. Merging to main runs CI and publishes nothing.

git tag v1.0.1 && git push origin v1.0.1

Verify and build live in the reusable workflow if_s_insightfactory_cli.yml in insightfactory-ai/if_sre_github_actions. This repository's .github/workflows/release.yml keeps only the OIDC publish job, because PyPI Trusted Publishing is registered for this repo, workflow release.yml, and environment pypi. A reusable workflow cannot be registered.

The PyPI project needs a one-time trusted-publisher registration for that triple. The publish job downloads the dist-release artifact staged by the reusable workflow and uploads it with pypa/gh-action-pypi-publish.

Trigger Published version PyPI role
Push / merge to develop 1.0.1.dev{N} pre-release (pip install --pre)
Push of a v* tag the version in pyproject.toml, which the tag must match latest
Merge to main nothing, CI only none

N is github.run_number of the reusable workflow. It is a per-workflow integer that goes up on every run and stays the same across re-runs of that run, so a failed publish can be retried under the same version. It does not depend on git history or clone depth. After a release, bump version in pyproject.toml on develop so later .devN builds sort after what just landed. PEP 440 puts 1.0.1.devN before 1.0.1, which is also why a spec of insightfactory-cli>=1.0.0 skips develop-channel builds and an in-process consumer that wants them must ask for insightfactory-cli>=1.0.0.dev0.

A v* tag whose name does not match pyproject.toml fails before publishing.

The two channels treat an already-published version differently, on purpose:

  • Release channel, a tag. Publishing fails and names the fix. A tag is a request to release that version. If it cannot be honoured, the run must say so rather than report success having published nothing. Git also refuses to push a tag that already exists, so this is hard to reach.
  • Dev channel, develop. Publishing is skipped quietly. .dev{N} uses github.run_number, which is stable across re-runs of one run, so an already-present version there can only mean a re-run of a run that already published. That is idempotency, not a swallowed mistake.

This split is why releasing moved off main. While a merge was the trigger, an already-published version had to be tolerated as a no-op for merges to stay green. A merge that released nothing then looked the same as one that released, and a forgotten version bump shipped nothing, silently.

Differences from the Node CLI

Behaviour matches @insightfactory-ai/if-cli except where the platforms differ:

  • Unknown-option wording is aligned to Unknown option '--flag'. Other argparse messages, such as a missing option value, still differ from Node's util.parseArgs.
  • Expiry timestamps in human-readable output use the C-locale %c format rather than JavaScript Date#toLocaleString().
  • --version reads the installed package metadata from 1.0.0 onward rather than an npm package.json.

On-disk profile and token-cache paths, JSON shapes, and file permissions stay compatible with the Node CLI, so you can switch implementations without re-authenticating.

Licence

Copyright 2026 insightfactory.ai. All rights reserved. This package is proprietary and may be used only under a separate written agreement with insightfactory.ai.

Metadata

Release files for insightfactory-cli 1.0.3.dev19

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for insightfactory-cli 1.0.3.dev19
File Size Uploaded Provenance
insightfactory_cli-1.0.3.dev19.tar.gz 141.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for insightfactory-cli 1.0.3.dev19
File Interpreter ABI Platform
insightfactory_cli-1.0.3.dev19-py3-none-any.whl Python 3 none any Details

Total release size: 190.1 kB

Release files / insightfactory_cli-1.0.3.dev19.tar.gz

Download URL insightfactory_cli-1.0.3.dev19.tar.gz
Size 141.7 kB
Tags Source
SHA-256 checksum
How to use checksums
f0be6cf5109b38eaa97f30a61b46c52edda367333897c8718780534d1046105f
BLAKE2b-256 checksum
How to use checksums
a0824d845e5f481d9923510a640780d27272aad6a5a1bb7300404362081dea64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / insightfactory_cli-1.0.3.dev19-py3-none-any.whl

Download URL insightfactory_cli-1.0.3.dev19-py3-none-any.whl
Size 48.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df0f714e2c211f8734c4bec58019319f3a9e4e8099e4864a32a2c0aa296d6e9d
BLAKE2b-256 checksum
How to use checksums
c1bd2983b451b5ee9193cd10ae6a19e463ac86fb995ed96666781e24fc5bf5c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Security for insightfactory-cli 1.0.3.dev19

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for insightfactory_cli-1.0.3.dev19.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page