Commit Graph
5339 Commits
Author SHA1 Message Date
Alex 4cc603db89 fix(frontend): guard CopyButton against concurrent copies
The button is disabled once isCopied is true, but copy() is now async, so
isCopied and the disabled prop only catch up after it resolves. Clicks
landing inside that window both passed the guard and started a write.

Nothing leaked, since the existing clearTimeout already handled the
duplicate timer, but the window did not exist before copy-to-clipboard 4
made the call async. A ref cleared in finally closes it.

Raised by CodeRabbit on #2762. 598 tests pass, build and lint unchanged.
2026-09-12 20:05:20 +01:00
Alex 88b4d89b4f chore(deps): e2e TypeScript 7
TypeScript 7 removed the baseUrl compiler option, so the path mapping is
rewritten relative ("./helpers/*"), which is what the removal notice
prescribes.

tsc --noEmit is clean and `playwright test --list` collects all 221 tests
across 42 files, so module resolution still works at runtime.
2026-09-12 19:35:22 +01:00
Alex 05c53195a3 chore(deps): react-widget majors -- markdown-it 15, babel 8, flow-bin 0.331
markdown-it 15 renders byte-identical HTML to 14.3.2 across headings,
lists, code, links, tables, blockquotes, raw HTML, javascript: URLs,
entities and rules, with the widget's custom table/thead/tr/td/th renderer
rules applied. Output still goes through DOMPurify before injection.

Babel 8 and flow-bin change nothing in the build: there is no babel config
in the package and Parcel transforms with SWC, so bundle sizes are
identical before and after. They are bumped to stay current.

Verified the built bundle in Chromium: modern/browser.js loads with no page
errors, renderDocsGPTWidget and renderSearchBar are both functions, and
both mount DOM.

ESLint 10 is not taken here either -- same eslint-plugin-react block as the
frontend, so eslint and @eslint/js stay on 9.

Build, tsc --noEmit and lint all clean, 0 advisories.
2026-09-12 19:34:51 +01:00
Alex def32a44fc chore(deps): declare eslint config deps, eslint-plugin-n 18
eslint.config.js imports @eslint/js and globals but neither was declared;
both resolved transitively through eslint. Declaring them fixes a latent
break and is a prerequisite for ESLint 10, which stops providing them.

ESLint 10 itself is not taken. eslint-plugin-react 7.37.5 is the latest
release and peers at eslint "^3 || ... || ^9.7", and under 10 every rule
load throws "contextOrFilename.getFilename is not a function" because
ESLint 10 removed context.getFilename(). The frontend stays on eslint 9
until that ships; note npm already marks 9.x as no longer supported.

eslint-plugin-n goes to 18, though the flat config does not reference it
(nor eslint-plugin-import, eslint-plugin-promise or eslint-config-prettier
-- all four are unused devDependencies).

Lint output unchanged from baseline.
2026-09-12 19:32:49 +01:00
Alex 05d9338983 chore(deps): mermaid 12
mermaid 12 needs no source change; mermaidSecurity.ts and its strict-mode
init-directive test pass unchanged.

Adds a lodash-es ^4.18.1 override. mermaid 12 depends on chevrotain ~11.1.2
(mermaid 11 resolved chevrotain 12, which has no lodash-es), and chevrotain
11 pulls lodash-es <= 4.17.23, carrying GHSA-r5fr-rjxr-66jc and
GHSA-f23m-r3pf-42rh, both high. 4.18.1 is the fixed line. Without the
override the frontend gains 5 high-severity advisories.

Verified rendering in Chromium rather than the unit suite, since happy-dom
has no SVG getBBox and cannot render any mermaid version. Flowchart,
sequence, pie, class, state, gantt, ER and mindmap all produce SVG on 12,
matching 11 to the byte except classDiagram (+17 bytes). A strict-mode
`click ... "javascript:..."` target is still stripped.

598 tests pass, build clean, 0 advisories.
2026-09-12 19:31:29 +01:00
Alex 6319b25b56 chore(deps): react-markdown 10
react-markdown 10 removed the `className` prop and no longer renders a
wrapper element; passing className now throws at render time. Each of the
three call sites gets an explicit wrapping div carrying the same classes,
which reproduces the v9 DOM exactly.

