@foggy-projects/deepseek-harness-plugin 0.4.0-rc.2 → 0.4.1-rc.1

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.
package/README.md CHANGED
@@ -3,31 +3,55 @@
3
3
  This package is the lightweight DeepSeek Harness Bundle for Foggy's Java data
4
4
  analysis engine. Installing the Bundle adds a native Foggy tab under **Settings
5
5
  → Plugins**. The private Python runtime, Launcher, and isolated CLI environment
6
- are downloaded only when the user selects **Initialize Foggy**.
6
+ are downloaded only when the user selects **Initialize and start** (or the
7
+ equivalent component action). Existing installations are not silently changed
8
+ when the plugin package is upgraded.
7
9
 
8
- DeepSeek Harness uses a system Node.js (`^22.19.0 || >=24.0.0`) and Foggy's Java
9
- Launcher uses a system Java 17+. Foggy manages its own pinned Python 3.12 runtime
10
+ The `0.4.1-rc.1` compatibility candidate targets DeepSeek Harness `0.1.5-rc.2`
11
+ and requires a system Node.js `>=24.0.0`. Foggy's Java Launcher uses a system
12
+ Java 17+. Foggy manages its own pinned Python 3.12 runtime
10
13
  inside the per-user component directory; it does not require a system Python,
11
14
  modify `PATH`, or register Python globally. Advanced users may explicitly set
12
15
  `FOGGY_PYTHON` to a compatible Python 3.11+ executable.
13
16
 
14
- ## Local beta installation
17
+ ## Local release-candidate installation
15
18
 
16
19
  ```powershell
17
- dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-rc.2.tgz
20
+ dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.1-rc.1.tgz
18
21
  ```
19
22
 
20
23
  Restart `dsh web`, use the browser it opens (or the complete printed URL,
21
24
  including `?token=...`), open Settings → Plugins → Foggy Data Analysis, and
22
25
  initialize the components from there. Do not share the launch-token URL.
23
26
 
24
- After npm beta publication, the corresponding one-line install is:
27
+ For the DSH 0.1.5-rc.2 compatibility pre-release, install the exact candidate
28
+ so the stable `beta` channel is not changed:
29
+
30
+ ```powershell
31
+ dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@0.4.1-rc.1
32
+ ```
33
+
34
+ The published candidate is also available through the dedicated `dsh015` npm
35
+ dist-tag:
36
+
37
+ ```powershell
38
+ dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@dsh015
39
+ ```
40
+
41
+ For the existing stable beta channel, the corresponding one-line install is:
25
42
 
26
43
  ```powershell
27
44
  dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@beta
28
45
  ```
29
46
 
30
- DeepSeek Harness 0.1.2-rc.1 applies pnpm's minimum-release-age policy to its
47
+ After updating the plugin package, open **Settings → Plugins → Foggy Data
48
+ Analysis** and use **Update components** (or **Update and start**) to download
49
+ the pinned CLI, Launcher, and Skills. The settings page shows current and target
50
+ versions. Updates are blocked while Runtime is running so the active Java
51
+ process and the next-launcher state cannot diverge. Component-specific repair
52
+ actions remain available under **Advanced repair**.
53
+
54
+ DeepSeek Harness 0.1.5-rc.2 applies pnpm's minimum-release-age policy to its
31
55
  profile lockfile. An upgrade performed shortly after publication can therefore
32
56
  fail while naming either the new plugin or the previously installed Beta. This
33
57
  is a DSH profile-policy failure, not a damaged Foggy package. Use the exact
@@ -104,10 +128,21 @@ rollback plan.
104
128
 
105
129
  See [`docs/PUBLIC-BETA-READINESS.md`](./docs/PUBLIC-BETA-READINESS.md) for the
106
130
  tested public Beta scope, release gates, and stable-release blockers.
131
+ The DSH 0.1.5-rc.2 compatibility candidate is documented in
132
+ [`docs/RELEASE-CANDIDATE-0.4.1-DSH-0.1.5-RC.1.md`](./docs/RELEASE-CANDIDATE-0.4.1-DSH-0.1.5-RC.1.md).
107
133
  Native Windows acceptance instructions are in
108
134
  [`docs/WINDOWS-BETA-ACCEPTANCE.md`](./docs/WINDOWS-BETA-ACCEPTANCE.md); database
109
135
  credentials intentionally remain outside the public repository.
110
136
 
