@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.
- package/LICENSE +201 -0
- package/README.md +34 -0
- package/cordis.patch.yml +6 -0
- package/experience/linux/README.md +54 -0
- package/experience/linux/prepare.sh +190 -0
- package/lib/client.js +254 -0
- package/lib/index.js +259 -0
- package/lib/remote-descriptor.js +29 -0
- package/lib/remote.js +8 -0
- package/lib/typert.js +9 -0
- package/lib/version.js +15 -0
- package/package.json +59 -0
- package/skills/foggy-deepseek-onboarding/SKILL.md +83 -0
- package/skills/foggy-deepseek-onboarding/assets/connection.schema.json +50 -0
- package/skills/foggy-deepseek-onboarding/assets/datasource.example.json +13 -0
- package/skills/foggy-deepseek-onboarding/assets/env.example +14 -0
- package/skills/foggy-deepseek-onboarding/assets/onboarding-state.schema.json +22 -0
- package/skills/foggy-deepseek-onboarding/assets/semantic-plan.example.json +8 -0
- package/skills/foggy-deepseek-onboarding/assets/semantic-plan.schema.json +21 -0
- package/skills/foggy-deepseek-onboarding/assets/versions.json +93 -0
- package/skills/foggy-deepseek-onboarding/references/onboarding-workflow.md +120 -0
- package/skills/foggy-deepseek-onboarding/scripts/doctor.ps1 +4 -0
- package/skills/foggy-deepseek-onboarding/scripts/doctor.sh +5 -0
- package/skills/foggy-deepseek-onboarding/scripts/install.ps1 +4 -0
- package/skills/foggy-deepseek-onboarding/scripts/install.sh +5 -0
- package/skills/foggy-deepseek-onboarding/scripts/onboard.ps1 +4 -0
- package/skills/foggy-deepseek-onboarding/scripts/onboard.sh +5 -0
- package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +2113 -0
- package/skills/foggy-deepseek-onboarding/scripts/runtime-start.ps1 +4 -0
- package/skills/foggy-deepseek-onboarding/scripts/runtime-start.sh +5 -0
- package/skills/foggy-deepseek-onboarding/scripts/runtime-stop.ps1 +4 -0
- package/skills/foggy-deepseek-onboarding/scripts/runtime-stop.sh +5 -0
- package/skills/foggy-deepseek-onboarding/scripts/uninstall.ps1 +4 -0
- 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
package/lib/typert.js
ADDED
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
|
+
}
|