The large diff is re-indentation from the added wrapper. `git diff -w`
shows the only substantive change per file is the moved className.

Fixes the 19 tests that failed on the upgrade. 598 tests pass, build clean,
lint unchanged.
2026-09-12 19:28:18 +01:00
Alex cf1c9d542d chore(deps): copy-to-clipboard 4, vite-plugin-svgr 5, @shadcn/react 0.3.1
copy-to-clipboard 4 returns Promise<boolean> instead of boolean, so
CopyButton's handleCopy awaits it. Without the await the success branch
always ran, since a Promise is always truthy. Awaiting also routes a
rejected copy into the existing catch.

vite-plugin-svgr 5 and @shadcn/react 0.3.1 needed no source changes.

598 tests pass, build clean, lint unchanged.
2026-09-12 19:26:20 +01:00
Alex 08a2c038bc chore(deps): react-widget in-range updates
Babel 7.29.7, React 19.3, styled-components 6.5.3, dompurify 3.4.15,
typescript-eslint 8.70, prettier 3.9.6, svgo 4.1, markdown-it 14.3.

Clears all 5 npm audit advisories, including svgo removeScripts
GHSA-w27v-7q3p-w38r and GHSA-4vpr-x523-8j87 (both high).

Prettier 3.9 changed ternary and union-type wrapping, so two src files are
reformatted by lint-fix. No behavior change.

Build, tsc --noEmit and lint all clean.
2026-09-12 19:25:00 +01:00
Alex d1f75b1d82 chore(deps): e2e in-range updates
Playwright 1.63, pg 8.23, @types/node 22.20. tsc --noEmit clean.
2026-09-12 19:23:07 +01:00
Alex ca889de288 chore(deps): docs in-range updates, pin zod, override xmldom
Next 16.3.5 and React 19.3 in docs/, plus the transitive refresh that came
with them (mermaid 11.17, katex 0.16.47, shiki 3.23, styled-components 6.5).

Two overrides were needed:

- zod ~4.3.6. nextra 4.6.1 declares `zod: ^4.1.12` but its MDX prop
  validation breaks on zod >= 4.4.0, failing prerender for every page with
  "Invalid input: expected nonoptional, received undefined -> at children".
  Bisected: 4.3.6 builds, 4.4.0 through 4.6.2 all fail. nextra 4.6.1 is the
  latest release, so there is no upstream fix to take yet. Drop this pin
  once nextra ships one.

- @xmldom/xmldom ^0.9.12. speech-rule-engine 4.1.4 (via mathjax-full ->
  better-react-mathjax -> nextra) pins xmldom at exactly 0.9.10, so
  `npm audit fix` cannot reach it. 0.9.12 clears 13 advisories including
  GHSA-6gmq-8vp8-gcm6 (high).

Docs build passes, 48 pages indexed, 0 advisories.
2026-09-12 19:23:07 +01:00
Alex 19ad1cb828 chore(deps): frontend in-range updates
Applies all semver-compatible updates in frontend/: React 19.3, Vite 8.3,
vitest 4.1.11, radix-ui 1.6.7, lucide-react 1.45, mermaid 11.17,
typescript-eslint 8.70, tailwind 4.3.3, i18next 26.4 and others.

Clears all 4 npm audit advisories (js-yaml GHSA-2883-xcg3-v3hh high,
@vitest/mocker GHSA-82fw-gwwq-j7x9, @humanfs/node GHSA-p498-v437-472g).

598 tests pass, build clean, lint unchanged from baseline.
2026-09-12 19:16:35 +01:00
Alex 6d1091f54a Merge pull request #2761 from arc53/chore/bump-backend-deps-2026-09
chore(deps): bring the backend dependencies up to date
2026-09-12 19:12:17 +01:00
Alex f1dc7d5b6b chore(deps): final upgrade pass (setuptools 84)
`uv lock --upgrade` after the major bumps; every direct dependency is now at
its latest release.

What stays behind is capped upstream, not by this project. The notable one:
huggingface-hub sits at 1.16.1 rather than 1.31.0 because 1.31 wants
click>=8.4.2 and gTTS 2.5.4 — the newest gTTS — caps click below 8.2.
marshmallow is held at 3.x by dataclasses-json, websockets by google-genai,
typer and semchunk by docling-core, portalocker by qdrant-client, and
pydantic-core is pinned exactly by pydantic.

