Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
57834a0
feat(connect-scope): scaffold Vue 3 + FastAPI extension
tdstein Feb 18, 2026
be0ce5a
feat(connect-scope): add content → jobs → traces UI flow
tdstein Feb 18, 2026
87d97bb
fix(connect-scope): parse NDJSON traces response into JSON array
tdstein Feb 18, 2026
78a972d
feat(connect-scope): replace raw JSON dump with flamegraph-style trac…
tdstein Feb 19, 2026
0532c41
refactor(connect-scope): replace color indicators with symbol and uni…
tdstein Feb 19, 2026
5580404
feat(connect-scope): add facet filter bar to trace viewer
tdstein Feb 19, 2026
b1b173d
feat(connect-scope): add deep link support via url query params
tdstein Feb 19, 2026
c3f23db
feat(connect-scope): add Visitor API integration and fix Connect base…
tdstein Feb 19, 2026
1c8bec3
update .gitignore
tdstein Feb 19, 2026
fc4f4b8
perf(connect-scope): optimize trace viewer span parsing and filter ev…
tdstein Feb 19, 2026
b903c67
fix(connect-scope): fix filter dropdown overlay and duration text wra…
tdstein Feb 19, 2026
02d9eff
feat(connect-scope): add flamegraph view toggle to trace viewer
tdstein Feb 19, 2026
f71fc45
feat(connect-scope): add expand/collapse all traces button
tdstein Feb 19, 2026
350cabe
style(connect-scope): improve visual clarity of expanded trace groups
tdstein Feb 19, 2026
25cddad
feat(connect-scope): replace trace detail page with slide-over panel
tdstein Feb 19, 2026
9536d19
revert(connect-scope): remove slide-over panel, restore inline trace …
tdstein Feb 23, 2026
99aee30
fix: add cachetools to pyproject.toml
tdstein Feb 23, 2026
5e60bb7
feat(connect-scope): replace filter dropdown with sidebar facets and …
tdstein Feb 23, 2026
288987d
feat(connect-scope): add performance diagnosis tools to trace viewer
tdstein Feb 23, 2026
104d222
style(connect-scope): replace duration filter pills with a range slider
tdstein Feb 23, 2026
68b53f7
refactor(connect-scope): extract utilities and SpanDetailPanel from T…
tdstein Feb 23, 2026
dcf48ca
fix(connect-scope): remove self time, color error spans in waterfall,…
tdstein Feb 23, 2026
39ee5d3
feat(connect-scope): make aggregate rows clickable to filter waterfal…
tdstein Feb 23, 2026
bb79733
refactor(connect-scope): decompose TraceViewer into composables and s…
tdstein Feb 23, 2026
2b4c2ab
feat(connect-scope): add per-job flame graph page
tdstein Feb 23, 2026
84d7416
feat(connect-scope): make flame graph the default job view
tdstein Feb 23, 2026
f7cfdf8
feat(connect-scope): collapse job list into flame graph page as right…
tdstein Feb 23, 2026
4f4cc30
fix(connect-scope): improve flame graph UX and error formatting
tdstein Feb 23, 2026
992a92c
fix(connect-scope): cancel stale trace fetches when switching jobs
tdstein Feb 23, 2026
56cd31e
feat(connect-scope): add shared zoom between histogram and flame chart
tdstein Feb 23, 2026
0fecce2
fix(connect-scope): remove duration from span detail panel
tdstein Feb 23, 2026
7983e24
fix(connect-scope): use float precision for span width percentages
tdstein Feb 23, 2026
1773226
chore(connect-scope): remove unused TraceViewer subtree
tdstein Feb 23, 2026
3152874
fix(connect-scope): add non-null assertions for array index access
tdstein Feb 23, 2026
af0baae
style(connect-scope): format trace-builder with consistent style
tdstein Feb 23, 2026
8f724c9
feat(connect-scope): redesign ContentList with My Content tab and search
tdstein Feb 23, 2026
731ab01
chore(connect-scope): regenerate manifest.json and requirements.txt
tdstein Feb 23, 2026
1c0f028
bump connectapi version (#330)
karawoo Feb 26, 2026
e3e8da6
Update extension list
github-actions[bot] Feb 26, 2026
eed4f0f
Merge branch 'tdstein/trace-viewer' into worktree-iridescent-discover…
tdstein Mar 3, 2026
696d226
fix(connect-scope): load flamegraph immediately on deep link navigation
tdstein Mar 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1424,9 +1424,9 @@
"homepage": "https://github.com/posit-dev/connect-extensions/tree/main/extensions/usage-metrics-dashboard",
"category": "extension",
"latestVersion": {
"version": "1.0.11",
"released": "2025-12-30T15:32:52Z",
"url": "https://github.com/posit-dev/connect-extensions/releases/download/usage-metrics-dashboard%40v1.0.11/usage-metrics-dashboard.tar.gz",
"version": "1.0.12",
"released": "2026-02-26T23:46:36Z",
"url": "https://github.com/posit-dev/connect-extensions/releases/download/usage-metrics-dashboard%40v1.0.12/usage-metrics-dashboard.tar.gz",
"minimumConnectVersion": "2025.04.0",
"requiredFeatures": [
"OAuth Integrations"
Expand All @@ -1438,6 +1438,20 @@
}
},
"versions": [
{
"version": "1.0.12",
"released": "2026-02-26T23:46:36Z",
"url": "https://github.com/posit-dev/connect-extensions/releases/download/usage-metrics-dashboard%40v1.0.12/usage-metrics-dashboard.tar.gz",
"minimumConnectVersion": "2025.04.0",
"requiredFeatures": [
"OAuth Integrations"
],
"requiredEnvironment": {
"r": {
"requires": "~=4.3"
}
}
},
{
"version": "1.0.11",
"released": "2025-12-30T15:32:52Z",
Expand Down
9 changes: 9 additions & 0 deletions extensions/connect-scope/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
__pycache__/
.env
.posit
.venv/
*.py[cod]
dist/
node_modules/
rsconnect-python
uv.lock
29 changes: 29 additions & 0 deletions extensions/connect-scope/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Connect Scope

A Vue 3 + FastAPI extension for Posit Connect.

## Development

### Backend

```bash
uv sync
uv run fastapi dev main.py
```

### Frontend

```bash
npm install
npm run dev
```

The Vite dev server proxies `/api` requests to `http://localhost:8000`.

### Build

```bash
npm run build
```

The built frontend is output to `dist/` and served as static files by FastAPI.
12 changes: 12 additions & 0 deletions extensions/connect-scope/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Connect Scope</title>
</head>
<body class="min-h-svh antialiased">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
99 changes: 99 additions & 0 deletions extensions/connect-scope/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import json
import os

from cachetools import TTLCache, cached
from fastapi import FastAPI, Header, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from posit import connect
from posit.connect.errors import ClientError

app = FastAPI()

client = connect.Client()

# Cache visitor clients per token with a 1-hour TTL
client_cache = TTLCache(maxsize=float("inf"), ttl=3600)


@cached(client_cache)
def get_visitor_client(token: str | None) -> connect.Client:
"""Create and cache an API client per session token with 1 hour TTL."""
if token:
return client.with_user_session_token(token)
return client


@app.get("/api/visitor-auth")
async def integration_status(posit_connect_user_session_token: str = Header(None)):
"""Check whether the Visitor API OAuth integration is configured."""
if os.getenv("RSTUDIO_PRODUCT") == "CONNECT":
if not posit_connect_user_session_token:
return {"authorized": False}
try:
get_visitor_client(posit_connect_user_session_token)
except ClientError as err:
if err.error_code == 212:
return {"authorized": False}
raise

return {"authorized": True}


@app.get("/api/user")
async def get_current_user(posit_connect_user_session_token: str = Header(None)):
visitor = get_visitor_client(posit_connect_user_session_token)
return visitor.me


@app.get("/api/content")
async def list_content(posit_connect_user_session_token: str = Header(None)):
visitor = get_visitor_client(posit_connect_user_session_token)
return (
content
for content in visitor.content.find(include="owner")
if content["trace_collection_enabled"]
)


@app.get("/api/content/{guid}/jobs")
async def list_jobs(guid: str, posit_connect_user_session_token: str = Header(None)):
visitor = get_visitor_client(posit_connect_user_session_token)
try:
return list(visitor.content.get(guid).jobs)
except Exception as e:
raise HTTPException(status_code=404, detail=str(e))


@app.get("/api/content/{guid}/jobs/{job_key}/traces")
async def get_traces(
guid: str,
job_key: str,
posit_connect_user_session_token: str = Header(None),
):
visitor = get_visitor_client(posit_connect_user_session_token)
try:
response = visitor.get(f"v1/content/{guid}/jobs/{job_key}/traces")
records = []
for line in response.content.decode("utf-8").splitlines():
if line.strip():
try:
records.append(json.loads(line))
except json.JSONDecodeError:
pass # skip truncated or malformed trailing line
return records
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))


@app.get("/", include_in_schema=False)
async def serve_index(request: Request):
root_path = request.scope.get("root_path", "").rstrip("/")
with open("dist/index.html") as f:
content = f.read()
script_tag = f'<script>window.__CONNECT_ROOT_PATH__ = "{root_path}";</script>'
content = content.replace("</head>", f" {script_tag}\n </head>")
return HTMLResponse(content)


app.mount("/", StaticFiles(directory="dist"), name="static")
48 changes: 48 additions & 0 deletions extensions/connect-scope/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"version": 1,
"metadata": {
"appmode": "python-fastapi",
"entrypoint": "main:app"
},
"python": {
"version": "3.13.12",
"package_manager": {
"name": "pip",
"version": "26.0.1",
"package_file": "requirements.txt"
}
},
"environment": {
"python": {
"requires": ">=3.9"
}
},
"files": {
"requirements.txt": {
"checksum": "0ae28f1352cba19969eff560ba67cd70"
},
"dist/assets/index-DDx0qRXz.js": {
"checksum": "28ca4b964317cbdd317bfdc17fb26fd4"
},
"dist/assets/index-DQr9rnlw.css": {
"checksum": "968991b28fea7485c180f3347f8220db"
},
"dist/index.html": {
"checksum": "831e6f241498640e5f59889676e5fb9f"
},
"main.py": {
"checksum": "d7a529385878efbc32cf1bdfeca554e9"
}
},
"extension": {
"name": "connect-scope",
"title": "Connect Scope",
"description": "connect-scope",
"homepage": "https://github.com/posit-dev/connect-extensions/tree/main/extensions/connect-scope",
"category": "extension",
"tags": ["python"],
"minimumConnectVersion": "2026.02.0",
"requiredFeatures": [],
"version": "0.0.0"
}
}
Loading
Loading