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

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,7 +3,9 @@
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
10
  DeepSeek Harness uses a system Node.js (`^22.19.0 || >=24.0.0`) and Foggy's Java
9
11
  Launcher uses a system Java 17+. Foggy manages its own pinned Python 3.12 runtime
@@ -14,7 +16,7 @@ modify `PATH`, or register Python globally. Advanced users may explicitly set
14
16
  ## Local beta installation
15
17
 
16
18
  ```powershell
17
- dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-rc.2.tgz
19
+ dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-rc.3.tgz
18
20
  ```
19
21
 
20
22
  Restart `dsh web`, use the browser it opens (or the complete printed URL,
@@ -27,6 +29,13 @@ After npm beta publication, the corresponding one-line install is:
27
29
  dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@beta
28
30
  ```
29
31
 
32
+ After updating the plugin package, open **Settings → Plugins → Foggy Data
33
+ Analysis** and use **Update components** (or **Update and start**) to download
34
+ the pinned CLI, Launcher, and Skills. The settings page shows current and target
35
+ versions. Updates are blocked while Runtime is running so the active Java
36
+ process and the next-launcher state cannot diverge. Component-specific repair
37
+ actions remain available under **Advanced repair**.
38
+
30
39
  DeepSeek Harness 0.1.2-rc.1 applies pnpm's minimum-release-age policy to its
31
40
  profile lockfile. An upgrade performed shortly after publication can therefore
32
41
  fail while naming either the new plugin or the previously installed Beta. This
@@ -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.
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, {
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foggy-projects/deepseek-harness-plugin",
3
- "version": "0.4.0-rc.2",
3
+ "version": "0.4.0-rc.3",
4
4
  "description": "Foggy Java data analysis engine integration for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -21,6 +21,7 @@
21
21
  "experience/linux/**",
22
22
  "docs/PUBLIC-BETA-READINESS.md",
23
23
  "docs/RELEASE-CANDIDATE-0.4.0-RC.2.md",
24
+ "docs/RELEASE-CANDIDATE-0.4.0-RC.3.md",
24
25
  "docs/WINDOWS-BETA-ACCEPTANCE.md",
25
26
  "THIRD-PARTY-RUNTIME-NOTICES.md",
26
27
  "README.md"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "foggy-deepseek-onboarding-versions/v1",
3
- "packageVersion": "0.4.0-rc.2",
3
+ "packageVersion": "0.4.0-rc.3",
4
4
  "validatedAt": "2026-09-06",
5
5
  "components": {
6
6
  "deepseekHarness": {