Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d82aef4
docs(plugin): add remote distribution spec and plan
zerob13 Sep 16, 2026
6fae5a6
feat(plugin): add distribution catalog contracts
zerob13 Sep 16, 2026
0dbd2a1
feat(plugin): add remote catalog installer
zerob13 Sep 16, 2026
9b3d3c2
feat(ocr): add remote runtime asset install
zerob13 Sep 16, 2026
ab2cd63
feat(app): wire remote distribution services
zerob13 Sep 16, 2026
37d2a81
feat(renderer): add runtime download UX
zerob13 Sep 16, 2026
3861bc2
build(plugin): add catalog release tooling
zerob13 Sep 16, 2026
605cc4d
fix(renderer): use existing lucide download icon
zerob13 Sep 16, 2026
6035f0a
fix(plugin): check enable result before remote install
zerob13 Sep 16, 2026
0ae077f
fix(plugin): reject plugin id mismatch before install
zerob13 Sep 16, 2026
acbd566
fix(ocr): cap payload size and verify per root
zerob13 Sep 16, 2026
b3592c0
fix(app): drain installers and sweep stale staging
zerob13 Sep 16, 2026
a297831
fix(plugin): require https urls and platform pins
zerob13 Sep 16, 2026
b8e5dfd
fix(renderer): fix i18n interpolation and state hydration
zerob13 Sep 16, 2026
7ac42a1
feat(plugin): add cua/ocr unbundle switches
zerob13 Sep 16, 2026
86413bb
chore(arch): refresh renderer boundary baseline
zerob13 Sep 16, 2026
144e261
Merge remote-tracking branch 'origin/dev' into feature/pluginize-heav…
zerob13 Sep 16, 2026
2fd73ae
chore(arch): sync renderer baseline after dev merge
zerob13 Sep 16, 2026
e916c9e
build(ci): test unbundled cua and ocr distribution
zerob13 Sep 16, 2026
8a78aa4
test(ci): pin conditional ocr smoke contract
zerob13 Sep 17, 2026
673f246
build(ci): upload unbundled dcplugin artifacts
zerob13 Sep 17, 2026
61811ad
fix(ocr): publish pending version on manual install progress
zerob13 Sep 17, 2026
d5c7ff9
build(ci): generate installable ocr runtime payload
zerob13 Sep 17, 2026
e6c9531
fix(ci): make staged ocr node runtime optional
zerob13 Sep 17, 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
55 changes: 54 additions & 1 deletion .github/workflows/_package-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ permissions:
env:
CI: 'true'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
# '1' builds cua for remote distribution instead of shipping it inside the
# app, and "Verify bundled plugins" then asserts its absence. Flipped for the
# unbundled distribution test; keep published catalog entries in sync before
# this ships.
DEEPCHAT_UNBUNDLE_CUA: '1'
# '1' ships without the bundled OCR runtime: the smoke step is replaced by
# an absence assertion and package-manifest tolerates the missing report.
DEEPCHAT_UNBUNDLE_OCR: '1'

jobs:
package:
Expand Down Expand Up @@ -142,6 +150,7 @@ jobs:
--resources-path "dist/${{ env.UNPACKED_DIRECTORY }}/resources"

- name: Verify packaged Light OCR offline
if: env.DEEPCHAT_UNBUNDLE_OCR != '1'
run: |
sudo unshare --net --setuid "$(id -u)" --setgid "$(id -g)" -- \
env HOME="$HOME" PATH="$PATH" pnpm run smoke:light-ocr -- \
Expand All @@ -153,6 +162,33 @@ jobs:
--require-execution \
--require-peak-rss

- name: Verify packaged OCR runtime is absent
if: env.DEEPCHAT_UNBUNDLE_OCR == '1'
run: |
ocr_runtime="dist/${UNPACKED_DIRECTORY}/resources/app.asar.unpacked/runtime/ocr"
if [[ -e "${ocr_runtime}" ]]; then
echo "::error::Bundled OCR runtime must not ship inside the app: ${ocr_runtime}"
exit 1
fi
echo "Verified OCR runtime is absent (distributed remotely)"

