insightfactory-cli 1.1.2.dev30__py3-none-any.whl → 1.2.0.dev34__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.
- if_cli/attribution.py +51 -0
- if_cli/commands/mcp.py +4 -1
- if_cli/constants.py +1 -0
- if_cli/http.py +11 -1
- if_cli/oauth.py +2 -0
- if_cli/router/log.py +9 -2
- if_cli/router/server.py +24 -1
- if_cli/router/upstream.py +6 -1
- {insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/METADATA +17 -1
- {insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/RECORD +13 -12
- {insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/WHEEL +0 -0
- {insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/entry_points.txt +0 -0
- {insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/licenses/LICENSE +0 -0
if_cli/attribution.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""The `X-IF-UA` client-attribution header.
|
|
2
|
+
|
|
3
|
+
The Interfaces API attributes work to the client that made the request by reading
|
|
4
|
+
`X-IF-UA`, a list of RFC 9110 product tokens. It takes the first token, splits it at
|
|
5
|
+
`/` into client and version, and ignores the rest. So a tool that drives `if-cli`
|
|
6
|
+
names itself first through `INSIGHTFACTORY_UA` and `if-cli` appends its own token:
|
|
7
|
+
|
|
8
|
+
X-IF-UA: if-cli/1.2.0 # INSIGHTFACTORY_UA unset
|
|
9
|
+
X-IF-UA: if-fde-harness/5.10.0 if-cli/1.2.0 # INSIGHTFACTORY_UA=if-fde-harness/5.10.0
|
|
10
|
+
|
|
11
|
+
The environment value is only ever sent after it matches the product-token grammar,
|
|
12
|
+
so a stray newline or shell fragment in it cannot add a header or reach the factory.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
from functools import cache
|
|
20
|
+
from importlib.metadata import version
|
|
21
|
+
|
|
22
|
+
from if_cli.constants import ENV_UA
|
|
23
|
+
|
|
24
|
+
HEADER_NAME = "X-IF-UA"
|
|
25
|
+
MAX_CALLER_PRODUCTS_LENGTH = 256
|
|
26
|
+
|
|
27
|
+
# RFC 9110 section 5.6.2: token = 1*tchar. Section 10.1.5: product = token ["/" product-version].
|
|
28
|
+
_TCHAR = r"[!#$%&'*+\-.^_`|~0-9A-Za-z]"
|
|
29
|
+
_PRODUCT = rf"{_TCHAR}+(?:/{_TCHAR}+)?"
|
|
30
|
+
_PRODUCTS = re.compile(rf"{_PRODUCT}(?: {_PRODUCT})*")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@cache
|
|
34
|
+
def _own_product() -> str:
|
|
35
|
+
return f"if-cli/{version('insightfactory-cli')}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def is_valid_products(value: str) -> bool:
|
|
39
|
+
"""Whether `value` is one or more space-separated product tokens, within the length cap."""
|
|
40
|
+
return len(value) <= MAX_CALLER_PRODUCTS_LENGTH and _PRODUCTS.fullmatch(value) is not None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def build_x_if_ua() -> str:
|
|
44
|
+
"""The `X-IF-UA` value: the caller's products from `INSIGHTFACTORY_UA`, then `if-cli`'s own.
|
|
45
|
+
|
|
46
|
+
An unset, empty or invalid `INSIGHTFACTORY_UA` leaves `if-cli`'s token alone.
|
|
47
|
+
"""
|
|
48
|
+
caller = os.environ.get(ENV_UA)
|
|
49
|
+
if caller and is_valid_products(caller):
|
|
50
|
+
return f"{caller} {_own_product()}"
|
|
51
|
+
return _own_product()
|
if_cli/commands/mcp.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import importlib.util
|
|
4
|
+
import platform
|
|
4
5
|
import sys
|
|
5
6
|
|
|
6
7
|
from if_cli.cli import Options, parse_args
|
|
7
8
|
from if_cli.config import config_file, get_profile, list_factories, load_config, parse_factory_section
|
|
8
9
|
from if_cli.http import parse_timeout_seconds
|
|
10
|
+
from if_cli.router.log import log
|
|
9
11
|
from if_cli.runtime import die
|
|
10
12
|
|
|
11
13
|
MCP_OPTIONS: Options = {
|
|
@@ -75,6 +77,8 @@ def _stray_positional(rest: list[str]) -> str | None:
|
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
def mcp_command(argv: list[str]) -> None:
|
|
80
|
+
log(f"starting on Python {platform.python_version()}")
|
|
81
|
+
|
|
78
82
|
if "-h" in argv or "--help" in argv:
|
|
79
83
|
sys.stdout.write(MCP_USAGE)
|
|
80
84
|
return
|
|
@@ -146,7 +150,6 @@ def mcp_command(argv: list[str]) -> None:
|
|
|
146
150
|
# import bug inside if_cli.router propagates instead of reading as a missing extra.
|
|
147
151
|
if importlib.util.find_spec("mcp") is None:
|
|
148
152
|
die(MISSING_MCP_EXTRA)
|
|
149
|
-
from if_cli.router.log import log
|
|
150
153
|
from if_cli.router.server import build_router, run_server
|
|
151
154
|
|
|
152
155
|
timeout = parse_timeout_seconds(values["timeout"], "--timeout")
|
if_cli/constants.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
ENV_PROFILE: str = "INSIGHTFACTORY_CONFIG_PROFILE"
|
|
2
2
|
ENV_REQUEST_TIMEOUT: str = "INSIGHTFACTORY_REQUEST_TIMEOUT"
|
|
3
|
+
ENV_UA: str = "INSIGHTFACTORY_UA"
|
|
3
4
|
DEFAULT_SCOPE: str = "openid profile email offline_access"
|
|
4
5
|
LOOPBACK_HOST: str = "127.0.0.1"
|
|
5
6
|
DEFAULT_CALLBACK_PORT: int = 53682
|
if_cli/http.py
CHANGED
|
@@ -10,6 +10,7 @@ from email.message import Message
|
|
|
10
10
|
from typing import NoReturn
|
|
11
11
|
from urllib.parse import ParseResult, urljoin, urlparse
|
|
12
12
|
|
|
13
|
+
from if_cli.attribution import HEADER_NAME, build_x_if_ua
|
|
13
14
|
from if_cli.constants import ENV_REQUEST_TIMEOUT
|
|
14
15
|
from if_cli.runtime import die
|
|
15
16
|
|
|
@@ -170,6 +171,7 @@ def fetch_with_timeout(
|
|
|
170
171
|
body: str | bytes | None = None,
|
|
171
172
|
timeout: float | None = None,
|
|
172
173
|
warn_unsafe_retry: bool = False,
|
|
174
|
+
attribute: bool = True,
|
|
173
175
|
) -> HttpResponse:
|
|
174
176
|
"""Issue one request, dying on timeout.
|
|
175
177
|
|
|
@@ -177,7 +179,15 @@ def fetch_with_timeout(
|
|
|
177
179
|
sense for a request the caller issued on the user's behalf against the factory.
|
|
178
180
|
It is wrong advice for an OAuth round trip, where retrying is the correct response
|
|
179
181
|
and there is no factory-side state to confirm.
|
|
182
|
+
|
|
183
|
+
`attribute` adds the `X-IF-UA` header (see `if_cli.attribution`) unless the
|
|
184
|
+
caller already set one. It is on for factory requests and off for the token
|
|
185
|
+
endpoint, which belongs to the identity provider: that is a third party, and the
|
|
186
|
+
header is for the factory's own client metrics.
|
|
180
187
|
"""
|
|
188
|
+
request_headers = dict(headers or {})
|
|
189
|
+
if attribute and not any(name.lower() == HEADER_NAME.lower() for name in request_headers):
|
|
190
|
+
request_headers[HEADER_NAME] = build_x_if_ua()
|
|
181
191
|
data: bytes | None
|
|
182
192
|
if body is None:
|
|
183
193
|
data = None
|
|
@@ -185,7 +195,7 @@ def fetch_with_timeout(
|
|
|
185
195
|
data = body
|
|
186
196
|
else:
|
|
187
197
|
data = body.encode("utf-8")
|
|
188
|
-
request = urllib.request.Request(url, data=data, headers=
|
|
198
|
+
request = urllib.request.Request(url, data=data, headers=request_headers, method=method)
|
|
189
199
|
effective_timeout = resolve_request_timeout(timeout)
|
|
190
200
|
try:
|
|
191
201
|
# Callers pass endpoints validated by parse_http_url or derived from a
|
if_cli/oauth.py
CHANGED
|
@@ -128,6 +128,8 @@ def _token_request(token_endpoint: str, form: dict[str, str]) -> dict[str, objec
|
|
|
128
128
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
129
129
|
body=urlencode(form),
|
|
130
130
|
timeout=DEFAULT_REQUEST_TIMEOUT_S,
|
|
131
|
+
# The token endpoint is the identity provider's, not the factory's.
|
|
132
|
+
attribute=False,
|
|
131
133
|
)
|
|
132
134
|
if not response.ok:
|
|
133
135
|
body = response.text()
|
if_cli/router/log.py
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
# Captured at import: this module loads before the router does anything else
|
|
7
|
+
# diagnosable, so it stands in for process start. Two `log()` lines can then
|
|
8
|
+
# be subtracted to time the phase between them.
|
|
9
|
+
_START = time.monotonic()
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
def log(message: str) -> None:
|
|
7
|
-
"""Write one diagnostic line to stderr.
|
|
13
|
+
"""Write one timestamped diagnostic line to stderr.
|
|
8
14
|
|
|
9
15
|
The router speaks MCP over stdout, so stderr is the only channel that can
|
|
10
16
|
carry anything to the operator. This lives in its own module because both
|
|
11
17
|
`server` and `upstream` write to it and `server` imports `upstream`.
|
|
12
18
|
"""
|
|
13
|
-
|
|
19
|
+
elapsed_ms = round((time.monotonic() - _START) * 1000)
|
|
20
|
+
sys.stderr.write(f"[if-cli mcp] +{elapsed_ms}ms {message}\n")
|
if_cli/router/server.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Any, TypeVar
|
|
|
9
9
|
import anyio
|
|
10
10
|
import mcp_types as types
|
|
11
11
|
from mcp.server import Server
|
|
12
|
-
from mcp.server.context import ServerRequestContext
|
|
12
|
+
from mcp.server.context import CallNext, HandlerResult, ServerMiddleware, ServerRequestContext
|
|
13
13
|
from mcp.server.stdio import stdio_server
|
|
14
14
|
from mcp.shared.exceptions import MCPError
|
|
15
15
|
|
|
@@ -137,6 +137,7 @@ class Router:
|
|
|
137
137
|
|
|
138
138
|
async def build_catalog(self) -> list[types.Tool]:
|
|
139
139
|
"""Build the merged catalogue. Safe to call again to pick up a new login."""
|
|
140
|
+
log(f"catalogue: building from {self.config.catalog_source}")
|
|
140
141
|
catalogues = await self._catalogues()
|
|
141
142
|
source = catalogues.get(self.config.catalog_source)
|
|
142
143
|
if source is None:
|
|
@@ -536,6 +537,27 @@ def advertised_extensions(router: Router) -> dict[str, dict[str, Any]]:
|
|
|
536
537
|
return {SKILLS_EXTENSION: {}} if router.config.skills else {}
|
|
537
538
|
|
|
538
539
|
|
|
540
|
+
def _log_first_initialize() -> ServerMiddleware[Any]:
|
|
541
|
+
"""Log once the router has answered a client's `initialize`.
|
|
542
|
+
|
|
543
|
+
Read against `prime_catalog`'s startup log lines, this tells an operator
|
|
544
|
+
apart a slow process (nothing logged yet), a slow factory (catalogue
|
|
545
|
+
build still running past this point), and a client that called before
|
|
546
|
+
the handshake (a tool call logged before this line ever appears).
|
|
547
|
+
"""
|
|
548
|
+
logged = False
|
|
549
|
+
|
|
550
|
+
async def middleware(ctx: ServerRequestContext[Any, Any], call_next: CallNext) -> HandlerResult:
|
|
551
|
+
nonlocal logged
|
|
552
|
+
result = await call_next(ctx)
|
|
553
|
+
if not logged and ctx.method == "initialize":
|
|
554
|
+
logged = True
|
|
555
|
+
log("answered the first initialize")
|
|
556
|
+
return result
|
|
557
|
+
|
|
558
|
+
return middleware
|
|
559
|
+
|
|
560
|
+
|
|
539
561
|
async def _serve(router: Router) -> None:
|
|
540
562
|
server: Server[Any] = Server(
|
|
541
563
|
SERVER_NAME,
|
|
@@ -553,6 +575,7 @@ async def _serve(router: Router) -> None:
|
|
|
553
575
|
# the handshake rebuilds its capabilities from `server.extensions` once the
|
|
554
576
|
# protocol version is known, and ignores the ones computed here.
|
|
555
577
|
server.extensions = advertised_extensions(router)
|
|
578
|
+
server.middleware.append(_log_first_initialize())
|
|
556
579
|
# Answer `initialize` immediately: the catalogue build runs as a background
|
|
557
580
|
# task so one hung factory reads as a slow tools/list, not a failed
|
|
558
581
|
# handshake (clients cap startup time).
|
if_cli/router/upstream.py
CHANGED
|
@@ -9,6 +9,7 @@ import mcp_types as types
|
|
|
9
9
|
from mcp.client import Client
|
|
10
10
|
from mcp.client.streamable_http import streamable_http_client
|
|
11
11
|
|
|
12
|
+
from if_cli.attribution import HEADER_NAME, build_x_if_ua
|
|
12
13
|
from if_cli.config import Profile
|
|
13
14
|
from if_cli.http import parse_url, url_origin
|
|
14
15
|
from if_cli.oauth import get_valid_token
|
|
@@ -200,7 +201,11 @@ class Upstream:
|
|
|
200
201
|
async def _run(self, action, *, elicitation_callback=None):
|
|
201
202
|
url = f"{self.profile['host']}/mcp"
|
|
202
203
|
try:
|
|
203
|
-
async with httpx2.AsyncClient(
|
|
204
|
+
async with httpx2.AsyncClient(
|
|
205
|
+
auth=ProfileBearerAuth(self.profile),
|
|
206
|
+
timeout=self.timeout,
|
|
207
|
+
headers={HEADER_NAME: build_x_if_ua()},
|
|
208
|
+
) as http_client:
|
|
204
209
|
async with Client(
|
|
205
210
|
streamable_http_client(url, http_client=http_client),
|
|
206
211
|
elicitation_callback=elicitation_callback,
|
{insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: insightfactory-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0.dev34
|
|
4
4
|
Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
|
|
5
5
|
Project-URL: Homepage, https://insightfactory.ai
|
|
6
6
|
Author-email: "insightfactory.ai Support" <support@insightfactory.ai>
|
|
@@ -128,6 +128,14 @@ accepted and completed the request after `if-cli` gave up, so re-issuing a
|
|
|
128
128
|
timeout message tells you to confirm the current state before retrying rather
|
|
129
129
|
than repeat the call.
|
|
130
130
|
|
|
131
|
+
Every request to the factory carries an `X-IF-UA` header naming the client,
|
|
132
|
+
`if-cli/<version>` by default. A tool that runs `if-cli` can name itself first
|
|
133
|
+
by setting `INSIGHTFACTORY_UA` to one or more RFC 9110 product tokens, such as
|
|
134
|
+
`if-fde-harness/5.10.0`, and the header becomes
|
|
135
|
+
`if-fde-harness/5.10.0 if-cli/<version>`. The CLI ignores a value that is not
|
|
136
|
+
valid product-token syntax or is longer than 256 characters. Token requests to
|
|
137
|
+
the identity provider never carry the header.
|
|
138
|
+
|
|
131
139
|
Override the config directory with `INSIGHTFACTORY_CONFIG_DIR`, which is useful
|
|
132
140
|
in tests and CI. The default is `~/.insightfactory`.
|
|
133
141
|
|
|
@@ -265,6 +273,14 @@ uv tool install 'insightfactory-cli[mcp]'
|
|
|
265
273
|
uvx --from 'insightfactory-cli[mcp]' if-cli mcp foundry
|
|
266
274
|
```
|
|
267
275
|
|
|
276
|
+
The first `uvx` launch resolves and downloads the package from PyPI before it runs, which
|
|
277
|
+
added roughly four to six seconds on top of the usual startup in local testing, against
|
|
278
|
+
under two seconds once `uv`'s cache was warm. A client with a fixed handshake budget
|
|
279
|
+
(Claude Code allows 30 seconds) usually has room for that, but a slow network can close
|
|
280
|
+
the gap. Two ways to avoid paying it on every launch: `uv tool install 'insightfactory-cli[mcp]'`
|
|
281
|
+
puts a real script on `PATH` with no resolve step, or run the same `uvx --from ...` command
|
|
282
|
+
by hand once before pointing a client at it, which warms `uv`'s cache for the launches after.
|
|
283
|
+
|
|
268
284
|
Add it to `.mcp.json`:
|
|
269
285
|
|
|
270
286
|
```json
|
{insightfactory_cli-1.1.2.dev30.dist-info → insightfactory_cli-1.2.0.dev34.dist-info}/RECORD
RENAMED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
if_cli/__init__.py,sha256=YYcz-hEiXXUjdBFGipOrVHynsyH4ZLqQOCDobvCAu8Y,26
|
|
2
2
|
if_cli/__main__.py,sha256=2hLFIiT-gocMb0VJVzVYtGVIoHrxyMChucRgexbXLSQ,68
|
|
3
|
+
if_cli/attribution.py,sha256=hNp1hBueRUh1EKYs61AZF76voehw70F7gZKiI5zOMCw,1869
|
|
3
4
|
if_cli/cache.py,sha256=UfvHs3GQIVg0sVfKYWw4UGlq8d-8BEXuZIDfC1hwwjA,6587
|
|
4
5
|
if_cli/callback_page.py,sha256=lTHd3-vzlT9ndAL7XYonBfy97lBW7apRw3V7SmhNObo,5907
|
|
5
6
|
if_cli/cli.py,sha256=fF7HvsUgpKhkLFdXLMJ1JXHGjp3RwJt2YmPSEW0d8gY,6270
|
|
6
7
|
if_cli/colour.py,sha256=2hbaM4ubTHGja6YZRD886eqnB7T2IaZyW5rvfOjxe4M,682
|
|
7
8
|
if_cli/config.py,sha256=QiphIhVLDN3sp39xMWKwXEyJWxx4IhXT28oEtmPc0ew,10601
|
|
8
|
-
if_cli/constants.py,sha256=
|
|
9
|
-
if_cli/http.py,sha256=
|
|
9
|
+
if_cli/constants.py,sha256=WxZozWfhMCFWU_Wupa__CaJI9UgqJ28W8cchGtp6p4Q,303
|
|
10
|
+
if_cli/http.py,sha256=LNkQtyUR1DAgLcR2CrNzTMHFQO_j_vfHuOBagmQEYNk,8249
|
|
10
11
|
if_cli/main.py,sha256=pn-X2wrHNplc8ceT5_nEv5XlrTN3KgpK8UU4gAmBiWY,3212
|
|
11
|
-
if_cli/oauth.py,sha256=
|
|
12
|
+
if_cli/oauth.py,sha256=lSJXzOsOzHlkZX56JWoh9G6-sD9MOGgmwpI18tdhA6c,16563
|
|
12
13
|
if_cli/runtime.py,sha256=Pz2iVclqy-uu1UMQWK2P_Hx7O4zAF30UpSFqQvGN0NY,2663
|
|
13
14
|
if_cli/assets/__init__.py,sha256=2vBBor8lUEQofiC6EvS5BvF_ANhfK8bfR9S0OXI8j3g,41
|
|
14
15
|
if_cli/assets/insightfactoryai-logo.svg,sha256=yFxHkoHl6VNm8ppCInTZCVN_bdOog9h-NV8R4Gw1Qew,8192
|
|
@@ -17,19 +18,19 @@ if_cli/commands/api.py,sha256=XeVhhD39Oho4xNbMJIGh1I6vi53on5EGGsAtsb_5TAg,9016
|
|
|
17
18
|
if_cli/commands/config.py,sha256=1KAxjme897NIbXTGBw1rNJlXnFvhbEUf5MAli8A93ns,1724
|
|
18
19
|
if_cli/commands/login.py,sha256=vxsYcRZWsaNEzekyN1zy4ROJO-OCGUpjaVUBhtbK1LY,3255
|
|
19
20
|
if_cli/commands/logout.py,sha256=TW0P4_tXcTtiHHR73xVPHyhlbOWD1jBnkZsMImCiCBw,668
|
|
20
|
-
if_cli/commands/mcp.py,sha256=
|
|
21
|
+
if_cli/commands/mcp.py,sha256=iaSsqWkV1lnFjIw9y1pX1ORB1MdKiOVWgK23EDIsDB4,7342
|
|
21
22
|
if_cli/commands/profiles.py,sha256=tgZP5sMqzbFHXlfvglL2plsGdU0M3joo3wvQKX45J1U,3950
|
|
22
23
|
if_cli/commands/set_token.py,sha256=BVyv_QmByvllr04pE2sz8JFEscHbIgOC-7ZPg61PRoc,1886
|
|
23
24
|
if_cli/commands/token.py,sha256=CyiXkyH7vxgH91-n-XJzeHnLI6mZJwO9Js5AHvfHaeM,1120
|
|
24
25
|
if_cli/router/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
26
|
if_cli/router/catalog.py,sha256=Er2ZbWTwDUVU-qBGCvyA0bIlFk3UYw_6sVUlYwBhMpI,4992
|
|
26
27
|
if_cli/router/content.py,sha256=POL2n5NIY8yenQHJe8pUbTuTNpPSUSm0Y6Txbvehzhs,14487
|
|
27
|
-
if_cli/router/log.py,sha256=
|
|
28
|
+
if_cli/router/log.py,sha256=wtaqsFcD3hPJRu4jCJaDMSedj-43Y07ym62YgrOm1yM,740
|
|
28
29
|
if_cli/router/policy.py,sha256=CclbjuOA5mwwoQiNDa17iQeN9eXRbfy11bS9JFd8gL4,2840
|
|
29
|
-
if_cli/router/server.py,sha256=
|
|
30
|
-
if_cli/router/upstream.py,sha256=
|
|
31
|
-
insightfactory_cli-1.
|
|
32
|
-
insightfactory_cli-1.
|
|
33
|
-
insightfactory_cli-1.
|
|
34
|
-
insightfactory_cli-1.
|
|
35
|
-
insightfactory_cli-1.
|
|
30
|
+
if_cli/router/server.py,sha256=yCjGAm-tnZBOTg5AfvojnLcKcZLEbQQeT9iqqX8XV5A,27397
|
|
31
|
+
if_cli/router/upstream.py,sha256=Ly4fcuQRywS6db2oDYoe8F4c9hryN75FdBqSWzA09g4,18720
|
|
32
|
+
insightfactory_cli-1.2.0.dev34.dist-info/METADATA,sha256=xbyLKunNzQ2g7wv-86s5ruh_-rnUyetLKVWwYINNBp8,24817
|
|
33
|
+
insightfactory_cli-1.2.0.dev34.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
34
|
+
insightfactory_cli-1.2.0.dev34.dist-info/entry_points.txt,sha256=UlX854D7f-7dBj5F0wGdakVy6vopvP9hA1BN-gv739w,44
|
|
35
|
+
insightfactory_cli-1.2.0.dev34.dist-info/licenses/LICENSE,sha256=8eZ1YAABL398qESVJc_FlK1voYQ0GAh-R_rNBQK6QH8,234
|
|
36
|
+
insightfactory_cli-1.2.0.dev34.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|