Verified beyond the suite: `docsgpt verify-offline` passes (tiktoken
encoding, both tokenizers, both embedding models, docling PDF conversion),
and the wheel builds and installs into a clean environment resolved from the
declared ranges rather than the lock, with the ASGI app importing there.
2026-09-12 18:21:33 +01:00
Alex 762686b520 chore(deps): torch 2.14, transformers 5.17, tokenizers 0.23 (docling extra)
Lifts the deliberate `transformers<5.9` cap. The cap existed because 5.9
broke docling's PDF layout model on Apple Silicon; 5.17 does not. Checked by
converting a four-document benchmark (two papers, a 30-page table corpus, a
10-page report) on this machine with both stacks: the Markdown is
byte-identical on three, and the fourth differs only by one dropped
`<!-- image -->` placeholder on a figure. Tables still render as GFM tables
and no text is lost.

With that cap gone tokenizers can move too — transformers 5.8.1 pinned it at
<=0.23.0 and no such release exists, which is what held it at 0.22.2.
torchvision follows torch.

Linux still resolves torch and torchvision from the CPU-only PyTorch index,
so the docling Docker variant does not pick up the CUDA stack.
2026-09-12 18:15:03 +01:00
Alex 108b8fd56b chore(deps): fastmcp 4 (mcp 2.2)
No code change needed: the server side (`FastMCP`, `@mcp.tool`,
`get_http_headers`, `http_app`) and the client side (`Client`, `BearerAuth`,
the three transports, and the `mcp.client.auth` / `mcp.shared.auth` OAuth
types imported directly) all kept their signatures.

Verified beyond the suite by driving the real `/mcp` mount over a live
uvicorn socket with the fastmcp client: tool discovery, a `search_docs` call
and bearer-token extraction all round-trip, with the token reaching
`search()` intact.

fastmcp 4 and mcp 2.2 also moved to httpx2, so the retry tuple's comment now
places them on that side of the split.
2026-09-12 17:54:18 +01:00
Alex f7baa1952f chore(deps): anthropic 1.5, openai 3.13, and the move to httpx2
Both SDKs' 1.x/3.x majors run on httpx2 (the maintained fork of httpx by its
original author, published by Pydantic at github.com/pydantic/httpx2, version
line 2.x) instead of httpx, which is what makes these two bumps one change.
httpx2 is now a declared dependency because two modules import it directly.
Everything else in the 0.x -> 1.x / 2.x -> 3.x change lists is absent here:
no Text Completions, no `with_raw_response`, no raw `output_format` dicts, no
Bedrock client, and `requires-python` is already 3.12.

Three code changes, all forced by the bump:

The BYOM DNS-pinning client in `docsgpt/security/safe_url.py` is handed to
`OpenAI(http_client=...)`, and the SDK rejects an old-httpx client at
construction — which would have taken the SSRF guard offline. It is built on
httpx2 now, and its `sni_hostname` extension carries a `str` rather than
ascii bytes: httpcore passes the value straight to
`ssl.SSLContext.wrap_socket`, and the truststore backend httpx2 uses for the
default system trust store encodes it instead of accepting bytes. Bytes
therefore failed every real handshake while passing the existing tests, which
stub the transport out; the test now pins the type and says why.

The stream-retry error tuple in `docsgpt/llm/base.py` named `httpx`
exceptions only. The two libraries' exception classes are unrelated types, so
after the bump the retry silently stopped firing for openai and anthropic
while still working for google-genai and elevenlabs. It now covers both
stacks, with a parametrized test over each.