# Produces the remotely distributed OCR runtime payload plus a catalog
# that pins every staged artifact. The loopback base URL makes the
# catalog directly usable for L1-style local testing: serve
# build/remote-plugins over http and point DEEPCHAT_PLUGIN_CATALOG at
# plugin-catalog.json in a dev build.
- name: Stage and package the remote OCR runtime payload
if: env.DEEPCHAT_UNBUNDLE_OCR == '1'
run: |
node scripts/stage-ocr-runtime.mjs --platform linux --arch "${TARGET_ARCH}" --out build/ocr-runtime-staging
mkdir -p build/remote-plugins
node scripts/plugin-catalog.mjs generate \
--runtime-dir "build/ocr-runtime-staging/resources/app.asar.unpacked/runtime" \
--artifacts-dir build/remote-plugins \
--catalog build/remote-plugins/plugin-catalog.json \
--base-url "http://127.0.0.1:8787/" \
--write

- name: Verify bundled CUA plugin
if: inputs.arch == 'x64'
run: >-
Expand All @@ -170,6 +206,19 @@ jobs:
--arch "${{ inputs.arch }}"
--plugin-root "dist/${{ env.UNPACKED_DIRECTORY }}/resources/app.asar.unpacked/plugins"

- name: Upload remote plugin packages
if: env.DEEPCHAT_UNBUNDLE_CUA == '1' || env.DEEPCHAT_UNBUNDLE_OCR == '1'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: deepchat-remote-plugins-linux-${{ inputs.arch }}
path: |
build/remote-plugins/*.dcplugin
build/remote-plugins/*.zip
build/remote-plugins/plugin-catalog.json
if-no-files-found: warn
retention-days: 7
overwrite: true

- name: Compare installer sizes
if: inputs.enforce-installer-size
run: |
Expand All @@ -187,12 +236,16 @@ jobs:
if [[ "${ENFORCE_INSTALLER_SIZE}" == 'true' ]]; then
size_report=(--installer-size-report "dist/package-size-linux-${TARGET_ARCH}.json")
fi
ocr_report=()
if [[ "${DEEPCHAT_UNBUNDLE_OCR}" != '1' ]]; then
ocr_report=(--report "dist/light-ocr-smoke-linux-${TARGET_ARCH}.json")
fi
node scripts/ci/package-manifest.mjs \
--platform linux \
--arch "${TARGET_ARCH}" \
--source-sha "${SOURCE_SHA}" \
--purpose "${PACKAGE_PURPOSE}" \
--report "dist/light-ocr-smoke-linux-${TARGET_ARCH}.json" \
"${ocr_report[@]}" \
--workflow-run-id "${GITHUB_RUN_ID}" \
--workflow-run-attempt "${GITHUB_RUN_ATTEMPT}" \
"${size_report[@]}"
Expand Down
60 changes: 58 additions & 2 deletions .github/workflows/_package-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ permissions:
env:
CI: 'true'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
# '1' builds cua for remote distribution instead of shipping it inside the
# app, and "Verify bundled plugins" then asserts its absence. Flipped for the
# unbundled distribution test; keep published catalog entries in sync before
# this ships.
DEEPCHAT_UNBUNDLE_CUA: '1'
# '1' ships without the bundled OCR runtime: the smoke steps are replaced by
# an absence assertion and package-manifest tolerates the missing reports.
DEEPCHAT_UNBUNDLE_OCR: '1'

jobs:
package:
Expand Down Expand Up @@ -190,6 +198,7 @@ jobs:
# Verification packages are deliberately unsigned and never leave this runner as installers.
# This keeps Apple credentials out of fork PR code while retaining native package coverage.
- name: Verify packaged Light OCR offline
if: env.DEEPCHAT_UNBUNDLE_OCR != '1'
run: |
sandbox-exec -p '(version 1) (allow default) (deny network*)' \
pnpm run smoke:light-ocr -- \
Expand All @@ -201,6 +210,7 @@ jobs:
--require-execution

- name: Verify packaged Light OCR performance
if: env.DEEPCHAT_UNBUNDLE_OCR != '1'
run: |
pnpm run smoke:light-ocr -- \
--platform darwin \
Expand All @@ -212,12 +222,52 @@ jobs:
--require-peak-rss \
--skip-compression

- name: Verify packaged OCR runtime is absent
if: env.DEEPCHAT_UNBUNDLE_OCR == '1'
run: |
ocr_runtime="${APP_DIRECTORY}/Contents/Resources/app.asar.unpacked/runtime/ocr"
if [[ -e "${ocr_runtime}" ]]; then
echo "::error::Bundled OCR runtime must not ship inside the app: ${ocr_runtime}"
exit 1
fi
echo "Verified OCR runtime is absent (distributed remotely)"

# Produces the remotely distributed OCR runtime payload plus a catalog
# that pins every staged artifact. The loopback base URL makes the
# catalog directly usable for L1-style local testing: serve
# build/remote-plugins over http and point DEEPCHAT_PLUGIN_CATALOG at
# plugin-catalog.json in a dev build.
- name: Stage and package the remote OCR runtime payload
if: env.DEEPCHAT_UNBUNDLE_OCR == '1'
run: |
node scripts/stage-ocr-runtime.mjs --platform darwin --arch "${TARGET_ARCH}" --out build/ocr-runtime-staging
mkdir -p build/remote-plugins
node scripts/plugin-catalog.mjs generate \
--runtime-dir "build/ocr-runtime-staging/DeepChat.app/Contents/Resources/app.asar.unpacked/runtime" \
--artifacts-dir build/remote-plugins \
--catalog build/remote-plugins/plugin-catalog.json \
--base-url "http://127.0.0.1:8787/" \
--write

- name: Verify bundled plugins
run: |
plugin_root="${APP_DIRECTORY}/Contents/Resources/app.asar.unpacked/plugins"
pnpm run plugin:verify -- --name cua --platform darwin --arch "${TARGET_ARCH}" --plugin-root "${plugin_root}"
pnpm run plugin:verify -- --name feishu --platform darwin --arch "${TARGET_ARCH}" --plugin-root "${plugin_root}"

- name: Upload remote plugin packages
if: env.DEEPCHAT_UNBUNDLE_CUA == '1' || env.DEEPCHAT_UNBUNDLE_OCR == '1'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: deepchat-remote-plugins-darwin-${{ inputs.arch }}
path: |
build/remote-plugins/*.dcplugin
build/remote-plugins/*.zip
build/remote-plugins/plugin-catalog.json
if-no-files-found: warn
retention-days: 7
overwrite: true

- name: Compare installer sizes
if: inputs.enforce-installer-size
run: |
Expand All @@ -236,14 +286,20 @@ jobs:
if [[ "${ENFORCE_INSTALLER_SIZE}" == 'true' ]]; then
size_report=(--installer-size-report "dist/package-size-darwin-${TARGET_ARCH}.json")
fi
ocr_report=()
if [[ "${DEEPCHAT_UNBUNDLE_OCR}" != '1' ]]; then
ocr_report=(
--report "dist/light-ocr-smoke-darwin-${TARGET_ARCH}-offline.json"
--report "dist/light-ocr-smoke-darwin-${TARGET_ARCH}.json"
)
fi
node scripts/ci/package-manifest.mjs \
--platform darwin \
--arch "${TARGET_ARCH}" \
--source-sha "${SOURCE_SHA}" \
--purpose "${PACKAGE_PURPOSE}" \
--mac-app-path "${APP_DIRECTORY}" \
--report "dist/light-ocr-smoke-darwin-${TARGET_ARCH}-offline.json" \
--report "dist/light-ocr-smoke-darwin-${TARGET_ARCH}.json" \
"${ocr_report[@]}" \
--workflow-run-id "${GITHUB_RUN_ID}" \
--workflow-run-attempt "${GITHUB_RUN_ATTEMPT}" \
"${size_report[@]}"
Expand Down
57 changes: 56 additions & 1 deletion .github/workflows/_package-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ permissions:
env:
CI: 'true'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
# '1' builds cua for remote distribution instead of shipping it inside the
# app, and "Verify bundled plugins" then asserts its absence. Flipped for the
# unbundled distribution test; keep published catalog entries in sync before
# this ships.
DEEPCHAT_UNBUNDLE_CUA: '1'
# '1' ships without the bundled OCR runtime: the smoke step is replaced by
# an absence assertion and package-manifest tolerates the missing report.
DEEPCHAT_UNBUNDLE_OCR: '1'

jobs:
package:
Expand Down Expand Up @@ -150,6 +158,7 @@ jobs:
# Blocks outbound traffic from the runner Node used by smoke-light-ocr.
# That Node version must remain 24.18.0 to match runtime-versions.json.
- name: Verify packaged Light OCR offline
if: env.DEEPCHAT_UNBUNDLE_OCR != '1'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
Expand All @@ -170,13 +179,55 @@ jobs:
Remove-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
}

- name: Verify packaged OCR runtime is absent
if: env.DEEPCHAT_UNBUNDLE_OCR == '1'
shell: bash
run: |
ocr_runtime="dist/${UNPACKED_DIRECTORY}/resources/app.asar.unpacked/runtime/ocr"
if [[ -e "${ocr_runtime}" ]]; then
echo "::error::Bundled OCR runtime must not ship inside the app: ${ocr_runtime}"
exit 1
fi
echo "Verified OCR runtime is absent (distributed remotely)"

# Produces the remotely distributed OCR runtime payload plus a catalog
# that pins every staged artifact. The loopback base URL makes the
# catalog directly usable for L1-style local testing: serve
# build/remote-plugins over http and point DEEPCHAT_PLUGIN_CATALOG at
# plugin-catalog.json in a dev build.
- name: Stage and package the remote OCR runtime payload
if: env.DEEPCHAT_UNBUNDLE_OCR == '1'
shell: bash
run: |
node scripts/stage-ocr-runtime.mjs --platform win32 --arch "${TARGET_ARCH}" --out build/ocr-runtime-staging
mkdir -p build/remote-plugins
node scripts/plugin-catalog.mjs generate \
--runtime-dir "build/ocr-runtime-staging/resources/app.asar.unpacked/runtime" \
--artifacts-dir build/remote-plugins \
--catalog build/remote-plugins/plugin-catalog.json \
--base-url "http://127.0.0.1:8787/" \
--write

- name: Verify bundled plugins
shell: bash
run: |
plugin_root="dist/${UNPACKED_DIRECTORY}/resources/app.asar.unpacked/plugins"
pnpm run plugin:verify -- --name cua --platform win32 --arch "${TARGET_ARCH}" --plugin-root "${plugin_root}"
pnpm run plugin:verify -- --name feishu --platform win32 --arch "${TARGET_ARCH}" --plugin-root "${plugin_root}"

- name: Upload remote plugin packages
if: env.DEEPCHAT_UNBUNDLE_CUA == '1' || env.DEEPCHAT_UNBUNDLE_OCR == '1'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: deepchat-remote-plugins-win32-${{ inputs.arch }}
path: |
build/remote-plugins/*.dcplugin
build/remote-plugins/*.zip
build/remote-plugins/plugin-catalog.json
if-no-files-found: warn
retention-days: 7
overwrite: true

- name: Compare installer sizes
if: inputs.enforce-installer-size
shell: bash
Expand All @@ -196,12 +247,16 @@ jobs:
if [[ "${ENFORCE_INSTALLER_SIZE}" == 'true' ]]; then
size_report=(--installer-size-report "dist/package-size-win32-${TARGET_ARCH}.json")
fi
ocr_report=()
if [[ "${DEEPCHAT_UNBUNDLE_OCR}" != '1' ]]; then
ocr_report=(--report "dist/light-ocr-smoke-win32-${TARGET_ARCH}.json")
fi
node scripts/ci/package-manifest.mjs \
--platform win32 \
--arch "${TARGET_ARCH}" \
--source-sha "${SOURCE_SHA}" \
--purpose "${PACKAGE_PURPOSE}" \
--report "dist/light-ocr-smoke-win32-${TARGET_ARCH}.json" \
"${ocr_report[@]}" \
--workflow-run-id "${GITHUB_RUN_ID}" \
--workflow-run-attempt "${GITHUB_RUN_ATTEMPT}" \
"${size_report[@]}"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ build/macx64.json
build/winarm.json
build/winx64.json
build/bundled-plugins/
build/remote-plugins/
build/managed-helpers/
runtime/**/*
plugins/*/runtime/**/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,10 @@
"file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue",
"specifier": "@/stores/uiSettingsStore"
},
{
"file": "src/renderer/settings/components/OcrSettings.vue",
"specifier": "@/components/plugins/RuntimeInstallControls.vue"
},
{
"file": "src/renderer/settings/components/OllamaProviderSettingsDetail.vue",
"specifier": "@/components/settings/ModelConfigItem.vue"
Expand Down Expand Up @@ -564,5 +568,5 @@
"specifier": "@/i18n/bootstrap"
}
],
"settingsToChatAppImportCount": 128
"settingsToChatAppImportCount": 129
}
Loading
Loading