137
+ ## Community discovery
138
+
139
+ DeepSeek Harness does not currently publish a first-party plugin marketplace.
140
+ For discoverability, this repository is intended to carry the GitHub
141
+ [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic. Independent community
142
+ directories and their submission rules are tracked in
143
+ [`docs/COMMUNITY-MARKETPLACES.md`](./docs/COMMUNITY-MARKETPLACES.md); inclusion in
144
+ any directory is community curation, not an endorsement by DeepSeek or Foggy.
145
+
111
146
  ## Linux and WSL2 experience
112
147
 
113
148
  Ubuntu and WSL2 users can use the checked-in preflighted installer under
@@ -0,0 +1,64 @@
1
+ # DeepSeek Harness 社区插件目录准备
2
+
3
+ 状态记录:2026-09-06。DeepSeek Harness 官方页面仍标注为开发者预览版,
4
+ 官方公开入口是源码、开发者文档、GitHub `dsh-plugin` Topic 和社区讨论,
5
+ 目前没有看到官方一方插件市场或官方审核收录入口。
6
+
7
+ ## 推荐顺序
8
+
9
+ ### 1. `awesome-dsh-plugin`(优先提交)
10
+
11
+ - 目录仓库:<https://github.com/awesome-dsh-plugin/awesome-dsh-plugin>
12
+ - 提交方式:向该仓库提交 PR,只新增一个
13
+ `data/plugins/foggy-projects__foggy-deepseek-harness-plugin.yml` 文件。
14
+ - 主要要求:真实可用代码、仓库至少存在 1 天、添加 `dsh-plugin` Topic、
15
+ 描述准确且不使用夸张营销语句、根包声明 `dsh.bundle`。
16
+ - 可选增强:在 npm 发布包中保留正确的 `repository` 字段;如提供 GitHub
17
+ Release `.tgz`,可在条目中声明固定版本的 `tarball`。
18
+ - 评审说明:CI 检查格式和 manifest,维护者会阅读目标仓库;收录不等于
19
+ 安全审计或官方认证。
20
+
21
+ 建议条目草稿:
22
+
23
+ ```yaml
24
+ url: https://github.com/foggy-projects/foggy-deepseek-harness-plugin
25
+ name: foggy-projects/foggy-deepseek-harness-plugin
26
+ category: tools
27
+ description:
28
+ en: Foggy Java data-analysis integration for DeepSeek Harness with a managed Runtime, CLI, Launcher, onboarding Skills, and semantic-layer workflows.
29
+ zh: 在 DeepSeek Harness 中管理 Foggy Java 数据分析 Runtime、CLI、Launcher 与 onboarding/语义查询 Skill,支持数据库接入和语义层建模。
30
+ ```
31
+
32
+ ### 2. `dsh-market`(市场应用,目录来自上游)
33
+
34
+ - 项目:<https://github.com/dsh-market/dsh-market>
35
+ - 它是可安装到 DSH 设置页的社区市场应用,本身不是目录数据源。
36
+ - 目录来自 `awesome-dsh-plugin`;其说明要求向上游目录提交 PR,
37
+ 市场会自动同步,通常不需要重复提交到 `dsh-market`。
38
+ - 适合验证“一键安装”体验,但它不是 DeepSeek 官方产品。
39
+
40
+ ### 3. `DSH-Plugins-Marketplace`(Topic 自动聚合)
41
+
42
+ - 项目:<https://github.com/bradeGithub/DSH-Plugins-Marketplace>
43
+ - 通过 GitHub `dsh-plugin` Topic 自动聚合,项目讨论中说明无需手工 PR,
44
+ Topic 出现后等待索引刷新即可。
45
+ - 它会安装到 DSH Web profile,适合做第二个发现入口;必须标注为第三方市场。
46
+
47
+ ### 4. `dshplugin.app`(独立目录与证据页)
48
+
49
+ - 目录:<https://dshplugin.app/>
50
+ - 支持提交公开 GitHub 仓库,随后进行源码、manifest、依赖、安装路径和兼容性
51
+ 信号分析。
52
+ - 页面明确声明其为独立社区项目,不隶属于 DeepSeek;“Indexed”不等于安全认证。
53
+ - 适合补充兼容性说明和安全信号展示,但不应把它描述为官方市场。
54
+
55
+ ## 当前可执行清单
56
+
57
+ 1. 保持当前 npm `rc` 预发布与 GitHub Release,不切换 `latest`。
58
+ 2. 给 GitHub 仓库添加 `dsh-plugin` Topic;可同时保留
59
+ `deepseek-harness`、`data-analysis`、`semantic-layer` 等描述性 Topic。
60
+ 3. 确认下一次 npm 发布继续携带 `repository`、`homepage`、`bugs` 和
61
+ `dsh-plugin` keywords,便于目录把 npm 包与源码仓库关联。
62
+ 4. 优先向 `awesome-dsh-plugin` 提交上面的单文件 PR,再等待 `dsh-market`
63
+ 同步;随后向 `dshplugin.app` 提交公开仓库 URL。
64
+ 5. 对外文案统一使用“社区目录/第三方市场”,不使用“官方市场/官方认证”。
@@ -0,0 +1,18 @@
1
+ # Foggy DeepSeek Harness plugin 0.4.0-rc.3 release candidate
2
+
3
+ Assessment date: 2026-09-06
4
+
5
+ ## Delta from rc.2
6
+
7
+ - Add a single **Initialize and start** action for first use.
8
+ - Add **Update components** and **Update and start** actions for managed CLI,
9
+ Launcher, and Skill assets after a plugin-package upgrade.
10
+ - Show current versus target component versions and an explicit update banner.
11
+ - Block component update/repair while Runtime is running; the active Launcher
12
+ process must not diverge from the persisted next-launcher version.
13
+ - Keep Refresh, diagnostics, and component-specific repair actions in the
14
+ secondary/advanced action areas.
15
+ - Add public documentation, plugin repository, release, and Runtime project
16
+ links to the settings page.
17
+
18
+ No Java engine or `foggy-runtime-cli` source was changed.
@@ -0,0 +1,60 @@
1
+ # Foggy DeepSeek Harness plugin 0.4.1-rc.1
2
+
3
+ This release candidate is the compatibility line for DeepSeek Harness
4
+ `0.1.5-rc.2`. It is published as a separate pre-release so the existing
5
+ `0.4.0-rc.3`/`beta` installation path remains unchanged while the new Harness
6
+ client is evaluated.
7
+
8
+ ## Compatibility changes
9
+
10
+ - Align DSH peer packages with `0.1.5-rc.2` and Cordis `4.0.2`.
11
+ - Remove the obsolete `@deepseek-ai/dsh-client-runtime` injection. DSH
12
+ `0.1.5-rc.2` does not ship that package and the plugin does not use its API.
13
+ - Require Node.js `>=24.0.0`. DSH 0.1.5 uses modern ESM entry-point behavior;
14
+ the plugin fails early with a visible prerequisite error on older Node lines.
15
+ - Pin the onboarding manifest to DSH `0.1.5-rc.2` and keep the Foggy CLI,
16
+ Launcher, and Skills pins unchanged.
17
+
18
+ The plugin's remote service, native Skill provider, locale registration, and
19
+ Settings → Plugins tab continue to use the same public APIs. The DSH 0.1.5
20
+ web client adds file/sidebar features, but none of those changes are required
21
+ by Foggy's integration.
22
+
23
+ ## Installation
24
+
25
+ Use Node.js 24 or newer and install the exact candidate in a separate profile:
26
+
27
+ ```powershell
28
+ dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@0.4.1-rc.1
29
+ dsh web
30
+ ```
31
+
32
+ The npm `dsh015` dist-tag points to this candidate. Existing users of the
33
+ stable `beta` tag are not moved automatically.
34
+
35
+ ## Verification gate
36
+
37
+ The candidate must pass all of the following before it replaces the stable
38
+ baseline:
39
+
40
+ 1. npm package shape and Node syntax checks;
41
+ 2. the repository onboarding/unit test suite;
42
+ 3. a clean DSH `0.1.5-rc.2` profile can add the package without an impossible
43
+ `dsh-client-runtime` peer resolution;
44
+ 4. `dsh --version` reports `0.1.5-rc.2` and `dsh web --no-open` starts under
45
+ Node 24;
46
+ 5. the web Settings → Plugins page loads Foggy's tab, native Skills are
47
+ discoverable, and the plugin status remote responds;
48
+ 6. Foggy initialization remains development-only and does not restart a
49
+ healthy Runtime during datasource/model onboarding.
50
+
51
+ The candidate does not change Foggy Runtime/Launcher binaries or production
52
+ deployment policy. If a DSH profile reports
53
+ `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`, rebuild only that profile's lockfile
54
+ and retry the exact add command; do not disable pnpm's policy globally.
55
+
56
+ ## Upstream references
57
+
58
+ - [DeepSeek Harness v0.1.5-rc.2 release](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.5-rc.2)
59
+ - [DeepSeek Harness release history](https://github.com/deepseek-ai/deepseek-harness/releases)
60
+ - [Node entry-point compatibility discussion](https://github.com/deepseek-ai/deepseek-harness/discussions/6124)
@@ -5,7 +5,7 @@ It has been designed for Ubuntu 22.04/24.04 x86_64 and WSL2.
5
5
 
6
6
  ## Prerequisites
7
7
 
8
- - Node.js `^22.19.0` or `>=24.0.0`
8
+ - Node.js `>=24.0.0` (the DSH 0.1.5-rc.2 compatibility candidate)
9
9
  - Java 17 or newer
10
10
  - Git, npm, and `tar`
11
11
  - At least 4 GiB free disk space and 2 GiB available memory; 4 GiB memory is recommended
@@ -22,7 +22,7 @@ bash experience/linux/prepare.sh --dry-run
22
22
  bash experience/linux/prepare.sh
23
23
  ```
24
24
 
25
- The script installs pinned pnpm and DeepSeek Harness versions inside
25
+ The script installs pinned pnpm and DeepSeek Harness 0.1.5-rc.2 inside
26
26
  `~/.local/share/foggy-deepseek-harness-experience`, clones and packs the public Foggy Bundle, and
27
27
  adds it to a clean DSH web profile. It does not use `sudo`, install operating-system packages, write
28
28
  model credentials, initialize Foggy components, or start the web process.
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- DSH_VERSION="0.1.2-rc.1"
4
+ DSH_VERSION="0.1.5-rc.2"
5
5
  PNPM_VERSION="11.7.0"
6
- PLUGIN_VERSION="0.4.0-rc.1"
6
+ PLUGIN_VERSION="0.4.1-rc.1"
7
7
  PLUGIN_REF="v${PLUGIN_VERSION}"
8
8
  PLUGIN_REPOSITORY="https://github.com/foggy-projects/foggy-deepseek-harness-plugin.git"
9
9
 
@@ -73,8 +73,8 @@ require_command git
73
73
  require_command df
74
74
  require_command tar
75
75
 
76
- node -e 'const [major, minor] = process.versions.node.split(".").map(Number); if (!((major === 22 && minor >= 19) || major >= 24)) process.exit(1)' \
77
- || fail "Node ^22.19.0 or >=24 required; detected $(node --version 2>/dev/null || echo unknown)"
76
+ node -e 'const [major] = process.versions.node.split(".").map(Number); if (major < 24) process.exit(1)' \
77
+ || fail "Node >=24 required for DeepSeek Harness 0.1.5-rc.2; detected $(node --version 2>/dev/null || echo unknown)"
78
78
 
79
79
  java_major="$(java -version 2>&1 | awk -F'[\".]' '/version/ { print $2; exit }')"
80
80
  [[ "$java_major" =~ ^[0-9]+$ ]] || fail "could not detect Java version"
package/lib/client.js CHANGED
@@ -35,7 +35,7 @@ window.__ModuleLoader__.load({
35
35
  package: '@foggy-projects/deepseek-harness-plugin',
36
36
  descriptors: [
37
37
  ...[
38
- 'status', 'plan', 'initialize', 'repair', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill', 'repairSemanticQuerySkill',
38
+ 'status', 'plan', 'initialize', 'initializeAndStart', 'repair', 'updateComponents', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill', 'repairSemanticQuerySkill',
39
39
  'migrateProfiles', 'diagnostics', 'runtimeStart', 'runtimeStop',
40
40
  ].map((method) => descriptor(method)),
41
41
  descriptor('saveRuntimeSettings', [{
@@ -54,7 +54,7 @@ window.__ModuleLoader__.load({
54
54
  const zh = {
55
55
  tab: 'Foggy 数据分析',
56
56
  title: 'Foggy Java 数据分析引擎',
57
- intro: '在 DeepSeek Harness 内管理 Foggy 私有 Python、CLI、Launcher、Skills 和本地 Runtime。大型组件在首次初始化时按需下载。',
57
+ intro: '在 DeepSeek Harness 内管理 Foggy 私有 Python、CLI、Launcher、Skills 和本地 Runtime。大型组件按需下载,插件升级后可在此处一键更新。',
58
58
  refresh: '刷新状态',
59
59
  loading: '正在读取 Foggy 状态…',
60
60
  failed: '暂时无法读取 Foggy 状态。',
@@ -73,11 +73,20 @@ window.__ModuleLoader__.load({
73
73
  java: 'Java',
74
74
  installed: '已安装',
75
75
  missing: '未安装',
76
+ upToDate: '最新',
77
+ updateAvailable: '有更新',
78
+ installedVersion: '当前版本',
79
+ targetVersion: '目标版本',
76
80
  available: '可用',
77
81
  unavailable: '不可用',
78
82
  initialize: '初始化 Foggy',
79
- repair: '重新下载 / 修复',
80
- repairTitle: '分组件检查与修复',
83
+ initializeAndStart: '初始化并启动',
84
+ initializeAndStartDone: 'Foggy 已初始化,Runtime 已启动。',
85
+ updateComponents: '更新组件',
86
+ updateAndStart: '更新并启动',
87
+ updateAndStartDone: '组件已更新,Runtime 已启动。',
88
+ repair: '修复组件',
89
+ repairTitle: '高级修复',
81
90
  repairPython: '检查 / 修复私有 Python',
82
91
  repairCli: '检查 / 修复 CLI',
83
92
  repairLauncher: '检查 / 修复 Launcher',
@@ -87,6 +96,16 @@ window.__ModuleLoader__.load({
87
96
  diagnosticsSaved: '诊断报告已保存',
88
97
  start: '启动 Runtime',
89
98
  stop: '停止 Runtime',
99
+ updateBlockedRunning: 'Runtime 运行中,停止后才能更新组件。',
100
+ updateReady: '检测到组件更新;可点击“更新并启动”一次完成更新。',
101
+ updateNone: '所有托管组件均为当前版本。',
102
+ operationTimeout: '操作等待超时,请刷新状态或导出诊断报告。',
103
+ resourceTitle: '文档与项目',
104
+ docs: '使用文档',
105
+ project: '插件项目',
106
+ releases: '版本记录',
107
+ runtimeProject: 'Runtime 项目',
108
+ openExternal: '在新标签页打开',
90
109
  working: '操作进行中…',
91
110
  progressWorking: '正在初始化 Foggy…',
92
111
  progressPreflight: '检查运行环境',
@@ -160,7 +179,7 @@ window.__ModuleLoader__.load({
160
179
  const en = {
161
180
  tab: 'Foggy Data Analysis',
162
181
  title: 'Foggy Java Data Analysis Engine',
163
- intro: 'Manage Foggy private Python, CLI, Launcher, Skills, and the local Runtime inside DeepSeek Harness. Large components are downloaded on first initialization.',
182
+ intro: 'Manage Foggy private Python, CLI, Launcher, Skills, and the local Runtime inside DeepSeek Harness. Large components download on demand, and plugin upgrades can be applied here in one step.',
164
183
  refresh: 'Refresh status',
165
184
  loading: 'Reading Foggy status…',
166
185
  failed: 'Foggy status is temporarily unavailable.',
@@ -179,11 +198,20 @@ window.__ModuleLoader__.load({
179
198
  java: 'Java',
180
199
  installed: 'Installed',
181
200
  missing: 'Missing',
201
+ upToDate: 'Up to date',
202
+ updateAvailable: 'Update available',
203
+ installedVersion: 'Current',
204
+ targetVersion: 'Target',
182
205
  available: 'Available',
183
206
  unavailable: 'Unavailable',
184
207
  initialize: 'Initialize Foggy',
185
- repair: 'Re-download / Repair',
186
- repairTitle: 'Component checks and repair',
208
+ initializeAndStart: 'Initialize and start',
209
+ initializeAndStartDone: 'Foggy was initialized and Runtime started.',
210
+ updateComponents: 'Update components',
211
+ updateAndStart: 'Update and start',
212
+ updateAndStartDone: 'Components were updated and Runtime started.',
213
+ repair: 'Repair components',
214
+ repairTitle: 'Advanced repair',
187
215
  repairPython: 'Check / repair private Python',
188
216
  repairCli: 'Check / repair CLI',
189
217
  repairLauncher: 'Check / repair Launcher',
@@ -193,6 +221,16 @@ window.__ModuleLoader__.load({
193
221
  diagnosticsSaved: 'Diagnostics saved',
194
222
  start: 'Start Runtime',
195
223
  stop: 'Stop Runtime',
224
+ updateBlockedRunning: 'Runtime is running. Stop it before updating components.',
225
+ updateReady: 'Component updates are available. Use “Update and start” to finish in one step.',
226
+ updateNone: 'All managed components are up to date.',
227
+ operationTimeout: 'The operation timed out. Refresh the status or export diagnostics.',
228
+ resourceTitle: 'Documentation and project',
229
+ docs: 'User documentation',
230
+ project: 'Plugin project',
231
+ releases: 'Releases',
232
+ runtimeProject: 'Runtime project',
233
+ openExternal: 'Opens in a new tab',
196
234
  working: 'Operation in progress…',
197
235
  progressWorking: 'Initializing Foggy…',
198
236
  progressPreflight: 'Checking prerequisites',
@@ -267,15 +305,17 @@ window.__ModuleLoader__.load({
267
305
  .foggy-settings{width:100%;max-width:820px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:16px}
268
306
  .foggy-hero{border:1px solid var(--dsw-alias-border-l2);background:linear-gradient(135deg,color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,var(--dsw-alias-bg-layer-3)),var(--dsw-alias-bg-layer-3));border-radius:12px;padding:18px}
269
307
  .foggy-title-row{display:flex;align-items:center;gap:10px}.foggy-title-row h3{margin:0;font-size:18px;line-height:26px}.foggy-beta{font-size:11px;padding:2px 7px;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 14%,transparent);color:var(--dsw-alias-state-business-primary)}
270
- .foggy-intro{margin:8px 0 0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}.foggy-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
308
+ .foggy-intro{margin:8px 0 0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}.foggy-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.foggy-toolbar-main,.foggy-toolbar-secondary{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.foggy-toolbar-secondary{width:100%;padding-top:2px;border-top:1px solid var(--dsw-alias-border-l2)}
271
309
  .foggy-state{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600}.foggy-dot{width:9px;height:9px;border-radius:50%;background:var(--dsw-alias-label-tertiary)}.foggy-dot[data-state=ready],.foggy-dot[data-state=running]{background:var(--dsw-alias-state-success-primary)}.foggy-dot[data-state=degraded]{background:var(--dsw-alias-state-error-primary)}
272
- .foggy-actions{display:flex;gap:8px;flex-wrap:wrap}.foggy-button{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;padding:7px 12px;cursor:pointer}.foggy-button:hover{background:var(--dsw-alias-interactive-bg-hover)}.foggy-button:disabled{opacity:.55;cursor:not-allowed}.foggy-button-primary{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary);color:white}
310
+ .foggy-actions{display:flex;gap:8px;flex-wrap:wrap}.foggy-button{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;padding:7px 12px;cursor:pointer;transition:background-color .15s ease,box-shadow .15s ease,transform .15s ease}.foggy-button:hover{background:var(--dsw-alias-interactive-bg-hover)}.foggy-button:active{transform:translateY(1px)}.foggy-button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.foggy-button:disabled{opacity:.55;cursor:not-allowed}.foggy-button-primary{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary);color:white}
273
311
  .foggy-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.foggy-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:13px 14px}.foggy-card-head{display:flex;justify-content:space-between;gap:8px}.foggy-card strong{font-size:13px}.foggy-badge{font-size:11px;color:var(--dsw-alias-label-secondary)}.foggy-card code{display:block;margin-top:8px;color:var(--dsw-alias-label-tertiary);font-size:11px;overflow-wrap:anywhere}
274
312
  .foggy-progress{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:13px 14px}.foggy-progress-head{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px}.foggy-progress-head strong{font-weight:600}.foggy-progress-percent{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}.foggy-progress-track{height:8px;margin-top:10px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}.foggy-progress-fill{height:100%;border-radius:inherit;background:var(--dsw-alias-state-business-primary);transition:width .25s ease}.foggy-progress-meta{display:flex;justify-content:space-between;gap:12px;margin-top:8px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.foggy-progress-file{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
275
313
  .foggy-runtime-settings{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:14px}.foggy-runtime-settings h4{margin:0;font-size:13px}.foggy-runtime-settings-row{display:grid;grid-template-columns:minmax(150px,220px) minmax(0,1fr) auto;align-items:end;gap:10px;margin-top:10px}.foggy-field{display:flex;flex-direction:column;gap:6px}.foggy-field label{font-size:12px;font-weight:600}.foggy-input{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-variant-numeric:tabular-nums;padding:8px 10px;outline:none}.foggy-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}.foggy-input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-state-error-primary) 14%,transparent)}.foggy-setting-meta{display:flex;flex-direction:column;gap:4px;min-width:0;color:var(--dsw-alias-label-tertiary);font-size:11px}.foggy-setting-meta code{overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary)}
314
+ .foggy-update-banner{border:1px solid color-mix(in srgb,var(--dsw-alias-state-warning-primary) 55%,var(--dsw-alias-border-l2));background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 9%,transparent);border-radius:10px;padding:11px 13px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px}.foggy-update-banner strong{color:var(--dsw-alias-state-warning-primary)}.foggy-update-list{margin:6px 0 0;padding-left:18px;color:var(--dsw-alias-label-secondary)}
315
+ .foggy-resources{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}.foggy-resources h4{margin:0 0 9px;font-size:13px}.foggy-resource-links{display:flex;gap:8px;flex-wrap:wrap}.foggy-resource-link{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;color:var(--dsw-alias-label-secondary);font-size:12px;text-decoration:none}.foggy-resource-link:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.foggy-resource-link:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
276
316
  .foggy-error-panel{border:2px solid var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,var(--dsw-alias-bg-layer-3));border-radius:10px;padding:13px 14px;box-shadow:0 6px 18px color-mix(in srgb,var(--dsw-alias-state-error-primary) 13%,transparent)}.foggy-error-panel-head{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-state-error-primary);font-size:14px;font-weight:700}.foggy-error-icon{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-state-error-primary);color:white;font-size:14px}.foggy-error-panel p{margin:7px 0 0;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;overflow-wrap:anywhere}.foggy-error-help{color:var(--dsw-alias-state-error-primary)!important;font-weight:600}
277
317
  .foggy-message{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}.foggy-message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.foggy-paths,.foggy-next,.foggy-section{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}.foggy-paths h4,.foggy-next h4,.foggy-section h4{margin:0 0 8px;font-size:13px}.foggy-paths dl{display:grid;grid-template-columns:100px minmax(0,1fr);gap:6px 10px;margin:0}.foggy-paths dt{color:var(--dsw-alias-label-tertiary);font-size:12px}.foggy-paths dd{margin:0;min-width:0;overflow-wrap:anywhere;font-family:var(--ds-font-family-code);font-size:11px}.foggy-next p,.foggy-section p{margin:0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}.foggy-section-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}.foggy-onboarding-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.foggy-onboarding-head code{font-size:11px;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.foggy-step-list{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px;margin-top:10px}.foggy-step{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px;font-size:11px;color:var(--dsw-alias-label-tertiary)}.foggy-step[data-state=completed]{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 45%,var(--dsw-alias-border-l2));color:var(--dsw-alias-state-success-primary)}.foggy-step[data-state=failed],.foggy-step[data-state=invalid]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.foggy-warning{border:1px solid color-mix(in srgb,var(--dsw-alias-state-warning-primary) 55%,var(--dsw-alias-border-l2));background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 8%,transparent);border-radius:10px;padding:12px}.foggy-query-warning{margin-top:10px}.foggy-query-warning p+p{margin-top:5px}.foggy-query-warning code{overflow-wrap:anywhere}
278
- @media (width<=680px){.foggy-grid{grid-template-columns:minmax(0,1fr)}.foggy-runtime-settings-row{grid-template-columns:minmax(0,1fr)}.foggy-runtime-settings-row .foggy-button{width:100%}}
318
+ @media (width<=680px){.foggy-grid{grid-template-columns:minmax(0,1fr)}.foggy-runtime-settings-row{grid-template-columns:minmax(0,1fr)}.foggy-runtime-settings-row .foggy-button{width:100%}.foggy-toolbar-main{width:100%}.foggy-toolbar-main .foggy-button{flex:1}.foggy-resource-link{min-height:36px}}
279
319
  `
280
320
 
281
321
  if (typeof document !== 'undefined' && !document.querySelector('style[data-foggy-settings]')) {
@@ -295,16 +335,23 @@ window.__ModuleLoader__.load({
295
335
 
296
336
  function componentCard(label, component, kind, t) {
297
337
  const available = kind === 'runtime' ? component?.installed : component?.available
298
- const stateText = kind === 'runtime'
299
- ? t(available ? 'installed' : 'missing')
300
- : t(available ? 'available' : 'unavailable')
338
+ const stateText = component?.updateAvailable
339
+ ? t('updateAvailable')
340
+ : kind === 'runtime'
341
+ ? t(available ? 'installed' : 'missing')
342
+ : t(available ? 'available' : 'unavailable')
301
343
  const source = component?.source === 'managed'
302
344
  ? t('managedPrivate')
303
345
  : component?.source === 'override'
304
346
  ? t('configuredOverride')
305
347
  : null
306
348
  const detail = [
307
- component?.version || component?.output || '—',
349
+ component?.installedVersion
350
+ ? `${t('installedVersion')} ${component.installedVersion}`
351
+ : component?.version || component?.output || '—',
352
+ component?.targetVersion && component?.targetVersion !== component?.installedVersion
353
+ ? `${t('targetVersion')} ${component.targetVersion}`
354
+ : null,
308
355
  component?.minimum ? `≥${component.minimum}` : null,
309
356
  source,
310
357
  ].filter(Boolean).join(' · ')
@@ -321,6 +368,19 @@ window.__ModuleLoader__.load({
321
368
  })
322
369
  }
323
370
 
371
+ const componentLabels = {
372
+ cli: 'cli',
373
+ launcher: 'launcher',
374
+ analysisSkill: 'analysisSkill',
375
+ semanticQuerySkill: 'semanticQuerySkill',
376
+ }
377
+
378
+ function updateSummary(status, t) {
379
+ return Object.entries(status?.components || {})
380
+ .filter(([key, component]) => componentLabels[key] && component?.updateAvailable)
381
+ .map(([key, component]) => `${t(componentLabels[key])}: ${component.installedVersion || '—'} → ${component.targetVersion}`)
382
+ }
383
+
324
384
  function progressPanel(progress, t) {
325
385
  if (!progress) return null
326
386
  const percent = Math.max(0, Math.min(100, Number(progress.percent) || 0))
@@ -443,6 +503,7 @@ window.__ModuleLoader__.load({
443
503
  const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false, errorCode: null })
444
504
  const [portDraft, setPortDraft] = useState('')
445
505
  const [portDirty, setPortDirty] = useState(false)
506
+ const [sequenceBusy, setSequenceBusy] = useState(false)
446
507
 
447
508
  const refresh = useCallback(async () => {
448
509
  setView((current) => ({ ...current, phase: current.status ? 'ready' : 'loading', error: false }))
@@ -469,6 +530,7 @@ window.__ModuleLoader__.load({
469
530
  }, [refresh, view.status?.operation?.state])
470
531
 
471
532
  const run = async (method) => {
533
+ setSequenceBusy(true)
472
534
  setView((current) => ({ ...current, message: t('working'), error: false, errorCode: null }))
473
535
  try {
474
536
  const result = unwrap(await api[method](), method)
@@ -481,6 +543,44 @@ window.__ModuleLoader__.load({
481
543
  await refresh()
482
544
  } catch (error) {
483
545
  setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true, errorCode: 'REMOTE_ERROR' }))
546
+ } finally {
547
+ setSequenceBusy(false)
548
+ }
549
+ }
550
+
551
+ const waitForOperation = async (operationId) => {
552
+ for (let attempt = 0; attempt < 900; attempt += 1) {
553
+ const status = unwrap(await api.status(), 'status')
554
+ const operation = status.operation
555
+ if (operation?.id === operationId && operation.state !== 'running') return status
556
+ await new Promise((resolve) => setTimeout(resolve, 1000))
557
+ }
558
+ throw new Error(t('operationTimeout'))
559
+ }
560
+
561
+ const runSequence = async (methods, successMessage) => {
562
+ setSequenceBusy(true)
563
+ setView((current) => ({ ...current, message: t('working'), error: false, errorCode: null }))
564
+ try {
565
+ for (const method of methods) {
566
+ const result = unwrap(await api[method](), method)
567
+ if (result.success === false) {
568
+ setView((current) => ({ ...current, message: result.error?.message || t('actionFailed'), error: true, errorCode: result.error?.code || null }))
569
+ return
570
+ }
571
+ if (result.accepted && result.operation?.id) {
572
+ const completed = await waitForOperation(result.operation.id)
573
+ if (completed.operation?.state === 'failed') {
574
+ throw new Error(completed.operation.error || completed.operation.result?.error?.message || t('actionFailed'))
575
+ }
576
+ }
577
+ }
578
+ setView((current) => ({ ...current, message: successMessage, error: false, errorCode: null }))
579
+ } catch (error) {
580
+ setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true, errorCode: 'REMOTE_ERROR' }))
581
+ } finally {
582
+ setSequenceBusy(false)
583
+ await refresh()
484
584
  }
485
585
  }
486
586
 
@@ -513,7 +613,7 @@ window.__ModuleLoader__.load({
513
613
  ] })
514
614
 
515
615
  const status = view.status
516
- const busy = status.operation?.state === 'running'
616
+ const busy = sequenceBusy || status.operation?.state === 'running'
517
617
  const portNumber = Number(portDraft)
518
618
  const portValid = Number.isInteger(portNumber) && portNumber >= 1024 && portNumber <= 65535
519
619
  const operationError = status.operation?.state === 'failed'
@@ -545,29 +645,57 @@ window.__ModuleLoader__.load({
545
645
  ? t('portUnavailableHelp')
546
646
  : alertCode === 'RUNTIME_PORT_INVALID'
547
647
  ? t('portInvalidHelp')
648
+ : alertCode === 'RUNTIME_RUNNING_UPDATE_REQUIRES_STOP'
649
+ ? t('updateBlockedRunning')
548
650
  : ''
549
651
  const progress = busy ? progressPanel(status.operation?.progress, t) : null
652
+ const pendingUpdates = updateSummary(status, t)
653
+ const hasPendingUpdates = pendingUpdates.length > 0
550
654
  const stateLabels = { 'not-installed': 'notInstalled', degraded: 'degraded', ready: 'ready', running: 'running' }
551
655
  return jsxs('section', { className: 'foggy-settings', children: [
552
656
  jsxs('div', { className: 'foggy-hero', children: [
553
657
  jsxs('div', { className: 'foggy-title-row', children: [jsx('h3', { children: t('title') }), jsx('span', { className: 'foggy-beta', children: t('beta') })] }),
554
658
  jsx('p', { className: 'foggy-intro', children: t('intro') }),
555
659
  ] }),
660
+ jsxs('div', { className: 'foggy-resources', children: [
661
+ jsx('h4', { children: t('resourceTitle') }),
662
+ jsxs('div', { className: 'foggy-resource-links', children: [
663
+ jsx('a', { className: 'foggy-resource-link', href: 'https://github.com/foggy-projects/foggy-deepseek-harness-plugin#readme', target: '_blank', rel: 'noreferrer noopener', title: t('openExternal'), children: t('docs') }),
664
+ jsx('a', { className: 'foggy-resource-link', href: 'https://github.com/foggy-projects/foggy-deepseek-harness-plugin', target: '_blank', rel: 'noreferrer noopener', title: t('openExternal'), children: t('project') }),
665
+ jsx('a', { className: 'foggy-resource-link', href: 'https://github.com/foggy-projects/foggy-deepseek-harness-plugin/releases', target: '_blank', rel: 'noreferrer noopener', title: t('openExternal'), children: t('releases') }),
666
+ jsx('a', { className: 'foggy-resource-link', href: 'https://github.com/f-projects/foggy-data-mcp-bridge', target: '_blank', rel: 'noreferrer noopener', title: t('openExternal'), children: t('runtimeProject') }),
667
+ ] }),
668
+ ] }),
556
669
  jsxs('div', { className: 'foggy-toolbar', children: [
557
- jsxs('div', { className: 'foggy-state', children: [jsx('span', { className: 'foggy-dot', 'data-state': status.state }), jsx('span', { children: t(stateLabels[status.state] || 'notInstalled') })] }),
558
- jsxs('div', { className: 'foggy-actions', children: [
670
+ jsxs('div', { className: 'foggy-state', children: [jsx('span', { className: 'foggy-dot', 'data-state': status.state }), jsx('span', { children: `${t(stateLabels[status.state] || 'notInstalled')}${hasPendingUpdates ? ` · ${t('updateAvailable')}` : ''}` })] }),
671
+ jsxs('div', { className: 'foggy-toolbar-main', children: [
672
+ status.state === 'not-installed'
673
+ ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => runSequence(['initializeAndStart'], t('initializeAndStartDone')), children: t('initializeAndStart') })
674
+ : status.running
675
+ ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') })
676
+ : hasPendingUpdates && status.components.java.available
677
+ ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => runSequence(['updateComponents', 'runtimeStart'], t('updateAndStartDone')), children: t('updateAndStart') })
678
+ : status.installed && status.components.java.available
679
+ ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('runtimeStart'), children: t('start') })
680
+ : jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('updateComponents'), children: t('updateComponents') }),
681
+ ] }),
682
+ jsxs('div', { className: 'foggy-toolbar-secondary', children: [
559
683
  jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: refresh, children: t('refresh') }),
560
684
  jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('diagnostics'), children: t('diagnostics') }),
561
- status.state === 'not-installed' ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('initialize'), children: t('initialize') }) : null,
562
- status.state !== 'not-installed' ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repair'), children: t('repair') }) : null,
563
- status.installed && !status.running && status.components.java.available ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('runtimeStart'), children: t('start') }) : null,
564
- status.running ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') }) : null,
685
+ status.state !== 'not-installed' && !status.running && hasPendingUpdates
686
+ ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('updateComponents'), children: t('updateComponents') })
687
+ : null,
565
688
  ] }),
