made-bridge 0.3.0 → 0.3.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/package.json +2 -2
- package/src/i18n.mjs +2 -0
- package/src/installed.mjs +11 -0
- package/src/server.mjs +9 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "made-bridge",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "码得(made)独立语音编程设备的电脑侧桥接器:工作台 API、设备接口、任务调度与插件运行时。一条命令启动,配合 ESP32 固件使用,源码在 github.com/jobop/made。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
"doctor": "node src/server.mjs --doctor",
|
|
48
48
|
"test": "node --test --experimental-test-isolation=none --test-concurrency=1"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
package/src/i18n.mjs
CHANGED
|
@@ -59,6 +59,8 @@ const messages = new Map([
|
|
|
59
59
|
['插件状态检查必须同步返回', 'The plugin status check must return synchronously'],
|
|
60
60
|
['任务已取消', 'Task cancelled'],
|
|
61
61
|
['默认编程助手未安装或已禁用', 'The default assistant is not installed or is disabled'],
|
|
62
|
+
['这是默认编程助手,不能停用;请先在设置里把默认编程助手切换为其他助手', 'This is the default assistant and cannot be disabled; switch the default assistant to another one in settings first'],
|
|
63
|
+
['这是默认编程助手,不能删除;请先在设置里把默认编程助手切换为其他助手', 'This is the default assistant and cannot be removed; switch the default assistant to another one in settings first'],
|
|
62
64
|
['配对请求格式无效', 'Invalid pairing request'],
|
|
63
65
|
['设备名称无效', 'Invalid device name'],
|
|
64
66
|
['电脑配对窗口未开启', 'Pairing is not open on the computer'],
|
package/src/installed.mjs
CHANGED
|
@@ -189,6 +189,12 @@ export async function setPluginEnabled(config, id, enabled) {
|
|
|
189
189
|
if (!PLUGIN_ID.test(id || '')) throw fail('插件不存在');
|
|
190
190
|
const installed = (await listInstalled(config)).plugins;
|
|
191
191
|
if (!installed.some((plugin) => plugin.id === id)) throw fail('插件不存在');
|
|
192
|
+
// 默认编程助手不能停用:loadConfiguredPlugins 会守卫 defaultProvider,
|
|
193
|
+
// 但那里的报错不含操作指引,用户只会看到一句没头没尾的「未安装或已禁用」。
|
|
194
|
+
// 在动作入口给出可操作的前置报错。
|
|
195
|
+
if (!enabled && id === config.defaultProvider) {
|
|
196
|
+
throw fail('这是默认编程助手,不能停用;请先在设置里把默认编程助手切换为其他助手');
|
|
197
|
+
}
|
|
192
198
|
const previous = [...(config.disabledPlugins || [])];
|
|
193
199
|
const next = new Set(previous);
|
|
194
200
|
if (enabled) next.delete(id);
|
|
@@ -246,6 +252,11 @@ export async function removePlugin(config, { id = '', file = '' } = {}) {
|
|
|
246
252
|
target = removalTarget(config, target);
|
|
247
253
|
const described = await describeModule(target, config);
|
|
248
254
|
const ids = described.map((plugin) => plugin.id).filter((item) => PLUGIN_ID.test(item));
|
|
255
|
+
// 同 setPluginEnabled:删默认助手会被 defaultProvider 守卫拦下但报错没有指引,
|
|
256
|
+
// 在动作入口就说清楚(2026-10-18 实锤:删除 codex 后桥接器重启都起不来)。
|
|
257
|
+
if (ids.includes(config.defaultProvider)) {
|
|
258
|
+
throw fail('这是默认编程助手,不能删除;请先在设置里把默认编程助手切换为其他助手');
|
|
259
|
+
}
|
|
249
260
|
const previousDisabled = [...(config.disabledPlugins || [])];
|
|
250
261
|
const previousPlugins = [...(config.plugins || [])];
|
|
251
262
|
config.disabledPlugins = [...new Set([...previousDisabled, ...ids])];
|
package/src/server.mjs
CHANGED
|
@@ -807,7 +807,7 @@ export function createApp(config, { run, transcribe, tunnel: providedTunnel, ena
|
|
|
807
807
|
return;
|
|
808
808
|
}
|
|
809
809
|
if (req.method === 'GET' && url.pathname === '/device/heartbeat') {
|
|
810
|
-
console.error(`[trace] hb upMs=${url.searchParams.get('upMs') ?? '-'} wifiReason=${url.searchParams.get('wifiReason') ?? '-'} wifiDrops=${url.searchParams.get('wifiDrops') ?? '-'} fwStage=${url.searchParams.get('fwStage') ?? '-'} fwWhy=${url.searchParams.get('fwWhy') ?? '-'} fwResult=${url.searchParams.get('fwResult') ?? '-'} fwWritten=${url.searchParams.get('fwWritten') ?? '-'} fwSize=${url.searchParams.get('fwSize') ?? '-'} fwHttp=${url.searchParams.get('fwHttp') ?? '-'} fwTasks=${url.searchParams.get('fwTasks') ?? '-'} fwTask=${url.searchParams.get('fwTask') ?? '-'} ${new Date().toISOString()}`);
|
|
810
|
+
console.error(`[trace] hb upMs=${url.searchParams.get('upMs') ?? '-'} wifiReason=${url.searchParams.get('wifiReason') ?? '-'} wifiDrops=${url.searchParams.get('wifiDrops') ?? '-'} wifiSelf=${url.searchParams.get('wifiSelf') ?? '-'} wifiHist=${url.searchParams.get('wifiHist') ?? '-'} heap=${url.searchParams.get('heap') ?? '-'} minHeap=${url.searchParams.get('minHeap') ?? '-'} psram=${url.searchParams.get('psram') ?? '-'} psramMin=${url.searchParams.get('psramMin') ?? '-'} fwStage=${url.searchParams.get('fwStage') ?? '-'} fwWhy=${url.searchParams.get('fwWhy') ?? '-'} fwResult=${url.searchParams.get('fwResult') ?? '-'} fwWritten=${url.searchParams.get('fwWritten') ?? '-'} fwSize=${url.searchParams.get('fwSize') ?? '-'} fwHttp=${url.searchParams.get('fwHttp') ?? '-'} fwTasks=${url.searchParams.get('fwTasks') ?? '-'} fwTask=${url.searchParams.get('fwTask') ?? '-'} ${new Date().toISOString()}`);
|
|
811
811
|
if (process.env.VIBE_UI_DIAGNOSTICS === '1') {
|
|
812
812
|
const fields = Object.fromEntries(['uiStage', 'uiAge', 'bootAge', 'exit', 'provider', 'displayStage', 'displayCount']
|
|
813
813
|
.map((key) => [key, url.searchParams.get(key)])
|
|
@@ -930,7 +930,11 @@ export function createApp(config, { run, transcribe, tunnel: providedTunnel, ena
|
|
|
930
930
|
projects: config.projects.filter(({ id }) => id === config.defaultProject).map(({ id, label }) => ({ id, label: devicePreview(label, 64) })),
|
|
931
931
|
defaultProject: config.defaultProject,
|
|
932
932
|
voice: { mode: voice.mode, available: voice.available },
|
|
933
|
-
|
|
933
|
+
// 只有固件仓库里存在已导入的当前包(有 sha/版本)才 advertise OTA:
|
|
934
|
+
// 只配了 otaUrl 却没传固件时,设备会拿到无 sha 的 url → 永远 nomark
|
|
935
|
+
// → 弹更新确认 → waiting_confirmation 任务常驻 → 素材 preload 被门禁
|
|
936
|
+
// 拦死(GIF 动作等新文件永远下载不到)。
|
|
937
|
+
firmwareUrl: firmwareSummary.current ? firmwareSummary.otaUrl : '',
|
|
934
938
|
firmwareVersion: firmwareSummary.current?.version || '',
|
|
935
939
|
firmwareSha256: firmwareSummary.current?.sha256 || '',
|
|
936
940
|
firmwareSize: firmwareSummary.current?.size || 0,
|
|
@@ -946,8 +950,10 @@ export function createApp(config, { run, transcribe, tunnel: providedTunnel, ena
|
|
|
946
950
|
return;
|
|
947
951
|
}
|
|
948
952
|
// 形象素材分片拉取:单次 ≤192KB,兼容 USB 帧转发 256KB 上限(docs/SPRITES.md)。
|
|
953
|
+
// 扩展名含 .gif:GIF 动作素材(actions[].gif)也在 files 清单里,路由
|
|
954
|
+
// 只放行 .png 的话设备永远拉不到,playAction 报 gif_unavailable。
|
|
949
955
|
const spriteMatch = req.method === 'GET' &&
|
|
950
|
-
url.pathname.match(/^\/device\/sprite\/([a-z0-9][a-z0-9_-]{0,39})\/([A-Za-z0-9_-]+\.png|manifest\.json)$/);
|
|
956
|
+
url.pathname.match(/^\/device\/sprite\/([a-z0-9][a-z0-9_-]{0,39})\/([A-Za-z0-9_-]+\.(?:png|gif)|manifest\.json)$/);
|
|
951
957
|
if (spriteMatch) {
|
|
952
958
|
const plugin = config.pluginsRuntime.agent(spriteMatch[1]);
|
|
953
959
|
if (spriteMatch[2] === 'manifest.json') {
|