anthropic 1.x dropped temperature/top_p/top_k from `messages.create`'s
signature (passing one raises TypeError) without dropping them from the API,
so the provider forwards them through `extra_body`. The wire request is
unchanged and a model that rejects them 400s exactly as before.
2026-09-12 17:44:18 +01:00
Alex b3326b3c33 chore(deps): redis 8, tiktoken 0.14, openapi3-parser 2, daytona 0.211, reportlab 5
Major bumps whose ceilings had to move. redis 8.1.0, tiktoken 0.14.0 and
daytona 0.211.2 needed no code change (the Daytona client, filesystem and
process signatures the sandbox calls are unchanged; redis 8 was checked
against a live server through the app's own sync and async clients).
reportlab 5.0.1 is test-only.

openapi-parser 2.0.0 is a rewrite onto pydantic spec models: `paths` is now
a dict keyed by URL rather than a list of objects carrying their own `url`,
and a path item exposes one field per HTTP method instead of an `operations`
list. `OpenAPI3Parser` reads both accordingly, iterating methods in the
order the spec declares them, and its rendered output is byte-identical to
before. The rewrite also drops prance, openapi-spec-validator and five more
transitive packages.

tokenizers stays at 0.22.2: transformers 5.8.1 caps it at <=0.23.0 and no
such release exists, so it moves with the transformers cap or not at all.
2026-09-12 17:28:58 +01:00
Alex 7e00197678 chore(deps): upgrade backend deps within their declared ranges
`uv lock --upgrade` plus the two code changes the new versions need.

firecrawl-anydoc 0.2.4 raises a dedicated `NeedsOcrError` where 0.2.3 raised
`UnsupportedError("... OCR is required")`, so the anydoc parser no longer
recognised a scanned PDF: the fallback still ran, but a near-empty result was
stored as an empty document instead of failing with the OCR_ENABLED hint.
`_needs_ocr` now accepts both spellings and looks the class up lazily, so an
older anydoc keeps working. 0.2.4 also refuses the CID-font NDA fixture
outright rather than dropping its Chinese column silently, so the PDF
trust-check tests stub that dropped output against the fixture's real bytes
(the check's own inputs) and a new test pins the refusal path.

ruff 0.16 widened its implicit default rule set, turning the dev-group bump
into 7131 findings across the tree. `.ruff.toml` now states the historical
selection (E4, E7, E9, F) explicitly and the CI pin moves to the locked
0.16.7, so lint no longer drifts with the version.
2026-09-12 17:16:59 +01:00
Alex 0d461d10a7 Merge pull request #2760 from arc53/chore/docs-widget-relock
chore(docs): relock docsgpt-react onto 0.7.1
2026-09-12 16:56:23 +01:00
Alex 8bef09e515 chore(docs): relock docsgpt-react onto 0.7.1
The published 0.7.0 bundle carries the stranded 'use client' directive
that breaks the webpack build, so the docs deploy has been failing since
the widget landed on main. 0.7.1 ships the fix.

The lockfile pinned 0.7.0 exactly and Vercel installs from the lockfile,
so publishing the fix was not enough on its own. Raising the range to
^0.7.1 keeps a fresh install off the broken build.
2026-09-12 16:49:47 +01:00
Alex 067b86637b Merge pull request #2759 from arc53/chore/bump-npm-v0.7.1
chore: bump npm libraries to v0.7.1
2026-09-12 16:48:59 +01:00
github-actions[bot] a448dd9ddd chore: bump npm libraries to v0.7.1 2026-09-12 15:41:23 +00:00
github-actions[bot]andgithub-actions[bot] 7014a68715 chore: bump npm libraries to v0.7.0 (#2757)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-12 16:38:43 +01:00
Alex 66ab337bd4 Merge pull request #2758 from arc53/feat/docs-chat-widget
feat(docs): mount the DocsGPT chat widget
2026-09-12 16:24:59 +01:00
Alex 2b77d1994e feat(docs): use the docs agent key by default
The widget now ships with the docs agent's embed token, so the site works
without any environment configuration. NEXT_PUBLIC_DOCSGPT_API_KEY still
overrides it for staging.

The key is public either way. NEXT_PUBLIC_* is inlined at build time, so
it lands in the client bundle whichever way it is supplied, and every
documented DocsGPT embed carries its key in the page. Abuse is bounded by
the agent's daily request and token limits, enforced by check_usage on
the answer and stream routes.
2026-09-12 16:17:02 +01:00
Alex 3711426c83 feat(docs): mount the DocsGPT chat widget
Adds a floating "Ask the docs" bubble to every docs page. docsgpt-react
was already a dependency but nothing imported it, so no widget rendered.

The widget reads window and navigator on mount, so it is lazy-loaded
with ssr: false from a client wrapper. Its light/dark theme follows the
site toggle via next-themes, promoted here from a transitive dependency
to a declared one.

The key comes from NEXT_PUBLIC_DOCSGPT_API_KEY and the host from
NEXT_PUBLIC_DOCSGPT_API_HOST. With no key set the wrapper renders
nothing rather than falling through to the demo key the package defaults
to.

Bumping docsgpt-react to ^0.7.0 also drops a duplicate React. The old
^0.6.3 pin depended on React 18 and npm installed a nested 18.3.1 beside
the app's 19.2.8, which would have thrown an invalid hook call on first
render.
2026-09-12 12:15:08 +01:00
Alex 74b4ae30f9 fix(react-widget): drop the stranded 'use client' directive
Parcel's library build concatenates modules, so the directive at the top
of DocsGPTWidget.tsx landed a thousand lines into dist/module.js and
dist/main.js. webpack rejects a 'use client' that is not the first
expression in the file, which broke every Next.js consumer of
docsgpt-react:

  x The "use client" directive must be placed before other expressions.
    1168 | 'use client';

The directive could not do its job from inside a bundle anyway. A React
Server Components boundary has to be declared by the importing module,
so consumers wrap the widget in their own client component instead.
2026-09-12 12:15:08 +01:00
Alex d76a8d417e feat: trusted publishing npm 2026-09-12 11:43:26 +01:00
Alex 51cacb4bff Merge pull request #2753 from arc53/feat/optional-agent-sources
feat(agents): make sources optional and drop the synthetic "Default" source
2026-09-11 14:10:52 +01:00
Alex 8a1e2d1d60 fix(upload): select the uploaded source by its own id
The chat selection was inferred by diffing the refreshed source list
against the snapshot taken before the upload, so any source that appeared
meanwhile -- another upload finishing, a new team share -- could be picked
instead. The upload response already carries the id; match on it, and
select nothing when it is absent rather than guessing.

Drops the now-unused sourceDocs read and its stale dependency.
2026-09-11 11:45:51 +01:00
Alex 8b4e2d0d52 chore: ignore root-level agent exports
An agent YAML sitting in the repo root is personal config -- chat ids,
webhook targets -- and nothing tracked matches the pattern, so keep it
out of accidental `git add -A`.
2026-09-11 11:28:30 +01:00
Alex 25987c8947 fix(agents): correct source selection in the agent form, chat and uploads
selectedSourceIdsFromAgent preferred the extra sources over the legacy
single source instead of merging them. Since every save writes both
fields, opening an agent that held both and saving -- even just a rename
-- silently detached the primary source. Retrieval reads both, so merge
them.

The edit form also reported unsaved changes the moment it loaded: the
snapshot taken after the fetch defaulted the retriever to "classic" and
left the default model empty, while the effects that run straight after
rewrite both. Normalise the snapshot through the same serializer and
rules the effects apply.

A source attached to a team-shared agent that the caller cannot list --
the owner's private source -- rendered no picker row, so it showed as
attached with nothing to switch it off. Append rows for selected ids
missing from the list, and memoise the label lookup the trigger and the
picker now share.

Uploading from the agent builder repointed the open conversation onto the
new source and persisted it, because the upload modal always dispatched
the chat selection. Gate that behind a prop the builder opts out of. The
wiki source is created synchronously and never refreshed the source list,
so the builder selected an id the picker could not render and the trigger
read "External KB"; refresh on every completion path that skips ingest
tracking.

Removing the synthetic "Default" source made an empty source list
reachable, which the stored-selection reconciler skipped, leaving a
deleted source checked forever with no way to clear it. Treat an empty
list as a real answer and only skip when the list has not loaded.

Finally, finish the picker extraction: the chat input still hand-rolled
the item mapping, so team users saw the same sources grouped in the agent
builder and flat in chat. Point it at the shared helper, with its own
locale keys, and make the sources link a router navigation instead of a
full page reload.
2026-09-11 11:28:26 +01:00
Alex c26b8a3403 fix(agents): gate agent pinning on visibility
PinAgent looked agents up with no ownership, share or team predicate, and
PinnedAgents returned those rows through a hand-rolled dict that bypassed
the blanking _format_agent_output applies. Anyone holding an agent id --
a share-link recipient learns the real UUID -- could pin it and keep
reading its name, description, prompt, tools, type, status and masked key
after the share was revoked.

Add _user_may_pin: the owner, a team grantee, a recipient of a still-live
share link, or a system template. PinAgent checks it before pinning and
PinnedAgents re-checks every row on read, so a revoked grant drops out of
the list instead of persisting. Unpinning stays open regardless of current
access, or a revoked share would strand a pin the user cannot clear. The
masked key is now owner-only, matching _format_agent_output.

Adds the first cross-user pin tests: every existing one pinned the
caller's own agent.
2026-09-11 11:28:14 +01:00
Alex d0af3fd7ad Merge pull request #2722 from ManishMadan2882/main
Refresh widget UI and add expand/collapse toggle
2026-09-10 15:12:25 +01:00
Alex 9778910abe test: list route keeps agents with neither source nor retriever
The coverage suite still asserted the old filter that hid such agents
from /api/get_agents. They are listed now, so the test checks that.
2026-09-10 08:36:10 +01:00
ManishMadan2882 4393197802 fix(widget): address review on the composer changes 2026-09-10 04:30:04 +05:30
Alex 9c8b38b7d1 Merge pull request #2752 from arc53/revamp-architecture-docs
feat: revamp architecture docs
2026-09-09 18:48:59 +01:00
Alex 0fe059db12 fix(agents): list source-less agents and select only the uploaded source
Review follow-ups:

- The agents list and the pinned list dropped agents that had neither a
  source nor a retriever. Publishing such an agent is now allowed, so it
  vanished from the list right after publish. Both filters are gone; a
  source-less agent skips retrieval and is still runnable. Tests cover
  both listings.
- The agent form selected an uploaded source by diffing the source
  catalog before and after the upload, which would also pick up any
  source created or shared in the meantime. Upload now passes the id of
  the source it created to onSuccessfulUpload, and the form selects only
  that id.
2026-09-09 18:39:09 +01:00
Alex 79d418f32a feat(agents): make sources optional and drop the synthetic "Default" source
The sources list used to start with a fake "Default" entry that had no id
and, at run time, meant "no source, skip retrieval". The agent form
pre-selected it, snapped back to it when the last source was deselected,
and refused to publish without it, so a new agent always looked like it
had a knowledge base when it had none.

Backend
- /api/sources returns only ingested sources; no placeholder row.
- Publishing an agent no longer requires a source on create or update.
  The legacy "default" value is still accepted and maps to NULL.

Frontend
- The agent source picker starts empty, can be cleared, and shows a hint
  that a source-less agent answers from the model and its tools only.
- The picker groups sources into "Your sources" and "Shared with team"
  when any team-shared source exists, shows "N sources selected" for a
  multi-selection, and gets the same "Go to Sources" / "Upload new"
  footer as the chat picker. A source uploaded from the form is selected
  when it lands.
- Source selection serialisation and the picker id live in one helper
  shared with the chat picker; the four copies in the form are gone.
- The client no longer seeds a placeholder source in the store, and the
  dead auto-select of a "default" document is removed.
2026-09-09 17:59:08 +01:00
Alex 7c280e15e0 feat: revamp architecture docs 2026-09-09 17:42:44 +01:00
Alex fb15649a3f Merge pull request #2751 from arc53/feat/ui-in-package
feat(package): ship the web UI in the wheel and serve it from the API
2026-09-09 12:17:10 +01:00
Alex 6dead25508 build(frontend): keep a developer's .env.production.local across the UI build
The script copied .env.development over frontend/.env.production.local and
deleted it on exit, destroying a local override. An existing file is now
set aside before the build and put back by the exit trap, whatever the
build's outcome; without one, nothing is left behind.
2026-09-09 11:26:16 +01:00
Alex 43f493b926 feat(package): ship the web UI in the wheel and serve it from the API
pip install docsgpt now brings the web UI with it: `docsgpt api` serves the
API and the UI on one port.

- scripts/build_frontend.sh builds the frontend into docsgpt/static
  (gitignored) the way the frontend image does: .env.development as the
  production baseline, and index.html loading /config.js ahead of the
  bundle. hatch admits the directory into the wheel and the sdist through
  `artifacts`; the package workflows run the script before `uv build` and
  fail if the wheel lacks the UI. The backend image keeps ignoring it.
- docsgpt/ui.py serves the build in front of Flask: files as they are,
  hashed assets immutable, Flask's own path prefixes (taken from its URL map,
  so new blueprints need no registration) passed through, every other GET
  rendered as index.html for the client-side router. /config.js is generated
  per request with VITE_API_HOST and VITE_BASE_URL set to the page's origin,
  VITE_* environment variables winning. SERVE_UI=false leaves the API alone.
- docsgpt api configures gunicorn in code (gunicorn.app.base.Application)
  instead of rewriting sys.argv, so the SIGUSR2 re-exec that gunicorn uses
  for zero-downtime upgrades runs the docsgpt console script again and
  works; verified with a live handover.
- Docs: the pip page says the UI is included, that DOCSGPT_HOME and
  DOCSGPT_ENV_FILE are process environment variables rather than .env
  entries, and the settings page describes SERVE_UI.
2026-09-09 11:09:09 +01:00
ManishMadan2882 7df153a798 feat(widget): attachments and voice input in the composer 2026-09-09 05:13:57 +05:30
Alex 0c348917b1 Merge pull request #2748 from arc53/ci/release-chain
ci: publish Docker images and the PyPI package from the release workflow
2026-09-08 22:20:54 +01:00
Alex 9e1cead170 ci: fail the PyPI publish workflow on an unknown target
A caller passing a target other than pypi or testpypi would skip both
publish jobs and upload nothing; the build job now fails with the value
instead. Empty (the release event) stays valid.
2026-09-08 19:12:28 +01:00
Alex fa34e0dc01 ci: remove the release-chain rehearsal workflow
Run 34260211236 on the pull request called pypi-publish.yml through
workflow_call aimed at TestPyPI: the trusted-publishing exchange succeeded,
attestations were generated, and both archives were uploaded (already
present, so skipped). Same-repository reusable workflows pass PyPI's
job_workflow_ref check, which names the called file.
2026-09-08 18:59:39 +01:00
Alex d47a86e3c7 ci: gate publishing on the release, rehearse the call path, and lint with a current zizmor
- backend-release checks the tag and the release separately and publishes
  only when it created the release in this run. A run that pushed the tag and
  then failed to create the release used to skip both on re-run.
- pypi-publish accepts a `target` from callers (default pypi) so the
  reusable-workflow path can be rehearsed against TestPyPI; the TestPyPI job
  skips files that already exist there.
- A temporary workflow, to be removed before merge, calls pypi-publish the
  way backend-release does, aimed at TestPyPI, on this pull request: PyPI
  verifies the job_workflow_ref claim, which names the called file, so the
  run proves the trusted-publishing path through workflow_call.
- zizmor-action v0.6.3: the pinned v0.5.2 bundles zizmor 1.23, which cannot
  parse the `uses: $/...` self-repository syntax and aborted the audit.
2026-09-08 18:57:47 +01:00
Alex d983febe43 ci: publish Docker images and the PyPI package from the release workflow
backend-release creates the GitHub release with GITHUB_TOKEN, and GitHub
never starts workflows from events that token produces, so the
`release: published` triggers on the Docker and PyPI publish workflows only
fired for releases made by hand. 0.18.0 got no Docker images for that
reason, and 0.19.0 reached PyPI by a manual run.

backend-release now calls both publish workflows after creating the release,
passing the version it tagged. Both workflows gain a `workflow_call` trigger
with a `version` input and read the tag from it or from the release event,
so a release created by hand still publishes through the release trigger.
The Docker Hub credentials are passed by name; the PyPI job authenticates
through trusted publishing, which matches the called workflow's filename and
environment, so the publisher configuration is unchanged.

Permissions in backend-release move from the workflow to the jobs: the
release job writes contents; the Docker call writes contents (the compose
file attached to the release) and packages; the PyPI call gets an OIDC token.
2026-09-08 18:46:20 +01:00
Alex 55239fc345 Merge pull request #2743 from arc53/feat/pypi-package
feat: publish the backend to PyPI as docsgpt
2026-09-08 18:31:48 +01:00