@foggy-projects/deepseek-harness-plugin 0.4.0-beta.2

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.
Files changed (34) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +34 -0
  3. package/cordis.patch.yml +6 -0
  4. package/experience/linux/README.md +54 -0
  5. package/experience/linux/prepare.sh +190 -0
  6. package/lib/client.js +254 -0
  7. package/lib/index.js +259 -0
  8. package/lib/remote-descriptor.js +29 -0
  9. package/lib/remote.js +8 -0
  10. package/lib/typert.js +9 -0
  11. package/lib/version.js +15 -0
  12. package/package.json +59 -0
  13. package/skills/foggy-deepseek-onboarding/SKILL.md +83 -0
  14. package/skills/foggy-deepseek-onboarding/assets/connection.schema.json +50 -0
  15. package/skills/foggy-deepseek-onboarding/assets/datasource.example.json +13 -0
  16. package/skills/foggy-deepseek-onboarding/assets/env.example +14 -0
  17. package/skills/foggy-deepseek-onboarding/assets/onboarding-state.schema.json +22 -0
  18. package/skills/foggy-deepseek-onboarding/assets/semantic-plan.example.json +8 -0
  19. package/skills/foggy-deepseek-onboarding/assets/semantic-plan.schema.json +21 -0
  20. package/skills/foggy-deepseek-onboarding/assets/versions.json +93 -0
  21. package/skills/foggy-deepseek-onboarding/references/onboarding-workflow.md +120 -0
  22. package/skills/foggy-deepseek-onboarding/scripts/doctor.ps1 +4 -0
  23. package/skills/foggy-deepseek-onboarding/scripts/doctor.sh +5 -0
  24. package/skills/foggy-deepseek-onboarding/scripts/install.ps1 +4 -0
  25. package/skills/foggy-deepseek-onboarding/scripts/install.sh +5 -0
  26. package/skills/foggy-deepseek-onboarding/scripts/onboard.ps1 +4 -0
  27. package/skills/foggy-deepseek-onboarding/scripts/onboard.sh +5 -0
  28. package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +2113 -0
  29. package/skills/foggy-deepseek-onboarding/scripts/runtime-start.ps1 +4 -0
  30. package/skills/foggy-deepseek-onboarding/scripts/runtime-start.sh +5 -0
  31. package/skills/foggy-deepseek-onboarding/scripts/runtime-stop.ps1 +4 -0
  32. package/skills/foggy-deepseek-onboarding/scripts/runtime-stop.sh +5 -0
  33. package/skills/foggy-deepseek-onboarding/scripts/uninstall.ps1 +4 -0
  34. package/skills/foggy-deepseek-onboarding/scripts/uninstall.sh +5 -0
