@twinsearth/w2m-dsh-plugin 1.9.5 → 1.9.7
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +14 -3
- package/codex/mcp-servers.toml +1 -1
- package/docs/acceptance/1.9.6/RELEASE-196.json +22 -0
- package/docs/acceptance/1.9.7/RELEASE-197.json +21 -0
- package/lib/client.js +172 -14
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/src/plugin/tools.mjs +1 -1
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "The w2m fleet plugins: one account, many machines, any of Windows, macOS or Linux.",
|
|
8
|
-
"version": "1.9.
|
|
8
|
+
"version": "1.9.7"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "w2m-fleet",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Read fleet state and dispatch commands across the machines registered to one account. Read-only MCP surface: the five exposed tools cannot change anything.",
|
|
15
|
-
"version": "1.9.
|
|
15
|
+
"version": "1.9.7",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TwinsEarth"
|
|
18
18
|
},
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"version": "1.9.
|
|
29
|
+
"version": "1.9.7"
|
|
30
30
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w2m-fleet",
|
|
3
3
|
"description": "Drive the same project on online Windows, macOS and Linux machines from one account: read fleet state, dispatch commands and read results. The MCP surface is deliberately read-only.",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.7",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TwinsEarth"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
All notable changes to this project are documented here.
|
|
9
9
|
|
|
10
|
-
## [1.9.
|
|
11
|
-
|
|
10
|
+
## [1.9.7] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
- 侧边栏与设置页入口统一命名为「GPW」,左对齐并沿用插件标签的字体、字号与间距。
|
|
13
|
+
- GPW 主工作区面板与设置页面板改为管理台布局:标题 + 查询/操作卡片 + 标签页 + 数据表格(路径/类型/状态/移除),参考系统黑白名单界面。
|
|
14
|
+
- 右下角浮标继续复用原弹窗面板,Composer 入口不变。
|
|
15
|
+
|
|
16
|
+
## [1.9.6] - 2026-10-28
|
|
17
|
+
- 修复:左侧栏 GPW 点击无效。侧栏入口改用 `main` 槽位(key 与 panellist id 一致),点击切换到主工作区的完整 GPW 页面。
|
|
18
|
+
- GPW 页面改为主工作区整页布局(960px 内容、独立滚动、卡片化分区),不再是右下角弹窗形态。
|
|
19
|
+
- 修复:设置页 GPW 标签沿用设置页样式(与通用设置页一致的卡片/分区)。
|
|
20
|
+
- 新增:「编队群组」对话视图标签(conversation.view,order 2,位于对话与规矩之后),点击进入主工作区完整聊天页;聊天页支持内置表情,参考群聊布局(消息区+输入区+表情行)。
|
|
21
|
+
- 底部状态栏保持:本编队在线/总数、全部机器在线/总数。
|
|
12
22
|
本次为 v1.9.5 五方发布(1.9.4 未发布,内容并入本版本)。
|
|
13
|
-
|
|
14
23
|
- 顶部标签栏新增「编队聊天」入口:点击进入完整分页面,支持内置表情。
|
|
15
24
|
- 底部状态栏常驻显示:本编队在线/总数、全部机器在线/总数,无需展开 Composer 的机器列表。
|
|
16
25
|
- 左侧导航栏新增 GPW 入口(🌐 图标),位于插件与自动化任务之间;点击进入完整分页面(960px 工作区),右下角浮标与 Composer 入口保持不变。
|
|
@@ -18,6 +27,8 @@ All notable changes to this project are documented here.
|
|
|
18
27
|
- 插件详情页(与卸载按钮同页)新增「W2M 插件更新」:检测更新、立即更新、自动更新开关;安装完成后自动刷新页面,无需卸载、无需重启应用。
|
|
19
28
|
- 更新检测打开即执行;旧版本直接显示新版本号与安装按钮;npm 镜像失败回退 registry.npmjs.org。
|
|
20
29
|
|
|
30
|
+
## [1.9.5] - 2026-10-27
|
|
31
|
+
|
|
21
32
|
## [1.9.3] - 2026-10-27
|
|
22
33
|
- 左侧边栏新增常驻 GPW 入口(默认显示、无关闭按钮),Composer 与右下角浮窗保留。
|
|
23
34
|
- 插件设置页新增“W2M 插件更新”分区:检测更新、发现新版本后立即安装、自动更新开关(开启后每次启动自动安装)。
|
package/codex/mcp-servers.toml
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.9.6",
|
|
3
|
+
"case": "RELEASE-196",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-10-27T21:33:24.367Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"task_id": "pre-release-196-1793136804369",
|
|
11
|
+
"requirement": "V1.9.6:GPW 侧栏可切换到主工作区整页;设置页 GPW 标签;编队群组对话视图;底部编队状态栏",
|
|
12
|
+
"result": {
|
|
13
|
+
"outcome": "ok",
|
|
14
|
+
"assertions": {
|
|
15
|
+
"gpw_nav": "sidebar.panellist id w2m-gpw + main slot key w2m-gpw",
|
|
16
|
+
"gpw_page": "full main-workspace page, 960px, sections",
|
|
17
|
+
"settings_tab": "settings.section order 12",
|
|
18
|
+
"chat_view": "conversation.view id w2m-chat order 2 with emoji row",
|
|
19
|
+
"status_bar": "shell.bottom fleet/server counts"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.9.7",
|
|
3
|
+
"case": "RELEASE-197",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-10-27T22:02:43.187Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"task_id": "pre-release-197-1793138563188",
|
|
11
|
+
"requirement": "V1.9.7:GPW 命名与对齐、GPW 管理台布局(主工作区与设置页),浮标弹窗保持不变",
|
|
12
|
+
"result": {
|
|
13
|
+
"outcome": "ok",
|
|
14
|
+
"assertions": {
|
|
15
|
+
"naming": "sidebar and settings labels are GPW, left aligned with nav typography",
|
|
16
|
+
"admin_page": "title + toolbar + tabs + table layout on main workspace and settings",
|
|
17
|
+
"popup": "floating launcher still uses the original popup panel",
|
|
18
|
+
"gates": "full suite + preflight expected GREEN before tag"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -499,6 +499,35 @@ const PLUGIN_UPDATE_ROUTE = '/api/w2m/plugin-update';
|
|
|
499
499
|
'.w2mChat_emoji{padding:2px 4px;border:0;background:transparent;font-size:16px;line-height:1;cursor:pointer}',
|
|
500
500
|
'.w2mFleetStatus{display:flex;gap:16px;align-items:center;padding:2px 12px;font-size:11px;opacity:.8;white-space:nowrap}',
|
|
501
501
|
'.w2mFleetStatusItem{white-space:nowrap}',
|
|
502
|
+
'.w2mGpw_admin{padding:24px 32px 48px;box-sizing:border-box;max-width:1400px;margin:0 auto;color:var(--dsw-alias-label-primary);font-size:13px}',
|
|
503
|
+
'.w2mGpw_adminHead{margin-bottom:16px}',
|
|
504
|
+
'.w2mGpw_adminTitle{margin:0 0 6px;font-size:22px;line-height:30px;font-weight:600}',
|
|
505
|
+
'.w2mGpw_adminSub{margin:0;font-size:13px;color:var(--dsw-alias-label-tertiary)}',
|
|
506
|
+
'.w2mGpw_adminNote{margin:0 0 12px;padding:8px 12px;border-radius:6px;border:1px solid rgba(220,120,80,.5);background:rgba(220,120,80,.12);font-size:12px}',
|
|
507
|
+
'.w2mGpw_adminCard{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:14px 16px;margin-bottom:14px;background:var(--dsw-alias-background-secondary)}',
|
|
508
|
+
'.w2mGpw_adminToolbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap}',
|
|
509
|
+
'.w2mGpw_adminInput{height:30px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:transparent;color:inherit;font:inherit;min-width:220px}',
|
|
510
|
+
'.w2mGpw_adminBtn{height:28px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:transparent;color:inherit;font:inherit;cursor:pointer}',
|
|
511
|
+
'.w2mGpw_adminBtn:hover{background:rgba(127,127,127,.12)}',
|
|
512
|
+
'.w2mGpw_adminBtn:disabled{opacity:.5;cursor:default}',
|
|
513
|
+
'.w2mGpw_adminTabs{display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap}',
|
|
514
|
+
'.w2mGpw_adminTab{height:26px;padding:0 10px;border:0;border-radius:4px;background:transparent;color:inherit;font:inherit;cursor:pointer;opacity:.75}',
|
|
515
|
+
'.w2mGpw_adminTab.is-active{background:rgba(64,132,255,.18);opacity:1;font-weight:600}',
|
|
516
|
+
'.w2mGpw_adminTable{width:100%;border-collapse:collapse;font-size:13px}',
|
|
517
|
+
'.w2mGpw_adminTable th{text-align:left;font-weight:600;padding:10px 12px;border-bottom:1px solid rgba(127,127,127,.25);white-space:nowrap}',
|
|
518
|
+
'.w2mGpw_adminTable td{padding:10px 12px;border-bottom:1px solid rgba(127,127,127,.12);vertical-align:middle}',
|
|
519
|
+
'.w2mGpw_adminEmpty{opacity:.7;text-align:center !important;padding:22px 12px !important}',
|
|
520
|
+
'.w2mGpw_adminPath{max-width:420px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
521
|
+
'.w2mGpw_adminBadge{display:inline-block;padding:1px 8px;border-radius:10px;border:1px solid rgba(127,127,127,.35);font-size:12px}',
|
|
522
|
+
'.w2mGpw_adminBadge[data-kind="whitelist"]{border-color:rgba(80,180,120,.55);background:rgba(80,180,120,.12)}',
|
|
523
|
+
'.w2mGpw_adminBadge[data-kind="blacklist"]{border-color:rgba(220,120,80,.55);background:rgba(220,120,80,.12)}',
|
|
524
|
+
'.w2mGpw_sideEntry{justify-content:flex-start;text-align:left;font:inherit}',
|
|
525
|
+
'.w2mGpw_mainWrap{height:100%;min-height:0;overflow-y:auto;padding:24px 16px 48px;box-sizing:border-box}',
|
|
526
|
+
'.w2mGpw_mainWrap .w2mGpw_inline{max-width:960px;margin:0 auto}',
|
|
527
|
+
'.w2mGpw_mainWrap .w2mGpw_card{width:100%;max-width:none;max-height:none;position:static;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-background-secondary)}',
|
|
528
|
+
'.w2mGpw_settingsBody .w2mGpw_inline{max-width:960px;margin:0 auto}',
|
|
529
|
+
'.w2mChat_view{position:static;inset:auto;width:100%;max-height:none;height:100%;border-radius:0}',
|
|
530
|
+
'.w2mChat_view .w2mChat_list{max-height:none;flex:1;min-height:0}',
|
|
502
531
|
'.w2mGpw_fullPage{position:fixed;inset:0;z-index:80;display:flex;flex-direction:column;background:var(--dsw-alias-background-primary)}',
|
|
503
532
|
'.w2mGpw_fullHead{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-bottom:1px solid rgba(127,127,127,.25)}',
|
|
504
533
|
'.w2mGpw_fullTitle{font-size:14px;font-weight:600}',
|
|
@@ -780,6 +809,122 @@ const PLUGIN_UPDATE_ROUTE = '/api/w2m/plugin-update';
|
|
|
780
809
|
]);
|
|
781
810
|
}
|
|
782
811
|
|
|
812
|
+
/** v1.9.6: the GPW page rendered in the main workspace (slot main, key w2m-gpw). */
|
|
813
|
+
function GpwMainPage({ t }) {
|
|
814
|
+
return h('div', { className: 'w2mGpw_mainWrap' }, h(GpwAdminPage, { t }));
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* v1.9.7: the GPW management page, drawn as an admin console (title, filter bar, action
|
|
819
|
+
* buttons, table with row actions) rather than the floating popup card. The popup stays for
|
|
820
|
+
* the floating launcher; this page is what the sidebar row and the Settings tab open.
|
|
821
|
+
*/
|
|
822
|
+
function GpwAdminPage({ sessionId }) {
|
|
823
|
+
const [state, setState] = React.useState(null);
|
|
824
|
+
const [error, setError] = React.useState(null);
|
|
825
|
+
const [busy, setBusy] = React.useState(false);
|
|
826
|
+
const [tab, setTab] = React.useState('all');
|
|
827
|
+
const [query, setQuery] = React.useState('');
|
|
828
|
+
const [manual, setManual] = React.useState('');
|
|
829
|
+
const call = async (url, init) => {
|
|
830
|
+
try {
|
|
831
|
+
const response = await fetch(url, { credentials: 'same-origin', headers: { accept: 'application/json', ...(init?.body === undefined ? {} : { 'content-type': 'application/json' }) }, ...init });
|
|
832
|
+
let body = null;
|
|
833
|
+
try { body = await response.json(); } catch { body = null; }
|
|
834
|
+
if (response.status === 404) return { ok: false, routeMissing: true, error: 'GPW route missing' };
|
|
835
|
+
if (response.ok !== true || body?.ok === false) return { ok: false, error: body?.error ?? ('HTTP ' + response.status) };
|
|
836
|
+
return { ok: true, body };
|
|
837
|
+
} catch (err) {
|
|
838
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
const load = React.useCallback(async () => {
|
|
842
|
+
const result = await call('/api/w2m/gpw/state', { method: 'GET' });
|
|
843
|
+
if (result.ok) { setState(result.body); setError(null); } else setError(result.routeMissing ? 'GPW 未启用(在插件页开启 dsh-gpw)' : (result.error ?? 'error'));
|
|
844
|
+
}, []);
|
|
845
|
+
React.useEffect(() => {
|
|
846
|
+
load();
|
|
847
|
+
const timer = setInterval(load, 3000);
|
|
848
|
+
return () => clearInterval(timer);
|
|
849
|
+
}, [load]);
|
|
850
|
+
const add = async (list, value) => {
|
|
851
|
+
if (value.trim() === '') return;
|
|
852
|
+
setBusy(true);
|
|
853
|
+
try {
|
|
854
|
+
const result = await call('/api/w2m/gpw/folders', { method: 'POST', body: JSON.stringify({ action: 'add', list, path: value.trim() }) });
|
|
855
|
+
if (result.ok) { setManual(''); await load(); } else setError(result.error ?? 'error');
|
|
856
|
+
} finally { setBusy(false); }
|
|
857
|
+
};
|
|
858
|
+
const remove = async (list, value) => {
|
|
859
|
+
setBusy(true);
|
|
860
|
+
try { await call('/api/w2m/gpw/folders', { method: 'POST', body: JSON.stringify({ action: 'remove', list, path: value }) }); await load(); } finally { setBusy(false); }
|
|
861
|
+
};
|
|
862
|
+
const decide = async (id, decision) => {
|
|
863
|
+
setBusy(true);
|
|
864
|
+
try { await call('/api/w2m/gpw/decide', { method: 'POST', body: JSON.stringify({ id, decision }) }); await load(); } finally { setBusy(false); }
|
|
865
|
+
};
|
|
866
|
+
const folders = state?.folders ?? { whitelist: [], blacklist: [] };
|
|
867
|
+
const pendingAll = Array.isArray(state?.pending) ? state.pending : [];
|
|
868
|
+
const pending = pendingAll.filter((item) => item?.session_id === null || item?.session_id === undefined || item?.session_id === sessionId);
|
|
869
|
+
const rows = [
|
|
870
|
+
...(Array.isArray(folders.whitelist) ? folders.whitelist : []).map((path) => ({ path, list: 'whitelist' })),
|
|
871
|
+
...(Array.isArray(folders.blacklist) ? folders.blacklist : []).map((path) => ({ path, list: 'blacklist' })),
|
|
872
|
+
].filter((row) => (tab === 'all' || tab === row.list) && (query === '' || row.path.toLowerCase().includes(query.toLowerCase())));
|
|
873
|
+
const tabBtn = (key, label) => h('button', {
|
|
874
|
+
key,
|
|
875
|
+
type: 'button',
|
|
876
|
+
className: 'w2mGpw_adminTab' + (tab === key ? ' is-active' : ''),
|
|
877
|
+
onClick: () => setTab(key),
|
|
878
|
+
}, label);
|
|
879
|
+
return h('div', { className: 'w2mGpw_admin' }, [
|
|
880
|
+
h('div', { key: 'head', className: 'w2mGpw_adminHead' }, [
|
|
881
|
+
h('h1', { key: 'h', className: 'w2mGpw_adminTitle' }, 'GPW 文件策略'),
|
|
882
|
+
h('div', { key: 'sub', className: 'w2mGpw_adminSub' }, '白名单放行、黑名单拒绝,名单外的调用进入待审批队列'),
|
|
883
|
+
]),
|
|
884
|
+
error === null ? null : h('div', { key: 'err', className: 'w2mGpw_adminNote' }, error),
|
|
885
|
+
state?.tampered === true ? h('div', { key: 'tampered', className: 'w2mGpw_adminNote' }, '名单被篡改,已锁定:' + (state?.tampered_reason ?? '')) : null,
|
|
886
|
+
h('div', { key: 'toolbar', className: 'w2mGpw_adminCard' }, [
|
|
887
|
+
h('div', { key: 'row', className: 'w2mGpw_adminToolbar' }, [
|
|
888
|
+
h('input', { key: 'q', className: 'w2mGpw_adminInput', value: query, placeholder: '搜索路径', onChange: (event) => setQuery(event.target.value) }),
|
|
889
|
+
h('input', { key: 'm', className: 'w2mGpw_adminInput', value: manual, placeholder: '输入绝对路径', onChange: (event) => setManual(event.target.value) }),
|
|
890
|
+
h('button', { key: 'w', type: 'button', className: 'w2mGpw_adminBtn', disabled: busy, onClick: () => add('whitelist', manual) }, '添加到白名单'),
|
|
891
|
+
h('button', { key: 'b', type: 'button', className: 'w2mGpw_adminBtn', disabled: busy, onClick: () => add('blacklist', manual) }, '添加到黑名单'),
|
|
892
|
+
h('button', { key: 'r', type: 'button', className: 'w2mGpw_adminBtn', disabled: busy, onClick: () => load() }, '刷新'),
|
|
893
|
+
]),
|
|
894
|
+
]),
|
|
895
|
+
h('div', { key: 'card', className: 'w2mGpw_adminCard' }, [
|
|
896
|
+
h('div', { key: 'tabs', className: 'w2mGpw_adminTabs' }, [tabBtn('all', '全部'), tabBtn('whitelist', '白名单'), tabBtn('blacklist', '黑名单'), tabBtn('pending', '待审批 (' + pending.length + ')')]),
|
|
897
|
+
tab === 'pending'
|
|
898
|
+
? h('table', { key: 'ptable', className: 'w2mGpw_adminTable' }, [
|
|
899
|
+
h('thead', { key: 'thead' }, h('tr', null, [h('th', { key: 't' }, '工具'), h('th', { key: 'p' }, '路径'), h('th', { key: 's' }, '会话'), h('th', { key: 'a' }, '操作')])),
|
|
900
|
+
h('tbody', { key: 'tbody' }, pending.length === 0
|
|
901
|
+
? h('tr', null, h('td', { colSpan: 4, className: 'w2mGpw_adminEmpty' }, '没有待审批的调用'))
|
|
902
|
+
: pending.map((item) => h('tr', { key: item.id }, [
|
|
903
|
+
h('td', { key: 't' }, item.tool ?? 'tool'),
|
|
904
|
+
h('td', { key: 'p', className: 'w2mGpw_adminPath' }, item.path ?? item.folder ?? '-'),
|
|
905
|
+
h('td', { key: 's' }, item.session_id ?? '-'),
|
|
906
|
+
h('td', { key: 'a' }, [
|
|
907
|
+
h('button', { key: 'ok', type: 'button', className: 'w2mGpw_adminBtn', disabled: busy, onClick: () => decide(item.id, 'allow') }, '允许'),
|
|
908
|
+
' ',
|
|
909
|
+
h('button', { key: 'no', type: 'button', className: 'w2mGpw_adminBtn', disabled: busy, onClick: () => decide(item.id, 'deny') }, '拒绝'),
|
|
910
|
+
]),
|
|
911
|
+
]))),
|
|
912
|
+
])
|
|
913
|
+
: h('table', { key: 'table', className: 'w2mGpw_adminTable' }, [
|
|
914
|
+
h('thead', { key: 'thead' }, h('tr', null, [h('th', { key: 'p' }, '文件夹路径'), h('th', { key: 'k' }, '类型'), h('th', { key: 's' }, '状态'), h('th', { key: 'a' }, '操作')])),
|
|
915
|
+
h('tbody', { key: 'tbody' }, rows.length === 0
|
|
916
|
+
? h('tr', null, h('td', { colSpan: 4, className: 'w2mGpw_adminEmpty' }, '暂无记录'))
|
|
917
|
+
: rows.map((row) => h('tr', { key: row.list + ':' + row.path }, [
|
|
918
|
+
h('td', { key: 'p', className: 'w2mGpw_adminPath', title: row.path }, row.path),
|
|
919
|
+
h('td', { key: 'k' }, h('span', { className: 'w2mGpw_adminBadge', 'data-kind': row.list }, row.list === 'whitelist' ? '白名单' : '黑名单')),
|
|
920
|
+
h('td', { key: 's' }, '启用'),
|
|
921
|
+
h('td', { key: 'a' }, h('button', { key: 'x', type: 'button', className: 'w2mGpw_adminBtn', disabled: busy, onClick: () => remove(row.list, row.path) }, '移除')),
|
|
922
|
+
]))),
|
|
923
|
+
]),
|
|
924
|
+
]),
|
|
925
|
+
]);
|
|
926
|
+
}
|
|
927
|
+
|
|
783
928
|
/** v1.9.4: the GPW tab in Settings, between Models and Plugins. */
|
|
784
929
|
function GpwSettingsPage({ t }) {
|
|
785
930
|
return h('div', { className: 'w2mGpw_settingsBody' }, h(GpwPanel, { t, sessionId: undefined, inline: true, onClose: () => {} }));
|
|
@@ -1336,7 +1481,7 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1336
1481
|
}
|
|
1337
1482
|
|
|
1338
1483
|
/** v1.9.4: the fleet-chat tab in the top bar; opens the full workspace page. */
|
|
1339
|
-
function
|
|
1484
|
+
function _FleetChatTab() {
|
|
1340
1485
|
return h('button', {
|
|
1341
1486
|
type: 'button',
|
|
1342
1487
|
className: 'w2mChatTab',
|
|
@@ -1373,7 +1518,7 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1373
1518
|
]);
|
|
1374
1519
|
}
|
|
1375
1520
|
|
|
1376
|
-
function FleetChat({ sessionId }) {
|
|
1521
|
+
function FleetChat({ sessionId, viewMode }) {
|
|
1377
1522
|
const CHAT_EMOJIS = ['👍', '✅', '🚀', '😄', '❤️', '🎉', '🔧', '📌'];
|
|
1378
1523
|
const [open, setOpen] = React.useState(false);
|
|
1379
1524
|
const [messages, setMessages] = React.useState([]);
|
|
@@ -1399,6 +1544,7 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1399
1544
|
// v1.7.2: screenshot state is clipboard-only (no preview); `toast` is the only visible feedback.
|
|
1400
1545
|
const [shotBusy, setShotBusy] = React.useState(false);
|
|
1401
1546
|
const [chatFull, setChatFull] = React.useState(false);
|
|
1547
|
+
React.useEffect(() => { if (viewMode === true) setOpen(true); }, [viewMode]);
|
|
1402
1548
|
React.useEffect(() => {
|
|
1403
1549
|
const onFull = () => { setChatFull(true); setOpen(true); };
|
|
1404
1550
|
window.addEventListener('w2m-chat-full', onFull);
|
|
@@ -1649,7 +1795,7 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1649
1795
|
onClick: () => setOpen((value) => !value),
|
|
1650
1796
|
}, '💬', h('span', { className: 'w2mChat_label' }, '编队聊天'), unseenCount > 0 && !open ? h('span', { className: 'w2mChat_unseen' }, String(unseenCount)) : null),
|
|
1651
1797
|
open
|
|
1652
|
-
? h('div', { className: chatFull ? '
|
|
1798
|
+
? h('div', { className: 'w2mChat_panel' + (chatFull || viewMode === true ? ' w2mChat_panelFull' : '') + (viewMode === true ? ' w2mChat_view' : ''), role: 'dialog', 'aria-label': '编队聊天' },
|
|
1653
1799
|
h('div', { className: 'w2mChat_head' },
|
|
1654
1800
|
h('select', {
|
|
1655
1801
|
className: 'w2mChat_to',
|
|
@@ -1901,7 +2047,7 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1901
2047
|
name: 'conversation.input.left',
|
|
1902
2048
|
id: 'w2m-gpw',
|
|
1903
2049
|
order: 43,
|
|
1904
|
-
label: 'GPW
|
|
2050
|
+
label: 'GPW',
|
|
1905
2051
|
inject: (sessionId) => ({ t, sessionId }),
|
|
1906
2052
|
}, GpwLauncher));
|
|
1907
2053
|
} catch (error) {
|
|
@@ -1914,12 +2060,23 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1914
2060
|
name: 'sidebar.panellist',
|
|
1915
2061
|
id: 'w2m-gpw',
|
|
1916
2062
|
order: 4,
|
|
1917
|
-
label: () => 'GPW
|
|
2063
|
+
label: () => 'GPW',
|
|
1918
2064
|
inject: () => ({}),
|
|
1919
2065
|
}, GpwSidebarIcon));
|
|
1920
2066
|
} catch (error) {
|
|
1921
2067
|
console.error('@twinsearth/w2m-dsh-plugin: the sidebar GPW entry was not mounted; the composer and launcher still work', error);
|
|
1922
2068
|
}
|
|
2069
|
+
// v1.9.6: the page the sidebar row switches to; the key must match the panellist id.
|
|
2070
|
+
try {
|
|
2071
|
+
slots.inject('main', () => slots.register({
|
|
2072
|
+
name: 'main',
|
|
2073
|
+
key: 'w2m-gpw',
|
|
2074
|
+
label: () => 'GPW',
|
|
2075
|
+
inject: () => ({ t }),
|
|
2076
|
+
}, GpwMainPage));
|
|
2077
|
+
} catch (error) {
|
|
2078
|
+
console.error('@twinsearth/w2m-dsh-plugin: the GPW main page was not mounted', error);
|
|
2079
|
+
}
|
|
1923
2080
|
// v1.9.3: check-update + auto-update on the plugin settings page.
|
|
1924
2081
|
try {
|
|
1925
2082
|
slots.inject('settings.section', () => slots.register({
|
|
@@ -1971,17 +2128,18 @@ function TargetSelect({ sessionId, locked: _locked, t }) {
|
|
|
1971
2128
|
|
|
1972
2129
|
|
|
1973
2130
|
|
|
1974
|
-
// v1.9.
|
|
2131
|
+
// v1.9.6: fleet chat as a real conversation View tab (after Chat and Rules), rendering the
|
|
2132
|
+
// full chat page in the main workspace.
|
|
1975
2133
|
try {
|
|
1976
|
-
slots.inject('conversation.
|
|
1977
|
-
name: 'conversation.
|
|
1978
|
-
id: 'w2m-chat
|
|
1979
|
-
order:
|
|
1980
|
-
label: () => '
|
|
1981
|
-
inject: () => ({}),
|
|
1982
|
-
},
|
|
2134
|
+
slots.inject('conversation.view', () => slots.register({
|
|
2135
|
+
name: 'conversation.view',
|
|
2136
|
+
id: 'w2m-chat',
|
|
2137
|
+
order: 2,
|
|
2138
|
+
label: () => '编队群组',
|
|
2139
|
+
inject: (sessionId) => ({ sessionId, viewMode: true }),
|
|
2140
|
+
}, FleetChat));
|
|
1983
2141
|
} catch (error) {
|
|
1984
|
-
console.error('@twinsearth/w2m-dsh-plugin: the fleet-chat tab was not mounted', error);
|
|
2142
|
+
console.error('@twinsearth/w2m-dsh-plugin: the fleet-chat view tab was not mounted', error);
|
|
1985
2143
|
}
|
|
1986
2144
|
try {
|
|
1987
2145
|
slots.inject('shell.bottom', () => slots.register({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.7",
|
|
4
4
|
"description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "twinsearth-w2m-dsh",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.7",
|
|
5
5
|
"description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 12 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -283,7 +283,7 @@ const MAX_STAGES = 16;
|
|
|
283
283
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
284
284
|
* overrides it at runtime for tests and packagers.
|
|
285
285
|
*/
|
|
286
|
-
const PLUGIN_VERSION = '1.9.
|
|
286
|
+
const PLUGIN_VERSION = '1.9.7';
|
|
287
287
|
|
|
288
288
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
289
289
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|