dsh-agent-office 4.6.0 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/lib/client.js +64 -2
- package/lib/index.js +19 -1
- package/lib/panel.html +239 -191
- package/lib/state.js +36 -0
- package/package.json +1 -1
- package/panel-src/index.html +50 -2
- package/panel-src/main.js +573 -71
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
- 📡 **真实数据**:状态、今日执行次数、本周/今日 **Token 用量** 全部来自本机会话日志,数字不编造;
|
|
34
34
|
- 🖱️ **可交互**:拖动旋转、滚轮缩放、点击立方体或名字看详情、详情卡 ×/Esc 关闭;
|
|
35
35
|
- 🧩 **原生插件**:bundle 插件,装完即用(宿主路由 + 浏览器半侧悬浮按钮),无外部依赖;
|
|
36
|
-
- 🔒
|
|
36
|
+
- 🔒 **默认只读本机**:只读 `$DSH_HOME/sessions`;只有你在设置里主动开启「本地天气」后,才会把坐标发给 Open-Meteo 获取天气,位置本身只保存在本地。
|
|
37
|
+
- 🏗️ **搭建入场**:每次打开面板先播放 0→1 的建造过程,楼层、工位、猫猫和 MCP 链路逐步落位。
|
|
38
|
+
- 🌦️ **本地天气**:设置里允许定位后,按所在城市显示晴/阴云/雨/雪/风;不开定位则不启用天气效果。
|
|
37
39
|
|
|
38
40
|
## 安装
|
|
39
41
|
|
package/lib/client.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
const CUSTOM_FACE_KEY = 'agent-office-custom-face'
|
|
10
10
|
const GLASS_KEY = 'agent-office-ui-opacity'
|
|
11
|
+
const WEATHER_KEY = 'agent-office-weather-location'
|
|
11
12
|
const CAT_BUTTON = 'data:image/png;base64,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'
|
|
12
13
|
|
|
13
14
|
window.__ModuleLoader__.load({
|
|
@@ -68,6 +69,13 @@ window.__ModuleLoader__.load({
|
|
|
68
69
|
const [message, setMessage] = useState('')
|
|
69
70
|
const [busy, setBusy] = useState(false)
|
|
70
71
|
const [glass, setGlass] = useState(readGlass())
|
|
72
|
+
const [weatherOn, setWeatherOn] = useState(() => {
|
|
73
|
+
try {
|
|
74
|
+
const value = JSON.parse(localStorage.getItem(WEATHER_KEY) ?? 'null')
|
|
75
|
+
return Number.isFinite(value?.lat) && Number.isFinite(value?.lon)
|
|
76
|
+
} catch { return false }
|
|
77
|
+
})
|
|
78
|
+
const [weatherBusy, setWeatherBusy] = useState(false)
|
|
71
79
|
|
|
72
80
|
const choose = async event => {
|
|
73
81
|
const file = event.target.files?.[0]
|
|
@@ -108,6 +116,43 @@ window.__ModuleLoader__.load({
|
|
|
108
116
|
setGlass(80)
|
|
109
117
|
}
|
|
110
118
|
|
|
119
|
+
const askWeather = () => {
|
|
120
|
+
if (!navigator.geolocation) {
|
|
121
|
+
setMessage('当前环境不支持定位,无法获取本地天气')
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
setWeatherBusy(true)
|
|
125
|
+
setMessage('请在系统弹窗里允许定位…')
|
|
126
|
+
navigator.geolocation.getCurrentPosition(
|
|
127
|
+
position => {
|
|
128
|
+
try {
|
|
129
|
+
localStorage.setItem(WEATHER_KEY, JSON.stringify({
|
|
130
|
+
lat: position.coords.latitude,
|
|
131
|
+
lon: position.coords.longitude,
|
|
132
|
+
at: Date.now(),
|
|
133
|
+
}))
|
|
134
|
+
setWeatherOn(true)
|
|
135
|
+
setMessage('已开启本地天气,面板会按所在城市显示天气效果')
|
|
136
|
+
} catch {
|
|
137
|
+
setMessage('位置保存失败')
|
|
138
|
+
}
|
|
139
|
+
setWeatherBusy(false)
|
|
140
|
+
},
|
|
141
|
+
error => {
|
|
142
|
+
setWeatherBusy(false)
|
|
143
|
+
setWeatherOn(false)
|
|
144
|
+
setMessage('没有拿到定位权限:' + String(error?.message ?? '未授权'))
|
|
145
|
+
},
|
|
146
|
+
{ enableHighAccuracy: false, timeout: 12000, maximumAge: 10 * 60 * 1000 },
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const closeWeather = () => {
|
|
151
|
+
try { localStorage.removeItem(WEATHER_KEY) } catch {}
|
|
152
|
+
setWeatherOn(false)
|
|
153
|
+
setMessage('已关闭本地天气')
|
|
154
|
+
}
|
|
155
|
+
|
|
111
156
|
const titleStyle = { fontSize: 15, fontWeight: 600, margin: '0 0 6px' }
|
|
112
157
|
const textStyle = { fontSize: 13, lineHeight: 1.6, margin: '0 0 10px', opacity: 0.72 }
|
|
113
158
|
const buttonStyle = { height: 32, padding: '0 14px', borderRadius: 8, border: '1px solid rgba(127,127,127,.35)', background: 'transparent', color: 'inherit', cursor: 'pointer', fontSize: 13 }
|
|
@@ -151,6 +196,23 @@ window.__ModuleLoader__.load({
|
|
|
151
196
|
] }),
|
|
152
197
|
],
|
|
153
198
|
}),
|
|
199
|
+
jsx('div', {
|
|
200
|
+
style: { marginTop: 14, padding: '14px 16px', border: '1px solid rgba(127,127,127,.28)', borderRadius: 12, background: 'rgba(127,127,127,.06)' },
|
|
201
|
+
children: [
|
|
202
|
+
jsx('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }, children: [
|
|
203
|
+
jsx('span', { style: { fontSize: 13, fontWeight: 600 }, children: '本地天气' }),
|
|
204
|
+
jsx('button', {
|
|
205
|
+
type: 'button',
|
|
206
|
+
className: 'ns-setting-btn',
|
|
207
|
+
style: { ...buttonStyle, height: 30, padding: '0 12px', flex: 'none' },
|
|
208
|
+
onClick: weatherOn ? closeWeather : askWeather,
|
|
209
|
+
disabled: weatherBusy,
|
|
210
|
+
children: weatherBusy ? '定位中…' : weatherOn ? '关闭天气' : '允许定位',
|
|
211
|
+
}),
|
|
212
|
+
] }),
|
|
213
|
+
jsx('p', { style: { ...textStyle, margin: '8px 0 0', fontSize: 12 }, children: weatherOn ? '已允许定位。面板会获取所在城市天气,显示晴天、云、雨、雪或风。位置只保存在本地。' : '允许定位后,面板会获取所在城市天气,显示晴天、云、雨、雪或风;位置只保存在本地,不上传服务器。' }),
|
|
214
|
+
],
|
|
215
|
+
}),
|
|
154
216
|
message ? jsx('p', { style: { ...textStyle, marginTop: 10, marginBottom: 0 }, children: message }) : null,
|
|
155
217
|
],
|
|
156
218
|
})
|
|
@@ -194,7 +256,7 @@ window.__ModuleLoader__.load({
|
|
|
194
256
|
<section class="dsh-ns-overlay" hidden>
|
|
195
257
|
<button type="button" class="dsh-ns-open">在新标签打开</button>
|
|
196
258
|
<button type="button" class="dsh-ns-close" aria-label="关闭">×</button>
|
|
197
|
-
<iframe title="Office 3D 面板"></iframe>
|
|
259
|
+
<iframe title="Office 3D 面板" allow="geolocation"></iframe>
|
|
198
260
|
</section>
|
|
199
261
|
`
|
|
200
262
|
document.body.append(host)
|
|
@@ -223,7 +285,7 @@ window.__ModuleLoader__.load({
|
|
|
223
285
|
if (event.key === 'Escape') close()
|
|
224
286
|
}
|
|
225
287
|
const open = () => {
|
|
226
|
-
|
|
288
|
+
frame.setAttribute('src', '/agent-office/?open=' + Date.now())
|
|
227
289
|
overlay.hidden = false
|
|
228
290
|
document.addEventListener('keydown', closedByEscape, true)
|
|
229
291
|
}
|
package/lib/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 不依赖 ntfy、不依赖外部进程;面板与 DSH 同源,直接轮询本机 API。
|
|
9
9
|
*/
|
|
10
10
|
import { readFile } from 'node:fs/promises'
|
|
11
|
-
import { createNightShiftStore, fetchDeepSeekBalance } from './state.js'
|
|
11
|
+
import { createNightShiftStore, fetchDeepSeekBalance, fetchWeather } from './state.js'
|
|
12
12
|
|
|
13
13
|
export const name = 'agent-office'
|
|
14
14
|
export const inject = ['webServer']
|
|
@@ -71,6 +71,24 @@ export function apply(ctx, config) {
|
|
|
71
71
|
},
|
|
72
72
|
}), 'agent-office: api')
|
|
73
73
|
|
|
74
|
+
ctx.effect(() => ctx.webServer.register({
|
|
75
|
+
kind: 'exact',
|
|
76
|
+
path: '/agent-office/api/weather',
|
|
77
|
+
handler: async (req, res) => {
|
|
78
|
+
if (!hostTrusted(req)) return json(res, { ok: false, error: '不被信任的 Host' }, 403)
|
|
79
|
+
try {
|
|
80
|
+
const url = new URL(req.url ?? '/', 'http://localhost')
|
|
81
|
+
return json(res, await fetchWeather({
|
|
82
|
+
lat: url.searchParams.get('lat'),
|
|
83
|
+
lon: url.searchParams.get('lon'),
|
|
84
|
+
}))
|
|
85
|
+
} catch (error) {
|
|
86
|
+
ctx.logger.error(error instanceof Error ? error : new Error(String(error)))
|
|
87
|
+
return json(res, { ok: false, error: '天气暂时不可用' }, 500)
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
}), 'agent-office: weather')
|
|
91
|
+
|
|
74
92
|
ctx.effect(() => ctx.webServer.register({
|
|
75
93
|
kind: 'exact',
|
|
76
94
|
path: '/agent-office/api/balance',
|