@shgroup/dsh-serenity-hooks 1.26.16 → 1.27.0
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/dsh.plugin.json +1 -1
- package/experiments/autotrajectory/SKILL.md +14 -8
- package/experiments/autotrajectory/scripts/autotrajectory-exp.ts +23 -6
- package/lib/autotrajectory.d.ts +8 -1
- package/lib/ccc.d.ts +50 -0
- package/lib/client.js +421 -1
- package/lib/index.js +397 -823
- package/lib/rolldown-runtime-D7D4PA-g.js +13 -0
- package/lib/skiff-core-BewZSbVH.js +424 -0
- package/lib/skiff-core.d.ts +3 -1
- package/lib/skiff-role-Dw7UKCUm.js +126 -0
- package/lib/weixin-api-b8HLmcPE.js +211 -0
- package/lib/weixin-api.d.ts +119 -0
- package/lib/weixin-bridge.d.ts +56 -0
- package/lib/weixin-route-CHCWBvLZ.js +473 -0
- package/lib/weixin-route.d.ts +61 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -4098,8 +4098,416 @@ Instruction-following style:
|
|
|
4098
4098
|
});
|
|
4099
4099
|
}
|
|
4100
4100
|
//#endregion
|
|
4101
|
+
//#region src/client/WeixinBridgeEditor.tsx
|
|
4102
|
+
/**
|
|
4103
|
+
* WeixinBridgeEditor.tsx — 「微信桥」配置区块(F4c-3,v1.27.0 实验性)
|
|
4104
|
+
*
|
|
4105
|
+
* **CCC 级配置**(S142 用户拍板:dsh 一个进程含多个 CCC,每个 CCC 独立对接微信桥):
|
|
4106
|
+
* - 顶部 **CCC 选择器**(数据源 /serenity/cccs——discoverCccs 复用)——显式选择配置目标,
|
|
4107
|
+
* 不隐式依赖"当前活跃会话"(WebUI 顶层全局,微信桥是配置写入必须显式)
|
|
4108
|
+
* - 选中 CCC 后:总开关 / 账号列表(扫码绑定)/ 路由表(user → role)
|
|
4109
|
+
* - 扫码绑定:POST login-start → qrcode-generator 生成二维码 SVG(复用 v1.24.6 TOTP 同款机制)
|
|
4110
|
+
* → 轮询 /serenity/weixin/login → confirmed 自动写凭据 + 账号元信息
|
|
4111
|
+
*
|
|
4112
|
+
* 全部端点带显式 ccc 参数 + x-serenity-ui 头(服务端校验)。
|
|
4113
|
+
*/
|
|
4114
|
+
/** 生成二维码 SVG(内容 = iLink qrcode_img_content URL——用户微信扫它打开 liteapp 确认页) */
|
|
4115
|
+
function qrSvgFor(content) {
|
|
4116
|
+
const qr = qrcode(0, "M");
|
|
4117
|
+
qr.addData(content);
|
|
4118
|
+
qr.make();
|
|
4119
|
+
return qr.createSvgTag({
|
|
4120
|
+
cellSize: 4,
|
|
4121
|
+
margin: 2,
|
|
4122
|
+
scalable: true
|
|
4123
|
+
});
|
|
4124
|
+
}
|
|
4125
|
+
/** 当前工作区 CCC(默认选择器初值:settings 面板无 workspace 参数——服务端 /serenity/weixin 显式 ccc;
|
|
4126
|
+
* 选择器默认选第一个;用户手选) */
|
|
4127
|
+
function WeixinBridgeEditor() {
|
|
4128
|
+
const [cccs, setCccs] = (0, react.useState)([]);
|
|
4129
|
+
const [selectedRoot, setSelectedRoot] = (0, react.useState)("");
|
|
4130
|
+
const [status, setStatus] = (0, react.useState)(null);
|
|
4131
|
+
const [loadError, setLoadError] = (0, react.useState)(null);
|
|
4132
|
+
const [loginPhase, setLoginPhase] = (0, react.useState)("idle");
|
|
4133
|
+
const [loginQrSvg, setLoginQrSvg] = (0, react.useState)("");
|
|
4134
|
+
const [loginKey, setLoginKey] = (0, react.useState)("");
|
|
4135
|
+
const [loginError, setLoginError] = (0, react.useState)(null);
|
|
4136
|
+
const [loginNotice, setLoginNotice] = (0, react.useState)(null);
|
|
4137
|
+
const [routesDraft, setRoutesDraft] = (0, react.useState)("");
|
|
4138
|
+
(0, react.useEffect)(() => {
|
|
4139
|
+
let alive = true;
|
|
4140
|
+
(async () => {
|
|
4141
|
+
try {
|
|
4142
|
+
const res = await fetch("/serenity/cccs", { headers: { accept: "application/json" } });
|
|
4143
|
+
if (!res.ok) return;
|
|
4144
|
+
const body = await res.json();
|
|
4145
|
+
if (!alive || !Array.isArray(body.cccs)) return;
|
|
4146
|
+
setCccs(body.cccs);
|
|
4147
|
+
if (body.cccs.length > 0) setSelectedRoot((prev) => prev || body.cccs[0].root);
|
|
4148
|
+
} catch {}
|
|
4149
|
+
})();
|
|
4150
|
+
return () => {
|
|
4151
|
+
alive = false;
|
|
4152
|
+
};
|
|
4153
|
+
}, []);
|
|
4154
|
+
(0, react.useEffect)(() => {
|
|
4155
|
+
if (!selectedRoot) return;
|
|
4156
|
+
let alive = true;
|
|
4157
|
+
setStatus(null);
|
|
4158
|
+
setLoadError(null);
|
|
4159
|
+
setLoginPhase("idle");
|
|
4160
|
+
(async () => {
|
|
4161
|
+
try {
|
|
4162
|
+
const res = await fetch(`/serenity/weixin?ccc=${encodeURIComponent(selectedRoot)}`, { headers: { accept: "application/json" } });
|
|
4163
|
+
if (!res.ok) {
|
|
4164
|
+
const body = await res.json().catch(() => null);
|
|
4165
|
+
if (alive) setLoadError(body?.error ?? `HTTP ${res.status}`);
|
|
4166
|
+
return;
|
|
4167
|
+
}
|
|
4168
|
+
const body = await res.json();
|
|
4169
|
+
if (!alive) return;
|
|
4170
|
+
setStatus(body);
|
|
4171
|
+
setRoutesDraft(JSON.stringify(body.routes ?? [], null, 2));
|
|
4172
|
+
} catch {}
|
|
4173
|
+
})();
|
|
4174
|
+
return () => {
|
|
4175
|
+
alive = false;
|
|
4176
|
+
};
|
|
4177
|
+
}, [selectedRoot]);
|
|
4178
|
+
(0, react.useEffect)(() => {
|
|
4179
|
+
if (loginPhase !== "waiting" || !loginKey) return;
|
|
4180
|
+
let alive = true;
|
|
4181
|
+
let timer;
|
|
4182
|
+
const poll = async () => {
|
|
4183
|
+
try {
|
|
4184
|
+
const res = await fetch(`/serenity/weixin/login?key=${encodeURIComponent(loginKey)}`, { headers: { accept: "application/json" } });
|
|
4185
|
+
if (!alive) return;
|
|
4186
|
+
if (!res.ok) {
|
|
4187
|
+
const body = await res.json().catch(() => null);
|
|
4188
|
+
setLoginPhase("error");
|
|
4189
|
+
setLoginError(body?.error ?? `HTTP ${res.status}`);
|
|
4190
|
+
return;
|
|
4191
|
+
}
|
|
4192
|
+
const body = await res.json();
|
|
4193
|
+
if (body.status === "confirmed") {
|
|
4194
|
+
setLoginPhase("confirmed");
|
|
4195
|
+
setLoginNotice(`账号 ${body.accountId} 已绑定 ✓(token 已写入 CCC localstore)`);
|
|
4196
|
+
if (selectedRoot) {
|
|
4197
|
+
const sbody = await (await fetch(`/serenity/weixin?ccc=${encodeURIComponent(selectedRoot)}`, { headers: { accept: "application/json" } })).json().catch(() => null);
|
|
4198
|
+
if (alive && sbody) setStatus(sbody);
|
|
4199
|
+
}
|
|
4200
|
+
return;
|
|
4201
|
+
}
|
|
4202
|
+
if (body.status === "expired" || body.status === "error") {
|
|
4203
|
+
setLoginPhase("error");
|
|
4204
|
+
setLoginError(body.error ?? "二维码已过期,请重新获取");
|
|
4205
|
+
return;
|
|
4206
|
+
}
|
|
4207
|
+
timer = setTimeout(() => void poll(), 1e3);
|
|
4208
|
+
} catch {
|
|
4209
|
+
if (alive) timer = setTimeout(() => void poll(), 1e3);
|
|
4210
|
+
}
|
|
4211
|
+
};
|
|
4212
|
+
poll();
|
|
4213
|
+
return () => {
|
|
4214
|
+
alive = false;
|
|
4215
|
+
if (timer) clearTimeout(timer);
|
|
4216
|
+
};
|
|
4217
|
+
}, [
|
|
4218
|
+
loginPhase,
|
|
4219
|
+
loginKey,
|
|
4220
|
+
selectedRoot
|
|
4221
|
+
]);
|
|
4222
|
+
const startLogin = async () => {
|
|
4223
|
+
if (!selectedRoot || loginPhase === "waiting") return;
|
|
4224
|
+
setLoginPhase("waiting");
|
|
4225
|
+
setLoginError(null);
|
|
4226
|
+
setLoginNotice(null);
|
|
4227
|
+
try {
|
|
4228
|
+
const res = await fetch("/serenity/weixin", {
|
|
4229
|
+
method: "POST",
|
|
4230
|
+
headers: {
|
|
4231
|
+
"content-type": "application/json",
|
|
4232
|
+
"x-serenity-ui": "1"
|
|
4233
|
+
},
|
|
4234
|
+
body: JSON.stringify({
|
|
4235
|
+
action: "login-start",
|
|
4236
|
+
ccc: selectedRoot
|
|
4237
|
+
})
|
|
4238
|
+
});
|
|
4239
|
+
const body = await res.json();
|
|
4240
|
+
if (!res.ok || !body.qrcode_img_content || !body.loginKey) {
|
|
4241
|
+
setLoginPhase("error");
|
|
4242
|
+
setLoginError(body.error ?? `HTTP ${res.status}`);
|
|
4243
|
+
return;
|
|
4244
|
+
}
|
|
4245
|
+
setLoginQrSvg(qrSvgFor(body.qrcode_img_content));
|
|
4246
|
+
setLoginKey(body.loginKey);
|
|
4247
|
+
} catch (err) {
|
|
4248
|
+
setLoginPhase("error");
|
|
4249
|
+
setLoginError(err instanceof Error ? err.message : String(err));
|
|
4250
|
+
}
|
|
4251
|
+
};
|
|
4252
|
+
const removeAccount = async (accountId) => {
|
|
4253
|
+
if (!selectedRoot) return;
|
|
4254
|
+
try {
|
|
4255
|
+
if (!(await fetch("/serenity/weixin", {
|
|
4256
|
+
method: "POST",
|
|
4257
|
+
headers: {
|
|
4258
|
+
"content-type": "application/json",
|
|
4259
|
+
"x-serenity-ui": "1"
|
|
4260
|
+
},
|
|
4261
|
+
body: JSON.stringify({
|
|
4262
|
+
action: "remove-account",
|
|
4263
|
+
ccc: selectedRoot,
|
|
4264
|
+
accountId
|
|
4265
|
+
})
|
|
4266
|
+
})).ok) return;
|
|
4267
|
+
const sbody = await (await fetch(`/serenity/weixin?ccc=${encodeURIComponent(selectedRoot)}`, { headers: { accept: "application/json" } })).json().catch(() => null);
|
|
4268
|
+
if (sbody) setStatus(sbody);
|
|
4269
|
+
} catch {}
|
|
4270
|
+
};
|
|
4271
|
+
const saveRoutes = async () => {
|
|
4272
|
+
if (!selectedRoot) return;
|
|
4273
|
+
let routes;
|
|
4274
|
+
try {
|
|
4275
|
+
routes = JSON.parse(routesDraft);
|
|
4276
|
+
if (!Array.isArray(routes) || routes.some((r) => typeof r?.user !== "string" || typeof r?.role !== "string")) throw new Error("bad");
|
|
4277
|
+
} catch {
|
|
4278
|
+
setLoadError("路由表 JSON 格式错误(期望 [{user, role}, ...])");
|
|
4279
|
+
return;
|
|
4280
|
+
}
|
|
4281
|
+
try {
|
|
4282
|
+
const res = await fetch("/serenity/weixin", {
|
|
4283
|
+
method: "POST",
|
|
4284
|
+
headers: {
|
|
4285
|
+
"content-type": "application/json",
|
|
4286
|
+
"x-serenity-ui": "1"
|
|
4287
|
+
},
|
|
4288
|
+
body: JSON.stringify({
|
|
4289
|
+
action: "save-routes",
|
|
4290
|
+
ccc: selectedRoot,
|
|
4291
|
+
routes
|
|
4292
|
+
})
|
|
4293
|
+
});
|
|
4294
|
+
const body = await res.json().catch(() => null);
|
|
4295
|
+
if (!res.ok) {
|
|
4296
|
+
setLoadError(body?.error ?? `HTTP ${res.status}`);
|
|
4297
|
+
return;
|
|
4298
|
+
}
|
|
4299
|
+
setLoadError(null);
|
|
4300
|
+
const sbody = await (await fetch(`/serenity/weixin?ccc=${encodeURIComponent(selectedRoot)}`, { headers: { accept: "application/json" } })).json().catch(() => null);
|
|
4301
|
+
if (sbody) setStatus(sbody);
|
|
4302
|
+
} catch {}
|
|
4303
|
+
};
|
|
4304
|
+
const toggleEnabled = async (on) => {
|
|
4305
|
+
if (!selectedRoot) return;
|
|
4306
|
+
try {
|
|
4307
|
+
const res = await fetch("/serenity/weixin", {
|
|
4308
|
+
method: "POST",
|
|
4309
|
+
headers: {
|
|
4310
|
+
"content-type": "application/json",
|
|
4311
|
+
"x-serenity-ui": "1"
|
|
4312
|
+
},
|
|
4313
|
+
body: JSON.stringify({
|
|
4314
|
+
action: "set-enabled",
|
|
4315
|
+
ccc: selectedRoot,
|
|
4316
|
+
enabled: on
|
|
4317
|
+
})
|
|
4318
|
+
});
|
|
4319
|
+
const body = await res.json().catch(() => null);
|
|
4320
|
+
if (!res.ok) {
|
|
4321
|
+
setLoadError(body?.error ?? `HTTP ${res.status}`);
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4324
|
+
setLoadError(null);
|
|
4325
|
+
const sbody = await (await fetch(`/serenity/weixin?ccc=${encodeURIComponent(selectedRoot)}`, { headers: { accept: "application/json" } })).json().catch(() => null);
|
|
4326
|
+
if (sbody) setStatus(sbody);
|
|
4327
|
+
} catch {}
|
|
4328
|
+
};
|
|
4329
|
+
const bridgeStateText = (accountId) => {
|
|
4330
|
+
const b = status?.bridge.find((x) => x.accountId === accountId);
|
|
4331
|
+
if (!b) return "未运行";
|
|
4332
|
+
if (b.lastError) return `错误: ${b.lastError}`;
|
|
4333
|
+
if (b.lastPollAt > 0) return `轮询中(${new Date(b.lastPollAt).toLocaleTimeString()})`;
|
|
4334
|
+
return "启动中";
|
|
4335
|
+
};
|
|
4336
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
4337
|
+
className: "ss-rows",
|
|
4338
|
+
children: [
|
|
4339
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4340
|
+
className: "ss-rowCard",
|
|
4341
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4342
|
+
className: "ss-rowText",
|
|
4343
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4344
|
+
className: "ss-rowName",
|
|
4345
|
+
children: "目标 CCC"
|
|
4346
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4347
|
+
className: "ss-rowDesc",
|
|
4348
|
+
children: "选择要配置微信桥的认知容器(显式,非当前活跃会话)"
|
|
4349
|
+
})]
|
|
4350
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4351
|
+
className: "ss-rowControl",
|
|
4352
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4353
|
+
className: "ss-select",
|
|
4354
|
+
value: selectedRoot,
|
|
4355
|
+
onChange: (e) => setSelectedRoot(e.target.value),
|
|
4356
|
+
children: [cccs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4357
|
+
value: "",
|
|
4358
|
+
children: "加载中…"
|
|
4359
|
+
}), cccs.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4360
|
+
value: c.root,
|
|
4361
|
+
children: c.name
|
|
4362
|
+
}, c.root))]
|
|
4363
|
+
})
|
|
4364
|
+
})]
|
|
4365
|
+
}) }),
|
|
4366
|
+
loadError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4367
|
+
className: "ss-rowCard",
|
|
4368
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4369
|
+
className: "ss-rowText",
|
|
4370
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4371
|
+
className: "ss-rowName",
|
|
4372
|
+
children: "提示"
|
|
4373
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4374
|
+
className: "ss-rowDesc",
|
|
4375
|
+
children: loadError
|
|
4376
|
+
})]
|
|
4377
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "ss-rowControl" })]
|
|
4378
|
+
}) }),
|
|
4379
|
+
status && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4380
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4381
|
+
className: "ss-rowCard",
|
|
4382
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4383
|
+
className: "ss-rowText",
|
|
4384
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4385
|
+
className: "ss-rowName",
|
|
4386
|
+
children: "总开关"
|
|
4387
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4388
|
+
className: "ss-rowDesc",
|
|
4389
|
+
children: "启用后该 CCC 的微信桥开始轮询(需先扫码绑定至少一个账号)"
|
|
4390
|
+
})]
|
|
4391
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4392
|
+
className: "ss-rowControl",
|
|
4393
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4394
|
+
className: "ss-switch",
|
|
4395
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4396
|
+
type: "checkbox",
|
|
4397
|
+
checked: status.enabled,
|
|
4398
|
+
onChange: (e) => void toggleEnabled(e.target.checked)
|
|
4399
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "ss-switchTrack" })]
|
|
4400
|
+
})
|
|
4401
|
+
})]
|
|
4402
|
+
}) }),
|
|
4403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4404
|
+
className: "ss-rowCard",
|
|
4405
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4406
|
+
className: "ss-rowText",
|
|
4407
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4408
|
+
className: "ss-rowName",
|
|
4409
|
+
children: "账号"
|
|
4410
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4411
|
+
className: "ss-rowDesc",
|
|
4412
|
+
children: status.accounts.length === 0 ? "未绑定任何微信账号——点击右侧「扫码绑定」" : status.accounts.map((a) => `${a.accountId}${a.name ? ` · ${a.name}` : ""}${a.bound ? " · 已绑定" : " · 未绑定凭据"}(${bridgeStateText(a.accountId)})`).join(";")
|
|
4413
|
+
})]
|
|
4414
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4415
|
+
className: "ss-rowControl",
|
|
4416
|
+
children: status.accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4417
|
+
type: "button",
|
|
4418
|
+
className: "ss-removeBtn",
|
|
4419
|
+
onClick: () => void removeAccount(status.accounts[0].accountId),
|
|
4420
|
+
children: "移除首个"
|
|
4421
|
+
})
|
|
4422
|
+
})]
|
|
4423
|
+
}) }),
|
|
4424
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4425
|
+
className: "ss-rowCard",
|
|
4426
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4427
|
+
className: "ss-rowText",
|
|
4428
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4429
|
+
className: "ss-rowName",
|
|
4430
|
+
children: "扫码绑定微信"
|
|
4431
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4432
|
+
className: "ss-rowDesc",
|
|
4433
|
+
children: [
|
|
4434
|
+
loginPhase === "idle" && "用手机微信「扫一扫」扫描二维码并确认,即绑定该 CCC 的微信桥账号",
|
|
4435
|
+
loginPhase === "waiting" && "二维码已生成(5 分钟内有效)——微信扫一扫 → 手机上确认绑定",
|
|
4436
|
+
loginPhase === "confirmed" && (loginNotice ?? "绑定成功"),
|
|
4437
|
+
loginPhase === "error" && (loginError ?? "绑定失败"),
|
|
4438
|
+
loginPhase === "expired" && "二维码已过期,请重新获取"
|
|
4439
|
+
]
|
|
4440
|
+
})]
|
|
4441
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4442
|
+
className: "ss-rowControl",
|
|
4443
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4444
|
+
type: "button",
|
|
4445
|
+
className: "ss-loginBtn",
|
|
4446
|
+
disabled: loginPhase === "waiting" || !status.enabled === false,
|
|
4447
|
+
onClick: () => void startLogin(),
|
|
4448
|
+
children: loginPhase === "waiting" ? "等待扫码…" : "扫码绑定"
|
|
4449
|
+
})
|
|
4450
|
+
})]
|
|
4451
|
+
}) }),
|
|
4452
|
+
loginPhase === "waiting" && loginQrSvg !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4453
|
+
className: "ss-rowCard ss-qrRow",
|
|
4454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4455
|
+
className: "ss-qrSvg",
|
|
4456
|
+
dangerouslySetInnerHTML: { __html: loginQrSvg }
|
|
4457
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4458
|
+
className: "ss-rowText",
|
|
4459
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4460
|
+
className: "ss-rowDesc",
|
|
4461
|
+
children: "1. 打开手机微信 → 扫一扫 2. 扫描此二维码 3. 在手机上确认绑定"
|
|
4462
|
+
})
|
|
4463
|
+
})]
|
|
4464
|
+
}) }),
|
|
4465
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4466
|
+
className: "ss-rowCard",
|
|
4467
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4468
|
+
className: "ss-rowText",
|
|
4469
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4470
|
+
className: "ss-rowName",
|
|
4471
|
+
children: "路由表"
|
|
4472
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4473
|
+
className: "ss-rowDesc",
|
|
4474
|
+
children: "微信用户 → 该 CCC 的 skiff 角色(`*` = 通配兜底;role 必须已定义)"
|
|
4475
|
+
})]
|
|
4476
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4477
|
+
className: "ss-rowControl",
|
|
4478
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4479
|
+
type: "button",
|
|
4480
|
+
className: "ss-loginBtn",
|
|
4481
|
+
onClick: () => void saveRoutes(),
|
|
4482
|
+
children: "保存路由"
|
|
4483
|
+
})
|
|
4484
|
+
})]
|
|
4485
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
4486
|
+
className: "ss-routesEditor",
|
|
4487
|
+
rows: 5,
|
|
4488
|
+
value: routesDraft,
|
|
4489
|
+
onChange: (e) => setRoutesDraft(e.target.value)
|
|
4490
|
+
})] })
|
|
4491
|
+
] }),
|
|
4492
|
+
!status && !loadError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4493
|
+
className: "ss-rowCard",
|
|
4494
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4495
|
+
className: "ss-rowText",
|
|
4496
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4497
|
+
className: "ss-rowName",
|
|
4498
|
+
children: "加载中…"
|
|
4499
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4500
|
+
className: "ss-rowDesc",
|
|
4501
|
+
children: "读取所选 CCC 的微信桥配置"
|
|
4502
|
+
})]
|
|
4503
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "ss-rowControl" })]
|
|
4504
|
+
}) })
|
|
4505
|
+
]
|
|
4506
|
+
});
|
|
4507
|
+
}
|
|
4508
|
+
//#endregion
|
|
4101
4509
|
//#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SettingsSection
|
|
4102
|
-
const css = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.3));\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary, #eee);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n/* Skiff 调试端口输入(number;关闭时跟随 disabled 半透明) */\n.ss-portInput {\n width: 96px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-fill-l1, rgba(128, 128, 128, 0.08));\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.ss-portInput:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 自主轨迹「立即唤起」按钮(v1.26.14 调试语义;官方 button 语言:primary 弱化) */\n.ss-wakeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-wakeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-wakeBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n";
|
|
4510
|
+
const css = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.3));\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary, #eee);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n/* Skiff 调试端口输入(number;关闭时跟随 disabled 半透明) */\n.ss-portInput {\n width: 96px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-fill-l1, rgba(128, 128, 128, 0.08));\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.ss-portInput:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 自主轨迹「立即唤起」按钮(v1.26.14 调试语义;官方 button 语言:primary 弱化) */\n.ss-wakeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-wakeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-wakeBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* ── 微信桥(F4c-3,v1.27.0 实验性)── */\n\n/* CCC 选择器(下拉) */\n.ss-select {\n max-width: 280px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-fill-l1, rgba(128, 128, 128, 0.08));\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* 扫码绑定按钮(复用 wakeBtn 语言) */\n.ss-loginBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-loginBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-loginBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 移除账号按钮(危险语义:红边框) */\n.ss-removeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-removeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary, #ef4444);\n color: var(--dsw-alias-state-error-primary, #ef4444);\n}\n\n/* 二维码展示行 */\n.ss-qrRow {\n align-items: flex-start;\n}\n\n.ss-qrSvg {\n flex: none;\n width: 160px;\n height: 160px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: #fff; /* 二维码需要白底才能被微信扫出 */\n padding: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.ss-qrSvg svg {\n width: 100%;\n height: 100%;\n}\n\n/* 路由表 JSON 编辑框 */\n.ss-routesEditor {\n width: 100%;\n box-sizing: border-box;\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-fill-l1, rgba(128, 128, 128, 0.08));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-family: var(--ds-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);\n resize: vertical;\n}\n";
|
|
4103
4511
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SettingsSection\"]") === null) {
|
|
4104
4512
|
const tag = document.createElement("style");
|
|
4105
4513
|
tag.setAttribute("data-sp-css", "SettingsSection");
|
|
@@ -4346,6 +4754,13 @@ Instruction-following style:
|
|
|
4346
4754
|
className: "ss-groupTitle",
|
|
4347
4755
|
children: "自主轨迹"
|
|
4348
4756
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTrajectoryStatusBlock, {})]
|
|
4757
|
+
}),
|
|
4758
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4759
|
+
className: "ss-group",
|
|
4760
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
4761
|
+
className: "ss-groupTitle",
|
|
4762
|
+
children: "微信桥"
|
|
4763
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WeixinBridgeEditor, {})]
|
|
4349
4764
|
})
|
|
4350
4765
|
]
|
|
4351
4766
|
});
|
|
@@ -4445,6 +4860,11 @@ Instruction-following style:
|
|
|
4445
4860
|
desc: status.biasProvider,
|
|
4446
4861
|
control: null
|
|
4447
4862
|
}) }),
|
|
4863
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
|
|
4864
|
+
title: "轨迹焦点 (topPrompt)",
|
|
4865
|
+
desc: status.topPrompt ? status.topPrompt : "未定义——CCC 定义 autotrajectory 时应填写本轨迹核心焦点(防多轮唤起焦点丢失)",
|
|
4866
|
+
control: null
|
|
4867
|
+
}) }),
|
|
4448
4868
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
|
|
4449
4869
|
title: "立即唤起",
|
|
4450
4870
|
desc: wakeResult ?? "调试:手动触发一次唤起(跳过窗口/间隔,仍校验配置与偏见脚本)",
|