package/lib/client.js ADDED
@@ -0,0 +1,254 @@
1
+ window.__ModuleLoader__.load({
2
+ id: '@foggy-projects/deepseek-harness-plugin',
3
+ factory: (require) => {
4
+ const module = { exports: {} }
5
+ const exports = module.exports
6
+ const { jsx, jsxs } = require('react/jsx-runtime')
7
+ const { useCallback, useEffect, useState } = require('react')
8
+
9
+ const NS = 'settings.foggy'
10
+ const passthroughSchema = { parse: (value) => value }
11
+ const TYPERT_REMOTE = {
12
+ package: '@foggy-projects/deepseek-harness-plugin',
13
+ descriptors: ['status', 'plan', 'initialize', 'repair', 'runtimeStart', 'runtimeStop'].map((method) => ({
14
+ id: `@foggy-projects/deepseek-harness-plugin#foggyIntegration/${method}`,
15
+ service: 'foggyIntegration',
16
+ namespace: 'foggyIntegration',
17
+ method,
18
+ invocation: { kind: 'direct' },
19
+ parameters: [],
20
+ result: {
21
+ mode: 'strict',
22
+ typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyIntegrationResult',
23
+ schema: passthroughSchema,
24
+ },
25
+ sourceLocation: { file: 'lib/index.js', line: 1, column: 1 },
26
+ })),
27
+ }
28
+
29
+ const zh = {
30
+ tab: 'Foggy 数据分析',
31
+ title: 'Foggy Java 数据分析引擎',
32
+ intro: '在 DeepSeek Harness 内管理 Foggy CLI、Launcher、Skills 和本地 Runtime。大型组件在首次初始化时按需下载。',
33
+ refresh: '刷新状态',
34
+ loading: '正在读取 Foggy 状态…',
35
+ failed: '暂时无法读取 Foggy 状态。',
36
+ retry: '重试',
37
+ notInstalled: '尚未初始化',
38
+ degraded: '需要修复',
39
+ ready: '已就绪',
40
+ running: 'Runtime 运行中',
41
+ cli: 'CLI',
42
+ launcher: 'Launcher',
43
+ analysisSkill: '分析 Skill',
44
+ python: 'Python',
45
+ java: 'Java',
46
+ installed: '已安装',
47
+ missing: '未安装',
48
+ available: '可用',
49
+ unavailable: '不可用',
50
+ initialize: '初始化 Foggy',
51
+ repair: '重新下载 / 修复',
52
+ start: '启动 Runtime',
53
+ stop: '停止 Runtime',
54
+ working: '操作进行中…',
55
+ accepted: '操作已开始;页面会自动刷新状态。',
56
+ actionFailed: '操作失败',
57
+ roots: '本地目录',
58
+ installRoot: '组件目录',
59
+ dataRoot: '数据目录',
60
+ runtimeUrl: 'Runtime 地址',
61
+ projectRoot: 'Skill 目标工作区',
62
+ nextTitle: '后续配置',
63
+ nextCopy: 'Runtime 启动成功后,将继续进入数据库连接与语义层向导。数据库密码不会写入 DSH 普通设置。',
64
+ beta: 'Beta',
65
+ }
66
+
67
+ const en = {
68
+ tab: 'Foggy Data Analysis',
69
+ title: 'Foggy Java Data Analysis Engine',
70
+ intro: 'Manage the Foggy CLI, Launcher, Skills, and local Runtime inside DeepSeek Harness. Large components are downloaded on first initialization.',
71
+ refresh: 'Refresh status',
72
+ loading: 'Reading Foggy status…',
73
+ failed: 'Foggy status is temporarily unavailable.',
74
+ retry: 'Retry',
75
+ notInstalled: 'Not initialized',
76
+ degraded: 'Repair required',
77
+ ready: 'Ready',
78
+ running: 'Runtime running',
79
+ cli: 'CLI',
80
+ launcher: 'Launcher',
81
+ analysisSkill: 'Analysis Skill',
82
+ python: 'Python',
83
+ java: 'Java',
84
+ installed: 'Installed',
85
+ missing: 'Missing',
86
+ available: 'Available',
87
+ unavailable: 'Unavailable',
88
+ initialize: 'Initialize Foggy',
89
+ repair: 'Re-download / Repair',
90
+ start: 'Start Runtime',
91
+ stop: 'Stop Runtime',
92
+ working: 'Operation in progress…',
93
+ accepted: 'The operation started; this page will refresh automatically.',
94
+ actionFailed: 'Operation failed',
95
+ roots: 'Local directories',
96
+ installRoot: 'Components',
97
+ dataRoot: 'Data',
98
+ runtimeUrl: 'Runtime URL',
99
+ projectRoot: 'Skill target workspace',
100
+ nextTitle: 'Next configuration',
101
+ nextCopy: 'After the Runtime starts, the database connection and semantic-layer wizard comes next. Database passwords are never stored in ordinary DSH settings.',
102
+ beta: 'Beta',
103
+ }
104
+
105
+ const css = `
106
+ .foggy-settings{width:100%;max-width:820px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:16px}
107
+ .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}
108
+ .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)}
109
+ .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}
110
+ .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)}
111
+ .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}
112
+ .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}
113
+ .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{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}.foggy-paths h4,.foggy-next 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{margin:0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}
114
+ @media (width<=680px){.foggy-grid{grid-template-columns:minmax(0,1fr)}}
115
+ `
116
+
117
+ if (typeof document !== 'undefined' && !document.querySelector('style[data-foggy-settings]')) {
118
+ const tag = document.createElement('style')
119
+ tag.dataset.foggySettings = 'true'
120
+ tag.textContent = css
121
+ document.head.appendChild(tag)
122
+ }
123
+
124
+ function unwrap(response, method) {
125
+ if (!response?.ok) {
126
+ const error = response?.error
127
+ throw new Error(error ? `${error.code}: ${error.message}` : `${method} failed`)
128
+ }
129
+ return response.value
130
+ }
131
+
132
+ function componentCard(label, component, kind, t) {
133
+ const available = kind === 'runtime' ? component?.installed : component?.available
134
+ const stateText = kind === 'runtime'
135
+ ? t(available ? 'installed' : 'missing')
136
+ : t(available ? 'available' : 'unavailable')
137
+ return jsxs('div', {
138
+ className: 'foggy-card',
139
+ children: [
140
+ jsxs('div', { className: 'foggy-card-head', children: [
141
+ jsx('strong', { children: label }),
142
+ jsx('span', { className: 'foggy-badge', children: stateText }),
143
+ ] }),
144
+ jsx('code', { children: `${component?.version || component?.output || '—'}${component?.minimum ? ` · ≥${component.minimum}` : ''}` }),
145
+ ],
146
+ })
147
+ }
148
+
149
+ function FoggySettingsTab({ api, t }) {
150
+ const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false })
151
+
152
+ const refresh = useCallback(async () => {
153
+ setView((current) => ({ ...current, phase: current.status ? 'ready' : 'loading', error: false }))
154
+ try {
155
+ const status = unwrap(await api.status(), 'status')
156
+ setView((current) => ({ ...current, phase: 'ready', status, error: false }))
157
+ } catch (error) {
158
+ setView((current) => ({ ...current, phase: current.status ? 'ready' : 'error', message: String(error.message || error), error: true }))
159
+ }
160
+ }, [api])
161
+
162
+ useEffect(() => { refresh() }, [refresh])
163
+ useEffect(() => {
164
+ if (view.status?.operation?.state !== 'running') return undefined
165
+ const timer = setInterval(refresh, 2000)
166
+ return () => clearInterval(timer)
167
+ }, [refresh, view.status?.operation?.state])
168
+
169
+ const run = async (method) => {
170
+ setView((current) => ({ ...current, message: t('working'), error: false }))
171
+ try {
172
+ const result = unwrap(await api[method](), method)
173
+ setView((current) => ({ ...current, message: result.accepted ? t('accepted') : '', error: result.success === false }))
174
+ await refresh()
175
+ } catch (error) {
176
+ setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true }))
177
+ }
178
+ }
179
+
180
+ if (view.phase === 'loading') return jsx('p', { className: 'foggy-message', children: t('loading') })
181
+ if (view.phase === 'error') return jsxs('div', { className: 'foggy-settings', children: [
182
+ jsx('p', { className: 'foggy-message', 'data-error': true, children: `${t('failed')} ${view.message}` }),
183
+ jsx('button', { className: 'foggy-button', type: 'button', onClick: refresh, children: t('retry') }),
184
+ ] })
185
+
186
+ const status = view.status
187
+ const busy = status.operation?.state === 'running'
188
+ const operationError = status.operation?.state === 'failed'
189
+ ? status.operation.error || status.operation.result?.error?.message || t('actionFailed')
190
+ : ''
191
+ const stateLabels = { 'not-installed': 'notInstalled', degraded: 'degraded', ready: 'ready', running: 'running' }
192
+ return jsxs('section', { className: 'foggy-settings', children: [
193
+ jsxs('div', { className: 'foggy-hero', children: [
194
+ jsxs('div', { className: 'foggy-title-row', children: [jsx('h3', { children: t('title') }), jsx('span', { className: 'foggy-beta', children: t('beta') })] }),
195
+ jsx('p', { className: 'foggy-intro', children: t('intro') }),
196
+ ] }),
197
+ jsxs('div', { className: 'foggy-toolbar', children: [
198
+ jsxs('div', { className: 'foggy-state', children: [jsx('span', { className: 'foggy-dot', 'data-state': status.state }), jsx('span', { children: t(stateLabels[status.state] || 'notInstalled') })] }),
199
+ jsxs('div', { className: 'foggy-actions', children: [
200
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: refresh, children: t('refresh') }),
201
+ !status.installed ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('initialize'), children: t('initialize') }) : null,
202
+ status.installed ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repair'), children: t('repair') }) : null,
203
+ 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,
204
+ status.running ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') }) : null,
205
+ ] }),
206
+ ] }),
207
+ busy || operationError || view.message ? jsx('p', { className: 'foggy-message', 'data-error': operationError || view.error ? 'true' : undefined, children: busy ? t('working') : operationError || view.message }) : null,
208
+ jsxs('div', { className: 'foggy-grid', children: [
209
+ componentCard(t('python'), status.components.python, 'tool', t),
210
+ componentCard(t('java'), status.components.java, 'tool', t),
211
+ componentCard(t('cli'), status.components.cli, 'runtime', t),
212
+ componentCard(t('launcher'), status.components.launcher, 'runtime', t),
213
+ componentCard(t('analysisSkill'), status.components.analysisSkill, 'runtime', t),
214
+ ] }),
215
+ jsxs('div', { className: 'foggy-paths', children: [
216
+ jsx('h4', { children: t('roots') }),
217
+ jsxs('dl', { children: [
218
+ jsx('dt', { children: t('installRoot') }), jsx('dd', { children: status.roots.installRoot }),
219
+ jsx('dt', { children: t('dataRoot') }), jsx('dd', { children: status.roots.dataRoot }),
220
+ jsx('dt', { children: t('projectRoot') }), jsx('dd', { children: status.projectRoot }),
221
+ status.runtimeUrl ? jsx('dt', { children: t('runtimeUrl') }) : null,
222
+ status.runtimeUrl ? jsx('dd', { children: status.runtimeUrl }) : null,
223
+ ] }),
224
+ ] }),
225
+ jsxs('div', { className: 'foggy-next', children: [jsx('h4', { children: t('nextTitle') }), jsx('p', { children: t('nextCopy') })] }),
226
+ ] })
227
+ }
228
+
229
+ const inject = ['slots', 'locale', 'remote']
230
+
231
+ async function apply(ctx) {
232
+ const disposeRemote = await ctx.remote.$mount(TYPERT_REMOTE)
233
+ ctx.effect(() => disposeRemote, 'foggy: remote contribution')
234
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'foggy: settings dictionaries')
235
+ const t = ctx.locale.bind(NS)
236
+ ctx.inject(['remote.foggyIntegration'], (remoteCtx) => {
237
+ const api = remoteCtx.remote.foggyIntegration
238
+ remoteCtx.slots.inject('settings.plugins.tab', () => remoteCtx.slots.register({
239
+ name: 'settings.plugins.tab',
240
+ id: 'foggy',
241
+ order: 5,
242
+ label: () => t('tab'),
243
+ locale: NS,
244
+ inject: () => ({ api, t }),
245
+ }, FoggySettingsTab))
246
+ })
247
+ }
248
+
249
+ exports.NS = NS
250
+ exports.inject = inject
251
+ exports.apply = apply
252
+ return module.exports
253
+ },
254
+ })
package/lib/index.js ADDED
@@ -0,0 +1,259 @@
1
+ import { execFile } from 'node:child_process'
2
+ import { access, readFile } from 'node:fs/promises'
3
+ import { constants as fsConstants } from 'node:fs'
4
+ import { dirname, join, delimiter } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { promisify } from 'node:util'
7
+ import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
8
+ import { compatible } from './version.js'
9
+
10
+ const execFileAsync = promisify(execFile)
11
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)))
12
+ const onboardingScript = join(packageRoot, 'skills', 'foggy-deepseek-onboarding', 'scripts', 'onboarding.py')
13
+ const versionsFile = join(packageRoot, 'skills', 'foggy-deepseek-onboarding', 'assets', 'versions.json')
14
+
15
+ function defaultRoots() {
16
+ if (process.platform === 'win32') {
17
+ const base = process.env.LOCALAPPDATA
18
+ if (!base) throw new Error('LOCALAPPDATA is not set')
19
+ return {
20
+ installRoot: join(base, 'Foggy', 'DeepSeekHarness'),
21
+ dataRoot: join(base, 'Foggy', 'DeepSeekHarnessData'),
22
+ }
23
+ }
24
+ const home = process.env.HOME
25
+ if (!home) throw new Error('HOME is not set')
26
+ return {
27
+ installRoot: process.env.XDG_DATA_HOME
28
+ ? join(process.env.XDG_DATA_HOME, 'foggy', 'deepseek-harness')
29
+ : join(home, '.local', 'share', 'foggy', 'deepseek-harness'),
30
+ dataRoot: process.env.XDG_STATE_HOME
31
+ ? join(process.env.XDG_STATE_HOME, 'foggy', 'deepseek-harness')
32
+ : join(home, '.local', 'state', 'foggy', 'deepseek-harness'),
33
+ }
34
+ }
35
+
36
+ async function exists(path) {
37
+ try {
38
+ await access(path, fsConstants.F_OK)
39
+ return true
40
+ } catch {
41
+ return false
42
+ }
43
+ }
44
+
45
+ async function readJson(path) {
46
+ return JSON.parse(await readFile(path, 'utf8'))
47
+ }
48
+
49
+ async function commandVersion(command, args) {
50
+ try {
51
+ const { stdout, stderr } = await execFileAsync(command, args, {
52
+ windowsHide: true,
53
+ timeout: 15_000,
54
+ maxBuffer: 256 * 1024,
55
+ })
56
+ return { available: true, output: `${stdout}\n${stderr}`.trim().split(/\r?\n/)[0] ?? '' }
57
+ } catch (error) {
58
+ return { available: false, output: '', error: error.code ?? 'COMMAND_FAILED' }
59
+ }
60
+ }
61
+
62
+ async function pythonCommand() {
63
+ const configured = process.env.FOGGY_PYTHON
64
+ const candidates = configured ? [configured] : process.platform === 'win32' ? ['python', 'py'] : ['python3', 'python']
65
+ for (const command of candidates) {
66
+ const probe = await commandVersion(command, ['--version'])
67
+ if (probe.available) return command
68
+ }
69
+ throw new Error('Python 3.11+ was not found; set FOGGY_PYTHON to its executable')
70
+ }
71
+
72
+ function parseOutput(stdout, stderr) {
73
+ const text = String(stdout ?? '').trim()
74
+ if (!text) throw new Error(String(stderr ?? '').trim() || 'Foggy onboarding returned no JSON')
75
+ try {
76
+ return JSON.parse(text)
77
+ } catch {
78
+ throw new Error(`Foggy onboarding returned invalid JSON: ${text.slice(0, 240)}`)
79
+ }
80
+ }
81
+
82
+ async function runOnboarding(args, timeout = 15 * 60_000) {
83
+ const python = await pythonCommand()
84
+ try {
85
+ const { stdout, stderr } = await execFileAsync(python, [onboardingScript, ...args], {
86
+ windowsHide: true,
87
+ timeout,
88
+ maxBuffer: 4 * 1024 * 1024,
89
+ cwd: process.env.FOGGY_PROJECT_ROOT || process.cwd(),
90
+ })
91
+ return parseOutput(stdout, stderr)
92
+ } catch (error) {
93
+ if (error.stdout) return parseOutput(error.stdout, error.stderr)
94
+ throw error
95
+ }
96
+ }
97
+
98
+ function processRunning(pid) {
99
+ if (!Number.isInteger(pid) || pid <= 0) return false
100
+ try {
101
+ process.kill(pid, 0)
102
+ return true
103
+ } catch {
104
+ return false
105
+ }
106
+ }
107
+
108
+ function operationView(operation) {
109
+ if (!operation) return { state: 'idle' }
110
+ const { promise: _promise, ...view } = operation
111
+ return view
112
+ }
113
+
114
+ export class FoggyIntegrationGateway extends TypertRemoteService {
115
+ operation = null
116
+
117
+ constructor(ctx) {
118
+ super(ctx, 'foggyIntegration')
119
+ for (const initialize of markerInitializers) initialize.call(this)
120
+ }
121
+
122
+ async status() {
123
+ const roots = defaultRoots()
124
+ const statePath = join(roots.installRoot, 'install-state.json')
125
+ const runtimeStatePath = join(roots.dataRoot, 'runtime-state.json')
126
+ const manifest = await readJson(versionsFile)
127
+ let pythonProbe
128
+ try {
129
+ pythonProbe = await commandVersion(await pythonCommand(), ['--version'])
130
+ } catch (error) {
131
+ pythonProbe = { available: false, output: '', error: String(error.message ?? error) }
132
+ }
133
+ const python = compatible(pythonProbe, '3.11')
134
+ const java = compatible(await commandVersion(process.env.JAVA_EXE || 'java', ['-version']), '17.0')
135
+ let state = null
136
+ let runtime = null
137
+ try { state = await readJson(statePath) } catch {}
138
+ try { runtime = await readJson(runtimeStatePath) } catch {}
139
+ const cliPath = state?.cli?.command
140
+ const launcherPath = state?.launcher?.path
141
+ const components = {
142
+ python,
143
+ java,
144
+ cli: {
145
+ installed: Boolean(cliPath && await exists(cliPath)),
146
+ version: state?.cli?.version ?? manifest.components.cli.version,
147
+ },
148
+ launcher: {
149
+ installed: Boolean(launcherPath && await exists(launcherPath)),
150
+ version: state?.launcher?.version ?? manifest.components.launcher.version,
151
+ },
152
+ analysisSkill: {
153
+ installed: Boolean(state?.skills?.analysis?.path && await exists(state.skills.analysis.path)),
154
+ version: state?.skills?.analysis?.version ?? manifest.components.analysisSkill.version,
155
+ },
156
+ }
157
+ const installed = components.cli.installed && components.launcher.installed && components.analysisSkill.installed
158
+ const running = Boolean(runtime && processRunning(Number(runtime.pid)))
159
+ return {
160
+ success: true,
161
+ packageVersion: manifest.packageVersion,
162
+ state: running ? 'running' : installed ? 'ready' : state ? 'degraded' : 'not-installed',
163
+ installed,
164
+ running,
165
+ runtimeUrl: running ? runtime.runtimeUrl ?? null : null,
166
+ roots,
167
+ projectRoot: process.env.FOGGY_PROJECT_ROOT || process.cwd(),
168
+ components,
169
+ operation: operationView(this.operation),
170
+ next: installed ? (running ? 'configure-database' : 'start-runtime') : 'initialize',
171
+ }
172
+ }
173
+
174
+ async plan() {
175
+ const roots = defaultRoots()
176
+ const manifest = await readJson(versionsFile)
177
+ return {
178
+ success: true,
179
+ roots,
180
+ projectRoot: process.env.FOGGY_PROJECT_ROOT || process.cwd(),
181
+ versions: Object.fromEntries(Object.entries(manifest.components).map(([name, value]) => [name, value.version])),
182
+ operations: [
183
+ 'create isolated Python environment',
184
+ 'download and verify pinned CLI and Launcher assets',
185
+ 'install Foggy analysis and onboarding Skills into the workspace',
186
+ 'write local install state',
187
+ ],
188
+ secretsInDshSettings: false,
189
+ }
190
+ }
191
+
192
+ async initialize() {
193
+ return this.startOperation('initialize', false)
194
+ }
195
+
196
+ async repair() {
197
+ return this.startOperation('repair', true)
198
+ }
199
+
200
+ async runtimeStart() {
201
+ return this.startOperation('runtime-start', false, ['runtime-start'])
202
+ }
203
+
204
+ async runtimeStop() {
205
+ return this.startOperation('runtime-stop', false, ['runtime-stop'])
206
+ }
207
+
208
+ startOperation(kind, replaceSkill, explicitArgs) {
209
+ if (this.operation?.state === 'running') {
210
+ return { success: false, accepted: false, operation: operationView(this.operation), error: 'another Foggy operation is running' }
211
+ }
212
+ const id = `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`
213
+ const args = explicitArgs ?? ['install', '--project-root', process.env.FOGGY_PROJECT_ROOT || process.cwd()]
214
+ if (replaceSkill) args.push('--replace-skill')
215
+ for (const cacheDir of (process.env.FOGGY_ASSET_CACHE_DIRS || '').split(delimiter).filter(Boolean)) {
216
+ args.push('--asset-cache-dir', cacheDir)
217
+ }
218
+ const operation = {
219
+ id,
220
+ kind,
221
+ state: 'running',
222
+ startedAt: new Date().toISOString(),
223
+ finishedAt: null,
224
+ result: null,
225
+ error: null,
226
+ promise: null,
227
+ }
228
+ operation.promise = runOnboarding(args)
229
+ .then((result) => {
230
+ operation.state = result.success === false ? 'failed' : 'succeeded'
231
+ operation.result = result
232
+ operation.finishedAt = new Date().toISOString()
233
+ })
234
+ .catch((error) => {
235
+ operation.state = 'failed'
236
+ operation.error = String(error.message ?? error)
237
+ operation.finishedAt = new Date().toISOString()
238
+ })
239
+ this.operation = operation
240
+ return { success: true, accepted: true, operation: operationView(operation) }
241
+ }
242
+ }
243
+
244
+ const markerInitializers = []
245
+ for (const method of ['status', 'plan', 'initialize', 'repair', 'runtimeStart', 'runtimeStop']) {
246
+ Remote(method)(FoggyIntegrationGateway.prototype[method], {
247
+ kind: 'method',
248
+ name: method,
249
+ static: false,
250
+ private: false,
251
+ access: {
252
+ has: (object) => method in object,
253
+ get: (object) => object[method],
254
+ },
255
+ addInitializer: (initializer) => markerInitializers.push(initializer),
256
+ })
257
+ }
258
+
259
+ export default FoggyIntegrationGateway
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod'
2
+
3
+ const resultSchema = z.unknown()
4
+
5
+ function descriptor(method) {
6
+ return {
7
+ id: `@foggy-projects/deepseek-harness-plugin#foggyIntegration/${method}`,
8
+ service: 'foggyIntegration',
9
+ namespace: 'foggyIntegration',
10
+ method,
11
+ invocation: { kind: 'direct' },
12
+ parameters: [],
13
+ result: {
14
+ mode: 'strict',
15
+ typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyIntegrationResult',
16
+ schema: resultSchema,
17
+ },
18
+ sourceLocation: { file: 'lib/index.js', line: 1, column: 1 },
19
+ }
20
+ }
21
+
22
+ export const descriptors = [
23
+ descriptor('status'),
24
+ descriptor('plan'),
25
+ descriptor('initialize'),
26
+ descriptor('repair'),
27
+ descriptor('runtimeStart'),
28
+ descriptor('runtimeStop'),
29
+ ]
package/lib/remote.js ADDED
@@ -0,0 +1,8 @@
1
+ import { descriptors } from './remote-descriptor.js'
2
+
3
+ export const TYPERT_REMOTE = {
4
+ package: '@foggy-projects/deepseek-harness-plugin',
5
+ descriptors,
6
+ }
7
+
8
+ export default TYPERT_REMOTE
package/lib/typert.js ADDED
@@ -0,0 +1,9 @@
1
+ import { descriptors } from './remote-descriptor.js'
2
+
3
+ export const TYPERT = {
4
+ package: '@foggy-projects/deepseek-harness-plugin',
5
+ face: 'host',
6
+ schemas: [],
7
+ invocations: descriptors,
8
+ model: { services: [], events: [], objects: [] },
9
+ }
package/lib/version.js ADDED
@@ -0,0 +1,15 @@
1
+ export function versionParts(text) {
2
+ const match = String(text).match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/)
3
+ return match ? match.slice(1).map((part) => Number(part || 0)) : []
4
+ }
5
+
6
+ export function compatible(probe, minimum) {
7
+ if (!probe.available) return { ...probe, detected: false, minimum }
8
+ const actual = versionParts(probe.output)
9
+ const wanted = versionParts(minimum)
10
+ for (let index = 0; index < wanted.length; index += 1) {
11
+ if ((actual[index] ?? 0) > wanted[index]) return { ...probe, detected: true, available: true, minimum }
12
+ if ((actual[index] ?? 0) < wanted[index]) return { ...probe, detected: true, available: false, minimum }
13
+ }
14
+ return { ...probe, detected: true, available: true, minimum }
15
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@foggy-projects/deepseek-harness-plugin",
3
+ "version": "0.4.0-beta.2",
4
+ "description": "Foggy Java data analysis engine integration for DeepSeek Harness",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./client": "./lib/client.js",
10
+ "./typert": "./lib/typert.js",
11
+ "./remote": "./lib/remote.js",
12
+ "./cordis.patch.yml": "./cordis.patch.yml",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "lib/**/*.js",
17
+ "skills/**",
18
+ "!skills/**/__pycache__/**",
19
+ "!skills/**/*.pyc",
20
+ "cordis.patch.yml",
21
+ "experience/linux/**",
22
+ "README.md"
23
+ ],
24
+ "license": "Apache-2.0",
25
+ "engines": {
26
+ "node": ">=22.19.0"
27
+ },
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ },
32
+ "client": {
33
+ "platform": "web",
34
+ "inject": [
35
+ "@deepseek-ai/dsh-api-remotes",
36
+ "@deepseek-ai/dsh-client-runtime",
37
+ "@deepseek-ai/dsh-client-locale",
38
+ "@deepseek-ai/dsh-client-ui-settings",
39
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
40
+ ]
41
+ }
42
+ },
43
+ "dependencies": {
44
+ "zod": "^4.4.3"
45
+ },
46
+ "peerDependencies": {
47
+ "@deepseek-ai/cordis": "^4.0.1",
48
+ "@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
49
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
50
+ "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
51
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
52
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.1-rc.2",
53
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2"
54
+ },
55
+ "scripts": {
56
+ "check": "node --check lib/index.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
57
+ "test": "node --test test/package.test.js"
58
+ }
59
+ }