566
689
  ] }),
567
690
  errorPanel(alertTitle, alertMessage, alertHelp),
568
691
  progress,
569
692
  busy ? jsx('p', { className: 'foggy-message', children: t('working') }) : null,
570
693
  !busy && view.message && !view.error && !operationError ? jsx('p', { className: 'foggy-message', children: view.message }) : null,
694
+ !busy && hasPendingUpdates ? jsxs('div', { className: 'foggy-update-banner', role: 'status', children: [
695
+ jsx('strong', { children: t('updateAvailable') }),
696
+ jsx('span', { children: ` · ${status.running ? t('updateBlockedRunning') : t('updateReady')}` }),
697
+ jsx('ul', { className: 'foggy-update-list', children: pendingUpdates.map((item) => jsx('li', { children: item }, item)) }),
698
+ ] }) : null,
571
699
  jsxs('div', { className: 'foggy-runtime-settings', children: [
572
700
  jsx('h4', { children: t('runtimeSettingsTitle') }),
573
701
  jsxs('div', { className: 'foggy-runtime-settings-row', children: [
@@ -617,11 +745,11 @@ window.__ModuleLoader__.load({
617
745
  jsxs('div', { className: 'foggy-section', children: [
618
746
  jsx('h4', { children: t('repairTitle') }),
619
747
  jsx('div', { className: 'foggy-section-actions', children: [
620
- jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairPython'), children: t('repairPython') }),
621
- jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairCli'), children: t('repairCli') }),
622
- jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairLauncher'), children: t('repairLauncher') }),
623
- jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairAnalysisSkill'), children: t('repairAnalysisSkill') }),
624
- jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairSemanticQuerySkill'), children: t('repairSemanticQuerySkill') }),
748
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy || status.running, title: status.running ? t('updateBlockedRunning') : undefined, onClick: () => run('repairPython'), children: t('repairPython') }),
749
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy || status.running, title: status.running ? t('updateBlockedRunning') : undefined, onClick: () => run('repairCli'), children: t('repairCli') }),
750
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy || status.running, title: status.running ? t('updateBlockedRunning') : undefined, onClick: () => run('repairLauncher'), children: t('repairLauncher') }),
751
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy || status.running, title: status.running ? t('updateBlockedRunning') : undefined, onClick: () => run('repairAnalysisSkill'), children: t('repairAnalysisSkill') }),
752
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy || status.running, title: status.running ? t('updateBlockedRunning') : undefined, onClick: () => run('repairSemanticQuerySkill'), children: t('repairSemanticQuerySkill') }),
625
753
  ] }),
626
754
  ] }),
627
755
  status.profileMigration?.pendingCount || status.profileMigration?.conflictCount ? jsxs('div', { className: 'foggy-warning', children: [
package/lib/index.js CHANGED
@@ -45,6 +45,18 @@ function defaultRoots() {
45
45
  }
46
46
  }
47
47
 
48
+ function componentReleaseState({ installed, installedVersion, targetVersion }) {
49
+ const normalizedInstalled = installedVersion || null
50
+ const normalizedTarget = targetVersion || null
51
+ const updateAvailable = Boolean(installed && normalizedInstalled && normalizedTarget && normalizedInstalled !== normalizedTarget)
52
+ return {
53
+ installed: Boolean(installed && !updateAvailable),
54
+ installedVersion: normalizedInstalled,
55
+ targetVersion: normalizedTarget,
56
+ updateAvailable,
57
+ }
58
+ }
59
+
48
60
  async function exists(path) {
49
61
  try {
50
62
  await access(path, fsConstants.F_OK)
@@ -66,6 +78,22 @@ async function readOptionalJson(path) {
66
78
  }
67
79
  }
68
80
 
81
+ async function runtimeRunningUpdateGuard() {
82
+ const roots = defaultRoots()
83
+ const runtime = await readOptionalJson(join(roots.dataRoot, 'runtime-state.json'))
84
+ if (runtime && processRunning(Number(runtime.pid))) {
85
+ return {
86
+ success: false,
87
+ accepted: false,
88
+ error: {
89
+ code: 'RUNTIME_RUNNING_UPDATE_REQUIRES_STOP',
90
+ message: 'Stop Foggy Runtime before updating or repairing managed components.',
91
+ },
92
+ }
93
+ }
94
+ return null
95
+ }
96
+
69
97
  async function commandVersion(command, args) {
70
98
  try {
71
99
  const { stdout, stderr } = await execFileAsync(command, args, {
@@ -127,7 +155,7 @@ async function runOnboarding(args, timeout = 15 * 60_000, options = {}) {
127
155
 
128
156
  async function assertSystemPrerequisites(kind) {
129
157
  if (kind === 'initialize' && !compatibleNode(process.versions.node)) {
130
- throw new Error(`DeepSeek Harness requires Node.js ^22.19.0 or >=24.0.0; detected ${process.versions.node}`)
158
+ throw new Error(`DeepSeek Harness 0.1.5-rc.2 requires Node.js >=24.0.0; detected ${process.versions.node}`)
131
159
  }
132
160
  if (kind === 'initialize' || kind === 'runtime-start') {
133
161
  const java = compatible(await commandVersion(process.env.JAVA_EXE || 'java', ['-version']), '17.0')
@@ -232,32 +260,50 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
232
260
  const onboardingSkillPath = join(packageRoot, 'skills', 'foggy-deepseek-onboarding')
233
261
  const analysisMarker = await readOptionalJson(join(analysisSkillPath, '.foggy-managed-skill.json'))
234
262
  const semanticQueryMarker = await readOptionalJson(join(semanticQuerySkillPath, '.foggy-managed-skill.json'))
263
+ const cliPresent = Boolean(cliPath && await exists(cliPath))
264
+ const launcherPresent = Boolean(launcherPath && await exists(launcherPath))
265
+ const analysisSkillPresent = await exists(join(analysisSkillPath, 'SKILL.md'))
266
+ && analysisMarker?.schemaVersion === 'foggy-managed-skill/v1'
267
+ const semanticQuerySkillPresent = await exists(join(semanticQuerySkillPath, 'SKILL.md'))
268
+ && semanticQueryMarker?.schemaVersion === 'foggy-managed-skill/v1'
269
+ const cliRelease = componentReleaseState({
270
+ installed: cliPresent,
271
+ installedVersion: state?.cli?.version,
272
+ targetVersion: manifest.components.cli.version,
273
+ })
274
+ const launcherRelease = componentReleaseState({
275
+ installed: launcherPresent,
276
+ installedVersion: state?.launcher?.version,
277
+ targetVersion: manifest.components.launcher.version,
278
+ })
279
+ const analysisRelease = componentReleaseState({
280
+ installed: analysisSkillPresent,
281
+ installedVersion: analysisMarker?.componentVersion || state?.skills?.analysis?.version,
282
+ targetVersion: manifest.components.analysisSkill.version,
283
+ })
284
+ const semanticQueryRelease = componentReleaseState({
285
+ installed: semanticQuerySkillPresent,
286
+ installedVersion: semanticQueryMarker?.componentVersion || state?.skills?.semanticQuery?.version,
287
+ targetVersion: manifest.components.semanticQuerySkill.version,
288
+ })
235
289
  const components = {
236
290
  python,
237
291
  java,
238
292
  cli: {
239
- installed: Boolean(cliPath && await exists(cliPath)),
240
- version: state?.cli?.version ?? manifest.components.cli.version,
293
+ ...cliRelease,
294
+ version: cliRelease.installedVersion ?? manifest.components.cli.version,
241
295
  },
242
296
  launcher: {
243
- installed: Boolean(launcherPath && await exists(launcherPath)),
244
- version: state?.launcher?.version ?? manifest.components.launcher.version,
297
+ ...launcherRelease,
298
+ version: launcherRelease.installedVersion ?? manifest.components.launcher.version,
245
299
  },
246
300
  analysisSkill: {
247
- installed: Boolean(
248
- await exists(join(analysisSkillPath, 'SKILL.md'))
249
- && analysisMarker?.schemaVersion === 'foggy-managed-skill/v1'
250
- && analysisMarker?.componentVersion === manifest.components.analysisSkill.version
251
- ),
252
- version: state?.skills?.analysis?.version ?? manifest.components.analysisSkill.version,
301
+ ...analysisRelease,
302
+ version: analysisRelease.installedVersion ?? manifest.components.analysisSkill.version,
253
303
  },
254
304
  semanticQuerySkill: {
255
- installed: Boolean(
256
- await exists(join(semanticQuerySkillPath, 'SKILL.md'))
257
- && semanticQueryMarker?.schemaVersion === 'foggy-managed-skill/v1'
258
- && semanticQueryMarker?.componentVersion === manifest.components.semanticQuerySkill.version
259
- ),
260
- version: state?.skills?.semanticQuery?.version ?? manifest.components.semanticQuerySkill.version,
305
+ ...semanticQueryRelease,
306
+ version: semanticQueryRelease.installedVersion ?? manifest.components.semanticQuerySkill.version,
261
307
  },
262
308
  onboardingSkill: {
263
309
  installed: await exists(join(onboardingSkillPath, 'SKILL.md')),
@@ -271,6 +317,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
271
317
  && components.analysisSkill.installed
272
318
  && components.semanticQuerySkill.installed
273
319
  && components.onboardingSkill.installed
320
+ const updateAvailable = Object.values(components).some((component) => component?.updateAvailable)
274
321
  const running = Boolean(runtime && processRunning(Number(runtime.pid)))
275
322
  const activePort = running && Number.isInteger(Number(runtime?.port)) ? Number(runtime.port) : null
276
323
  const progress = await readOptionalJson(progressPath)
@@ -294,6 +341,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
294
341
  packageVersion: manifest.packageVersion,
295
342
  state: running ? 'running' : installed ? 'ready' : state ? 'degraded' : 'not-installed',
296
343
  installed,
344
+ updateAvailable,
297
345
  running,
298
346
  runtimeUrl: running ? runtime.runtimeUrl ?? null : null,
299
347
  runtimeSettings: {
@@ -309,7 +357,13 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
309
357
  operation,
310
358
  onboarding,
311
359
  profileMigration,
312
- next: installed ? (running ? 'configure-database' : 'start-runtime') : 'initialize',
360
+ next: !state
361
+ ? 'initialize'
362
+ : updateAvailable
363
+ ? 'update-components'
364
+ : installed
365
+ ? (running ? 'configure-database' : 'start-runtime')
366
+ : 'repair',
313
367
  }
314
368
  }
315
369
 
@@ -337,27 +391,96 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
337
391
  return this.startOperation('initialize', false)
338
392
  }
339
393
 
394
+ async initializeAndStart() {
395
+ const status = await this.status()
396
+ if (status.state !== 'not-installed') {
397
+ return {
398
+ success: false,
399
+ accepted: false,
400
+ error: { code: 'FOGGY_ALREADY_INITIALIZED', message: 'Foggy is already initialized; use Start Runtime or Update components.' },
401
+ }
402
+ }
403
+ return this.startOperation('initialize-and-start', false, undefined, async ({ operation, progressPath, pythonProgress }) => {
404
+ await assertSystemPrerequisites('initialize')
405
+ const cacheArgs = (process.env.FOGGY_ASSET_CACHE_DIRS || '').split(delimiter).filter(Boolean)
406
+ .flatMap((cacheDir) => ['--asset-cache-dir', cacheDir])
407
+ const install = await runOnboarding([
408
+ 'install', '--progress-file', progressPath, '--operation-id', operation.id, '--operation-kind', 'initialize',
409
+ ...cacheArgs,
410
+ ], 15 * 60_000, {
411
+ ensurePython: true,
412
+ onPythonProgress: (detail) => pythonProgress?.update(detail),
413
+ flushPythonProgress: () => pythonProgress?.flush(),
414
+ })
415
+ if (install.success === false) return install
416
+
417
+ await assertSystemPrerequisites('runtime-start')
418
+ const roots = defaultRoots()
419
+ const manifest = await readJson(versionsFile)
420
+ const settings = await readRuntimeSettings(roots.dataRoot, {
421
+ defaultPort: manifest.defaults?.port ?? DEFAULT_RUNTIME_PORT,
422
+ })
423
+ if (!settings.valid) {
424
+ return {
425
+ success: false,
426
+ error: { code: 'RUNTIME_SETTINGS_INVALID', message: `Runtime settings are invalid: ${settings.error}` },
427
+ }
428
+ }
429
+ return runOnboarding([
430
+ 'runtime-start', '--install-root', roots.installRoot, '--data-root', roots.dataRoot,
431
+ '--port', String(settings.port), '--progress-file', progressPath, '--operation-id', operation.id,
432
+ '--operation-kind', 'runtime-start',
433
+ ], 15 * 60_000, { ensurePython: false })
434
+ })
435
+ }
436
+
340
437
  async repair() {
438
+ const blocked = await runtimeRunningUpdateGuard()
439
+ if (blocked) return blocked
341
440
  return this.startOperation('repair', true)
342
441
  }
343
442
 
443
+ async updateComponents() {
444
+ const blocked = await runtimeRunningUpdateGuard()
445
+ if (blocked) return blocked
446
+ const status = await this.status()
447
+ if (status.state === 'not-installed') {
448
+ return {
449
+ success: false,
450
+ accepted: false,
451
+ error: { code: 'FOGGY_NOT_INITIALIZED', message: 'Initialize Foggy before updating managed components.' },
452
+ }
453
+ }
454
+ return this.startOperation('update-components', true)
455
+ }
456
+
344
457
  async repairCli() {
458
+ const blocked = await runtimeRunningUpdateGuard()
459
+ if (blocked) return blocked
345
460
  return this.startOperation('repair-cli', false, ['install', '--repair-component', 'cli'])
346
461
  }
347
462
 
348
463
  async repairPython() {
464
+ const blocked = await runtimeRunningUpdateGuard()
465
+ if (blocked) return blocked
349
466
  return this.startOperation('repair-python', false, ['install'])
350
467
  }
351
468
 
352
469
  async repairLauncher() {
470
+ const blocked = await runtimeRunningUpdateGuard()
471
+ if (blocked) return blocked
353
472
  return this.startOperation('repair-launcher', false, ['install', '--repair-component', 'launcher'])
354
473
  }
355
474
 
356
475
  async repairAnalysisSkill() {
476
+ const blocked = await runtimeRunningUpdateGuard()
477
+ if (blocked) return blocked
357
478
  return this.startOperation('repair-analysis-skill', true, ['install', '--repair-component', 'analysis-skill'])
358
479
  }
359
480
 
360
481
  async repairSemanticQuerySkill() {
482
+ const blocked = await runtimeRunningUpdateGuard()
483
+ if (blocked) return blocked
361
484
  return this.startOperation('repair-semantic-query-skill', true, ['install', '--repair-component', 'semantic-query-skill'])
362
485
  }
363
486
 
@@ -459,22 +582,22 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
459
582
  }
460
583
  }
461
584
 
462
- startOperation(kind, replaceSkill, explicitArgs) {
585
+ startOperation(kind, replaceSkill, explicitArgs, runner = null) {
463
586
  if (this.operation?.state === 'running') {
464
587
  return { success: false, accepted: false, operation: operationView(this.operation), error: 'another Foggy operation is running' }
465
588
  }
466
589
  const id = `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`
467
590
  const args = explicitArgs ?? ['install']
468
- const reportsProgress = args[0] === 'install' || args[0] === 'runtime-start'
591
+ const reportsProgress = args[0] === 'install' || args[0] === 'runtime-start' || runner !== null
592
+ const progressPath = reportsProgress ? join(defaultRoots().dataRoot, 'operation-progress.json') : null
469
593
  if (reportsProgress) {
470
- const roots = defaultRoots()
471
- args.push('--progress-file', join(roots.dataRoot, 'operation-progress.json'), '--operation-id', id, '--operation-kind', kind)
594
+ args.push('--progress-file', progressPath, '--operation-id', id, '--operation-kind', kind)
472
595
  }
473
596
  if (replaceSkill) args.push('--replace-skill')
474
597
  for (const cacheDir of (process.env.FOGGY_ASSET_CACHE_DIRS || '').split(delimiter).filter(Boolean)) {
475
598
  args.push('--asset-cache-dir', cacheDir)
476
599
  }
477
- const runtimeStart = kind === 'runtime-start'
600
+ const runtimeStart = kind === 'runtime-start' || kind.endsWith('-and-start')
478
601
  const operation = {
479
602
  id,
480
603
  kind,
@@ -497,10 +620,11 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
497
620
  promise: null,
498
621
  }
499
622
  const pythonProgress = reportsProgress
500
- ? createPythonProgressReporter(operation, join(defaultRoots().dataRoot, 'operation-progress.json'))
623
+ ? createPythonProgressReporter(operation, progressPath)
501
624
  : null
502
625
  operation.promise = (async () => {
503
626
  await assertSystemPrerequisites(kind)
627
+ if (runner) return runner({ operation, progressPath, pythonProgress })
504
628
  return runOnboarding(args, 15 * 60_000, {
505
629
  ensurePython: true,
506
630
  forcePython: kind === 'repair-python',
@@ -545,7 +669,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
545
669
 
546
670
  const markerInitializers = []
547
671
  for (const method of [
548
- 'status', 'plan', 'initialize', 'repair', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill', 'repairSemanticQuerySkill',
672
+ 'status', 'plan', 'initialize', 'initializeAndStart', 'repair', 'updateComponents', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill', 'repairSemanticQuerySkill',
549
673
  'migrateProfiles', 'diagnostics', 'runtimeStart', 'runtimeStop',
550
674
  'saveRuntimeSettings',
551
675
  ]) {
@@ -26,7 +26,9 @@ export const descriptors = [
26
26
  descriptor('status'),
27
27
  descriptor('plan'),
28
28
  descriptor('initialize'),
29
+ descriptor('initializeAndStart'),
29
30
  descriptor('repair'),
31
+ descriptor('updateComponents'),
30
32
  descriptor('repairPython'),
31
33
  descriptor('repairSemanticQuerySkill'),
32
34
  descriptor('runtimeStart'),
package/lib/version.js CHANGED
@@ -15,6 +15,6 @@ export function compatible(probe, minimum) {
15
15
  }
16
16
 
17
17
  export function compatibleNode(version) {
18
- const [major, minor] = String(version).split('.').map(Number)
19
- return (major === 22 && minor >= 19) || major >= 24
18
+ const [major] = String(version).split('.').map(Number)
19
+ return major >= 24
20
20
  }
package/package.json CHANGED
@@ -1,7 +1,24 @@
1
1
  {
2
2
  "name": "@foggy-projects/deepseek-harness-plugin",
3
- "version": "0.4.0-rc.2",
3
+ "version": "0.4.1-rc.1",
4
4
  "description": "Foggy Java data analysis engine integration for DeepSeek Harness",
5
+ "keywords": [
6
+ "deepseek-harness",
7
+ "dsh",
8
+ "dsh-plugin",
9
+ "foggy",
10
+ "data-analysis",
11
+ "semantic-layer",
12
+ "runtime"
13
+ ],
14
+ "homepage": "https://github.com/foggy-projects/foggy-deepseek-harness-plugin#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/foggy-projects/foggy-deepseek-harness-plugin/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/foggy-projects/foggy-deepseek-harness-plugin.git"
21
+ },
5
22
  "type": "module",
6
23
  "main": "./lib/index.js",
7
24
  "exports": {
@@ -21,13 +38,16 @@
21
38
  "experience/linux/**",
22
39
  "docs/PUBLIC-BETA-READINESS.md",
23
40
  "docs/RELEASE-CANDIDATE-0.4.0-RC.2.md",
41
+ "docs/RELEASE-CANDIDATE-0.4.0-RC.3.md",
42
+ "docs/RELEASE-CANDIDATE-0.4.1-DSH-0.1.5-RC.1.md",
43
+ "docs/COMMUNITY-MARKETPLACES.md",
24
44
  "docs/WINDOWS-BETA-ACCEPTANCE.md",
25
45
  "THIRD-PARTY-RUNTIME-NOTICES.md",
26
46
  "README.md"
27
47
  ],
28
48
  "license": "Apache-2.0",
29
49
  "engines": {
30
- "node": "^22.19.0 || >=24.0.0"
50
+ "node": ">=24.0.0"
31
51
  },
32
52
  "dsh": {
33
53
  "bundle": {
@@ -37,7 +57,6 @@
37
57
  "platform": "web",
38
58
  "inject": [
39
59
  "@deepseek-ai/dsh-api-remotes",
40
- "@deepseek-ai/dsh-client-runtime",
41
60
  "@deepseek-ai/dsh-client-locale",
42
61
  "@deepseek-ai/dsh-client-ui-settings",
43
62
  "@deepseek-ai/dsh-client-ui-settings-plugins"
@@ -48,14 +67,13 @@
48
67
  "zod": "^4.4.3"
49
68
  },
50
69
  "peerDependencies": {
51
- "@deepseek-ai/cordis": "^4.0.1",
52
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-rc.1",
53
- "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
54
- "@deepseek-ai/dsh-client-runtime": "^0.1.2-rc.1",
55
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
56
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-rc.1",
57
- "@deepseek-ai/dsh-skill": "^0.1.2-rc.1",
58
- "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1"
70
+ "@deepseek-ai/cordis": "^4.0.2",
71
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
72
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
73
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
74
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
75
+ "@deepseek-ai/dsh-skill": "^0.1.5-rc.2",
76
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.2"
59
77
  },
60
78
  "scripts": {
61
79
  "check": "node --check lib/index.js && node --check lib/atomic-json.js && node --check lib/diagnostics.js && node --check lib/python-runtime.js && node --check lib/runtime-settings.js && node --check lib/skill-provider.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": "foggy-deepseek-onboarding-versions/v1",
3
- "packageVersion": "0.4.0-rc.2",
4
- "validatedAt": "2026-09-06",
3
+ "packageVersion": "0.4.1-rc.1",
4
+ "validatedAt": "2026-09-13",
5
5
  "components": {
6
6
  "deepseekHarness": {
7
- "version": "0.1.2-rc.1",
8
- "minimumNodeVersion": "22.19.0"
7
+ "version": "0.1.5-rc.2",
8
+ "minimumNodeVersion": "24.0.0"
9
9
  },
10
10
  "python": {
11
11
  "version": "3.12.13",